comparison genshi/template/eval.py @ 647:5af131b37ab4 experimental-sandboxed

restricted is the new secure
author aronacher
date Wed, 26 Sep 2007 17:56:41 +0000
parents b6cdfcb37496
children 20de9eea3b5f
comparison
equal deleted inserted replaced
646:b6cdfcb37496 647:5af131b37ab4
35 __docformat__ = 'restructuredtext en' 35 __docformat__ = 'restructuredtext en'
36 36
37 37
38 class Code(object): 38 class Code(object):
39 """Abstract base class for the `Expression` and `Suite` classes.""" 39 """Abstract base class for the `Expression` and `Suite` classes."""
40 __slots__ = ['source', 'code', 'ast', 'secure', '_globals'] 40 __slots__ = ['source', 'code', 'ast', 'restricted', '_globals']
41 41
42 def __init__(self, source, filename=None, lineno=-1, lookup='strict', 42 def __init__(self, source, filename=None, lineno=-1, lookup='strict',
43 xform=None, secure=False): 43 xform=None, restricted=False):
44 """Create the code object, either from a string, or from an AST node. 44 """Create the code object, either from a string, or from an AST node.
45 45
46 :param source: either a string containing the source code, or an AST 46 :param source: either a string containing the source code, or an AST
47 node 47 node
48 :param filename: the (preferably absolute) name of the file containing 48 :param filename: the (preferably absolute) name of the file containing
52 up in the context; can be either "strict" (the default), 52 up in the context; can be either "strict" (the default),
53 "lenient", or a custom lookup class 53 "lenient", or a custom lookup class
54 :param xform: the AST transformer that should be applied to the code; 54 :param xform: the AST transformer that should be applied to the code;
55 if `None`, the appropriate transformation is chosen 55 if `None`, the appropriate transformation is chosen
56 depending on the mode 56 depending on the mode
57 :param secure: If security features should be enabled. 57 :param restricted: If security features should be enabled.
58 """ 58 """
59 if isinstance(source, basestring): 59 if isinstance(source, basestring):
60 self.source = source 60 self.source = source
61 node = _parse(source, mode=self.mode) 61 node = _parse(source, mode=self.mode)
62 else: 62 else:
69 node = ast.Module(None, source) 69 node = ast.Module(None, source)
70 70
71 self.ast = node 71 self.ast = node
72 self.code = _compile(node, self.source, mode=self.mode, 72 self.code = _compile(node, self.source, mode=self.mode,
73 filename=filename, lineno=lineno, xform=xform, 73 filename=filename, lineno=lineno, xform=xform,
74 secure=secure) 74 restricted=restricted)
75 if lookup is None: 75 if lookup is None:
76 lookup = LenientLookup 76 lookup = LenientLookup
77 elif isinstance(lookup, basestring): 77 elif isinstance(lookup, basestring):
78 lookup = { 78 lookup = {
79 'lenient': LenientLookup, 79 'lenient': LenientLookup,
80 'strict': StrictLookup 80 'strict': StrictLookup
81 }[lookup] 81 }[lookup]
82 if secure: 82 if restricted:
83 lookup = SecurityLookupWrapper(lookup) 83 lookup = RestrictedLookupWrapper(lookup)
84 self.secure = secure 84 self.restricted = restricted
85 self._globals = lookup.globals() 85 self._globals = lookup.globals()
86 86
87 def __eq__(self, other): 87 def __eq__(self, other):
88 return (type(other) == type(self)) and (self.code == other.code) 88 return (type(other) == type(self)) and (self.code == other.code)
89 89
372 __traceback_hide__ = True 372 __traceback_hide__ = True
373 raise UndefinedError(key, owner=owner) 373 raise UndefinedError(key, owner=owner)
374 undefined = classmethod(undefined) 374 undefined = classmethod(undefined)
375 375
376 376
377 class SecurityLookupWrapper(object): 377 class RestrictedLookupWrapper(object):
378 """ 378 """
379 Special class that wraps a lookup so that insecure accesses result 379 Special class that wraps a lookup so that insecure accesses result
380 in undefined. Additionally the globals are secured. 380 in undefined. Additionally the globals are secured.
381 """ 381 """
382 382
446 if isinstance(source, unicode): 446 if isinstance(source, unicode):
447 source = '\xef\xbb\xbf' + source.encode('utf-8') 447 source = '\xef\xbb\xbf' + source.encode('utf-8')
448 return parse(source, mode) 448 return parse(source, mode)
449 449
450 def _compile(node, source=None, mode='eval', filename=None, lineno=-1, 450 def _compile(node, source=None, mode='eval', filename=None, lineno=-1,
451 xform=None, secure=False): 451 xform=None, restricted=False):
452 if xform is None: 452 if xform is None:
453 xform = {'eval': ExpressionASTTransformer}.get(mode, 453 xform = {'eval': ExpressionASTTransformer}.get(mode,
454 TemplateASTTransformer) 454 TemplateASTTransformer)
455 tree = xform(secure).visit(node) 455 tree = xform(restricted).visit(node)
456 if isinstance(filename, unicode): 456 if isinstance(filename, unicode):
457 # unicode file names not allowed for code objects 457 # unicode file names not allowed for code objects
458 filename = filename.encode('utf-8', 'replace') 458 filename = filename.encode('utf-8', 'replace')
459 elif not filename: 459 elif not filename:
460 filename = '<string>' 460 filename = '<string>'
485 BUILTINS = __builtin__.__dict__.copy() 485 BUILTINS = __builtin__.__dict__.copy()
486 BUILTINS.update({'Markup': Markup, 'Undefined': Undefined}) 486 BUILTINS.update({'Markup': Markup, 'Undefined': Undefined})
487 487
488 # XXX: if we weaken the rule for global name resultion so that leading 488 # XXX: if we weaken the rule for global name resultion so that leading
489 # underscores are valid we have to add __import__ here. 489 # underscores are valid we have to add __import__ here.
490 UNSAFE_NAMES = ['file', 'open', 'eval', 'locals', 'globals', 'vars', 490 UNSAFE_NAMES = ['file', 'open', 'eval', 'locals', 'globals', 'vars', 'buffer',
491 'help', 'quit', 'exit', 'input', 'raw_input', 'setattr', 491 'help', 'quit', 'exit', 'input', 'raw_input', 'setattr',
492 'getattr', 'delattr', 'reload', 'compile', 'type'] 492 'getattr', 'delattr', 'reload', 'compile', 'type', 'intern']
493 493
494 # XXX: provide a secure range function
495 SECURE_BUILTINS = BUILTINS.copy() 494 SECURE_BUILTINS = BUILTINS.copy()
496 for _unsafe_name in UNSAFE_NAMES: 495 for _unsafe_name in UNSAFE_NAMES:
497 del SECURE_BUILTINS[_unsafe_name] 496 SECURE_BUILTINS.pop(_unsafe_name, None)
498 del _unsafe_name 497 del _unsafe_name
499 SECURE_BUILTINS['range'] = safe_range 498 SECURE_BUILTINS['range'] = safe_range
500 499
501 CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis']) 500 CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
502 501
506 505
507 Every visitor method can be overridden to return an AST node that has been 506 Every visitor method can be overridden to return an AST node that has been
508 altered or replaced in some way. 507 altered or replaced in some way.
509 """ 508 """
510 509
511 def __init__(self, secure): 510 def __init__(self, restricted):
512 self.secure = secure 511 self.restricted = restricted
513 512
514 def visit(self, node): 513 def visit(self, node):
515 if node is None: 514 if node is None:
516 return None 515 return None
517 if type(node) is tuple: 516 if type(node) is tuple:
742 class TemplateASTTransformer(ASTTransformer): 741 class TemplateASTTransformer(ASTTransformer):
743 """Concrete AST transformer that implements the AST transformations needed 742 """Concrete AST transformer that implements the AST transformations needed
744 for code embedded in templates. 743 for code embedded in templates.
745 """ 744 """
746 745
747 def __init__(self, secure): 746 def __init__(self, restricted):
748 ASTTransformer.__init__(self, secure) 747 ASTTransformer.__init__(self, restricted)
749 self.locals = [CONSTANTS] 748 self.locals = [CONSTANTS]
750 749
751 def visitConst(self, node): 750 def visitConst(self, node):
752 if isinstance(node.value, str): 751 if isinstance(node.value, str):
753 try: # If the string is ASCII, return a `str` object 752 try: # If the string is ASCII, return a `str` object
Copyright (C) 2012-2017 Edgewall Software