Invalid Regular Expression
ID |
kotlin.bad_regex_syntax |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Kotlin |
Tags |
reliability |
Description
Reports a Regex(…) construction, a String.toRegex() call or a Pattern.compile(…) call whose pattern is a statically known string that is not valid regular-expression syntax.
Validity is decided by compiling the pattern with the very engine Kotlin’s Regex delegates to, so a finding is not an approximation: the pattern will throw.
Rationale
// Bad
val group = Regex("(unclosed") // PatternSyntaxException: Unclosed group
val range = Regex("a{3,1}") // PatternSyntaxException: Illegal repetition range
val empty = Regex("[]") // PatternSyntaxException: Unclosed character class
-
The exception is thrown the moment the expression is evaluated, so the code path containing it is dead on arrival — it can never do the matching it was written for.
-
Nothing about the source hints at it: an invalid pattern compiles fine as Kotlin and only fails at run time, often on a rarely taken branch that no test covers.
-
Patterns are frequently copied between languages, and the grammars differ.
[],[^], a lone{anda{,3}are legal in JavaScript and rejected here; conversely the possessive quantifiera++and the unscoped inline flag group(?i)are rejected in JavaScript and perfectly valid here.
Remediation
Fix the pattern. The exception’s description names the exact problem — an unclosed group or character class, a repetition range whose bounds are in the wrong order, an unsupported escape.
// Good
val group = Regex("(closed)")
val range = Regex("a{1,3}")
val empty = Regex("[a-z]")
What is not reported
A pattern that is not statically known — built by concatenation, interpolated, held in a variable, or returned by another call — is not reported, since the string the engine will compile is not known here.
String.replace("(unclosed", "-") is not reported either: that overload takes a plain string and replaces it literally, so no regular expression is compiled. The same holds for String.split(vararg String) and String.replaceFirst(String, String), and for Regex.fromLiteral(…), which matches its argument as plain text.