Ian Lewis avatar Ian Lewis committed 7768ec8 Merge

Merged pull request #20.

* Updated safe_join to properly support multiple paths
* Added tests for safe_join

Comments (0)

Files changed (2)

storages/backends/s3boto.py

     """
     from urlparse import urljoin
     base_path = force_unicode(base)
-    paths = map(lambda p: force_unicode(p), paths)
-    final_path = urljoin(base_path +
-        ("/" if not base_path.endswith("/") else ""), *paths)
+    base_path = base_path.rstrip('/')
+    paths = [force_unicode(p) for p in paths]
+
+    final_path = base_path
+    for path in paths:
+        final_path = urljoin(final_path.rstrip('/') + "/", path.rstrip("/"))
+
     # Ensure final_path starts with base_path and that the next character after
     # the final path is '/' (or nothing, in which case final_path must be
     # equal to base_path).
        or final_path[base_path_len:base_path_len + 1] not in ('', '/'):
         raise ValueError('the joined path is located outside of the base path'
                          ' component')
-    return final_path
+
+    return final_path.lstrip('/')
 
 # Dates returned from S3's API look something like this:
 # "Sun, 11 Mar 2012 17:01:41 GMT"
         the directory specified by the LOCATION setting.
         """
         try:
-            return safe_join(self.location, name).lstrip('/')
+            return safe_join(self.location, name)
         except ValueError:
             raise SuspiciousOperation("Attempted access to '%s' denied." %
                                       name)

storages/tests/s3boto.py

 from storages.backends import s3boto
 
 __all__ = (
+    'SafeJoinTest',
     'S3BotoStorageTests',
     #'S3BotoStorageFileTests',
 )
     @mock.patch('storages.backends.s3boto.S3Connection')
     def setUp(self, S3Connection):
         self.storage = s3boto.S3BotoStorage()
+
+
+class SafeJoinTest(TestCase):
+    def test_normal(self):
+        path = s3boto.safe_join("", "path/to/somewhere", "other", "path/to/somewhere")
+        self.assertEquals(path, "path/to/somewhere/other/path/to/somewhere")
+
+    def test_with_dot(self):
+        path = s3boto.safe_join("", "path/./somewhere/../other", "..",
+                                ".", "to/./somewhere")
+        self.assertEquals(path, "path/to/somewhere")
+
+    def test_suspicious_operation(self):
+        self.assertRaises(ValueError,
+            s3boto.safe_join, "base", "../../../../../../../etc/passwd")
     
 class S3BotoStorageTests(S3BotoTestCase):
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.