Index: TreeTag.py
===================================================================
RCS file: /cvs-repository/Zope/lib/python/TreeDisplay/TreeTag.py,v
retrieving revision 1.53
diff -c -2 -r1.53 TreeTag.py
*** TreeTag.py 14 Aug 2002 22:02:25 -0000 1.53
--- TreeTag.py 16 Dec 2002 20:44:48 -0000
***************
*** 19,25 ****
from DocumentTemplate.DT_String import String
from string import translate
from urllib import quote, unquote
! from zlib import compress, decompress
from binascii import b2a_base64, a2b_base64
import re
--- 19,26 ----
from DocumentTemplate.DT_String import String
+ from cPickle import dumps
from string import translate
from urllib import quote, unquote
! from zlib import compress, decompressobj
from binascii import b2a_base64, a2b_base64
import re
***************
*** 104,116 ****
['eagle'], ['jeep', [1983, 1985]] # eagle, jeep, 1983 jeep and 1985 jeep
! where the items are object ids. The state will be converted to a
compressed and base64ed string that gets unencoded, uncompressed,
! and evaluated 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.
"""
--- 105,117 ----
['eagle'], ['jeep', [1983, 1985]] # eagle, jeep, 1983 jeep and 1985 jeep
! where the items are object ids. The state will be pickled to a
compressed and base64ed string that gets unencoded, uncompressed,
! 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 unpickling safe, we use the MiniPickle module,
! that only creates safe objects
"""
***************
*** 324,328 ****
####################################
# Mostly inline encode_seq for speed
! s=compress(str(diff))
if len(s) > 57: s=encode_str(s)
else:
--- 325,329 ----
####################################
# Mostly inline encode_seq for speed
! s=compress(dumps(diff,1))
if len(s) > 57: s=encode_str(s)
else:
***************
*** 515,519 ****
def encode_seq(state):
"Convert a sequence to an encoded string"
! state=compress(str(state))
l=len(state)
--- 516,520 ----
def encode_seq(state):
"Convert a sequence to an encoded string"
! state=compress(dumps(state))
l=len(state)
***************
*** 575,582 ****
state=decompress(state)
! if state.find('*') >= 0: raise 'Illegal State', state
! try: return list(eval(state,{'__builtins__':{}}))
except: return []
def tpStateLevel(state, level=0):
--- 576,593 ----
state=decompress(state)
! 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):
***************
*** 625,626 ****
--- 636,716 ----
#icoPlus =''
#icoMinus=''
+
+
+
+
+
+ ###############################################################################
+ ## Everthing below here should go in a MiniPickle.py module, but keeping it
+ ## internal makes an easier patch
+
+
+ import pickle
+ from cStringIO import StringIO
+
+
+ if pickle.format_version!="1.3":
+ # Maybe the format changed, and opened a security hole
+ raise 'Invalid pickle version'
+
+
+ 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
+ else:
+ # Someone added a key but did not increment the version.
+ raise 'Invalid pickle key',k
+ 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()