Merge pull request #19634 from Napalys/js/url_obj_propagation

JS: Add URL constructor taint tracking for request forgery
This commit is contained in:
Napalys Klicius
2025-06-02 17:32:44 +02:00
committed by GitHub
4 changed files with 42 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added taint flow through the `URL` constructor from the `url` package, improving the identification of SSRF vulnerabilities.

View File

@@ -82,6 +82,13 @@ module RequestForgery {
pred = url.getArgument(0)
)
or
exists(DataFlow::NewNode url |
url = API::moduleImport("url").getMember("URL").getAnInstantiation()
|
succ = url and
pred = url.getArgument(0)
)
or
exists(HtmlSanitizerCall call |
pred = call.getInput() and
succ = call

View File

@@ -30,6 +30,9 @@
| serverSide.js:117:20:117:30 | new ws(url) | serverSide.js:115:25:115:35 | request.url | serverSide.js:117:27:117:29 | url | The $@ of this request depends on a $@. | serverSide.js:117:27:117:29 | url | URL | serverSide.js:115:25:115:35 | request.url | user-provided value |
| serverSide.js:125:5:128:6 | axios({ ... \\n }) | serverSide.js:123:29:123:35 | req.url | serverSide.js:127:14:127:20 | tainted | The $@ of this request depends on a $@. | serverSide.js:127:14:127:20 | tainted | URL | serverSide.js:123:29:123:35 | req.url | user-provided value |
| serverSide.js:131:5:131:20 | axios.get(myUrl) | serverSide.js:123:29:123:35 | req.url | serverSide.js:131:15:131:19 | myUrl | The $@ of this request depends on a $@. | serverSide.js:131:15:131:19 | myUrl | URL | serverSide.js:123:29:123:35 | req.url | user-provided value |
| serverSide.js:141:3:141:30 | axios.g ... ring()) | serverSide.js:139:17:139:29 | req.query.url | serverSide.js:141:13:141:29 | target.toString() | The $@ of this request depends on a $@. | serverSide.js:141:13:141:29 | target.toString() | URL | serverSide.js:139:17:139:29 | req.query.url | user-provided value |
| serverSide.js:142:3:142:19 | axios.get(target) | serverSide.js:139:17:139:29 | req.query.url | serverSide.js:142:13:142:18 | target | The $@ of this request depends on a $@. | serverSide.js:142:13:142:18 | target | URL | serverSide.js:139:17:139:29 | req.query.url | user-provided value |
| serverSide.js:143:3:143:24 | axios.g ... t.href) | serverSide.js:139:17:139:29 | req.query.url | serverSide.js:143:13:143:23 | target.href | The $@ of this request depends on a $@. | serverSide.js:143:13:143:23 | target.href | URL | serverSide.js:139:17:139:29 | req.query.url | user-provided value |
edges
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | Request/app/api/proxy/route2.serverSide.ts:4:9:4:34 | url | provenance | |
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:34 | url | Request/app/api/proxy/route2.serverSide.ts:5:27:5:29 | url | provenance | |
@@ -106,6 +109,15 @@ edges
| serverSide.js:123:29:123:35 | req.url | serverSide.js:123:19:123:42 | url.par ... , true) | provenance | |
| serverSide.js:130:9:130:45 | myUrl | serverSide.js:131:15:131:19 | myUrl | provenance | |
| serverSide.js:130:37:130:43 | tainted | serverSide.js:130:9:130:45 | myUrl | provenance | |
| serverSide.js:139:9:139:29 | input | serverSide.js:140:26:140:30 | input | provenance | |
| serverSide.js:139:17:139:29 | req.query.url | serverSide.js:139:9:139:29 | input | provenance | |
| serverSide.js:140:9:140:31 | target | serverSide.js:141:13:141:18 | target | provenance | |
| serverSide.js:140:9:140:31 | target | serverSide.js:142:13:142:18 | target | provenance | |
| serverSide.js:140:9:140:31 | target | serverSide.js:143:13:143:18 | target | provenance | |
| serverSide.js:140:18:140:31 | new URL(input) | serverSide.js:140:9:140:31 | target | provenance | |
| serverSide.js:140:26:140:30 | input | serverSide.js:140:18:140:31 | new URL(input) | provenance | Config |
| serverSide.js:141:13:141:18 | target | serverSide.js:141:13:141:29 | target.toString() | provenance | |
| serverSide.js:143:13:143:18 | target | serverSide.js:143:13:143:23 | target.href | provenance | |
nodes
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | semmle.label | { url } |
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:34 | url | semmle.label | url |
@@ -199,4 +211,14 @@ nodes
| serverSide.js:130:9:130:45 | myUrl | semmle.label | myUrl |
| serverSide.js:130:37:130:43 | tainted | semmle.label | tainted |
| serverSide.js:131:15:131:19 | myUrl | semmle.label | myUrl |
| serverSide.js:139:9:139:29 | input | semmle.label | input |
| serverSide.js:139:17:139:29 | req.query.url | semmle.label | req.query.url |
| serverSide.js:140:9:140:31 | target | semmle.label | target |
| serverSide.js:140:18:140:31 | new URL(input) | semmle.label | new URL(input) |
| serverSide.js:140:26:140:30 | input | semmle.label | input |
| serverSide.js:141:13:141:18 | target | semmle.label | target |
| serverSide.js:141:13:141:29 | target.toString() | semmle.label | target.toString() |
| serverSide.js:142:13:142:18 | target | semmle.label | target |
| serverSide.js:143:13:143:18 | target | semmle.label | target |
| serverSide.js:143:13:143:23 | target.href | semmle.label | target.href |
subpaths

View File

@@ -133,3 +133,12 @@ var server2 = http.createServer(function(req, res) {
var myEncodedUrl = `${something}/bla/${encodeURIComponent(tainted)}`;
axios.get(myEncodedUrl);
})
var server2 = http.createServer(function(req, res) {
const { URL } = require('url');
const input = req.query.url; // $Source[js/request-forgery]
const target = new URL(input);
axios.get(target.toString()); // $Alert[js/request-forgery]
axios.get(target); // $Alert[js/request-forgery]
axios.get(target.href); // $Alert[js/request-forgery]
});