From 178da21fb8868280826e3d1dbec5a1e43f673717 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 25 Nov 2024 11:53:00 +0100 Subject: [PATCH 01/36] JS: Added test case for CWE-178 RegExp with unknown flags --- .../CWE-178/CaseSensitiveMiddlewarePath.expected | 1 + .../ql/test/query-tests/Security/CWE-178/tst.js | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected index 2195bbf00297..2be1780188a0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected @@ -2,6 +2,7 @@ | tst.js:14:5:14:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/FOO/1' will bypass the middleware. | tst.js:14:5:14:28 | new Reg ... (.*)?') | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | case-insensitive path | | tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/FOO/1' will bypass the middleware. | tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | case-insensitive path | | tst.js:64:5:64:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAR/1' will bypass the middleware. | tst.js:64:5:64:28 | new Reg ... (.*)?') | pattern | tst.js:73:1:74:2 | app.get ... ware\\n}) | case-insensitive path | +| tst.js:64:5:64:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAR/1' will bypass the middleware. | tst.js:64:5:64:28 | new Reg ... (.*)?') | pattern | tst.js:107:1:108:2 | app.get ... ware\\n}) | case-insensitive path | | tst.js:76:9:76:20 | /\\/baz\\/bla/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ/BLA' will bypass the middleware. | tst.js:76:9:76:20 | /\\/baz\\/bla/ | pattern | tst.js:77:1:79:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ3/A' will bypass the middleware. | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | pattern | tst.js:87:1:89:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/CURRENTGAME' will bypass the middleware. | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | pattern | tst.js:93:1:95:2 | app.get ... O");\\n}) | case-insensitive path | diff --git a/javascript/ql/test/query-tests/Security/CWE-178/tst.js b/javascript/ql/test/query-tests/Security/CWE-178/tst.js index 5fcc3cc94a0f..3dd24029ebe0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-178/tst.js @@ -93,3 +93,16 @@ app.use(/\/summonerByName|\/currentGame/,apiLimit1, apiLimit2); app.get('/currentGame', function (req, res) { res.send("FOO"); }); + +app.get( + new RegExp('^/bar(.*)?', unknownFlag()), // NOT OK - Currently not flagged. + unknown(), + function(req, res, next) { + if (req.params.blah) { + next(); + } + } +); + +app.get('/bar/*', (req, res) => { // OK - not a middleware +}); From e38b63ebcda88f698ab728c8d2aabd12cef06c51 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 25 Nov 2024 11:56:06 +0100 Subject: [PATCH 02/36] JS: previously js/case-sensitive-middleware-path was not taking into consideration unknown flags --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql | 2 +- .../Security/CWE-178/CaseSensitiveMiddlewarePath.expected | 1 + javascript/ql/test/query-tests/Security/CWE-178/tst.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index d16f72d4172a..2045d801c702 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -65,7 +65,7 @@ predicate isCaseSensitiveMiddleware( arg = call.getArgument(0) and regexp.getAReference().flowsTo(arg) and exists(string flags | - flags = regexp.getFlags() and + flags = regexp.tryGetFlags() and not RegExp::isIgnoreCase(flags) ) ) diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected index 2be1780188a0..cb1720260ef8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected @@ -6,3 +6,4 @@ | tst.js:76:9:76:20 | /\\/baz\\/bla/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ/BLA' will bypass the middleware. | tst.js:76:9:76:20 | /\\/baz\\/bla/ | pattern | tst.js:77:1:79:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ3/A' will bypass the middleware. | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | pattern | tst.js:87:1:89:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/CURRENTGAME' will bypass the middleware. | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | pattern | tst.js:93:1:95:2 | app.get ... O");\\n}) | case-insensitive path | +| tst.js:98:5:98:43 | new Reg ... Flag()) | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAR/1' will bypass the middleware. | tst.js:98:5:98:43 | new Reg ... Flag()) | pattern | tst.js:107:1:108:2 | app.get ... ware\\n}) | case-insensitive path | diff --git a/javascript/ql/test/query-tests/Security/CWE-178/tst.js b/javascript/ql/test/query-tests/Security/CWE-178/tst.js index 3dd24029ebe0..69a203cb8df0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-178/tst.js @@ -95,7 +95,7 @@ app.get('/currentGame', function (req, res) { }); app.get( - new RegExp('^/bar(.*)?', unknownFlag()), // NOT OK - Currently not flagged. + new RegExp('^/bar(.*)?', unknownFlag()), // NOT OK - Might be OK if the unknown flag evaluates to case insensitive one unknown(), function(req, res, next) { if (req.params.blah) { From d6372aebc76cb196665f34e05671b0006bc6445c Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 25 Nov 2024 12:11:07 +0100 Subject: [PATCH 03/36] Update javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql Co-authored-by: Erik Krogh Kristensen --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql | 2 +- .../Security/CWE-178/CaseSensitiveMiddlewarePath.expected | 1 - javascript/ql/test/query-tests/Security/CWE-178/tst.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index 2045d801c702..997ee8971a5a 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -66,7 +66,7 @@ predicate isCaseSensitiveMiddleware( regexp.getAReference().flowsTo(arg) and exists(string flags | flags = regexp.tryGetFlags() and - not RegExp::isIgnoreCase(flags) + not RegExp::maybeIgnoreCase(flags) ) ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected index cb1720260ef8..2be1780188a0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected @@ -6,4 +6,3 @@ | tst.js:76:9:76:20 | /\\/baz\\/bla/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ/BLA' will bypass the middleware. | tst.js:76:9:76:20 | /\\/baz\\/bla/ | pattern | tst.js:77:1:79:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAZ3/A' will bypass the middleware. | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | pattern | tst.js:87:1:89:2 | app.get ... });\\n}) | case-insensitive path | | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/CURRENTGAME' will bypass the middleware. | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | pattern | tst.js:93:1:95:2 | app.get ... O");\\n}) | case-insensitive path | -| tst.js:98:5:98:43 | new Reg ... Flag()) | This route uses a case-sensitive path $@, but is guarding a $@. A path such as '/BAR/1' will bypass the middleware. | tst.js:98:5:98:43 | new Reg ... Flag()) | pattern | tst.js:107:1:108:2 | app.get ... ware\\n}) | case-insensitive path | diff --git a/javascript/ql/test/query-tests/Security/CWE-178/tst.js b/javascript/ql/test/query-tests/Security/CWE-178/tst.js index 69a203cb8df0..81bbe993291a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-178/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-178/tst.js @@ -95,7 +95,7 @@ app.get('/currentGame', function (req, res) { }); app.get( - new RegExp('^/bar(.*)?', unknownFlag()), // NOT OK - Might be OK if the unknown flag evaluates to case insensitive one + new RegExp('^/bar(.*)?', unknownFlag()), // OK - Might be OK if the unknown flag evaluates to case insensitive one unknown(), function(req, res, next) { if (req.params.blah) { From 41f21d429ba46b16aa3285f75482e5fbb1228a00 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 09:18:51 +0100 Subject: [PATCH 04/36] JS: Added test case which is not flagged but should be abusing new RegExp with global flag --- .../Security/CWE-116/IncompleteSanitization/tst.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js index dbd04664a52c..1a10415a3177 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js @@ -327,4 +327,8 @@ function incompleteComplexSanitizers() { if (str === "\"") return """; }) + '"'; -} \ No newline at end of file +} + +function typicalBadHtmlSanitizers(s) { + s().replace(new RegExp("[<>]", "g"),''); // NOT OK -- should be not okay, but is not flagged +} From 38be0e4c0a0b7368c8d3f43785f48fe257f6b702 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 09:20:34 +0100 Subject: [PATCH 05/36] JS: Now BadHtmlSanitizers also flags new RegExp as potential issue --- .../javascript/security/IncompleteBlacklistSanitizer.qll | 2 +- .../IncompleteBlacklistSanitizer.expected | 3 +++ .../query-tests/Security/CWE-116/IncompleteSanitization/tst.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll b/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll index 6b05e2c754d8..5864d8ca7c8d 100644 --- a/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll +++ b/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll @@ -74,7 +74,7 @@ private StringReplaceCall getAStringReplaceMethodCall(StringReplaceCall n) { module HtmlSanitization { private predicate fixedGlobalReplacement(StringReplaceCallSequence chain) { forall(StringReplaceCall member | member = chain.getAMember() | - member.isGlobal() and member.getArgument(0) instanceof DataFlow::RegExpLiteralNode + member.isGlobal() and member.getArgument(0) instanceof DataFlow::RegExpCreationNode ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected index 4223a4224d33..00d07f976572 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected @@ -65,3 +65,6 @@ | tst.js:305:10:305:34 | s().rep ... ]/g,'') | This HTML sanitizer does not sanitize double quotes | | tst.js:309:10:318:3 | s().rep ... ;";\\n\\t}) | This HTML sanitizer does not sanitize single quotes | | tst.js:320:9:329:3 | s().rep ... ;";\\n\\t}) | This HTML sanitizer does not sanitize single quotes | +| tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize ampersands | +| tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize double quotes | +| tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize single quotes | diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js index 1a10415a3177..af75382662b0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js @@ -330,5 +330,5 @@ function incompleteComplexSanitizers() { } function typicalBadHtmlSanitizers(s) { - s().replace(new RegExp("[<>]", "g"),''); // NOT OK -- should be not okay, but is not flagged + s().replace(new RegExp("[<>]", "g"),''); // NOT OK } From 89f3b6f8d34027d6ba35b0ab9816970134896ce0 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 09:35:27 +0100 Subject: [PATCH 06/36] JS: Added test case for bad sanitizer with unknown flags, currently not flagged. --- .../Security/CWE-116/IncompleteSanitization/tst.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js index af75382662b0..d15351e28603 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js @@ -332,3 +332,7 @@ function incompleteComplexSanitizers() { function typicalBadHtmlSanitizers(s) { s().replace(new RegExp("[<>]", "g"),''); // NOT OK } + +function typicalBadHtmlSanitizers(s) { + s().replace(new RegExp("[<>]", unknown()),''); // NOT OK -- should be flagged, because it is st ill a bad sanitizer +} From 18c7b18f82c4fd1fb795e983f032c4fb0feb7f6f Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 09:37:57 +0100 Subject: [PATCH 07/36] JS: Now BadHtmlSanitizers new RegExp with unknown flags is also flagged. --- javascript/ql/lib/semmle/javascript/StandardLibrary.qll | 8 ++++++++ .../javascript/security/IncompleteBlacklistSanitizer.qll | 2 +- .../IncompleteBlacklistSanitizer.expected | 3 +++ .../Security/CWE-116/IncompleteSanitization/tst.js | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll index b40f10d93691..db21d883fc1c 100644 --- a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll @@ -117,6 +117,14 @@ class StringReplaceCall extends DataFlow::MethodCallNode { */ predicate isGlobal() { this.getRegExp().isGlobal() or this.getMethodName() = "replaceAll" } + /** + * Holds if this is a global replacement, that is, the first argument is a regular expression + * with the `g` flag, or this is a call to `.replaceAll()`. + */ + predicate maybeGlobal() { + RegExp::maybeGlobal(this.getRegExp().tryGetFlags()) or this.getMethodName() = "replaceAll" + } + /** * Holds if this call to `replace` replaces `old` with `new`. */ diff --git a/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll b/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll index 5864d8ca7c8d..13d5033458af 100644 --- a/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll +++ b/javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll @@ -74,7 +74,7 @@ private StringReplaceCall getAStringReplaceMethodCall(StringReplaceCall n) { module HtmlSanitization { private predicate fixedGlobalReplacement(StringReplaceCallSequence chain) { forall(StringReplaceCall member | member = chain.getAMember() | - member.isGlobal() and member.getArgument(0) instanceof DataFlow::RegExpCreationNode + member.maybeGlobal() and member.getArgument(0) instanceof DataFlow::RegExpCreationNode ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected index 00d07f976572..379ffbdc9c36 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteBlacklistSanitizer.expected @@ -68,3 +68,6 @@ | tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize ampersands | | tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize double quotes | | tst.js:333:2:333:40 | s().rep ... g"),'') | This HTML sanitizer does not sanitize single quotes | +| tst.js:337:2:337:46 | s().rep ... ()),'') | This HTML sanitizer does not sanitize ampersands | +| tst.js:337:2:337:46 | s().rep ... ()),'') | This HTML sanitizer does not sanitize double quotes | +| tst.js:337:2:337:46 | s().rep ... ()),'') | This HTML sanitizer does not sanitize single quotes | diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js index d15351e28603..f2972f0be769 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js @@ -334,5 +334,5 @@ function typicalBadHtmlSanitizers(s) { } function typicalBadHtmlSanitizers(s) { - s().replace(new RegExp("[<>]", unknown()),''); // NOT OK -- should be flagged, because it is st ill a bad sanitizer + s().replace(new RegExp("[<>]", unknown()),''); // NOT OK } From 41fef0f2b331ecbc0a1fe043e084cb670475cee9 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 11:30:20 +0100 Subject: [PATCH 08/36] JS: Added test cases which cover new RegExp creation with replace on protytpe pulluting --- .../PrototypePollutingAssignment.expected | 20 +++++++++++++++++++ .../PrototypePollutingAssignment/tst.js | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected index 891aeff42218..bad75ffe9244 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected @@ -190,6 +190,16 @@ nodes | tst.js:105:5:105:17 | object[taint] | | tst.js:105:5:105:17 | object[taint] | | tst.js:105:12:105:16 | taint | +| tst.js:130:5:130:53 | obj[req ... ), '')] | +| tst.js:130:5:130:53 | obj[req ... ), '')] | +| tst.js:130:9:130:19 | req.query.x | +| tst.js:130:9:130:19 | req.query.x | +| tst.js:130:9:130:52 | req.que ... '), '') | +| tst.js:131:5:131:65 | obj[req ... ), '')] | +| tst.js:131:5:131:65 | obj[req ... ), '')] | +| tst.js:131:9:131:19 | req.query.x | +| tst.js:131:9:131:19 | req.query.x | +| tst.js:131:9:131:64 | req.que ... )), '') | edges | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | @@ -366,6 +376,14 @@ edges | tst.js:102:24:102:37 | req.query.data | tst.js:102:17:102:38 | String( ... y.data) | | tst.js:105:12:105:16 | taint | tst.js:105:5:105:17 | object[taint] | | tst.js:105:12:105:16 | taint | tst.js:105:5:105:17 | object[taint] | +| tst.js:130:9:130:19 | req.query.x | tst.js:130:9:130:52 | req.que ... '), '') | +| tst.js:130:9:130:19 | req.query.x | tst.js:130:9:130:52 | req.que ... '), '') | +| tst.js:130:9:130:52 | req.que ... '), '') | tst.js:130:5:130:53 | obj[req ... ), '')] | +| tst.js:130:9:130:52 | req.que ... '), '') | tst.js:130:5:130:53 | obj[req ... ), '')] | +| tst.js:131:9:131:19 | req.query.x | tst.js:131:9:131:64 | req.que ... )), '') | +| tst.js:131:9:131:19 | req.query.x | tst.js:131:9:131:64 | req.que ... )), '') | +| tst.js:131:9:131:64 | req.que ... )), '') | tst.js:131:5:131:65 | obj[req ... ), '')] | +| tst.js:131:9:131:64 | req.que ... )), '') | tst.js:131:5:131:65 | obj[req ... ), '')] | #select | lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | library input | | lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input | @@ -394,3 +412,5 @@ edges | tst.js:94:5:94:37 | obj[req ... ', '')] | tst.js:94:9:94:19 | req.query.x | tst.js:94:5:94:37 | obj[req ... ', '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:94:9:94:19 | req.query.x | user controlled input | | tst.js:97:5:97:46 | obj[req ... g, '')] | tst.js:97:9:97:19 | req.query.x | tst.js:97:5:97:46 | obj[req ... g, '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:97:9:97:19 | req.query.x | user controlled input | | tst.js:105:5:105:17 | object[taint] | tst.js:102:24:102:37 | req.query.data | tst.js:105:5:105:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:102:24:102:37 | req.query.data | user controlled input | +| tst.js:130:5:130:53 | obj[req ... ), '')] | tst.js:130:9:130:19 | req.query.x | tst.js:130:5:130:53 | obj[req ... ), '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:130:9:130:19 | req.query.x | user controlled input | +| tst.js:131:5:131:65 | obj[req ... ), '')] | tst.js:131:9:131:19 | req.query.x | tst.js:131:5:131:65 | obj[req ... ), '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:131:9:131:19 | req.query.x | user controlled input | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js index d301fe40bf69..1cc830d0d2d2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js @@ -123,3 +123,10 @@ app.get('/assign', (req, res) => { Object.assign(dest, plainObj[taint]); dest[taint] = taint; // OK - 'dest' is not Object.prototype itself (but possibly a copy) }); + +app.get('/foo', (req, res) => { + let obj = {}; + obj[req.query.x.replace(new RegExp('_', 'g'), '')].x = 'foo'; // OK + obj[req.query.x.replace(new RegExp('_', ''), '')].x = 'foo'; // NOT OK + obj[req.query.x.replace(new RegExp('_', unknownFlags()), '')].x = 'foo'; // OK -- Might be okay but it is currently flagged as a problem +}); From faef9dd8774358c6bdca232bbbb7220cb0556ae4 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 11:32:15 +0100 Subject: [PATCH 09/36] JS: protyte poluting now treats unknownFlags as potentially good sanitization. --- .../dataflow/PrototypePollutingAssignmentQuery.qll | 2 +- .../PrototypePollutingAssignment.expected | 10 ---------- .../CWE-915/PrototypePollutingAssignment/tst.js | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll index 0ba2f26b24c7..197b85942447 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll @@ -46,7 +46,7 @@ class Configuration extends TaintTracking::Configuration { // Replacing with "_" is likely to be exploitable not replace.getRawReplacement().getStringValue() = "_" and ( - replace.isGlobal() + replace.maybeGlobal() or // Non-global replace with a non-empty string can also prevent __proto__ by // inserting a chunk of text that doesn't fit anywhere in __proto__ diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected index bad75ffe9244..50262859441b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected @@ -195,11 +195,6 @@ nodes | tst.js:130:9:130:19 | req.query.x | | tst.js:130:9:130:19 | req.query.x | | tst.js:130:9:130:52 | req.que ... '), '') | -| tst.js:131:5:131:65 | obj[req ... ), '')] | -| tst.js:131:5:131:65 | obj[req ... ), '')] | -| tst.js:131:9:131:19 | req.query.x | -| tst.js:131:9:131:19 | req.query.x | -| tst.js:131:9:131:64 | req.que ... )), '') | edges | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | @@ -380,10 +375,6 @@ edges | tst.js:130:9:130:19 | req.query.x | tst.js:130:9:130:52 | req.que ... '), '') | | tst.js:130:9:130:52 | req.que ... '), '') | tst.js:130:5:130:53 | obj[req ... ), '')] | | tst.js:130:9:130:52 | req.que ... '), '') | tst.js:130:5:130:53 | obj[req ... ), '')] | -| tst.js:131:9:131:19 | req.query.x | tst.js:131:9:131:64 | req.que ... )), '') | -| tst.js:131:9:131:19 | req.query.x | tst.js:131:9:131:64 | req.que ... )), '') | -| tst.js:131:9:131:64 | req.que ... )), '') | tst.js:131:5:131:65 | obj[req ... ), '')] | -| tst.js:131:9:131:64 | req.que ... )), '') | tst.js:131:5:131:65 | obj[req ... ), '')] | #select | lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | library input | | lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input | @@ -413,4 +404,3 @@ edges | tst.js:97:5:97:46 | obj[req ... g, '')] | tst.js:97:9:97:19 | req.query.x | tst.js:97:5:97:46 | obj[req ... g, '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:97:9:97:19 | req.query.x | user controlled input | | tst.js:105:5:105:17 | object[taint] | tst.js:102:24:102:37 | req.query.data | tst.js:105:5:105:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:102:24:102:37 | req.query.data | user controlled input | | tst.js:130:5:130:53 | obj[req ... ), '')] | tst.js:130:9:130:19 | req.query.x | tst.js:130:5:130:53 | obj[req ... ), '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:130:9:130:19 | req.query.x | user controlled input | -| tst.js:131:5:131:65 | obj[req ... ), '')] | tst.js:131:9:131:19 | req.query.x | tst.js:131:5:131:65 | obj[req ... ), '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:131:9:131:19 | req.query.x | user controlled input | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js index 1cc830d0d2d2..a622a8913905 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js @@ -128,5 +128,5 @@ app.get('/foo', (req, res) => { let obj = {}; obj[req.query.x.replace(new RegExp('_', 'g'), '')].x = 'foo'; // OK obj[req.query.x.replace(new RegExp('_', ''), '')].x = 'foo'; // NOT OK - obj[req.query.x.replace(new RegExp('_', unknownFlags()), '')].x = 'foo'; // OK -- Might be okay but it is currently flagged as a problem + obj[req.query.x.replace(new RegExp('_', unknownFlags()), '')].x = 'foo'; // OK }); From 7db6f7c7215183cbee297797e246fb54b5b22ee2 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 12:27:11 +0100 Subject: [PATCH 10/36] JS: Added test cases with new RegExp for Tainted paths, currently works only with literals --- .../dataflow/TaintedPathCustomizations.qll | 6 +- .../CWE-022/TaintedPath/Consistency.expected | 1 + .../CWE-022/TaintedPath/TaintedPath.expected | 313 ++++++++++++++++++ .../CWE-022/TaintedPath/TaintedPath.js | 9 + 4 files changed, 326 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index c24ea7f61100..e6f1a5203d74 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -221,10 +221,10 @@ module TaintedPath { this instanceof StringReplaceCall and input = this.getReceiver() and output = this and - not exists(RegExpLiteral literal, RegExpTerm term | - this.(StringReplaceCall).getRegExp().asExpr() = literal and + not exists(DataFlow::RegExpCreationNode regexp, RegExpTerm term | + this.(StringReplaceCall).getRegExp() = regexp and this.(StringReplaceCall).isGlobal() and - literal.getRoot() = term + regexp.getRoot() = term | term.getAMatchedString() = "/" or term.getAMatchedString() = "." or diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index e69de29bb2d1..81656a2af728 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -0,0 +1 @@ +| TaintedPath.js:207 | did not expect an alert, but found an alert for TaintedPath | OK -- Might be okay depending on what unknownFlags evaluates to. | | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 0022ca69c6ba..4fdd33dcbbab 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -1517,6 +1517,141 @@ nodes | TaintedPath.js:198:35:198:38 | path | | TaintedPath.js:198:35:198:38 | path | | TaintedPath.js:198:35:198:38 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:24:202:30 | req.url | +| TaintedPath.js:202:24:202:30 | req.url | +| TaintedPath.js:202:24:202:30 | req.url | +| TaintedPath.js:202:24:202:30 | req.url | +| TaintedPath.js:202:24:202:30 | req.url | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | @@ -6680,6 +6815,182 @@ edges | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:195:14:195:37 | url.par ... , true) | | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:195:14:195:37 | url.par ... , true) | | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:195:14:195:37 | url.par ... , true) | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:43 | url.par ... ).query | TaintedPath.js:202:14:202:48 | url.par ... ry.path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:14:202:48 | url.par ... ry.path | TaintedPath.js:202:7:202:48 | path | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:202:14:202:37 | url.par ... , true) | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | +| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | @@ -10499,6 +10810,8 @@ edges | TaintedPath.js:196:31:196:34 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:196:31:196:34 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:197:45:197:48 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:197:45:197:48 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:198:35:198:38 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:198:35:198:38 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | +| TaintedPath.js:206:29:206:85 | path.re ... '), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:206:29:206:85 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | +| TaintedPath.js:207:29:207:97 | path.re ... )), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:207:29:207:97 | path.re ... )), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value | | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value | | handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on a $@. | handlebars.js:29:46:29:60 | req.params.path | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index d28549da0ec0..13948fc431a9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -197,3 +197,12 @@ var server = http.createServer(function(req, res) { cp.execFileSync("foobar", ["args"], {cwd: path}); // NOT OK cp.execFileSync("foobar", {cwd: path}); // NOT OK }); + +var server = http.createServer(function(req, res) { + let path = url.parse(req.url, true).query.path; + + // Removal of forward-slash or dots. + res.write(fs.readFileSync(path.replace(new RegExp("[\\]\\[*,;'\"`<>\\?/]", 'g'), ''))); // OK + res.write(fs.readFileSync(path.replace(new RegExp("[\\]\\[*,;'\"`<>\\?/]", ''), ''))); // NOT OK. + res.write(fs.readFileSync(path.replace(new RegExp("[\\]\\[*,;'\"`<>\\?/]", unknownFlags()), ''))); // OK -- Might be okay depending on what unknownFlags evaluates to. +}); From eca7a8861555695c7330c7d0cd48f9e234e049c7 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 12:41:59 +0100 Subject: [PATCH 11/36] JS: Fixed docs description --- javascript/ql/lib/semmle/javascript/StandardLibrary.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll index db21d883fc1c..e886ff50eb43 100644 --- a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll @@ -119,7 +119,7 @@ class StringReplaceCall extends DataFlow::MethodCallNode { /** * Holds if this is a global replacement, that is, the first argument is a regular expression - * with the `g` flag, or this is a call to `.replaceAll()`. + * with the `g` flag or unknown flags, or this is a call to `.replaceAll()`. */ predicate maybeGlobal() { RegExp::maybeGlobal(this.getRegExp().tryGetFlags()) or this.getMethodName() = "replaceAll" From 23b18aeca9618acc31ffb996f2deb77268247896 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 14:20:04 +0100 Subject: [PATCH 12/36] JS: Now unknown flags are not flagged in taint paths --- .../dataflow/TaintedPathCustomizations.qll | 2 +- .../CWE-022/TaintedPath/Consistency.expected | 1 - .../CWE-022/TaintedPath/TaintedPath.expected | 82 ------------------- 3 files changed, 1 insertion(+), 84 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index e6f1a5203d74..f2dd4a95cf02 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -223,7 +223,7 @@ module TaintedPath { output = this and not exists(DataFlow::RegExpCreationNode regexp, RegExpTerm term | this.(StringReplaceCall).getRegExp() = regexp and - this.(StringReplaceCall).isGlobal() and + this.(StringReplaceCall).maybeGlobal() and regexp.getRoot() = term | term.getAMatchedString() = "/" or diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index 81656a2af728..e69de29bb2d1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -1 +0,0 @@ -| TaintedPath.js:207 | did not expect an alert, but found an alert for TaintedPath | OK -- Might be okay depending on what unknownFlags evaluates to. | | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 4fdd33dcbbab..5fa027c40da4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -1619,39 +1619,6 @@ nodes | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | @@ -6831,22 +6798,6 @@ edges | TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | | TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | | TaintedPath.js:202:7:202:48 | path | TaintedPath.js:206:29:206:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | -| TaintedPath.js:202:7:202:48 | path | TaintedPath.js:207:29:207:32 | path | | TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | | TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | | TaintedPath.js:202:14:202:37 | url.par ... , true) | TaintedPath.js:202:14:202:43 | url.par ... ).query | @@ -6959,38 +6910,6 @@ edges | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | -| TaintedPath.js:207:29:207:32 | path | TaintedPath.js:207:29:207:97 | path.re ... )), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | @@ -10811,7 +10730,6 @@ edges | TaintedPath.js:197:45:197:48 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:197:45:197:48 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:198:35:198:38 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:198:35:198:38 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:206:29:206:85 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | -| TaintedPath.js:207:29:207:97 | path.re ... )), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:207:29:207:97 | path.re ... )), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value | | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value | | handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on a $@. | handlebars.js:29:46:29:60 | req.params.path | user-provided value | From 155f1fca85d146acb44fe8cb7d0d91d1b53c78f9 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 15:22:32 +0100 Subject: [PATCH 13/36] JS: Added test cases for unsafe shell command sanitization with RegExpr Object, instead of literal --- .../Security/CWE-078/Consistency.expected | 1 + .../UnsafeShellCommandConstruction.expected | 36 +++++++++++++++++++ .../UnsafeShellCommandConstruction/lib/lib.js | 11 ++++++ 3 files changed, 48 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected index e69de29bb2d1..5844d8d221de 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected @@ -0,0 +1 @@ +| UnsafeShellCommandConstruction/lib/lib.js:640 | did not expect an alert, but found an alert for UnsafeShellCommandConstruction | OK -- Currently this is flagged as a bad sanitization, but it is not certain that it is bad. | ComandInjection | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected index b4022c8550c3..83c4a4718546 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected @@ -319,6 +319,22 @@ nodes | lib/lib.js:626:29:626:32 | name | | lib/lib.js:629:25:629:28 | name | | lib/lib.js:629:25:629:28 | name | +| lib/lib.js:632:38:632:41 | name | +| lib/lib.js:632:38:632:41 | name | +| lib/lib.js:633:6:633:68 | sanitized | +| lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | +| lib/lib.js:633:24:633:27 | name | +| lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | +| lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | +| lib/lib.js:634:22:634:30 | sanitized | +| lib/lib.js:634:22:634:30 | sanitized | +| lib/lib.js:639:6:639:84 | sanitized | +| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | +| lib/lib.js:639:24:639:27 | name | +| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | +| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | +| lib/lib.js:640:22:640:30 | sanitized | +| lib/lib.js:640:22:640:30 | sanitized | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | | lib/subLib2/compiled-file.ts:4:25:4:28 | name | @@ -749,6 +765,22 @@ edges | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | +| lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:27 | name | +| lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:27 | name | +| lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:27 | name | +| lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:27 | name | +| lib/lib.js:633:6:633:68 | sanitized | lib/lib.js:634:22:634:30 | sanitized | +| lib/lib.js:633:6:633:68 | sanitized | lib/lib.js:634:22:634:30 | sanitized | +| lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | lib/lib.js:633:6:633:68 | sanitized | +| lib/lib.js:633:24:633:27 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | +| lib/lib.js:633:24:633:27 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | +| lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | +| lib/lib.js:639:6:639:84 | sanitized | lib/lib.js:640:22:640:30 | sanitized | +| lib/lib.js:639:6:639:84 | sanitized | lib/lib.js:640:22:640:30 | sanitized | +| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | lib/lib.js:639:6:639:84 | sanitized | +| lib/lib.js:639:24:639:27 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | +| lib/lib.js:639:24:639:27 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | +| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | @@ -879,6 +911,10 @@ edges | lib/lib.js:609:10:609:25 | "rm -rf " + name | lib/lib.js:608:42:608:45 | name | lib/lib.js:609:22:609:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:608:42:608:45 | name | library input | lib/lib.js:609:2:609:26 | cp.exec ... + name) | shell command | | lib/lib.js:626:17:626:32 | "rm -rf " + name | lib/lib.js:608:42:608:45 | name | lib/lib.js:626:29:626:32 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:608:42:608:45 | name | library input | lib/lib.js:626:9:626:33 | cp.exec ... + name) | shell command | | lib/lib.js:629:13:629:28 | "rm -rf " + name | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:608:42:608:45 | name | library input | lib/lib.js:629:5:629:29 | cp.exec ... + name) | shell command | +| lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:634:2:634:31 | cp.exec ... itized) | shell command | +| lib/lib.js:634:10:634:30 | "rm -rf ... nitized | lib/lib.js:632:38:632:41 | name | lib/lib.js:634:22:634:30 | sanitized | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:634:2:634:31 | cp.exec ... itized) | shell command | +| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:640:2:640:31 | cp.exec ... itized) | shell command | +| lib/lib.js:640:10:640:30 | "rm -rf ... nitized | lib/lib.js:632:38:632:41 | name | lib/lib.js:640:22:640:30 | sanitized | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:640:2:640:31 | cp.exec ... itized) | shell command | | lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/compiled-file.ts:3:26:3:29 | name | library input | lib/subLib2/compiled-file.ts:4:5:4:29 | cp.exec ... + name) | shell command | | lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/special-file.js:3:28:3:31 | name | library input | lib/subLib2/special-file.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib3/my-file.ts:3:28:3:31 | name | library input | lib/subLib3/my-file.ts:4:2:4:26 | cp.exec ... + name) | shell command | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js index 504de998c1c3..d3190ceb6462 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js @@ -628,3 +628,14 @@ module.exports.veryIndeirect = function (name) { cp.exec("rm -rf " + name); // NOT OK } + +module.exports.sanitizer = function (name) { + var sanitized = "'" + name.replace(new RegExp("\'"), "'\\''") + "'" + cp.exec("rm -rf " + sanitized); // NOT OK + + var sanitized = "'" + name.replace(new RegExp("\'", 'g'), "'\\''") + "'" + cp.exec("rm -rf " + sanitized); // OK + + var sanitized = "'" + name.replace(new RegExp("\'", unknownFlags()), "'\\''") + "'" + cp.exec("rm -rf " + sanitized); // OK -- Currently this is flagged as a bad sanitization, but it is not certain that it is bad. +} From a0df33c3ac53b0e2722e661bc2a35e2f6e90b864 Mon Sep 17 00:00:00 2001 From: Napalys Date: Tue, 26 Nov 2024 15:27:33 +0100 Subject: [PATCH 14/36] JS: UnsafeShellCommand Using unknown flags in the RegExp object is no longer flagged as bad sanitization to reduce false positives. --- ...feShellCommandConstructionCustomizations.qll | 2 +- .../Security/CWE-078/Consistency.expected | 1 - .../UnsafeShellCommandConstruction.expected | 17 ----------------- .../UnsafeShellCommandConstruction/lib/lib.js | 2 +- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll index 77625874df9f..8e753a5ef63b 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll @@ -245,7 +245,7 @@ module UnsafeShellCommandConstruction { class ReplaceQuotesSanitizer extends Sanitizer, StringReplaceCall { ReplaceQuotesSanitizer() { this.getAReplacedString() = "'" and - this.isGlobal() and + this.maybeGlobal() and this.getRawReplacement().mayHaveStringValue(["'\\''", ""]) } } diff --git a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected index 5844d8d221de..e69de29bb2d1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/Consistency.expected @@ -1 +0,0 @@ -| UnsafeShellCommandConstruction/lib/lib.js:640 | did not expect an alert, but found an alert for UnsafeShellCommandConstruction | OK -- Currently this is flagged as a bad sanitization, but it is not certain that it is bad. | ComandInjection | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected index 83c4a4718546..f2fa354a3050 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected @@ -328,13 +328,6 @@ nodes | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | | lib/lib.js:634:22:634:30 | sanitized | | lib/lib.js:634:22:634:30 | sanitized | -| lib/lib.js:639:6:639:84 | sanitized | -| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | -| lib/lib.js:639:24:639:27 | name | -| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | -| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | -| lib/lib.js:640:22:640:30 | sanitized | -| lib/lib.js:640:22:640:30 | sanitized | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | | lib/subLib2/compiled-file.ts:4:25:4:28 | name | @@ -767,20 +760,12 @@ edges | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | | lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:27 | name | | lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:27 | name | -| lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:27 | name | -| lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:27 | name | | lib/lib.js:633:6:633:68 | sanitized | lib/lib.js:634:22:634:30 | sanitized | | lib/lib.js:633:6:633:68 | sanitized | lib/lib.js:634:22:634:30 | sanitized | | lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | lib/lib.js:633:6:633:68 | sanitized | | lib/lib.js:633:24:633:27 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | | lib/lib.js:633:24:633:27 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | -| lib/lib.js:639:6:639:84 | sanitized | lib/lib.js:640:22:640:30 | sanitized | -| lib/lib.js:639:6:639:84 | sanitized | lib/lib.js:640:22:640:30 | sanitized | -| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | lib/lib.js:639:6:639:84 | sanitized | -| lib/lib.js:639:24:639:27 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | -| lib/lib.js:639:24:639:27 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | -| lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | @@ -913,8 +898,6 @@ edges | lib/lib.js:629:13:629:28 | "rm -rf " + name | lib/lib.js:608:42:608:45 | name | lib/lib.js:629:25:629:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:608:42:608:45 | name | library input | lib/lib.js:629:5:629:29 | cp.exec ... + name) | shell command | | lib/lib.js:633:18:633:68 | "'" + n ... ) + "'" | lib/lib.js:632:38:632:41 | name | lib/lib.js:633:24:633:62 | name.re ... '\\\\''") | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:634:2:634:31 | cp.exec ... itized) | shell command | | lib/lib.js:634:10:634:30 | "rm -rf ... nitized | lib/lib.js:632:38:632:41 | name | lib/lib.js:634:22:634:30 | sanitized | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:634:2:634:31 | cp.exec ... itized) | shell command | -| lib/lib.js:639:18:639:84 | "'" + n ... ) + "'" | lib/lib.js:632:38:632:41 | name | lib/lib.js:639:24:639:78 | name.re ... '\\\\''") | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:640:2:640:31 | cp.exec ... itized) | shell command | -| lib/lib.js:640:10:640:30 | "rm -rf ... nitized | lib/lib.js:632:38:632:41 | name | lib/lib.js:640:22:640:30 | sanitized | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:632:38:632:41 | name | library input | lib/lib.js:640:2:640:31 | cp.exec ... itized) | shell command | | lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/compiled-file.ts:3:26:3:29 | name | library input | lib/subLib2/compiled-file.ts:4:5:4:29 | cp.exec ... + name) | shell command | | lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/special-file.js:3:28:3:31 | name | library input | lib/subLib2/special-file.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib3/my-file.ts:3:28:3:31 | name | library input | lib/subLib3/my-file.ts:4:2:4:26 | cp.exec ... + name) | shell command | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js index d3190ceb6462..09488f0a8871 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js @@ -637,5 +637,5 @@ module.exports.sanitizer = function (name) { cp.exec("rm -rf " + sanitized); // OK var sanitized = "'" + name.replace(new RegExp("\'", unknownFlags()), "'\\''") + "'" - cp.exec("rm -rf " + sanitized); // OK -- Currently this is flagged as a bad sanitization, but it is not certain that it is bad. + cp.exec("rm -rf " + sanitized); // OK -- Most likely should be okay and not flagged to reduce false positives. } From aa557cf950fa1204208b7673d2069e19fb859e72 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 27 Nov 2024 08:29:02 +0100 Subject: [PATCH 15/36] JS: Added tests for DotRemovingReplaceCall with RegExp Object. --- .../CWE-022/TaintedPath/Consistency.expected | 1 + .../CWE-022/TaintedPath/TaintedPath.expected | 119 ++++++++++++++++++ .../CWE-022/TaintedPath/TaintedPath.js | 13 ++ 3 files changed, 133 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index e69de29bb2d1..8396f55ff16d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -0,0 +1 @@ +| TaintedPath.js:213 | expected an alert, but found none | NOT OK (can be absolute) | | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 5fa027c40da4..c196425a5caa 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -1619,6 +1619,60 @@ nodes | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | @@ -6910,6 +6964,70 @@ edges | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | +| TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | | examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath | @@ -10730,6 +10848,7 @@ edges | TaintedPath.js:197:45:197:48 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:197:45:197:48 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:198:35:198:38 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:198:35:198:38 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:206:29:206:85 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | +| TaintedPath.js:216:31:216:69 | path.re ... '), '') | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:216:31:216:69 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value | | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value | | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value | | handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on a $@. | handlebars.js:29:46:29:60 | req.params.path | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index 13948fc431a9..924aec029e98 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -206,3 +206,16 @@ var server = http.createServer(function(req, res) { res.write(fs.readFileSync(path.replace(new RegExp("[\\]\\[*,;'\"`<>\\?/]", ''), ''))); // NOT OK. res.write(fs.readFileSync(path.replace(new RegExp("[\\]\\[*,;'\"`<>\\?/]", unknownFlags()), ''))); // OK -- Might be okay depending on what unknownFlags evaluates to. }); + +var server = http.createServer(function(req, res) { + let path = url.parse(req.url, true).query.path; + + res.write(fs.readFileSync(path.replace(new RegExp("[.]", 'g'), ''))); // NOT OK (can be absolute) -- Currently not flagged because it is not a literal + + if (!pathModule.isAbsolute(path)) { + res.write(fs.readFileSync(path.replace(new RegExp("[.]", ''), ''))); // NOT OK + res.write(fs.readFileSync(path.replace(new RegExp("[.]", 'g'), ''))); // OK + res.write(fs.readFileSync(path.replace(new RegExp("[.]", unknownFlags()), ''))); // OK + } +}); + From 875478c1c6c9a01587f35f2777648d8bef55baf4 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 27 Nov 2024 08:55:21 +0100 Subject: [PATCH 16/36] JS: Fixed path query not flagging new RegExp with DotRemovingReplaceCall --- .../dataflow/TaintedPathCustomizations.qll | 6 +- .../CWE-022/TaintedPath/Consistency.expected | 1 - .../CWE-022/TaintedPath/TaintedPath.expected | 110 ++++++++++++++++++ .../CWE-022/TaintedPath/TaintedPath.js | 2 +- 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index f2dd4a95cf02..8798c9260865 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -305,9 +305,9 @@ module TaintedPath { input = this.getReceiver() and output = this and this.isGlobal() and - exists(RegExpLiteral literal, RegExpTerm term | - this.getRegExp().asExpr() = literal and - literal.getRoot() = term and + exists(DataFlow::RegExpCreationNode regexp, RegExpTerm term | + this.getRegExp() = regexp and + regexp.getRoot() = term and not term.getAMatchedString() = "/" | term.getAMatchedString() = "." or diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected index 8396f55ff16d..e69de29bb2d1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/Consistency.expected @@ -1 +0,0 @@ -| TaintedPath.js:213 | expected an alert, but found none | NOT OK (can be absolute) | | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index c196425a5caa..6a45147a4e2b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -1627,6 +1627,15 @@ nodes | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:14:211:37 | url.par ... , true) | @@ -1635,6 +1644,15 @@ nodes | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:43 | url.par ... ).query | @@ -1643,6 +1661,20 @@ nodes | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | @@ -1656,6 +1688,19 @@ nodes | TaintedPath.js:211:24:211:30 | req.url | | TaintedPath.js:211:24:211:30 | req.url | | TaintedPath.js:211:24:211:30 | req.url | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | | TaintedPath.js:216:31:216:34 | path | | TaintedPath.js:216:31:216:34 | path | | TaintedPath.js:216:31:216:34 | path | @@ -6964,6 +7009,14 @@ edges | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | | TaintedPath.js:206:29:206:32 | path | TaintedPath.js:206:29:206:85 | path.re ... '), '') | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | +| TaintedPath.js:211:7:211:48 | path | TaintedPath.js:213:29:213:32 | path | | TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | | TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | | TaintedPath.js:211:7:211:48 | path | TaintedPath.js:216:31:216:34 | path | @@ -6980,6 +7033,19 @@ edges | TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | | TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:37 | url.par ... , true) | TaintedPath.js:211:14:211:43 | url.par ... ).query | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | @@ -6988,6 +7054,15 @@ edges | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | | TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:43 | url.par ... ).query | TaintedPath.js:211:14:211:48 | url.par ... ry.path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | @@ -6996,6 +7071,24 @@ edges | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | | TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:14:211:48 | url.par ... ry.path | TaintedPath.js:211:7:211:48 | path | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | @@ -7012,6 +7105,22 @@ edges | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | +| TaintedPath.js:213:29:213:32 | path | TaintedPath.js:213:29:213:68 | path.re ... '), '') | | TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | | TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | | TaintedPath.js:216:31:216:34 | path | TaintedPath.js:216:31:216:69 | path.re ... '), '') | @@ -10848,6 +10957,7 @@ edges | TaintedPath.js:197:45:197:48 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:197:45:197:48 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:198:35:198:38 | path | TaintedPath.js:195:24:195:30 | req.url | TaintedPath.js:198:35:198:38 | path | This path depends on a $@. | TaintedPath.js:195:24:195:30 | req.url | user-provided value | | TaintedPath.js:206:29:206:85 | path.re ... '), '') | TaintedPath.js:202:24:202:30 | req.url | TaintedPath.js:206:29:206:85 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:202:24:202:30 | req.url | user-provided value | +| TaintedPath.js:213:29:213:68 | path.re ... '), '') | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:213:29:213:68 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value | | TaintedPath.js:216:31:216:69 | path.re ... '), '') | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:216:31:216:69 | path.re ... '), '') | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value | | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value | | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js index 924aec029e98..fd768fecfff8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.js @@ -210,7 +210,7 @@ var server = http.createServer(function(req, res) { var server = http.createServer(function(req, res) { let path = url.parse(req.url, true).query.path; - res.write(fs.readFileSync(path.replace(new RegExp("[.]", 'g'), ''))); // NOT OK (can be absolute) -- Currently not flagged because it is not a literal + res.write(fs.readFileSync(path.replace(new RegExp("[.]", 'g'), ''))); // NOT OK (can be absolute) if (!pathModule.isAbsolute(path)) { res.write(fs.readFileSync(path.replace(new RegExp("[.]", ''), ''))); // NOT OK From 9c2366a660654de149b26ec03a3b1cd5cd474abe Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 27 Nov 2024 09:42:43 +0100 Subject: [PATCH 17/36] JS: Added tests for ReDos with unknownFlags, everything seems to be good --- .../CWE-400/ReDoS/PolynomialBackTracking.expected | 3 +++ .../CWE-400/ReDoS/PolynomialReDoS.expected | 15 +++++++++++++++ .../Security/CWE-400/ReDoS/polynomial-redos.js | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected index 9a03bdcd34ef..106b143111f8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected @@ -130,6 +130,9 @@ | polynomial-redos.js:133:22:133:23 | f+ | Strings starting with 'f' and with many repetitions of 'f' can start matching anywhere after the start of the preceeding ff+G | | polynomial-redos.js:136:25:136:26 | h+ | Strings starting with 'h' and with many repetitions of 'h' can start matching anywhere after the start of the preceeding hh+I | | polynomial-redos.js:138:322:138:323 | .* | Strings starting with 'AAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' and with many repetitions of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' can start matching anywhere after the start of the preceeding (AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)(AA\|BB)C.*X | +| polynomial-redos.js:140:33:140:34 | h+ | Strings starting with 'h' and with many repetitions of 'h' can start matching anywhere after the start of the preceeding hh+I | +| polynomial-redos.js:141:33:141:34 | h+ | Strings starting with 'h' and with many repetitions of 'h' can start matching anywhere after the start of the preceeding hh+I | +| polynomial-redos.js:142:33:142:34 | h+ | Strings starting with 'h' and with many repetitions of 'h' can start matching anywhere after the start of the preceeding hh+I | | regexplib/address.js:27:3:27:5 | \\s* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding (\\s*\\(?0\\d{4}\\)?(\\s*\|-)\\d{3}(\\s*\|-)\\d{3}\\s*) | | regexplib/address.js:27:48:27:50 | \\s* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding (\\s*\\(?0\\d{3}\\)?(\\s*\|-)\\d{3}(\\s*\|-)\\d{4}\\s*) | | regexplib/address.js:27:93:27:95 | \\s* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding (\\s*(7\|8)(\\d{7}\|\\d{3}(\\-\|\\s{1})\\d{4})\\s*) | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected index 4c534fffe134..f95049441608 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected @@ -249,6 +249,12 @@ nodes | polynomial-redos.js:136:5:136:13 | modified3 | | polynomial-redos.js:138:5:138:11 | tainted | | polynomial-redos.js:138:5:138:11 | tainted | +| polynomial-redos.js:140:2:140:10 | modified3 | +| polynomial-redos.js:140:2:140:10 | modified3 | +| polynomial-redos.js:141:2:141:10 | modified3 | +| polynomial-redos.js:141:2:141:10 | modified3 | +| polynomial-redos.js:142:2:142:10 | modified3 | +| polynomial-redos.js:142:2:142:10 | modified3 | edges | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | @@ -489,6 +495,12 @@ edges | polynomial-redos.js:132:18:132:50 | tainted ... g, "e") | polynomial-redos.js:132:6:132:50 | modified2 | | polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:136:5:136:13 | modified3 | | polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:136:5:136:13 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:140:2:140:10 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:140:2:140:10 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:141:2:141:10 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:141:2:141:10 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:142:2:142:10 | modified3 | +| polynomial-redos.js:135:9:135:47 | modified3 | polynomial-redos.js:142:2:142:10 | modified3 | | polynomial-redos.js:135:21:135:27 | tainted | polynomial-redos.js:135:21:135:47 | tainted ... /g, "") | | polynomial-redos.js:135:21:135:47 | tainted ... /g, "") | polynomial-redos.js:135:9:135:47 | modified3 | #select @@ -590,3 +602,6 @@ edges | polynomial-redos.js:133:2:133:32 | modifie ... g, "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:133:2:133:10 | modified2 | This $@ that depends on $@ may run slow on strings starting with 'f' and with many repetitions of 'f'. | polynomial-redos.js:133:22:133:23 | f+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:136:5:136:35 | modifie ... g, "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:136:5:136:13 | modified3 | This $@ that depends on $@ may run slow on strings starting with 'h' and with many repetitions of 'h'. | polynomial-redos.js:136:25:136:26 | h+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:138:5:138:326 | tainted ... )C.*X/) | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:138:5:138:11 | tainted | This $@ that depends on $@ may run slow on strings starting with 'AAAAAAAAAAAAAAAAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' and with many repetitions of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'. | polynomial-redos.js:138:322:138:323 | .* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | +| polynomial-redos.js:140:2:140:48 | modifie ... ), "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:140:2:140:10 | modified3 | This $@ that depends on $@ may run slow on strings starting with 'h' and with many repetitions of 'h'. | polynomial-redos.js:140:33:140:34 | h+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | +| polynomial-redos.js:141:2:141:59 | modifie ... ), "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:141:2:141:10 | modified3 | This $@ that depends on $@ may run slow on strings starting with 'h' and with many repetitions of 'h'. | polynomial-redos.js:141:33:141:34 | h+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | +| polynomial-redos.js:142:2:142:47 | modifie ... ), "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:142:2:142:10 | modified3 | This $@ that depends on $@ may run slow on strings starting with 'h' and with many repetitions of 'h'. | polynomial-redos.js:142:33:142:34 | h+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/polynomial-redos.js b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/polynomial-redos.js index fc0ddde66b21..860472428ff0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/polynomial-redos.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/polynomial-redos.js @@ -136,4 +136,8 @@ app.use(function(req, res) { modified3.replace(/hh+I/g, "b"); // NOT OK tainted.match(/(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*X/); // NOT OK + + modified3.replace(new RegExp("hh+I", "g"), "b"); // NOT OK + modified3.replace(new RegExp("hh+I", unknownFlags()), "b"); // NOT OK + modified3.replace(new RegExp("hh+I", ""), "b"); // NOT OK }); From 76318035ffd61035f5a448f2d73aae5985645ca4 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 27 Nov 2024 10:53:43 +0100 Subject: [PATCH 18/36] JS: Add test cases for RegExp object usage in replace within incomplete sanitization --- ...ompleteMultiCharacterSanitization.expected | 1 + .../CWE-116/IncompleteSanitization/tst.js | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.expected b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.expected index 32400608814f..96a48fec6cb8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.expected +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteMultiCharacterSanitization.expected @@ -39,3 +39,4 @@ | tst-multi-character-sanitization.js:145:13:145:90 | content ... /g, '') | This string may still contain $@, which may cause an HTML element injection vulnerability. | tst-multi-character-sanitization.js:145:30:145:30 | < |