Add :allow-symlink? option to wrap-file middleware (defaults to false).
|
|
@@ -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)
|
|
|
|
|
|
@@ -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)))
|
|
|
|
|
|
@@ -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 {}}
|
|
|
|