diff --git a/javascript/ql/lib/change-notes/2025-03-24-axios-additional-methods.md b/javascript/ql/lib/change-notes/2025-03-24-axios-additional-methods.md new file mode 100644 index 000000000000..9f31730bc14b --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-03-24-axios-additional-methods.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +Enhanced `axios` support with new methods (`postForm`, `putForm`, `patchForm`, `getUri`, `create`) and added support for `interceptors.request` and `interceptors.response`. diff --git a/javascript/ql/lib/ext/axios.model.yml b/javascript/ql/lib/ext/axios.model.yml new file mode 100644 index 000000000000..69740c31e299 --- /dev/null +++ b/javascript/ql/lib/ext/axios.model.yml @@ -0,0 +1,12 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["axios", "Member[interceptors].Member[request].Member[use].Argument[0].Parameter[0].Member[url]", "request-forgery"] + + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["axios", "Member[interceptors].Member[response].Member[use].Argument[0].Parameter[0]", "response"] diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll index c2b01cf73178..673bdf2de33f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll @@ -222,7 +222,10 @@ module ClientRequest { method = "request" or this = axios().getMember(method).getACall() and - method = [httpMethodName(), "request"] + method = [httpMethodName(), "request", "postForm", "putForm", "patchForm", "getUri"] + or + this = axios().getMember("create").getReturn().getACall() and + method = "request" } private int getOptionsArgIndex() { @@ -254,6 +257,8 @@ module ClientRequest { method = ["post", "put"] and result = [this.getArgument(1), this.getOptionArgument(2, "data")] or + method = ["postForm", "putForm", "patchForm"] and result = this.getArgument(1) + or result = this.getOptionArgument([0 .. 2], ["headers", "params"]) } diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index bb3a73004536..f787a7e60603 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -103,6 +103,13 @@ test_ClientRequest | tst.js:334:5:334:25 | got.pag ... rl, {}) | | tst.js:337:5:337:20 | jsonClient.get() | | tst.js:340:5:340:21 | jsonClient2.get() | +| tst.js:344:5:344:37 | axios.p ... config) | +| tst.js:345:5:345:28 | axios.p ... , data) | +| tst.js:346:5:346:36 | axios.p ... config) | +| tst.js:347:5:347:30 | axios.p ... , data) | +| tst.js:348:5:348:38 | axios.p ... config) | +| tst.js:349:5:349:30 | axios.g ... url }) | +| tst.js:352:5:352:66 | axiosIn ... text"}) | test_getADataNode | axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:15:18:15:55 | { 'Cont ... json' } | | axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:16:15:16:35 | {x: 'te ... 'test'} | @@ -146,6 +153,11 @@ test_getADataNode | tst.js:257:1:262:2 | form.su ... rs()\\n}) | tst.js:255:25:255:35 | 'new_value' | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:288:21:288:35 | 'Hello Server!' | | tst.js:321:5:321:32 | superag ... st(url) | tst.js:321:39:321:42 | data | +| tst.js:344:5:344:37 | axios.p ... config) | tst.js:344:25:344:28 | data | +| tst.js:345:5:345:28 | axios.p ... , data) | tst.js:345:24:345:27 | data | +| tst.js:346:5:346:36 | axios.p ... config) | tst.js:346:24:346:27 | data | +| tst.js:347:5:347:30 | axios.p ... , data) | tst.js:347:26:347:29 | data | +| tst.js:348:5:348:38 | axios.p ... config) | tst.js:348:26:348:29 | data | test_getHost | tst.js:87:5:87:39 | http.ge ... host}) | tst.js:87:34:87:37 | host | | tst.js:89:5:89:23 | axios({host: host}) | tst.js:89:18:89:21 | host | @@ -268,6 +280,14 @@ test_getUrl | tst.js:337:5:337:20 | jsonClient.get() | tst.js:336:41:336:43 | url | | tst.js:340:5:340:21 | jsonClient2.get() | tst.js:339:42:339:44 | url | | tst.js:340:5:340:21 | jsonClient2.get() | tst.js:339:61:339:63 | url | +| tst.js:344:5:344:37 | axios.p ... config) | tst.js:344:20:344:22 | url | +| tst.js:345:5:345:28 | axios.p ... , data) | tst.js:345:19:345:21 | url | +| tst.js:346:5:346:36 | axios.p ... config) | tst.js:346:19:346:21 | url | +| tst.js:347:5:347:30 | axios.p ... , data) | tst.js:347:21:347:23 | url | +| tst.js:348:5:348:38 | axios.p ... config) | tst.js:348:21:348:23 | url | +| tst.js:349:5:349:30 | axios.g ... url }) | tst.js:349:18:349:29 | { url: url } | +| tst.js:352:5:352:66 | axiosIn ... text"}) | tst.js:352:19:352:65 | {method ... "text"} | +| tst.js:352:5:352:66 | axiosIn ... text"}) | tst.js:352:40:352:42 | url | test_getAResponseDataNode | axiosTest.js:4:5:7:6 | axios({ ... \\n }) | axiosTest.js:4:5:7:6 | axios({ ... \\n }) | json | true | | axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:12:5:17:6 | axios({ ... \\n }) | json | true | @@ -354,3 +374,10 @@ test_getAResponseDataNode | tst.js:334:5:334:25 | got.pag ... rl, {}) | tst.js:334:5:334:25 | got.pag ... rl, {}) | text | true | | tst.js:337:5:337:20 | jsonClient.get() | tst.js:337:5:337:20 | jsonClient.get() | text | true | | tst.js:340:5:340:21 | jsonClient2.get() | tst.js:340:5:340:21 | jsonClient2.get() | text | true | +| tst.js:344:5:344:37 | axios.p ... config) | tst.js:344:5:344:37 | axios.p ... config) | json | true | +| tst.js:345:5:345:28 | axios.p ... , data) | tst.js:345:5:345:28 | axios.p ... , data) | json | true | +| tst.js:346:5:346:36 | axios.p ... config) | tst.js:346:5:346:36 | axios.p ... config) | json | true | +| tst.js:347:5:347:30 | axios.p ... , data) | tst.js:347:5:347:30 | axios.p ... , data) | json | true | +| tst.js:348:5:348:38 | axios.p ... config) | tst.js:348:5:348:38 | axios.p ... config) | json | true | +| tst.js:349:5:349:30 | axios.g ... url }) | tst.js:349:5:349:30 | axios.g ... url }) | json | true | +| tst.js:352:5:352:66 | axiosIn ... text"}) | tst.js:352:5:352:66 | axiosIn ... text"}) | text | true | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index c9fc40dc5068..3cd086fae0e7 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -339,3 +339,15 @@ function gotTests(url){ const jsonClient2 = got.extend({url: url}).extend({url: url}); jsonClient2.get(); } + +function moreAxiosTests(url, data, config){ + axios.postForm(url, data, config); + axios.putForm(url, data); + axios.putForm(url, data, config); + axios.patchForm(url, data); + axios.patchForm(url, data, config); + axios.getUri({ url: url }); + + const axiosInstance = axios.create({}); + axiosInstance({method: "get", url: url, responseType: "text"}); +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index ef0c88098b98..afc30e246085 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -1,4 +1,5 @@ #select +| interceptors.js:9:56:9:72 | userGeneratedHtml | interceptors.js:7:6:7:13 | response | interceptors.js:9:56:9:72 | userGeneratedHtml | Cross-site scripting vulnerability due to $@. | interceptors.js:7:6:7:13 | response | user-provided value | | test.jsx:27:29:27:32 | data | test.jsx:5:28:5:63 | fetch(" ... ntent") | test.jsx:27:29:27:32 | data | Cross-site scripting vulnerability due to $@. | test.jsx:5:28:5:63 | fetch(" ... ntent") | user-provided value | | test.ts:21:57:21:76 | response.description | test.ts:8:9:8:79 | this.#h ... query') | test.ts:21:57:21:76 | response.description | Cross-site scripting vulnerability due to $@. | test.ts:8:9:8:79 | this.#h ... query') | user-provided value | | test.ts:24:36:24:90 | `