changeset 545:619340e2d805 trunk

Support for Python code blocks in templates can now be disabled. Closes #123.
author cmlenz
date Thu, 28 Jun 2007 23:00:24 +0000
parents 82e37439c5f4
children 2a3fc03b11de
files ChangeLog doc/plugin.txt doc/templates.txt genshi/template/base.py genshi/template/loader.py genshi/template/markup.py genshi/template/plugin.py genshi/template/tests/markup.py
diffstat 8 files changed, 71 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,8 @@
    understand.
  * Added support for the XPath 2 `matches()` function in XPath expressions,
    which allow matching against regular expressions.
+ * Support for Python code blocks in templates can now be disabled
+   (ticket #123).
 
 
 Version 0.4.3
--- a/doc/plugin.txt
+++ b/doc/plugin.txt
@@ -101,6 +101,14 @@
 or may not be made available by your framework. TurboGears 1.0, for example,
 only passes a fixed set of options to all plugins.
 
+``genshi.allow_exec``
+--------------------------
+Whether the Python code blocks should be permitted in templates. Specify "yes"
+to allow code blocks (which is the default), or "no" otherwise. Please note
+that disallowing code blocks in templates does not turn Genshi into a
+sandboxable template engine; there are sufficient ways to do harm even using
+plain expressions.
+
 ``genshi.auto_reload``
 ----------------------
 Whether the template loader should check the last modification time of template 
--- a/doc/templates.txt
+++ b/doc/templates.txt
@@ -240,6 +240,14 @@
 design. If you're using many code blocks, that may be a sign that you should
 move such code into separate Python modules.
 
+If you'd rather not allow the use of Python code blocks in templates, you can
+simply set the ``allow_exec`` parameter (available on the ``Template`` and the
+``TemplateLoader`` initializers) to ``False``. In that case Genshi will raise
+a syntax error when a ``<?python ?>`` processing instruction is encountered.
+But please note that disallowing code blocks in templates does not turn Genshi
+into a sandboxable template engine; there are sufficient ways to do harm even
+using plain expressions.
+
 .. note:: Code blocks are not currently supported in text templates.
 
 
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -288,7 +288,7 @@
     """
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient'):
+                 encoding=None, lookup='lenient', allow_exec=True):
         """Initialize a template from either a string, a file-like object, or
         an already parsed markup stream.
         
@@ -305,6 +305,10 @@
         :param encoding: the encoding of the `source`
         :param lookup: the variable lookup mechanism; either "lenient" (the
                        default), "strict", or a custom lookup class
+        :param allow_exec: whether Python code blocks in templates should be
+                           allowed
+        
+        :note: Changed in 0.5: Added the `allow_exec` argument
         """
         self.basedir = basedir
         self.filename = filename
@@ -314,6 +318,7 @@
             self.filepath = filename
         self.loader = loader
         self.lookup = lookup
+        self.allow_exec = allow_exec
 
         if isinstance(source, basestring):
             source = StringIO(source)
--- a/genshi/template/loader.py
+++ b/genshi/template/loader.py
@@ -73,7 +73,7 @@
     """
     def __init__(self, search_path=None, auto_reload=False,
                  default_encoding=None, max_cache_size=25, default_class=None,
-                 variable_lookup='lenient', callback=None):
+                 variable_lookup='lenient', allow_exec=True, callback=None):
         """Create the template laoder.
         
         :param search_path: a list of absolute path names that should be
@@ -90,12 +90,15 @@
         :param variable_lookup: the variable lookup mechanism; either "lenient"
                                 (the default), "strict", or a custom lookup
                                 class
+        :param allow_exec: whether to allow Python code blocks in templates
         :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
                          callback can be used for example to add any desired
                          filters to the template
         :see: `LenientLookup`, `StrictLookup`
+        
+        :note: Changed in 0.5: Added the `allow_exec` argument
         """
         from genshi.template.markup import MarkupTemplate
 
@@ -108,6 +111,7 @@
         self.default_encoding = default_encoding
         self.default_class = default_class or MarkupTemplate
         self.variable_lookup = variable_lookup
+        self.allow_exec = allow_exec
         if callback is not None and not callable(callback):
             raise TypeError('The "callback" parameter needs to be callable')
         self.callback = callback
@@ -199,8 +203,9 @@
                             filename = os.path.join(dirname, filename)
                             dirname = ''
                         tmpl = cls(fileobj, basedir=dirname, filename=filename,
-                                   loader=self, lookup=self.variable_lookup,
-                                   encoding=encoding)
+                                   loader=self, encoding=encoding,
+                                   lookup=self.variable_lookup,
+                                   allow_exec=self.allow_exec)
                         if self.callback:
                             self.callback(tmpl)
                         self._cache[filename] = tmpl
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -67,9 +67,10 @@
                   ('strip', StripDirective)]
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient'):
+                 encoding=None, lookup='lenient', allow_exec=True):
         Template.__init__(self, source, basedir=basedir, filename=filename,
-                          loader=loader, encoding=encoding, lookup=lookup)
+                          loader=loader, encoding=encoding, lookup=lookup,
+                          allow_exec=allow_exec)
         # Make sure the include filter comes after the match filter
         if loader:
             self.filters.remove(self._include)
@@ -185,6 +186,9 @@
                                               pos)]
 
             elif kind is PI and data[0] == 'python':
+                if not self.allow_exec:
+                    raise TemplateSyntaxError('Python code blocks not allowed',
+                                              self.filepath, *pos[1:])
                 try:
                     # As Expat doesn't report whitespace between the PI target
                     # and the data, we have to jump through some hoops here to
--- a/genshi/template/plugin.py
+++ b/genshi/template/plugin.py
@@ -67,11 +67,18 @@
             raise ConfigurationError('Unknown lookup errors mode "%s"' %
                                      lookup_errors)
 
+        try:
+            allow_exec = bool(options.get('genshi.allow_exec', True))
+        except ValueError:
+            raise ConfigurationError('Invalid value for allow_exec "%s"' %
+                                     options.get('genshi.allow_exec'))
+
         self.loader = TemplateLoader(filter(None, search_path),
                                      auto_reload=auto_reload,
                                      max_cache_size=max_cache_size,
                                      default_class=self.template_class,
                                      variable_lookup=lookup_errors,
+                                     allow_exec=allow_exec,
                                      callback=loader_callback)
 
     def load_template(self, templatename, template_string=None):
--- a/genshi/template/tests/markup.py
+++ b/genshi/template/tests/markup.py
@@ -449,6 +449,32 @@
         finally:
             shutil.rmtree(dirname)
 
+    def test_allow_exec_false(self): 
+        xml = ("""<?python
+          title = "A Genshi Template"
+          ?>
+          <html xmlns:py="http://genshi.edgewall.org/">
+            <head>
+              <title py:content="title">This is replaced.</title>
+            </head>
+        </html>""")
+        try:
+            tmpl = MarkupTemplate(xml, filename='test.html',
+                                  allow_exec=False)
+            self.fail('Expected SyntaxError')
+        except TemplateSyntaxError, e:
+            pass
+
+    def test_allow_exec_true(self): 
+        xml = ("""<?python
+          title = "A Genshi Template"
+          ?>
+          <html xmlns:py="http://genshi.edgewall.org/">
+            <head>
+              <title py:content="title">This is replaced.</title>
+            </head>
+        </html>""")
+        tmpl = MarkupTemplate(xml, filename='test.html', allow_exec=True)
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software