mirror of https://github.com/django/django.git
Raised SuspiciousFileOperation in safe_join.
Added a test for the condition safe_join is designed to prevent. Previously, a generic ValueError was raised. It was impossible to tell an intentional exception raised to implement safe_join's contract from an unintentional exception caused by incorrect inputs or unexpected conditions. That resulted in bizarre exception catching patterns, which this patch removes. Since safe_join is a private API and since the change is unlikely to create security issues for users who use it anyway -- at worst, an uncaught SuspiciousFileOperation exception will bubble up -- it isn't documented.
This commit is contained in:
parent
40ba6f21bb
commit
b8ba73cd0c
|
@ -277,11 +277,7 @@ class FileSystemStorage(Storage):
|
||||||
return directories, files
|
return directories, files
|
||||||
|
|
||||||
def path(self, name):
|
def path(self, name):
|
||||||
try:
|
return safe_join(self.location, name)
|
||||||
path = safe_join(self.location, name)
|
|
||||||
except ValueError:
|
|
||||||
raise SuspiciousFileOperation("Attempted access to '%s' denied." % name)
|
|
||||||
return os.path.normpath(path)
|
|
||||||
|
|
||||||
def size(self, name):
|
def size(self, name):
|
||||||
return os.path.getsize(self.path(name))
|
return os.path.getsize(self.path(name))
|
||||||
|
|
|
@ -8,6 +8,7 @@ import sys
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from django.template.base import TemplateDoesNotExist
|
from django.template.base import TemplateDoesNotExist
|
||||||
from django.template.loader import BaseLoader
|
from django.template.loader import BaseLoader
|
||||||
from django.utils._os import safe_join
|
from django.utils._os import safe_join
|
||||||
|
@ -47,11 +48,9 @@ class Loader(BaseLoader):
|
||||||
for template_dir in template_dirs:
|
for template_dir in template_dirs:
|
||||||
try:
|
try:
|
||||||
yield safe_join(template_dir, template_name)
|
yield safe_join(template_dir, template_name)
|
||||||
except UnicodeDecodeError:
|
except SuspiciousFileOperation:
|
||||||
# The template dir name was a bytestring that wasn't valid UTF-8.
|
# The joined path was located outside of this template_dir
|
||||||
raise
|
# (it might be inside another one, so this isn't fatal).
|
||||||
except ValueError:
|
|
||||||
# The joined path was located outside of template_dir.
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def load_template_source(self, template_name, template_dirs=None):
|
def load_template_source(self, template_name, template_dirs=None):
|
||||||
|
|
|
@ -3,6 +3,7 @@ Wrapper for loading templates from the filesystem.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from django.template.base import TemplateDoesNotExist
|
from django.template.base import TemplateDoesNotExist
|
||||||
from django.template.loader import BaseLoader
|
from django.template.loader import BaseLoader
|
||||||
from django.utils._os import safe_join
|
from django.utils._os import safe_join
|
||||||
|
@ -22,13 +23,9 @@ class Loader(BaseLoader):
|
||||||
for template_dir in template_dirs:
|
for template_dir in template_dirs:
|
||||||
try:
|
try:
|
||||||
yield safe_join(template_dir, template_name)
|
yield safe_join(template_dir, template_name)
|
||||||
except UnicodeDecodeError:
|
except SuspiciousFileOperation:
|
||||||
# The template dir name was a bytestring that wasn't valid UTF-8.
|
# The joined path was located outside of this template_dir
|
||||||
raise
|
# (it might be inside another one, so this isn't fatal).
|
||||||
except ValueError:
|
|
||||||
# The joined path was located outside of this particular
|
|
||||||
# template_dir (it might be inside another one, so this isn't
|
|
||||||
# fatal).
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def load_template_source(self, template_name, template_dirs=None):
|
def load_template_source(self, template_name, template_dirs=None):
|
||||||
|
|
|
@ -4,6 +4,7 @@ import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from os.path import join, normcase, normpath, abspath, isabs, sep, dirname
|
from os.path import join, normcase, normpath, abspath, isabs, sep, dirname
|
||||||
|
|
||||||
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
@ -77,8 +78,9 @@ def safe_join(base, *paths):
|
||||||
if (not normcase(final_path).startswith(normcase(base_path + sep)) and
|
if (not normcase(final_path).startswith(normcase(base_path + sep)) and
|
||||||
normcase(final_path) != normcase(base_path) and
|
normcase(final_path) != normcase(base_path) and
|
||||||
dirname(normcase(base_path)) != normcase(base_path)):
|
dirname(normcase(base_path)) != normcase(base_path)):
|
||||||
raise ValueError('The joined path (%s) is located outside of the base '
|
raise SuspiciousFileOperation(
|
||||||
'path component (%s)' % (final_path, base_path))
|
'The joined path ({}) is located outside of the base path '
|
||||||
|
'component ({})'.format(final_path, base_path))
|
||||||
return final_path
|
return final_path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from django.utils._os import safe_join
|
from django.utils._os import safe_join
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,3 +25,7 @@ class SafeJoinTests(unittest.TestCase):
|
||||||
path,
|
path,
|
||||||
os.path.sep,
|
os.path.sep,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_parent_path(self):
|
||||||
|
with self.assertRaises(SuspiciousFileOperation):
|
||||||
|
safe_join("/abc/", "../def")
|
||||||
|
|
Loading…
Reference in New Issue