From 7823ff968c252f581a3234a4802624fedddb7a95 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Tue, 18 Jul 2023 15:55:11 +0100
Subject: [PATCH 1/6] JavaScript: Improve query help for
`js/server-side-unvalidated-url-redirection`.
---
.../Security/CWE-601/ServerSideUrlRedirect.qhelp | 11 +++++++++++
.../CWE-601/examples/ServerSideUrlRedirect.js | 4 ++--
.../CWE-601/examples/ServerSideUrlRedirectGood.js | 9 ++++++---
.../examples/ServerSideUrlRedirectGood2.js | 15 +++++++++++++++
.../ServerSideUrlRedirect.expected | 5 +++++
.../ServerSideUrlRedirect.js | 6 ++++++
.../ServerSideUrlRedirectGood.js | 13 +++++++++++++
.../ServerSideUrlRedirectGood2.js | 15 +++++++++++++++
8 files changed, 73 insertions(+), 5 deletions(-)
create mode 100644 javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
create mode 100644 javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
diff --git a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
index 2052f16146bc..2faa5f8f9d72 100644
--- a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
+++ b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
@@ -16,6 +16,10 @@ To guard against untrusted URL redirection, it is advisable to avoid putting use
directly into a redirect URL. Instead, maintain a list of authorized
redirects on the server; then choose from that list based on the user input provided.
+
+If this is not possible, then the user input should be validated in some other way,
+for example by verifying that the target URL is on the same host as the current page.
+
@@ -32,6 +36,13 @@ before doing the redirection:
+
+
+Alternatively, we can check that the target URL does not redirect to a different host:
+
+
+
+
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
index 0aa4eff9a8f8..d2ae7511c932 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
@@ -1,6 +1,6 @@
const app = require("express")();
-app.get('/some/path', function(req, res) {
+app.get('/redirect', function(req, res) {
// BAD: a request parameter is incorporated without validation into a URL redirect
- res.redirect(req.param("target"));
+ res.redirect(req.params["target"]);
});
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
index d2f2a95ef680..8903ddf09fae 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
@@ -2,9 +2,12 @@ const app = require("express")();
const VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
-app.get('/some/path', function(req, res) {
+app.get('/redirect', function(req, res) {
// GOOD: the request parameter is validated against a known fixed string
- let target = req.param("target");
- if (VALID_REDIRECT === target)
+ let target = req.params["target"]
+ if (VALID_REDIRECT === target) {
res.redirect(target);
+ } else {
+ res.redirect("/");
+ }
});
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
new file mode 100644
index 000000000000..5eaedd0aac58
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
@@ -0,0 +1,15 @@
+const app = require("express")();
+
+function isLocalUrl(url) {
+ return url.startsWith("/") && !url.startsWith("//") && !url.startsWith("/\\");
+}
+
+app.get('/redirect', function(req, res) {
+ // GOOD: check that we don't redirect to a different host
+ let target = req.params["target"];
+ if (isLocalUrl(target)) {
+ res.redirect(target);
+ } else {
+ res.redirect("/");
+ }
+});
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
index e0ef0bfaae84..2883cd3fdac0 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
@@ -1,4 +1,7 @@
nodes
+| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
+| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
+| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
@@ -114,6 +117,7 @@ nodes
| react-native.js:9:26:9:32 | tainted |
| react-native.js:9:26:9:32 | tainted |
edges
+| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") |
| express.js:27:7:27:34 | target | express.js:33:18:33:23 | target |
@@ -211,6 +215,7 @@ edges
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
#select
+| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | Untrusted URL redirection depends on a $@. | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | user-provided value |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:7:16:7:34 | req.param("target") | user-provided value |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:12:26:12:44 | req.param("target") | user-provided value |
| express.js:33:18:33:23 | target | express.js:27:16:27:34 | req.param("target") | express.js:33:18:33:23 | target | Untrusted URL redirection depends on a $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
new file mode 100644
index 000000000000..d2ae7511c932
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
@@ -0,0 +1,6 @@
+const app = require("express")();
+
+app.get('/redirect', function(req, res) {
+ // BAD: a request parameter is incorporated without validation into a URL redirect
+ res.redirect(req.params["target"]);
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
new file mode 100644
index 000000000000..8903ddf09fae
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
@@ -0,0 +1,13 @@
+const app = require("express")();
+
+const VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
+
+app.get('/redirect', function(req, res) {
+ // GOOD: the request parameter is validated against a known fixed string
+ let target = req.params["target"]
+ if (VALID_REDIRECT === target) {
+ res.redirect(target);
+ } else {
+ res.redirect("/");
+ }
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
new file mode 100644
index 000000000000..5eaedd0aac58
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
@@ -0,0 +1,15 @@
+const app = require("express")();
+
+function isLocalUrl(url) {
+ return url.startsWith("/") && !url.startsWith("//") && !url.startsWith("/\\");
+}
+
+app.get('/redirect', function(req, res) {
+ // GOOD: check that we don't redirect to a different host
+ let target = req.params["target"];
+ if (isLocalUrl(target)) {
+ res.redirect(target);
+ } else {
+ res.redirect("/");
+ }
+});
\ No newline at end of file
From 87364137df810ab89c2c0dc2173c1d8f99918de5 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Fri, 18 Aug 2023 14:43:57 +0100
Subject: [PATCH 2/6] Use more sensible validator in example.
---
.../CWE-601/examples/ServerSideUrlRedirect.js | 4 ++--
.../CWE-601/examples/ServerSideUrlRedirectGood.js | 4 ++--
.../CWE-601/examples/ServerSideUrlRedirectGood2.js | 12 ++++++------
.../ServerSideUrlRedirect.expected | 10 +++++-----
.../ServerSideUrlRedirect/ServerSideUrlRedirect.js | 4 ++--
.../ServerSideUrlRedirectGood.js | 4 ++--
.../ServerSideUrlRedirectGood2.js | 12 ++++++------
7 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
index d2ae7511c932..38538fc0f964 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirect.js
@@ -1,6 +1,6 @@
const app = require("express")();
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// BAD: a request parameter is incorporated without validation into a URL redirect
- res.redirect(req.params["target"]);
+ res.redirect(req.query["target"]);
});
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
index 8903ddf09fae..179f28ccb0bd 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood.js
@@ -2,9 +2,9 @@ const app = require("express")();
const VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// GOOD: the request parameter is validated against a known fixed string
- let target = req.params["target"]
+ let target = req.query["target"];
if (VALID_REDIRECT === target) {
res.redirect(target);
} else {
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
index 5eaedd0aac58..78b3be6c260f 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
@@ -1,15 +1,15 @@
const app = require("express")();
-function isLocalUrl(url) {
- return url.startsWith("/") && !url.startsWith("//") && !url.startsWith("/\\");
+function isRelativePath(path) {
+ return !/^(\w+:)?[/\\]{2}/.test(path);
}
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// GOOD: check that we don't redirect to a different host
- let target = req.params["target"];
- if (isLocalUrl(target)) {
+ let target = req.query["target"];
+ if (isRelativePath(target)) {
res.redirect(target);
} else {
res.redirect("/");
}
-});
\ No newline at end of file
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
index 2883cd3fdac0..c03f57e7dd5c 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected
@@ -1,7 +1,7 @@
nodes
-| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
-| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
-| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
+| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
+| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
+| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
| express.js:7:16:7:34 | req.param("target") |
@@ -117,7 +117,7 @@ nodes
| react-native.js:9:26:9:32 | tainted |
| react-native.js:9:26:9:32 | tainted |
edges
-| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] |
+| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") |
| express.js:27:7:27:34 | target | express.js:33:18:33:23 | target |
@@ -215,7 +215,7 @@ edges
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
#select
-| ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | Untrusted URL redirection depends on a $@. | ServerSideUrlRedirect.js:5:16:5:35 | req.params["target"] | user-provided value |
+| ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | Untrusted URL redirection depends on a $@. | ServerSideUrlRedirect.js:5:16:5:34 | req.query["target"] | user-provided value |
| express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | express.js:7:16:7:34 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:7:16:7:34 | req.param("target") | user-provided value |
| express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | express.js:12:26:12:44 | req.param("target") | Untrusted URL redirection depends on a $@. | express.js:12:26:12:44 | req.param("target") | user-provided value |
| express.js:33:18:33:23 | target | express.js:27:16:27:34 | req.param("target") | express.js:33:18:33:23 | target | Untrusted URL redirection depends on a $@. | express.js:27:16:27:34 | req.param("target") | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
index d2ae7511c932..38538fc0f964 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.js
@@ -1,6 +1,6 @@
const app = require("express")();
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// BAD: a request parameter is incorporated without validation into a URL redirect
- res.redirect(req.params["target"]);
+ res.redirect(req.query["target"]);
});
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
index 8903ddf09fae..179f28ccb0bd 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood.js
@@ -2,9 +2,9 @@ const app = require("express")();
const VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html";
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// GOOD: the request parameter is validated against a known fixed string
- let target = req.params["target"]
+ let target = req.query["target"];
if (VALID_REDIRECT === target) {
res.redirect(target);
} else {
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
index 5eaedd0aac58..78b3be6c260f 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
@@ -1,15 +1,15 @@
const app = require("express")();
-function isLocalUrl(url) {
- return url.startsWith("/") && !url.startsWith("//") && !url.startsWith("/\\");
+function isRelativePath(path) {
+ return !/^(\w+:)?[/\\]{2}/.test(path);
}
-app.get('/redirect', function(req, res) {
+app.get("/redirect", function (req, res) {
// GOOD: check that we don't redirect to a different host
- let target = req.params["target"];
- if (isLocalUrl(target)) {
+ let target = req.query["target"];
+ if (isRelativePath(target)) {
res.redirect(target);
} else {
res.redirect("/");
}
-});
\ No newline at end of file
+});
From a02f373e79fb3ff3d9be7d6865266c28b1855c3c Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Wed, 6 Sep 2023 14:06:16 +0100
Subject: [PATCH 3/6] Use better sanitiser.
---
.../CWE-601/examples/ServerSideUrlRedirectGood2.js | 12 +++++++++---
.../ServerSideUrlRedirectGood2.js | 12 +++++++++---
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
index 78b3be6c260f..a36721a3717c 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
@@ -1,13 +1,19 @@
const app = require("express")();
-function isRelativePath(path) {
- return !/^(\w+:)?[/\\]{2}/.test(path);
+function isLocalUrl(path) {
+ try {
+ return (
+ new URL(path, "https://example.com").origin === "https://example.com"
+ );
+ } catch (e) {
+ return false;
+ }
}
app.get("/redirect", function (req, res) {
// GOOD: check that we don't redirect to a different host
let target = req.query["target"];
- if (isRelativePath(target)) {
+ if (isLocalUrl(target)) {
res.redirect(target);
} else {
res.redirect("/");
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
index 78b3be6c260f..a36721a3717c 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
@@ -1,13 +1,19 @@
const app = require("express")();
-function isRelativePath(path) {
- return !/^(\w+:)?[/\\]{2}/.test(path);
+function isLocalUrl(path) {
+ try {
+ return (
+ new URL(path, "https://example.com").origin === "https://example.com"
+ );
+ } catch (e) {
+ return false;
+ }
}
app.get("/redirect", function (req, res) {
// GOOD: check that we don't redirect to a different host
let target = req.query["target"];
- if (isRelativePath(target)) {
+ if (isLocalUrl(target)) {
res.redirect(target);
} else {
res.redirect("/");
From 46d7165885da3b80c7d4b45dc29d7069be3e0a38 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Thu, 7 Sep 2023 09:12:07 +0100
Subject: [PATCH 4/6] Explain about redirects to example.com.
---
.../src/Security/CWE-601/ServerSideUrlRedirect.qhelp | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
index 2faa5f8f9d72..b2217d357d12 100644
--- a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
+++ b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
@@ -38,11 +38,19 @@ before doing the redirection:
-Alternatively, we can check that the target URL does not redirect to a different host:
+Alternatively, we can check that the target URL does not redirect to a different host
+by parsing it relative to a base URL with a known host and verifying that the host
+stays the same:
+
+Note that as written, the above code will allow redirects to URLs on example.com
,
+which is harmless but perhaps not intended. Substitute your own domain name for
+example.com
to prevent this.
+
+
From 7ddb7da65efdbc938064788c6a70f75df5995f28 Mon Sep 17 00:00:00 2001
From: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
Date: Tue, 12 Sep 2023 16:47:23 +0100
Subject: [PATCH 5/6] Apply suggestions from code review
Co-authored-by: Felicity Chapman
---
javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
index b2217d357d12..6c74b7b1a5da 100644
--- a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
+++ b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
@@ -18,7 +18,7 @@ redirects on the server; then choose from that list based on the user input prov
If this is not possible, then the user input should be validated in some other way,
-for example by verifying that the target URL is on the same host as the current page.
+for example, by verifying that the target URL is on the same host as the current page.
From a9e81672f039aead086bce412220d98727fb1adc Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Tue, 12 Sep 2023 16:53:36 +0100
Subject: [PATCH 6/6] Make suggestion to replace example.com more explicit.
---
javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp | 2 +-
.../src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js | 1 +
.../CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
index 6c74b7b1a5da..9a4d7d1c0b7c 100644
--- a/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
+++ b/javascript/ql/src/Security/CWE-601/ServerSideUrlRedirect.qhelp
@@ -47,7 +47,7 @@ stays the same:
Note that as written, the above code will allow redirects to URLs on example.com
,
-which is harmless but perhaps not intended. Substitute your own domain name for
+which is harmless but perhaps not intended. You can substitute your own domain (if known) for
example.com
to prevent this.
diff --git a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
index a36721a3717c..54ac26811b31 100644
--- a/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/src/Security/CWE-601/examples/ServerSideUrlRedirectGood2.js
@@ -3,6 +3,7 @@ const app = require("express")();
function isLocalUrl(path) {
try {
return (
+ // TODO: consider substituting your own domain for example.com
new URL(path, "https://example.com").origin === "https://example.com"
);
} catch (e) {
diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
index a36721a3717c..54ac26811b31 100644
--- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
+++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirectGood2.js
@@ -3,6 +3,7 @@ const app = require("express")();
function isLocalUrl(path) {
try {
return (
+ // TODO: consider substituting your own domain for example.com
new URL(path, "https://example.com").origin === "https://example.com"
);
} catch (e) {