Mercurial > genshi > genshi-test
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 |