Hi All,
I bumped my Django project to the latest 3.2.4 release from 3.2. All tests passed (OK, I didn’t have a test for this endpoint) and I was happy as can be. I was testing my application which is a JS SPA + Django API and I noticed that media upload to AWS which goes via my Django backend were suddenly getting 400 responses. Specifically, I was getting:
SuspiciousFileOperation: Detected path traversal attempt in '/case_data/dogs/01000/dogs_1000_1622660073141.jpeg'
I took a look at the 3.2.4 release notes and saw that there was a patch for a potential directory traversal vulnerability and I started to suspect it might be hitting my code.
The exception is raised when I return instance in the below code:
def create_media(self, file, filename, user, short_name=None, annotation=None):
try:
instance = self.model(
file=filename, uploaded_by=user, short_name=short_name, annotation=annotation
)
instance.save()
instance.file.save(filename, file)
return instance
except IntegrityError:
And this is my model which inherits from a Meta class, also below.
class Media(models.Model):
secure_file = models.CharField(null=True, max_length=512)
key = models.CharField(max_length=512, null=True)
uploaded_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True
)
class Meta:
abstract = True
class Image(Media):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
file = models.FileField(upload_to="images", unique=True)
annotation = models.CharField(max_length=1024, null=True, blank=True)
short_name = models.CharField(max_length=256, null=True, blank=True)
url = models.URLField(null=True, blank=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="images")
So far, at least to my eye, everything looks very standard.
The Django source code which is causing the problem is:
# My own comment: This is from 3.2.4 source,
# but it is identical to 3.2 source as best I can tell
if allow_relative_path:
# Use PurePosixPath() because this branch is checked only in
# FileField.generate_filename() where all file paths are expected to be
# Unix style (with forward slashes).
path = pathlib.PurePosixPath(name)
if path.is_absolute() or '..' in path.parts:
raise SuspiciousFileOperation(
"Detected path traversal attempt in '%s'" % name
)
What is perhaps most interesting is that my file path which begins with a / is true when executing path.is_absolute()
>>> import pathlib
>>> name = '/case_data/snakes/01001/snakes_1001_1622659350659.jpeg'
>>> path = pathlib.PurePosixPath(name)
>>> path.is_absolute()
True
>>> '..' in path.parts
False
>>> name = 'snakes_1001_1622659350659.jpeg'
>>> path = pathlib.PurePosixPath(name)
>>> path.is_absolute()
False
>>> name = 'case_data/snakes/01001/snakes_1001_1622659350659.jpeg'
>>> path = pathlib.PurePosixPath(name)
>>> path.is_absolute()
False
>>>
And finally, my question:
Given my file path string, '/case_data/dogs/01000/dogs_1000_1622660073141.jpeg' is the same when I run 3.2 and 3.2.4 code and I can reliably reproduce the above described behaviour, why am I not getting the SuspiciousFileOperation in 3.2 but I am in 3.2.4?
I considered opening a ticket, but I’m loathe to do so without checking here first lest I’m missing something obvious.
As always, thank you for your help.
Cheers,
Conor