Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add :allow-symlink? option to wrap-file middleware (defaults to false).

  • Loading branch information...
commit efeb62c103c7e2a6774778cd2fb616cf5d6aa83d 1 parent 87d8ed7
@lynaghk lynaghk authored
View
2  ring-core/src/ring/middleware/file.clj
@@ -20,7 +20,7 @@
to the ring.util.response/file-response function."
[app ^String root-path & [opts]]
(ensure-dir root-path)
- (let [opts (merge {:root root-path, :index-files? true} opts)]
+ (let [opts (merge {:root root-path, :index-files? true, :allow-symlinks? false} opts)]
(fn [req]
(if-not (= :get (:request-method req))
(app req)
View
16 ring-core/src/ring/util/response.clj
@@ -39,6 +39,13 @@
(.startsWith (.getCanonicalPath (File. root path))
(.getCanonicalPath (File. root))))
+(defn- directory-transversal?
+ "Check if a path contains '..'."
+ [^String path]
+ (-> (str/split path #"/|\\")
+ (set)
+ (contains? "..")))
+
(defn- find-index-file
"Search the directory for an index file."
[^File dir]
@@ -52,7 +59,9 @@
explanation of options."
[^String path opts]
(if-let [^File file (if-let [^String root (:root opts)]
- (and (safe-path? root path) (File. root path))
+ (if (or (safe-path? root path)
+ (and (:allow-symlinks? opts) (not (directory-transversal? path))))
+ (File. root path))
(File. path))]
(cond
(.isDirectory file)
@@ -64,8 +73,9 @@
"Returns a Ring response to serve a static file, or nil if an appropriate
file does not exist.
Options:
- :root - take the filepath relative to this root path
- :index-files? - look for index.* files in directories, defaults to true"
+ :root - take the filepath relative to this root path
+ :index-files? - look for index.* files in directories, defaults to true
+ :allow-symlinks? - serve files through symbolic links, defaults to false"
[filepath & [opts]]
(if-let [file (get-file filepath opts)]
(response file)))
View
1  ring-core/test/ring/assets/bars/backlink
View
19 ring-core/test/ring/util/test/response.clj
@@ -104,6 +104,25 @@
(is (= (slurp (:body resp))
"Hello World\n")))))
+(deftest test-file-response
+ (testing "response map"
+ (let [resp (file-response "foo.html" {:root "test/ring/assets"})]
+ (is (= (resp :status) 200))
+ (is (= (resp :headers) {}))
+ (is (= (slurp (resp :body)) "foo"))))
+
+ (testing "file path cannot contain '..' "
+ (is (nil? (file-response "../../../project.clj" {:root "test/ring/assets"})))
+ (is (nil? (file-response "../../../project.clj" {:root "test/ring/assets/bars" :allow-symlinks? true}))))
+
+ (testing "file response optionally follows symlinks"
+ (let [resp (file-response "backlink/foo.html" {:root "test/ring/assets/bars" :allow-symlinks? true})]
+ (is (= (resp :status) 200))
+ (is (= (resp :headers) {}))
+ (is (= (slurp (resp :body)) "foo")))
+
+ (is (nil? (file-response "backlink/foo.html" {:root "test/ring/assets/bars"})))))
+
(deftest test-set-cookie
(is (= {:status 200 :headers {} :cookies {"Foo" {:value "Bar"}}}
(set-cookie {:status 200 :headers {}}
Please sign in to comment.
Something went wrong with that request. Please try again.