Fixed #25283 -- Fixed collectstatic crash if a URL contains a fragment with a path.

A @font-face declaration may contain a fragment that looks like a relative path,
e.g. @font-face { src: url('../fonts/font.svg#../path/like/fragment'); }
In this case, an incorrect path was passed to the storage backend, which raised
an error that caused collectstatic to crash.
This commit is contained in:
msaelices 2015-08-17 17:59:31 +02:00 committed by Tim Graham
parent ce4914eab4
commit 7624fdb9f8
3 changed files with 13 additions and 6 deletions

View File

@ -166,12 +166,15 @@ class HashedFilesMixin(object):
name_parts = name.split(os.sep)
# Using posix normpath here to remove duplicates
url = posixpath.normpath(url)
url_parts = url.split('/')
parent_level, sub_level = url.count('..'), url.count('/')
if url.startswith('/'):
# Strip off the fragment so that a path-like fragment won't confuse
# the lookup.
url_path, fragment = urldefrag(url)
url_parts = url_path.split('/')
parent_level, sub_level = url_path.count('..'), url_path.count('/')
if url_path.startswith('/'):
sub_level -= 1
url_parts = url_parts[1:]
if parent_level or not url.startswith('/'):
if parent_level or not url_path.startswith('/'):
start, end = parent_level + 1, parent_level
else:
if sub_level:
@ -183,7 +186,9 @@ class HashedFilesMixin(object):
joined_result = '/'.join(name_parts[:-start] + url_parts[end:])
hashed_url = self.url(unquote(joined_result), force=True)
file_name = hashed_url.split('/')[-1:]
relative_url = '/'.join(url.split('/')[:-1] + file_name)
relative_url = '/'.join(url_path.split('/')[:-1] + file_name)
if fragment:
relative_url += '?#%s' % fragment if '?#' in url else '#%s' % fragment
# Return the hashed version to the file
return template % unquote(relative_url)

View File

@ -1,6 +1,7 @@
@font-face {
src: url('fonts/font.eot?#iefix') format('embedded-opentype'),
url('fonts/font.svg#webfontIyfZbseF') format('svg');
url('fonts/font.svg#../path/to/fonts/font.svg') format('svg');
url('data:font/woff;charset=utf-8;base64,d09GRgABAAAAADJoAA0AAAAAR2QAAQAAAAAAAAAAAAA');
}
div {

View File

@ -85,11 +85,12 @@ class TestHashedFiles(object):
def test_path_with_querystring_and_fragment(self):
relpath = self.hashed_file_path("cached/css/fragments.css")
self.assertEqual(relpath, "cached/css/fragments.75433540b096.css")
self.assertEqual(relpath, "cached/css/fragments.ef92012a8c16.css")
with storage.staticfiles_storage.open(relpath) as relfile:
content = relfile.read()
self.assertIn(b'fonts/font.a4b0478549d0.eot?#iefix', content)
self.assertIn(b'fonts/font.b8d603e42714.svg#webfontIyfZbseF', content)
self.assertIn(b'fonts/font.b8d603e42714.svg#../path/to/fonts/font.svg', content)
self.assertIn(b'data:font/woff;charset=utf-8;base64,d09GRgABAAAAADJoAA0AAAAAR2QAAQAAAAAAAAAAAAA', content)
self.assertIn(b'#default#VML', content)