def test_filefield_awss3_storage(self): """ Simulate a FileField with an S3 storage which uses keys rather than folders and names. FileField and Storage shouldn't have any os.path() calls that break the key. """ storage = AWSS3Storage() folder = 'not/a/folder/' f = FileField(upload_to=folder, storage=storage) key = 'my-file-key\\with odd characters' data = ContentFile('test') expected_key = AWSS3Storage.prefix + folder + key # Simulate call to f.save() result_key = f.generate_filename(None, key) self.assertEqual(result_key, expected_key) result_key = storage.save(result_key, data) self.assertEqual(result_key, expected_key) # Repeat test with a callable. def upload_to(instance, filename): # Return a non-normalized path on purpose. return folder + filename f = FileField(upload_to=upload_to, storage=storage) # Simulate call to f.save() result_key = f.generate_filename(None, key) self.assertEqual(result_key, expected_key) result_key = storage.save(result_key, data) self.assertEqual(result_key, expected_key)
def test_filefield_awss3_storage(self): """ Simulate a FileField with an S3 storage which uses keys rather than folders and names. FileField and Storage shouldn't have any os.path() calls that break the key. """ storage = AWSS3Storage() folder = 'not/a/folder/' f = FileField(upload_to=folder, storage=storage) key = 'my-file-key\\with odd characters' data = ContentFile('test') expected_key = AWSS3Storage.prefix + folder + key # Simulate call to f.save() result_key = f.generate_filename(None, key) self.assertEqual(result_key, expected_key) result_key = storage.save(result_key, data) self.assertEqual(result_key, expected_key) # Repeat test with a callable. def upload_to(instance, filename): # Return a non-normalized path on purpose. return folder + filename f = FileField(upload_to=upload_to, storage=storage) # Simulate call to f.save() result_key = f.generate_filename(None, key) self.assertEqual(result_key, expected_key) result_key = storage.save(result_key, data) self.assertEqual(result_key, expected_key)
def test_filefield_dangerous_filename(self): candidates = ['..', '.', '', '???', '$.$.$'] f = FileField(upload_to='some/folder/') msg = "Could not derive file name from '%s'" for file_name in candidates: with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg % file_name): f.generate_filename(None, file_name)
def test_filefield_generate_filename_absolute_path(self): f = FileField(upload_to='some/folder/') candidates = [ '/tmp/path', '/tmp/../path', ] for file_name in candidates: msg = f"Detected path traversal attempt in '{file_name}'" with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, file_name)
def test_filefield_generate_filename_upload_to_dangerous_filename(self): def upload_to(instance, filename): return '/tmp/' + filename f = FileField(upload_to=upload_to) candidates = ['..', '.', ''] for file_name in candidates: msg = f"Could not derive file name from '/tmp/{file_name}'" with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, file_name)
def test_filefield_dangerous_filename(self): candidates = [ ('..', 'some/folder/..'), ('.', 'some/folder/.'), ('', 'some/folder/'), ('???', '???'), ('$.$.$', '$.$.$'), ] f = FileField(upload_to='some/folder/') for file_name, msg_file_name in candidates: msg = f"Could not derive file name from '{msg_file_name}'" with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, file_name)
def test_filefield_dangerous_filename(self): candidates = [ ("..", "some/folder/.."), (".", "some/folder/."), ("", "some/folder/"), ("???", "???"), ("$.$.$", "$.$.$"), ] f = FileField(upload_to="some/folder/") for file_name, msg_file_name in candidates: msg = f"Could not derive file name from '{msg_file_name}'" with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, file_name)
def test_filefield_generate_filename_with_upload_to(self): def upload_to(instance, filename): return 'some/folder/' + filename f = FileField(upload_to=upload_to) self.assertEqual(f.generate_filename(None, 'test with space.txt'), os.path.normpath('some/folder/test_with_space.txt'))
def test_filefield_generate_filename_upload_to_absolute_path(self): def upload_to(instance, filename): return '/tmp/' + filename f = FileField(upload_to=upload_to) candidates = [ 'path', '../path', '???', '$.$.$', ] for file_name in candidates: msg = f"Detected path traversal attempt in '/tmp/{file_name}'" with self.subTest(file_name=file_name): with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, file_name)
def test_filefield_generate_filename_with_upload_to(self): def upload_to(instance, filename): return 'some/folder/' + filename f = FileField(upload_to=upload_to) self.assertEqual( f.generate_filename(None, 'test with space.txt'), os.path.normpath('some/folder/test_with_space.txt') )
def test_filefield_generate_filename_upload_to_overrides_dangerous_filename(self): def upload_to(instance, filename): return 'test.txt' f = FileField(upload_to=upload_to) candidates = [ '/tmp/.', '/tmp/..', '/tmp/../path', '/tmp/path', 'some/folder/', 'some/folder/.', 'some/folder/..', 'some/folder/???', 'some/folder/$.$.$', 'some/../test.txt', '', ] for file_name in candidates: with self.subTest(file_name=file_name): self.assertEqual(f.generate_filename(None, file_name), 'test.txt')
def test_filefield_generate_filename_upload_to_overrides_dangerous_filename(self): def upload_to(instance, filename): return "test.txt" f = FileField(upload_to=upload_to) candidates = [ "/tmp/.", "/tmp/..", "/tmp/../path", "/tmp/path", "some/folder/", "some/folder/.", "some/folder/..", "some/folder/???", "some/folder/$.$.$", "some/../test.txt", "", ] for file_name in candidates: with self.subTest(file_name=file_name): self.assertEqual(f.generate_filename(None, file_name), "test.txt")
def test_filefield_generate_filename(self): f = FileField(upload_to='some/folder/') self.assertEqual(f.generate_filename(None, 'test with space.txt'), os.path.normpath('some/folder/test_with_space.txt'))
def test_filefield_dangerous_filename_dir(self): f = FileField(upload_to='some/folder/') msg = "File name '/tmp/path' includes path elements" with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, '/tmp/path')
def test_filefield_dangerous_filename_dot_segments(self): f = FileField(upload_to='some/folder/') msg = "Detected path traversal attempt in 'some/folder/../path'" with self.assertRaisesMessage(SuspiciousFileOperation, msg): f.generate_filename(None, '../path')
def test_filefield_generate_filename(self): f = FileField(upload_to='some/folder/') self.assertEqual( f.generate_filename(None, 'test with space.txt'), os.path.normpath('some/folder/test_with_space.txt') )