diff --git a/java/general/input-validation/matcher-replaceall-tainted-replacement.yaml b/java/general/input-validation/matcher-replaceall-tainted-replacement.yaml new file mode 100644 index 0000000..557656b --- /dev/null +++ b/java/general/input-validation/matcher-replaceall-tainted-replacement.yaml @@ -0,0 +1,33 @@ +rules: + - id: codevigilant.java.general.input-validation.matcher-replaceall-tainted-replacement + mode: taint + pattern-sources: + - pattern: $MAP.get($KEY) + - pattern: $MAP.getOrDefault($KEY, $DEF) + - pattern: System.getenv($NAME) + - pattern: System.getProperty($NAME) + - pattern: $REQ.getParameter($NAME) + pattern-sinks: + - pattern: $MATCHER.replaceAll($REPL) + - pattern: $MATCHER.appendReplacement($SB, $REPL) + message: | + Detected a non-literal replacement string flowing into + Matcher.replaceAll/appendReplacement. Java replacement strings treat + '$' and '\' as metacharacters: a '$' followed by a digit or '{' is + resolved as a back-reference to a capturing group, so an + attacker-influenced replacement (e.g. a build parameter or environment + value such as '$1') throws an unchecked exception ('No group 1') or + silently substitutes the wrong text, enabling denial of service or + string-manipulation bugs. Wrap the replacement with + Matcher.quoteReplacement() before passing it to the matcher. + metadata: + category: security + cwe: "CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component" + owasp: "A03:2021 - Injection" + confidence: MEDIUM + references: + - https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#quoteReplacement-java.lang.String- + source: independent security review + license: MIT + languages: [java] + severity: MEDIUM diff --git a/testcases/java/negative/ReplaceAllNeg.java b/testcases/java/negative/ReplaceAllNeg.java new file mode 100644 index 0000000..489315a --- /dev/null +++ b/testcases/java/negative/ReplaceAllNeg.java @@ -0,0 +1,9 @@ +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +public class ReplaceAllNeg { + public String sanitize(String cmdln) { + Matcher m = Pattern.compile("\\s+").matcher(cmdln); + return m.replaceAll(" "); + } +} diff --git a/testcases/java/positive/ReplaceAllPos.java b/testcases/java/positive/ReplaceAllPos.java new file mode 100644 index 0000000..6fa99d9 --- /dev/null +++ b/testcases/java/positive/ReplaceAllPos.java @@ -0,0 +1,19 @@ +import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +public class ReplaceAllPos { + public String expand(String cmdln, Map envMap) { + final Pattern expr = Pattern.compile("\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}"); + Matcher matcher = expr.matcher(cmdln); + while (matcher.find()) { + String envValue = envMap.get(matcher.group(1).toUpperCase()); + if (envValue == null) { + envValue = ""; + } + Matcher subexpr = Pattern.compile(Pattern.quote(matcher.group(0))).matcher(cmdln); + cmdln = subexpr.replaceAll(envValue); + } + return cmdln; + } +}