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()