Regex Anchor Precedence
ID |
java.regex_anchor_precedence |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
code-style |
Description
Reports an alternation whose first branch starts with ^/\A or whose last branch ends with $/\Z/\z, while at least one other branch of the same alternation carries no such anchor.
Rationale
Anchors bind tighter than |, so ^a|b|c$ is not "match a, b or c, anchored at both ends" — it parses as (^a)|b|(c$):
Pattern.compile("^a|b|c$");
// "a" only matches at the very start, "c" only at the very end,
// but "b" matches anywhere in the input
The layout suggests every branch is bounded; only the outermost ones actually are.
Remediation
Wrap the alternatives in a non-capturing group so the anchors apply to the whole thing, or repeat the anchors on every branch if each was really meant to be anchored on its own:
Pattern.compile("^(?:a|b|c)$");
// or
Pattern.compile("^a$|^b$|^c$");
What is not reported
An alternation where every branch carries the same anchor on its own (a$|b$|^c$) is not reported — there is no gap between branches to be misled by. An alternation already wrapped in a group (^(?:a|b|c)$), or one whose anchored branches are each explicitly scoped in their own group ((?:^a)|b|(?:c$)), reads exactly as intended and is not reported either. An alternation with no anchor at either end is out of scope.