Django

Code

Changeset 9161

Show
Ignore:
Timestamp:
10/06/08 01:34:54 (3 months ago)
Author:
mtredinnick
Message:

Added some better error reporting and path handling when creating template paths.

We now raise UnicodeDecodeError? for non-UTF-8 bytestrings (thanks to Daniel
Pope for diagnosing this was being swallowed by ValueError?) and allow UTF-8
bytestrings as template directories.

Refs #8965.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/template/loaders/app_directories.py

    r5750 r9161  
    3434 
    3535def get_template_sources(template_name, template_dirs=None): 
     36    """ 
     37    Returns the absolute paths to "template_name", when appended to each 
     38    directory in "template_dirs". Any paths that don't lie inside one of the 
     39    template dirs are excluded from the result set, for security reasons. 
     40    """ 
    3641    if not template_dirs: 
    3742        template_dirs = app_template_dirs 
     
    3944        try: 
    4045            yield safe_join(template_dir, template_name) 
     46        except UnicodeDecodeError: 
     47            # The template dir name was a bytestring that wasn't valid UTF-8. 
     48            raise 
    4149        except ValueError: 
    4250            # The joined path was located outside of template_dir. 
  • django/trunk/django/template/loaders/filesystem.py

    r5750 r9161  
    88 
    99def get_template_sources(template_name, template_dirs=None): 
     10    """ 
     11    Returns the absolute paths to "template_name", when appended to each 
     12    directory in "template_dirs". Any paths that don't lie inside one of the 
     13    template dirs are excluded from the result set, for security reasons. 
     14    """ 
    1015    if not template_dirs: 
    1116        template_dirs = settings.TEMPLATE_DIRS 
     
    1318        try: 
    1419            yield safe_join(template_dir, template_name) 
     20        except UnicodeDecodeError: 
     21            # The template dir name was a bytestring that wasn't valid UTF-8. 
     22            raise 
    1523        except ValueError: 
    16             # The joined path was located outside of template_dir. 
     24            # The joined path was located outside of this particular 
     25            # template_dir (it might be inside another one, so this isn't 
     26            # fatal). 
    1727            pass 
    1828 
  • django/trunk/django/utils/_os.py

    r5876 r9161  
    11from os.path import join, normcase, abspath, sep 
     2from django.utils.encoding import force_unicode 
    23 
    34def safe_join(base, *paths): 
     
    1112    # We need to use normcase to ensure we don't false-negative on case 
    1213    # insensitive operating systems (like Windows). 
     14    base = force_unicode(base) 
     15    paths = [force_unicode(p) for p in paths] 
    1316    final_path = normcase(abspath(join(base, *paths))) 
    1417    base_path = normcase(abspath(base)) 
  • django/trunk/tests/regressiontests/templates/tests.py

    r8777 r9161  
    9393    def test_loaders_security(self): 
    9494        def test_template_sources(path, template_dirs, expected_sources): 
    95             # Fix expected sources so they are normcased and abspathed 
    96             expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources] 
    97             # Test app_directories loader 
    98             sources = app_directories.get_template_sources(path, template_dirs) 
    99             self.assertEqual(list(sources), expected_sources) 
    100             # Test filesystem loader 
    101             sources = filesystem.get_template_sources(path, template_dirs) 
    102             self.assertEqual(list(sources), expected_sources) 
     95            if isinstance(expected_sources, list): 
     96                # Fix expected sources so they are normcased and abspathed 
     97                expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources] 
     98            # Test the two loaders (app_directores and filesystem). 
     99            func1 = lambda p, t: list(app_directories.get_template_sources(p, t)) 
     100            func2 = lambda p, t: list(filesystem.get_template_sources(p, t)) 
     101            for func in (func1, func2): 
     102                if isinstance(expected_sources, list): 
     103                    self.assertEqual(func(path, template_dirs), expected_sources) 
     104                else: 
     105                    self.assertRaises(expected_sources, func, path, template_dirs) 
    103106 
    104107        template_dirs = ['/dir1', '/dir2'] 
    105108        test_template_sources('index.html', template_dirs, 
    106109                              ['/dir1/index.html', '/dir2/index.html']) 
    107         test_template_sources('/etc/passwd', template_dirs, 
    108                               []) 
     110        test_template_sources('/etc/passwd', template_dirs, []) 
    109111        test_template_sources('etc/passwd', template_dirs, 
    110112                              ['/dir1/etc/passwd', '/dir2/etc/passwd']) 
    111         test_template_sources('../etc/passwd', template_dirs, 
    112                               []) 
    113         test_template_sources('../../../etc/passwd', template_dirs, 
    114                               []) 
     113        test_template_sources('../etc/passwd', template_dirs, []) 
     114        test_template_sources('../../../etc/passwd', template_dirs, []) 
    115115        test_template_sources('/dir1/index.html', template_dirs, 
    116116                              ['/dir1/index.html']) 
    117117        test_template_sources('../dir2/index.html', template_dirs, 
    118118                              ['/dir2/index.html']) 
    119         test_template_sources('/dir1blah', template_dirs, 
    120                               []) 
    121         test_template_sources('../dir1blah', template_dirs, 
    122                               []) 
     119        test_template_sources('/dir1blah', template_dirs, []) 
     120        test_template_sources('../dir1blah', template_dirs, []) 
     121 
     122        # UTF-8 bytestrings are permitted. 
     123        test_template_sources('\xc3\x85ngstr\xc3\xb6m', template_dirs, 
     124                              [u'/dir1/Ångström', u'/dir2/Ångström']) 
     125        # Unicode strings are permitted. 
     126        test_template_sources(u'Ångström', template_dirs, 
     127                              [u'/dir1/Ångström', u'/dir2/Ångström']) 
     128        test_template_sources(u'Ångström', ['/Straße'], [u'/Straße/Ångström']) 
     129        test_template_sources('\xc3\x85ngstr\xc3\xb6m', ['/Straße'], 
     130                              [u'/Straße/Ångström']) 
     131        # Invalid UTF-8 encoding in bytestrings is not. Should raise a 
     132        # semi-useful error message. 
     133        test_template_sources('\xc3\xc3', template_dirs, UnicodeDecodeError) 
    123134 
    124135        # Case insensitive tests (for win32). Not run unless we're on 
     
    373384            # Numbers as filter arguments should work 
    374385            'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."), 
    375              
     386 
    376387            #filters should accept empty string constants 
    377388            'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),