diff --git a/CHANGES b/CHANGES index a6bae660a35fdea7ed68eb2281aeb6641e83bbad..01ec4dc86fee63d93a8970328ba1eb2b89bf7b05 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,22 @@ +0.5 +- A Template is explicitly disallowed + from having a url that normalizes to relative outside + of the root. That is, if the Lookup is based + at /home/mytemplates, an include that would place + the ultimate template at + /home/mytemplates/../some_other_directory, + i.e. outside of /home/mytemplates, + is disallowed. This usage was never intended + despite the lack of an explicit check. + The main issue this causes + is that module files can be written outside + of the module root (or raise an error, if file perms aren't + set up), and can also lead to the same template being + cached in the lookup under multiple, relative roots. + TemplateLookup instead has always supported multiple + file roots for this purpose. + [ticket:174] + 0.4.2 - Fixed bug regarding <%call>/def calls w/ content whereby the identity of the "caller" callable diff --git a/mako/__init__.py b/mako/__init__.py index c9913ebf62af0eef5d888a39585ecdc3c8418bc6..5b067a3717e367f040fa8954f9463ca71b523064 100644 --- a/mako/__init__.py +++ b/mako/__init__.py @@ -5,5 +5,5 @@ # the MIT License: http://www.opensource.org/licenses/mit-license.php -__version__ = '0.4.2' +__version__ = '0.5.0' diff --git a/mako/lookup.py b/mako/lookup.py index b397d21f001b4430c9ca221bd2d1ebdb40ba3a31..e3d92da217929f8e96078f9cf50ae1fef5f58b68 100644 --- a/mako/lookup.py +++ b/mako/lookup.py @@ -204,7 +204,7 @@ class TemplateLookup(TemplateCollection): Note the "relativeto" argument is not supported here at the moment. """ - + try: if self.filesystem_checks: return self._check(uri, self._collection[uri]) diff --git a/mako/template.py b/mako/template.py index 903dc4254c9e315417f7995e4abeb42f9e12efef..3d02c55e618e532fd70425d68e6bc110e78ed7db 100644 --- a/mako/template.py +++ b/mako/template.py @@ -163,7 +163,17 @@ class Template(object): else: self.module_id = "memory:" + hex(id(self)) self.uri = self.module_id - + + u_norm = self.uri + if u_norm.startswith("/"): + u_norm = u_norm[1:] + u_norm = os.path.normpath(u_norm) + if u_norm.startswith(".."): + raise exceptions.TemplateLookupException( + "Template uri \"%s\" is invalid - " + "it cannot be relative outside " + "of the root path." % self.uri) + self.input_encoding = input_encoding self.output_encoding = output_encoding self.encoding_errors = encoding_errors @@ -203,18 +213,14 @@ class Template(object): if module_filename is not None: path = module_filename elif module_directory is not None: - u = self.uri - if u[0] == '/': - u = u[1:] path = os.path.abspath( os.path.join( os.path.normpath(module_directory), - os.path.normpath(u) + ".py" + u_norm + ".py" ) ) else: path = None - module = self._compile_from_file(path, filename) else: raise exceptions.RuntimeException( diff --git a/test/templates/othersubdir/foo.html b/test/templates/othersubdir/foo.html new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/test_lookup.py b/test/test_lookup.py index 190d8a516dbd18171bd6bc62bd8fa2cee5d5e0ed..40b900919a1655b983b2ae002556620ce687c82b 100644 --- a/test/test_lookup.py +++ b/test/test_lookup.py @@ -1,9 +1,11 @@ from mako.template import Template -from mako import lookup, exceptions +from mako import lookup, exceptions, runtime +from mako.util import FastEncodingBuffer from util import flatten_result, result_lines import unittest +import os -from test import TemplateTest, template_base, module_base +from test import TemplateTest, template_base, module_base, assert_raises_message tl = lookup.TemplateLookup(directories=[template_base]) class LookupTest(unittest.TestCase): @@ -74,3 +76,29 @@ class LookupTest(unittest.TestCase): ) assert f.uri not in tl._collection + def test_dont_accept_relative_outside_of_root(self): + """test the mechanics of an include where + the include goes outside of the path""" + tl = lookup.TemplateLookup(directories=[os.path.join(template_base, "subdir")]) + index = tl.get_template("index.html") + + ctx = runtime.Context(FastEncodingBuffer()) + ctx._with_template=index + + assert_raises_message( + exceptions.TemplateLookupException, + "Template uri \"../index.html\" is invalid - it " + "cannot be relative outside of the root path", + runtime._lookup_template, ctx, "../index.html", index.uri + ) + + assert_raises_message( + exceptions.TemplateLookupException, + "Template uri \"../othersubdir/foo.html\" is invalid - it " + "cannot be relative outside of the root path", + runtime._lookup_template, ctx, "../othersubdir/foo.html", index.uri + ) + + # this is OK since the .. cancels out + t = runtime._lookup_template(ctx, "foo/../index.html", index.uri) + diff --git a/test/test_template.py b/test/test_template.py index a62783e46b2e54a04b21b2c439da9a5f409bd465..4d301aab19281bb10d416edb4d2970cf693a74c8 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -9,7 +9,7 @@ import os from util import flatten_result, result_lines import codecs from test import TemplateTest, eq_, template_base, module_base, \ - skip_if, assert_raises + skip_if, assert_raises, assert_raises_message class EncodingTest(TemplateTest): def test_unicode(self): @@ -918,8 +918,25 @@ class FilenameToURITest(TemplateTest): finally: os.path = current_path - - + def test_dont_accept_relative_outside_of_root(self): + assert_raises_message( + exceptions.TemplateLookupException, + "Template uri \"../../foo.html\" is invalid - it " + "cannot be relative outside of the root path", + Template, "test", uri="../../foo.html", + ) + + assert_raises_message( + exceptions.TemplateLookupException, + "Template uri \"/../../foo.html\" is invalid - it " + "cannot be relative outside of the root path", + Template, "test", uri="/../../foo.html", + ) + + # normalizes in the root is OK + t = Template("test", uri="foo/bar/../../foo.html") + eq_(t.uri, "foo/bar/../../foo.html") + class ModuleTemplateTest(TemplateTest): def test_module_roundtrip(self): lookup = TemplateLookup()