From 318324f7d088b916565af40e5a1bea9e9772d264 Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:40:19 +0000 Subject: [PATCH] feat: extend bracket pair removal exclusion to handle regex patterns The isDelimiterUnwrapper predicate now also handles cases where bracket pairs are removed using regex patterns (e.g., s.replace(/{/, '').replace(/}/, '')), not just string literals. This reduces false positives for display-oriented bracket stripping. --- .../Security/CWE-116/IncompleteSanitization.ql | 16 ++++++++++++++-- .../CWE-116/IncompleteSanitization/tst.js | 9 ++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql index 7d0dc71a2a84..754917de1b14 100644 --- a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql +++ b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql @@ -100,6 +100,18 @@ predicate removesFirstOccurrence(StringReplaceCall repl, string str) { not exists(repl.getRegExp()) and repl.replaces(str, "") } +/** + * Holds if `repl` removes a single character `str` (either via a string argument or a regex). + */ +predicate removesCharacter(StringReplaceCall repl, string str) { + removesFirstOccurrence(repl, str) + or + exists(DataFlow::Node re | re = repl.getRegExp() | + getAMatchedString(re) = str and + repl.replaces(str, "") + ) +} + /** * Holds if `leftUnwrap` and `rightUnwrap` unwraps a string from a pair of surrounding delimiters. */ @@ -117,8 +129,8 @@ predicate isDelimiterUnwrapper( or left = "'" and right = "'" | - removesFirstOccurrence(leftUnwrap, left) and - removesFirstOccurrence(rightUnwrap, right) and + removesCharacter(leftUnwrap, left) and + removesCharacter(rightUnwrap, right) and leftUnwrap.getAMethodCall() = rightUnwrap ) } 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 d457af27f143..f4ab00a8ea4a 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 @@ -137,10 +137,17 @@ function goodOrBad12(s) { s = s.replace('[', ''); s = s.replace(']', ''); - s.replace(/{/, '').replace(/}/, ''); // $ Alert[js/incomplete-sanitization] - should have used a string literal if a single replacement was intended + s.replace(/{/, '').replace(/}/, ''); // bracket pair removal via regex - no alert expected s.replace(']', '').replace('[', ''); // $ Alert[js/incomplete-sanitization] - probably OK, but still flagged } +function goodBracketPairRegex(s) { + // Bracket pair removal via regex should not be flagged + s.replace(/\[/, '').replace(/\]/, ''); + s.replace(/\{/, '').replace(/\}/, ''); + s.replace(/\(/, '').replace(/\)/, ''); +} + function newlines(s) { // motivation for whitelist require("child_process").execSync("which emacs").toString().replace("\n", "");