From 7cddd8a02e60332c0d02f565c450b0eea0d88438 Mon Sep 17 00:00:00 2001
From: Henry Dang <henrydangprg@gmail.com>
Date: Sat, 26 Nov 2016 13:23:03 -0500
Subject: [PATCH] Fixed #27358 -- Added a system check to prevent FileField's
 upload_to from starting with a slash.

Thanks Frank Bijlsma for the initial patch.
---
 django/db/models/fields/files.py              | 15 +++++++++++
 docs/ref/checks.txt                           |  2 ++
 .../test_ordinary_fields.py                   | 25 +++++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index e3497f2494..53a31c3ed6 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -240,6 +240,7 @@ class FileField(Field):
     def check(self, **kwargs):
         errors = super(FileField, self).check(**kwargs)
         errors.extend(self._check_primary_key())
+        errors.extend(self._check_upload_to())
         return errors
 
     def _check_primary_key(self):
@@ -254,6 +255,20 @@ class FileField(Field):
         else:
             return []
 
+    def _check_upload_to(self):
+        if isinstance(self.upload_to, six.string_types) and self.upload_to[0] == '/':
+            return [
+                checks.Error(
+                    "%s's 'upload_to' argument must be a relative path, not an "
+                    "absolute path." % self.__class__.__name__,
+                    obj=self,
+                    id='fields.E202',
+                    hint='Remove the leading slash.',
+                )
+            ]
+        else:
+            return []
+
     def deconstruct(self):
         name, path, args, kwargs = super(FileField, self).deconstruct()
         if kwargs.get("max_length") == 100:
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index a3c659aad5..1f35c5bb0f 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -189,6 +189,8 @@ File Fields
 * **fields.E200**: ``unique`` is not a valid argument for a ``FileField``.
   *This check is removed in Django 1.11*.
 * **fields.E201**: ``primary_key`` is not a valid argument for a ``FileField``.
+* **fields.E202**: ``FileField``’s ``upload_to`` argument must be a relative
+  path, not an absolute path.
 * **fields.E210**: Cannot use ``ImageField`` because Pillow is not installed.
 
 Related Fields
diff --git a/tests/invalid_models_tests/test_ordinary_fields.py b/tests/invalid_models_tests/test_ordinary_fields.py
index aff6a74659..c005fe9331 100644
--- a/tests/invalid_models_tests/test_ordinary_fields.py
+++ b/tests/invalid_models_tests/test_ordinary_fields.py
@@ -455,6 +455,31 @@ class FileFieldTests(SimpleTestCase):
         ]
         self.assertEqual(errors, expected)
 
+    def test_upload_to_starts_with_slash(self):
+        class Model(models.Model):
+            field = models.FileField(upload_to='/somewhere')
+
+        field = Model._meta.get_field('field')
+        self.assertEqual(field.check(), [
+            Error(
+                "FileField's 'upload_to' argument must be a relative path, not "
+                "an absolute path.",
+                obj=field,
+                id='fields.E202',
+                hint='Remove the leading slash.',
+            )
+        ])
+
+    def test_upload_to_callable_not_checked(self):
+        def callable(instance, filename):
+            return '/' + filename
+
+        class Model(models.Model):
+            field = models.FileField(upload_to=callable)
+
+        field = Model._meta.get_field('field')
+        self.assertEqual(field.check(), [])
+
 
 @isolate_apps('invalid_models_tests')
 class FilePathFieldTests(SimpleTestCase):