Skip to content

Commit 55885f1

Browse files
SONARJAVA-3747 FPs in S5852 when repetition overlaps with non-repetition part (#3494)
1 parent 3fa749e commit 55885f1

File tree

3 files changed

+21
-16
lines changed

3 files changed

+21
-16
lines changed

its/ruling/src/test/resources/regex-examples/java-S5852.json

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@
115115
473,
116116
475,
117117
479,
118-
511,
119-
529,
120118
551,
121119
557,
122120
717,
@@ -142,7 +140,6 @@
142140
1559,
143141
1673,
144142
1741,
145-
1801,
146143
1871,
147144
1897,
148145
1899,
@@ -612,7 +609,6 @@
612609
1509,
613610
1523,
614611
1771,
615-
1795,
616612
1805,
617613
1807,
618614
1817,
@@ -632,7 +628,6 @@
632628
225,
633629
227,
634630
231,
635-
261,
636631
277,
637632
331,
638633
347,
@@ -643,7 +638,6 @@
643638
633,
644639
637,
645640
645,
646-
649,
647641
657,
648642
659,
649643
665,
@@ -654,7 +648,6 @@
654648
697,
655649
699,
656650
715,
657-
729,
658651
791,
659652
861,
660653
869,
@@ -664,9 +657,6 @@
664657
929,
665658
933,
666659
935,
667-
945,
668-
947,
669-
949,
670660
951,
671661
953,
672662
965,

java-checks-test-sources/src/main/java/checks/regex/RedosCheck.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ void alwaysQuadratic(String str) {
5656
str.matches("x*a*x*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
5757
str.matches("x*,a*x*"); // Compliant, can fail between the two quantifiers
5858
str.matches("x*(xy?)*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
59-
str.matches("(ab)*a(ba)*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
59+
str.matches("(ab)*a(ba)*"); // False Negative :-(
60+
str.matches("x*xx*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
61+
str.matches("x*yx*"); // Compliant
6062
str.matches("x*a*b*c*d*e*f*g*h*i*x*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
6163
str.matches("x*a*b*c*d*e*f*g*h*i*j*x*"); // FN because we forget about the first x* when the maximum number of tracked repetitions is exceeded
6264
str.matches("x*a*b*c*d*e*f*g*h*i*j*x*x*"); // Noncompliant {{Make sure the regex used here, which is vulnerable to polynomial runtime due to backtracking, cannot lead to denial of service.}}
@@ -125,8 +127,8 @@ void compliant(String str) {
125127
str.matches("(;*,)*");
126128
str.matches("x*|x*");
127129
str.matches("a*b*");
128-
str.matches("a*a?b*"); // Noncompliant - false positive :-(
129-
str.matches("a*(a?b)*"); // Noncompliant - false positive :-(
130+
str.matches("a*a?b*");
131+
str.matches("a*(a?b)*");
130132
str.matches("a*(ab)*");
131133
str.split("x*x*");
132134
str.matches("(?s)x*.*");

java-checks/src/main/java/org/sonar/java/checks/regex/RedosCheck.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,12 @@ private void checkForOverlappingRepetitions(RepetitionTree tree) {
179179
if (tree.getQuantifier().isOpenEnded() && canFail(tree)) {
180180
for (RepetitionTree repetition : nonPossessiveRepetitions) {
181181
if (reachabilityChecker.canReach(repetition, tree)) {
182-
SubAutomaton auto1 = new SubAutomaton(repetition.getElement(), tree.continuation(), false);
183-
SubAutomaton auto2 = new SubAutomaton(repetition.continuation(), tree.continuation(), false);
184-
if (intersectionChecker.check(auto1, auto2)) {
182+
SubAutomaton repetitionAuto = new SubAutomaton(repetition.getElement(), repetition.continuation(), false);
183+
SubAutomaton continuationAuto = new SubAutomaton(repetition.continuation(), tree, false);
184+
SubAutomaton treeAuto = new SubAutomaton(tree.getElement(), tree.continuation(), false);
185+
if (subAutomatonCanConsume(repetitionAuto, continuationAuto)
186+
&& automatonIsEmptyOrIntersects(continuationAuto, treeAuto)
187+
&& intersectionChecker.check(repetitionAuto, treeAuto)) {
185188
addBacktracking(BacktrackingType.ALWAYS_QUADRATIC);
186189
}
187190
}
@@ -193,6 +196,16 @@ private void checkForOverlappingRepetitions(RepetitionTree tree) {
193196
}
194197
}
195198

199+
private boolean subAutomatonCanConsume(SubAutomaton auto1, SubAutomaton auto2) {
200+
return canReachWithoutConsumingInputOrGoingThroughBoundaries(auto1.end, auto2.end)
201+
|| intersectionChecker.check(auto1, auto2);
202+
}
203+
204+
private boolean automatonIsEmptyOrIntersects(SubAutomaton auto1, SubAutomaton auto2) {
205+
return canReachWithoutConsumingInputOrGoingThroughBoundaries(auto1.start, auto1.end)
206+
|| intersectionChecker.check(auto1, auto2);
207+
}
208+
196209
private void addIfNonPossessive(RepetitionTree tree) {
197210
if (!tree.isPossessive()) {
198211
nonPossessiveRepetitions.add(tree);

0 commit comments

Comments
 (0)