[Zope-Checkins] CVS: Zope/lib/python/TreeDisplay -
TreeTag.py:1.53.6.1
Tres Seaver
tseaver at zope.com
Thu Jan 8 15:42:05 EST 2004
Update of /cvs-repository/Zope/lib/python/TreeDisplay
In directory cvs.zope.org:/tmp/cvs-serv2462/lib/python/TreeDisplay
Modified Files:
Tag: Zope-2_6-branch
TreeTag.py
Log Message:
- The dtml-tree tag used an "eval" of user-supplied data; its
efforts to prevent abuse were ineffective.
=== Zope/lib/python/TreeDisplay/TreeTag.py 1.53 => 1.53.6.1 ===
--- Zope/lib/python/TreeDisplay/TreeTag.py:1.53 Wed Aug 14 18:02:25 2002
+++ Zope/lib/python/TreeDisplay/TreeTag.py Thu Jan 8 15:42:04 2004
@@ -18,9 +18,10 @@
from DocumentTemplate.DT_Util import *
from DocumentTemplate.DT_String import String
+from cPickle import dumps
from string import translate
from urllib import quote, unquote
-from zlib import compress, decompress
+from zlib import compress, decompressobj
from binascii import b2a_base64, a2b_base64
import re
@@ -103,15 +104,15 @@
['eagle'], # eagle is open
['eagle'], ['jeep', [1983, 1985]] # eagle, jeep, 1983 jeep and 1985 jeep
- where the items are object ids. The state will be converted to a
+ where the items are object ids. The state will be pickled to a
compressed and base64ed string that gets unencoded, uncompressed,
- and evaluated on the other side.
+ and unpickled on the other side.
Note that ids used in state need not be connected to urls, since
state manipulation is internal to rendering logic.
- Note that to make eval safe, we do not allow the character '*' in
- the state.
+ Note that to make unpickling safe, we use the MiniPickle module,
+ that only creates safe objects
"""
data=[]
@@ -323,7 +324,7 @@
####################################
# Mostly inline encode_seq for speed
- s=compress(str(diff))
+ s=compress(dumps(diff,1))
if len(s) > 57: s=encode_str(s)
else:
s=b2a_base64(s)[:-1]
@@ -514,7 +515,7 @@
def encode_seq(state):
"Convert a sequence to an encoded string"
- state=compress(str(state))
+ state=compress(dumps(state))
l=len(state)
if l > 57:
@@ -574,10 +575,20 @@
state=a2b_base64(state)
state=decompress(state)
- if state.find('*') >= 0: raise 'Illegal State', state
- try: return list(eval(state,{'__builtins__':{}}))
+ try: return list(MiniUnpickler(StringIO(state)).load())
except: return []
+def decompress(input,max_size=10240):
+ # This sillyness can go away in python 2.2
+ d = decompressobj()
+ output = ''
+ while input:
+ fragment_size = max(1,(max_size-len(output))/1000)
+ fragment,input = input[:fragment_size],input[fragment_size:]
+ output += d.decompress(fragment)
+ if len(output)>max_size:
+ raise ValueError('Compressed input too large')
+ return output+d.flush()
def tpStateLevel(state, level=0):
for sub in state:
@@ -624,3 +635,76 @@
#icoSpace='<IMG SRC="Blank_icon" BORDER="0">'
#icoPlus ='<IMG SRC="Plus_icon" BORDER="0">'
#icoMinus='<IMG SRC="Minus_icon" BORDER="0">'
+
+
+
+
+
+###############################################################################
+## Everthing below here should go in a MiniPickle.py module, but keeping it
+## internal makes an easier patch
+
+
+import pickle
+from cStringIO import StringIO
+
+
+
+class MiniUnpickler(pickle.Unpickler):
+ """An unpickler that can only handle simple types.
+ """
+ def refuse_to_unpickle(self):
+ raise pickle.UnpicklingError, 'Refused'
+
+ dispatch = pickle.Unpickler.dispatch.copy()
+
+ for k,v in dispatch.items():
+ if k=='' or k in '().012FGIJKLMNTUVX]adeghjlpqrstu}':
+ # This key is necessary and safe, so leave it in the map
+ pass
+ else:
+ dispatch[k] = refuse_to_unpickle
+ # Anything unnecessary is banned, but here is some logic to explain why
+ if k in [pickle.GLOBAL, pickle.OBJ, pickle.INST, pickle.REDUCE, pickle.BUILD]:
+ # These are definite security holes
+ pass
+ elif k in [pickle.PERSID, pickle.BINPERSID]:
+ # These are just unnecessary
+ pass
+ elif k in [pickle.STRING]:
+ # This one is controversial: A string is harmlessm, but the
+ # implementation of pickle leaks memory (strings may be interned)
+ # The problem can be avoided by using binary pickles.
+ pass
+
+ del k
+ del v
+
+def _should_succeed(x,binary=1):
+ if x != MiniUnpickler(StringIO(pickle.dumps(x,binary))).load():
+ raise ValueError(x)
+
+def _should_fail(x,binary=1):
+ try:
+ MiniUnpickler(StringIO(pickle.dumps(x,binary))).load()
+ raise ValueError(x)
+ except pickle.UnpicklingError, e:
+ if e[0]!='Refused': raise ValueError(x)
+
+class _junk_class: pass
+
+def _test():
+ _should_fail('hello',0)
+ _should_succeed('hello')
+ _should_succeed(1)
+ _should_succeed(1L)
+ _should_succeed(1.0)
+ _should_succeed((1,2,3))
+ _should_succeed([1,2,3])
+ _should_succeed({1:2,3:4})
+ _should_fail(open)
+ _should_fail(_junk_class)
+ _should_fail(_junk_class())
+
+# Test MiniPickle on every import
+_test()
More information about the Zope-Checkins
mailing list