Skip to content

Commit fdb79da

Browse files
SONARJAVA-5909 FN on S3752 when @RequestMapping in class (#5412)
1 parent c5519e7 commit fdb79da

4 files changed

Lines changed: 70 additions & 9 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ruleKey": "S3752",
33
"hasTruePositives": true,
4-
"falseNegatives": 26,
4+
"falseNegatives": 38,
55
"falsePositives": 0
66
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ruleKey": "S4488",
33
"hasTruePositives": false,
4-
"falseNegatives": 14,
4+
"falseNegatives": 20,
55
"falsePositives": 0
6-
}
6+
}

java-checks-test-sources/default/src/main/java/checks/spring/SpringRequestMappingMethodCheckSample.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,39 @@ String get() {
6464
return "Hello from get";
6565
}
6666

67+
@RequestMapping(value = "/post", method = RequestMethod.POST) // Noncompliant
68+
String post() {
69+
return "Hello from post";
70+
}
71+
72+
@RequestMapping(value = "/put", method = RequestMethod.PUT) // Noncompliant
73+
String put() {
74+
return "Hello from put";
75+
}
76+
77+
@RequestMapping(value = "/delete", method = RequestMethod.DELETE) // Noncompliant
78+
String delete() {
79+
return "Hello from delete";
80+
}
81+
}
82+
83+
@RestController
84+
@RequestMapping(path = "/update", method = RequestMethod.POST)
85+
public static class UpdateController {
86+
@RequestMapping(value = "/", method = RequestMethod.GET) // Noncompliant
87+
String get() {
88+
return "Hello from get";
89+
}
90+
91+
@RequestMapping(value = "/head", method = RequestMethod.HEAD) // Noncompliant
92+
String head() {
93+
return "Hello from head";
94+
}
95+
96+
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
97+
String delete() {
98+
return "Hello from delete";
99+
}
67100
}
68101

69102
public static class DerivedController extends OtherController {

java-checks/src/main/java/org/sonar/java/checks/spring/SpringRequestMappingMethodCheck.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public class SpringRequestMappingMethodCheck extends IssuableSubscriptionVisitor
4545
private static final String REQUEST_METHOD = "method";
4646
public static final String MESSAGE = "Make sure allowing safe and unsafe HTTP methods is safe here.";
4747

48+
private boolean classHasSafeMethods;
49+
private boolean classHasUnsafeMethods;
50+
private boolean methodHasSafeMethods;
51+
private boolean methodHasUnsafeMethods;
52+
4853
@Override
4954
public List<Tree.Kind> nodesToVisit() {
5055
return Collections.singletonList(Tree.Kind.CLASS);
@@ -53,11 +58,11 @@ public List<Tree.Kind> nodesToVisit() {
5358
@Override
5459
public void visitNode(Tree tree) {
5560
ClassTree classTree = (ClassTree) tree;
61+
resetFlags();
5662
findRequestMappingAnnotation(classTree.modifiers())
5763
.flatMap(SpringRequestMappingMethodCheck::findRequestMethods)
58-
.filter(SpringRequestMappingMethodCheck::mixSafeAndUnsafeMethods)
64+
.filter(this::mixesSafeAndUnsafeMethodsOnClass)
5965
.ifPresent(methods -> reportIssue(methods, MESSAGE));
60-
6166
classTree.members().stream()
6267
.filter(member -> member.is(Tree.Kind.METHOD))
6368
.forEach(member -> checkMethod((MethodTree) member, classTree.symbol()));
@@ -69,9 +74,15 @@ private void checkMethod(MethodTree method, Symbol.TypeSymbol classSymbol) {
6974
.flatMap(SpringRequestMappingMethodCheck::findRequestMethods);
7075

7176
if (requestMethods.isPresent()) {
72-
requestMethods
73-
.filter(SpringRequestMappingMethodCheck::mixSafeAndUnsafeMethods)
74-
.ifPresent(methods -> reportIssue(methods, MESSAGE));
77+
Optional<ExpressionTree> expressionTree = requestMethods
78+
.filter(this::mixesSafeAndUnsafeMethodsOnMethod);
79+
if (expressionTree.isPresent()) {
80+
reportIssue(expressionTree.get(), MESSAGE);
81+
} else {
82+
if ((classHasSafeMethods && methodHasUnsafeMethods) || (classHasUnsafeMethods && methodHasSafeMethods)) {
83+
reportIssue(requestMethods.get(), MESSAGE);
84+
}
85+
}
7586
} else if (requestMappingAnnotation.isPresent() && !inheritRequestMethod(classSymbol)) {
7687
reportIssue(requestMappingAnnotation.get().annotationType(), MESSAGE);
7788
}
@@ -109,12 +120,29 @@ private static boolean inheritRequestMethod(Symbol.TypeSymbol symbol) {
109120
return false;
110121
}
111122

112-
private static boolean mixSafeAndUnsafeMethods(ExpressionTree requestMethodsAssignment) {
123+
private boolean mixesSafeAndUnsafeMethodsOnClass(ExpressionTree requestMethodsAssignment) {
124+
HttpMethodVisitor visitor = new HttpMethodVisitor();
125+
requestMethodsAssignment.accept(visitor);
126+
classHasSafeMethods = visitor.hasSafeMethods;
127+
classHasUnsafeMethods = visitor.hasUnsafeMethods;
128+
return visitor.hasSafeMethods && visitor.hasUnsafeMethods;
129+
}
130+
131+
private boolean mixesSafeAndUnsafeMethodsOnMethod(ExpressionTree requestMethodsAssignment) {
113132
HttpMethodVisitor visitor = new HttpMethodVisitor();
114133
requestMethodsAssignment.accept(visitor);
134+
methodHasSafeMethods = visitor.hasSafeMethods;
135+
methodHasUnsafeMethods = visitor.hasUnsafeMethods;
115136
return visitor.hasSafeMethods && visitor.hasUnsafeMethods;
116137
}
117138

139+
private void resetFlags() {
140+
classHasSafeMethods = false;
141+
classHasUnsafeMethods = false;
142+
methodHasSafeMethods = false;
143+
methodHasUnsafeMethods = false;
144+
}
145+
118146
private static class HttpMethodVisitor extends BaseTreeVisitor {
119147
private static final Set<String> SAFE_METHODS = new HashSet<>(Arrays.asList("GET", "HEAD", "OPTIONS", "TRACE"));
120148
private static final Set<String> UNSAFE_METHODS = new HashSet<>(Arrays.asList("DELETE", "PATCH", "POST", "PUT"));

0 commit comments

Comments
 (0)