From f207f4a199452da9159d73145ddb44cac8f7f24f Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Mon, 16 Jul 2007 11:53:12 -0700
Subject: [PATCH] webish.py: disallow slashes in POSTed filenames. Closes #75.

---
 docs/webapi.txt                |  3 ++-
 src/allmydata/test/test_web.py | 24 +++++++++++++++++++++++-
 src/allmydata/webish.py        |  4 ++++
 3 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/docs/webapi.txt b/docs/webapi.txt
index 1089b805..1a9f75b8 100644
--- a/docs/webapi.txt
+++ b/docs/webapi.txt
@@ -234,7 +234,8 @@ for files and directories which do not yet exist.
   this because forms are the only way for a web browser to upload a file
   (browsers do not know how to do PUT or DELETE). The file's contents and the
   new child name will be included in the form's arguments. This can only be
-  used to upload a single file at a time.
+  used to upload a single file at a time. To avoid confusion, name= is not
+  allowed to contain a slash (a 400 Bad Request error will result).
 
  POST DIRURL
   t=mkdir
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index ff450d71..e31f8efe 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -310,13 +310,18 @@ class Web(unittest.TestCase):
         return client.getPage(url, method="POST", postdata=body,
                               headers=headers, followRedirect=False)
 
-    def shouldFail(self, res, expected_failure, which, substring=None):
+    def shouldFail(self, res, expected_failure, which,
+                   substring=None, response_substring=None):
         if isinstance(res, failure.Failure):
             res.trap(expected_failure)
             if substring:
                 self.failUnless(substring in str(res),
                                 "substring '%s' not in '%s'"
                                 % (substring, str(res)))
+            if response_substring:
+                self.failUnless(response_substring in res.value.response,
+                                "respose substring '%s' not in '%s'"
+                                % (response_substring, res.value.response))
         else:
             self.fail("%s was supposed to raise %s, not get '%s'" %
                       (which, expected_failure, res))
@@ -776,6 +781,23 @@ class Web(unittest.TestCase):
         d.addCallback(_check)
         return d
 
+    def test_POST_upload_named_badfilename(self): # YES
+        d = self.POST("/vdrive/global/foo", t="upload",
+                      name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
+        d.addBoth(self.shouldFail, error.Error,
+                  "test_POST_upload_named_badfilename",
+                  "400 Bad Request",
+                  "name= may not contain a slash",
+                  )
+        def _check(res):
+            # make sure that nothing was added
+            kids = sorted(self._foo_node.children.keys())
+            self.failUnlessEqual(sorted(["bar.txt", "blockingfile",
+                                         "empty", "sub"]),
+                                 kids)
+        d.addCallback(_check)
+        return d
+
     def test_POST_mkdir(self): # YES, return value?
         d = self.POST("/vdrive/global/foo", t="mkdir", name="newdir")
         def _check(res):
diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py
index 94b89a6d..adcb575e 100644
--- a/src/allmydata/webish.py
+++ b/src/allmydata/webish.py
@@ -502,6 +502,10 @@ class POSTHandler(rend.Page):
             name = req.args["name"][0]
         elif name in req.fields:
             name = req.fields["name"].value
+        if "/" in name:
+            req.setResponseCode(http.BAD_REQUEST)
+            req.setHeader("content-type", "text/plain")
+            return "name= may not contain a slash"
 
         when_done = None
         if "when_done" in req.args:
-- 
2.45.2