From 36e18c2a892ec88bb60dc0a07c94f2eed8855236 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 10 Sep 2025 10:56:34 +0200 Subject: [PATCH 1/3] JS: Enable inline expectations in BuildArtifactLeak The tests already have the annotations, it just seems to have been disable by accident --- .../ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.qlref | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.qlref b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.qlref index ac51e69c797b..b0fc1218f95e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.qlref +++ b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.qlref @@ -1 +1,2 @@ query: Security/CWE-312/BuildArtifactLeak.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql From 602dae059240333c303d02b984d8016b18c95190 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 10 Sep 2025 10:58:34 +0200 Subject: [PATCH 2/3] JS: Add test showing FP --- .../CWE-312/BuildArtifactLeak.expected | 19 +++++++++++++++---- .../Security/CWE-312/build-leaks.js | 12 +++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected index 2e8c7462e2de..56f41bab444e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected +++ b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected @@ -1,3 +1,8 @@ +#select +| build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | build-leaks.js:5:35:5:45 | process.env | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | This creates a build artifact that depends on $@. | build-leaks.js:5:35:5:45 | process.env | sensitive data returned byprocess environment | +| build-leaks.js:34:26:34:57 | getEnv( ... ngified | build-leaks.js:15:24:15:34 | process.env | build-leaks.js:34:26:34:57 | getEnv( ... ngified | This creates a build artifact that depends on $@. | build-leaks.js:15:24:15:34 | process.env | sensitive data returned byprocess environment | +| build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | build-leaks.js:40:14:40:60 | url.par ... assword | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | This creates a build artifact that depends on $@. | build-leaks.js:40:14:40:60 | url.par ... assword | sensitive data returned byan access to current_password | +| build-leaks.js:102:30:102:46 | getFilteredEnv4() | build-leaks.js:97:43:97:53 | process.env | build-leaks.js:102:30:102:46 | getFilteredEnv4() | This creates a build artifact that depends on $@. | build-leaks.js:97:43:97:53 | process.env | sensitive data returned byprocess environment | edges | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | provenance | | | build-leaks.js:5:35:5:45 | process.env | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | provenance | | @@ -23,6 +28,11 @@ edges | build-leaks.js:40:14:40:60 | url.par ... assword | build-leaks.js:40:9:40:10 | pw | provenance | | | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | provenance | | | build-leaks.js:41:82:41:83 | pw | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | provenance | | +| build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | build-leaks.js:102:30:102:46 | getFilteredEnv4() | provenance | | +| build-leaks.js:97:17:97:19 | [post update] env | build-leaks.js:98:24:98:26 | env | provenance | | +| build-leaks.js:97:43:97:53 | process.env | build-leaks.js:97:17:97:19 | [post update] env | provenance | Config | +| build-leaks.js:98:24:98:26 | env | build-leaks.js:22:49:22:51 | env | provenance | | +| build-leaks.js:98:24:98:26 | env | build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | provenance | | nodes | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | semmle.label | {\\n " ... leak]\\n} | | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | semmle.label | JSON.st ... ss.env) | @@ -50,10 +60,11 @@ nodes | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | semmle.label | { "proc ... y(pw) } | | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | semmle.label | JSON.stringify(pw) | | build-leaks.js:41:82:41:83 | pw | semmle.label | pw | +| build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | semmle.label | ["FOO", ... }, {}) | +| build-leaks.js:97:17:97:19 | [post update] env | semmle.label | [post update] env | +| build-leaks.js:97:43:97:53 | process.env | semmle.label | process.env | +| build-leaks.js:98:24:98:26 | env | semmle.label | env | +| build-leaks.js:102:30:102:46 | getFilteredEnv4() | semmle.label | getFilteredEnv4() | subpaths | build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:23:39:23:41 | raw | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) | -#select -| build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | build-leaks.js:5:35:5:45 | process.env | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | This creates a build artifact that depends on $@. | build-leaks.js:5:35:5:45 | process.env | sensitive data returned byprocess environment | -| build-leaks.js:34:26:34:57 | getEnv( ... ngified | build-leaks.js:15:24:15:34 | process.env | build-leaks.js:34:26:34:57 | getEnv( ... ngified | This creates a build artifact that depends on $@. | build-leaks.js:15:24:15:34 | process.env | sensitive data returned byprocess environment | -| build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | build-leaks.js:40:14:40:60 | url.par ... assword | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | This creates a build artifact that depends on $@. | build-leaks.js:40:14:40:60 | url.par ... assword | sensitive data returned byan access to current_password | diff --git a/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js b/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js index e99122cb6af0..fb501535296f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js +++ b/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js @@ -90,4 +90,14 @@ var server = https.createServer(function (req, res) { } new webpack.DefinePlugin(getOnlyReactVariables3()); -})(); \ No newline at end of file + + function getFilteredEnv4() { + return ["FOO", "BAR", "BAZ"] + .reduce((env, key) => { + env[key] = JSON.stringify(process.env[key]); // $ SPURIOUS: Source[js/build-artifact-leak] + return env; + }, {}); + } + + new webpack.DefinePlugin(getFilteredEnv4()); // $ SPURIOUS: Alert[js/build-artifact-leak] +})(); From 2a4d6830ec5d7fae5f499c066d25bde6dc1c383a Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 10 Sep 2025 11:02:45 +0200 Subject: [PATCH 3/3] JS: An array of constants should be considered "filtered" --- .../dataflow/CleartextLoggingCustomizations.qll | 14 ++++++++++++++ .../Security/CWE-312/BuildArtifactLeak.expected | 11 ----------- .../query-tests/Security/CWE-312/build-leaks.js | 4 ++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll index dbb775f99b58..a7bbc73ce038 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll @@ -247,10 +247,24 @@ module CleartextLogging { reduceCall.getABoundCallbackParameter(0, 1) = name | reduceCall.getReceiver+().(DataFlow::MethodCallNode).getMethodName() = "filter" + or + isArrayOfConstants(reduceCall.getReceiver+()) ) or exists(StringOps::RegExpTest test | test.getStringOperand().getALocalSource() = name) or exists(MembershipCandidate test | test.getAMemberNode().getALocalSource() = name) } + + private predicate isArrayOfConstants(DataFlow::ArrayCreationNode array) { + forex(DataFlow::Node node | + node = + [ + array.getAnElement(), array.getAPropertyWrite().getRhs(), + array.getAMethodCall("push").getArgument(0) + ] + | + exists(node.getStringValue()) + ) + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected index 56f41bab444e..59560d7c7f39 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected +++ b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected @@ -2,7 +2,6 @@ | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | build-leaks.js:5:35:5:45 | process.env | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | This creates a build artifact that depends on $@. | build-leaks.js:5:35:5:45 | process.env | sensitive data returned byprocess environment | | build-leaks.js:34:26:34:57 | getEnv( ... ngified | build-leaks.js:15:24:15:34 | process.env | build-leaks.js:34:26:34:57 | getEnv( ... ngified | This creates a build artifact that depends on $@. | build-leaks.js:15:24:15:34 | process.env | sensitive data returned byprocess environment | | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | build-leaks.js:40:14:40:60 | url.par ... assword | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | This creates a build artifact that depends on $@. | build-leaks.js:40:14:40:60 | url.par ... assword | sensitive data returned byan access to current_password | -| build-leaks.js:102:30:102:46 | getFilteredEnv4() | build-leaks.js:97:43:97:53 | process.env | build-leaks.js:102:30:102:46 | getFilteredEnv4() | This creates a build artifact that depends on $@. | build-leaks.js:97:43:97:53 | process.env | sensitive data returned byprocess environment | edges | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | provenance | | | build-leaks.js:5:35:5:45 | process.env | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | provenance | | @@ -28,11 +27,6 @@ edges | build-leaks.js:40:14:40:60 | url.par ... assword | build-leaks.js:40:9:40:10 | pw | provenance | | | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | provenance | | | build-leaks.js:41:82:41:83 | pw | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | provenance | | -| build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | build-leaks.js:102:30:102:46 | getFilteredEnv4() | provenance | | -| build-leaks.js:97:17:97:19 | [post update] env | build-leaks.js:98:24:98:26 | env | provenance | | -| build-leaks.js:97:43:97:53 | process.env | build-leaks.js:97:17:97:19 | [post update] env | provenance | Config | -| build-leaks.js:98:24:98:26 | env | build-leaks.js:22:49:22:51 | env | provenance | | -| build-leaks.js:98:24:98:26 | env | build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | provenance | | nodes | build-leaks.js:4:39:6:1 | {\\n " ... leak]\\n} | semmle.label | {\\n " ... leak]\\n} | | build-leaks.js:5:20:5:46 | JSON.st ... ss.env) | semmle.label | JSON.st ... ss.env) | @@ -60,11 +54,6 @@ nodes | build-leaks.js:41:43:41:86 | { "proc ... y(pw) } | semmle.label | { "proc ... y(pw) } | | build-leaks.js:41:67:41:84 | JSON.stringify(pw) | semmle.label | JSON.stringify(pw) | | build-leaks.js:41:82:41:83 | pw | semmle.label | pw | -| build-leaks.js:95:16:99:18 | ["FOO", ... }, {}) | semmle.label | ["FOO", ... }, {}) | -| build-leaks.js:97:17:97:19 | [post update] env | semmle.label | [post update] env | -| build-leaks.js:97:43:97:53 | process.env | semmle.label | process.env | -| build-leaks.js:98:24:98:26 | env | semmle.label | env | -| build-leaks.js:102:30:102:46 | getFilteredEnv4() | semmle.label | getFilteredEnv4() | subpaths | build-leaks.js:22:36:22:38 | raw | build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) | | build-leaks.js:22:36:22:38 | raw | build-leaks.js:23:39:23:41 | raw | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) | diff --git a/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js b/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js index fb501535296f..ca81d1fab73a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js +++ b/javascript/ql/test/query-tests/Security/CWE-312/build-leaks.js @@ -94,10 +94,10 @@ var server = https.createServer(function (req, res) { function getFilteredEnv4() { return ["FOO", "BAR", "BAZ"] .reduce((env, key) => { - env[key] = JSON.stringify(process.env[key]); // $ SPURIOUS: Source[js/build-artifact-leak] + env[key] = JSON.stringify(process.env[key]); return env; }, {}); } - new webpack.DefinePlugin(getFilteredEnv4()); // $ SPURIOUS: Alert[js/build-artifact-leak] + new webpack.DefinePlugin(getFilteredEnv4()); })();