diff --git a/backend/src/app/util/ssrf.clj b/backend/src/app/util/ssrf.clj index 5348f1eaf7..30463479fe 100644 --- a/backend/src/app/util/ssrf.clj +++ b/backend/src/app/util/ssrf.clj @@ -184,13 +184,17 @@ (not (contains? allowed-schemes (str/lower scheme)))) (ex/raise :type :validation :code :ssrf-blocked-target - :hint "url scheme is not allowed")) + :hint "url scheme is not allowed" + :uri (str uri) + :scheme scheme)) ;; Validate host presence (when (or (nil? host) (str/blank? host)) (ex/raise :type :validation :code :ssrf-blocked-target - :hint "url host is missing")) + :hint "url host is missing" + :uri (str uri) + :host host)) ;; Check allowlist (let [allowed-hosts (cf/get :ssrf-allowed-hosts #{}) @@ -210,13 +214,15 @@ (when (or (nil? addresses) (zero? (alength addresses))) (ex/raise :type :validation :code :ssrf-blocked-target - :hint "url host could not be resolved")) + :hint "uri host could not be resolved" + :uri (str uri))) ;; All-or-nothing: if ANY resolved address is blocked, reject (when (some blocked-address? (seq addresses)) (ex/raise :type :validation :code :ssrf-blocked-target - :hint "url target is not allowed"))))) + :hint "uri target is not allowed" + :uri (str uri)))))) (str uri))) (defn safe-url? diff --git a/backend/test/backend_tests/util_ssrf_test.clj b/backend/test/backend_tests/util_ssrf_test.clj index 04e86291fd..4e6c8ed93f 100644 --- a/backend/test/backend_tests/util_ssrf_test.clj +++ b/backend/test/backend_tests/util_ssrf_test.clj @@ -130,8 +130,42 @@ (ssrf/validate-uri "http://127.0.0.1/foo") (t/is false "should have thrown") (catch Exception e - (t/is (= :validation (:type (ex-data e)))) - (t/is (= :ssrf-blocked-target (:code (ex-data e))))))) + (let [data (ex-data e)] + (t/is (= :validation (:type data))) + (t/is (= :ssrf-blocked-target (:code data))) + (t/is (= "http://127.0.0.1/foo" (:uri data))))))) + +(t/deftest validate-url-throw-on-scheme + (try + (ssrf/validate-uri "file:///etc/passwd") + (t/is false "should have thrown") + (catch Exception e + (let [data (ex-data e)] + (t/is (= :validation (:type data))) + (t/is (= :ssrf-blocked-target (:code data))) + (t/is (= "file:///etc/passwd" (:uri data))) + (t/is (= "file" (:scheme data))))))) + +(t/deftest validate-url-throw-on-missing-host + (try + (ssrf/validate-uri "http:///path") + (t/is false "should have thrown") + (catch Exception e + (let [data (ex-data e)] + (t/is (= :validation (:type data))) + (t/is (= :ssrf-blocked-target (:code data))) + (t/is (= "http:///path" (:uri data))) + (t/is (nil? (:host data))))))) + +(t/deftest validate-url-throw-on-dns-failure + (try + (ssrf/validate-uri "http://nonexistent.invalid/foo") + (t/is false "should have thrown") + (catch Exception e + (let [data (ex-data e)] + (t/is (= :validation (:type data))) + (t/is (= :ssrf-blocked-target (:code data))) + (t/is (= "http://nonexistent.invalid/foo" (:uri data))))))) ;; --------------------------------------------------------------------------- ;; http/req automatic SSRF validation