# HG changeset patch # User aronacher # Date 1190818305 0 # Node ID b82dfc7623dc8c59e37a212244fbe959a75c77a3 # Parent 25d0368491acfacf2fdcf178b9f73703b9ebf5b7 some more work on the sandbox diff --git a/genshi/template/eval.py b/genshi/template/eval.py --- a/genshi/template/eval.py +++ b/genshi/template/eval.py @@ -28,7 +28,7 @@ from genshi.core import Markup from genshi.template.base import TemplateRuntimeError -from genshi.util import flatten +from genshi.util import flatten, safe_range __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup', 'Undefined', 'UndefinedError'] @@ -489,13 +489,14 @@ # underscores are valid we have to add __import__ here. UNSAFE_NAMES = ['file', 'open', 'eval', 'locals', 'globals', 'vars', 'help', 'quit', 'exit', 'input', 'raw_input', 'setattr', - 'delattr', 'reload', 'compile', 'range', 'type'] + 'delattr', 'reload', 'compile', 'type'] # XXX: provide a secure range function SECURE_BUILTINS = BUILTINS.copy() for _unsafe_name in UNSAFE_NAMES: del SECURE_BUILTINS[_unsafe_name] del _unsafe_name +SECURE_BUILTINS['range'] = safe_range CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis']) @@ -831,22 +832,6 @@ node = ast.CallFunc(ast.Name('_lookup_name'), func_args) return node - def visitGetattr(self, node): - if self.secure: - return ast.CallFunc(ast.Name('_lookup_attr'), [ - ast.Name('data'), self.visit(node.expr), - ast.Const(node.attrname) - ]) - return ASTTransformer.visitGetattr(self, node) - - def visitSubscript(self, node): - if self.secure: - return ast.CallFunc(ast.Name('_lookup_item'), [ - ast.Name('data'), self.visit(node.expr), - ast.Tuple([self.visit(sub) for sub in node.subs]) - ]) - return ASTTransformer.visitSubscript(self, node) - class ExpressionASTTransformer(TemplateASTTransformer): """Concrete AST transformer that implements the AST transformations needed diff --git a/genshi/template/loader.py b/genshi/template/loader.py --- a/genshi/template/loader.py +++ b/genshi/template/loader.py @@ -77,7 +77,8 @@ """ def __init__(self, search_path=None, auto_reload=False, default_encoding=None, max_cache_size=25, default_class=None, - variable_lookup='strict', allow_exec=True, callback=None): + variable_lookup='strict', allow_exec=True, + secure=False, callback=None): """Create the template laoder. :param search_path: a list of absolute path names that should be @@ -95,6 +96,7 @@ (the default), "lenient", or a custom lookup class :param allow_exec: whether to allow Python code blocks in templates + :param secure: use secure template evaluation :param callback: (optional) a callback function that is invoked after a template was initialized by this loader; the function is passed the template object as only argument. This @@ -120,6 +122,7 @@ self.default_class = default_class or MarkupTemplate self.variable_lookup = variable_lookup self.allow_exec = allow_exec + self.secure = secure if callback is not None and not callable(callback): raise TypeError('The "callback" parameter needs to be callable') self.callback = callback @@ -213,7 +216,8 @@ tmpl = cls(fileobj, basedir=dirname, filename=filename, loader=self, encoding=encoding, lookup=self.variable_lookup, - allow_exec=self.allow_exec) + allow_exec=self.allow_exec, + secure=self.secure) if self.callback: self.callback(tmpl) self._cache[filename] = tmpl diff --git a/genshi/util.py b/genshi/util.py --- a/genshi/util.py +++ b/genshi/util.py @@ -243,3 +243,12 @@ :return: the text with tags removed """ return _STRIPTAGS_RE.sub('', text) + +SAFE_RANGE_MAX = 10000 +def safe_range(*args): + """Save version of a normal range.""" + rng = xrange(*args) + if len(rng) > SAFE_RANGE_MAX: + raise ValueError('cannot generate ranges with more than %d items.' % + SAFE_RANGE_MAX) + return list(rng)