Skip to content

Commit a5d9cb1

Browse files
authored
Merge pull request #20930 from owen-mc/java/spring-rest-template-request-forgery-sinks
Java: add more Spring RestTemplate request forgery sinks
2 parents d3fc254 + 97e0b4e commit a5d9cb1

File tree

4 files changed

+478
-145
lines changed

4 files changed

+478
-145
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* URI template variables of all Spring `RestTemplate` methods are now considered as request forgery sinks. Previously only the `getForObject` method was considered. This may lead to more alerts for the query `java/ssrf`.

java/ql/lib/semmle/code/java/frameworks/spring/SpringWebClient.qll

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,55 @@ class SpringWebClient extends Interface {
3131
}
3232
}
3333

34-
/** The method `getForObject` on `org.springframework.web.reactive.function.client.RestTemplate`. */
35-
class SpringRestTemplateGetForObjectMethod extends Method {
36-
SpringRestTemplateGetForObjectMethod() {
34+
/**
35+
* A method on `org.springframework.web.client.RestTemplate`
36+
* which has a parameter `uriVariables` (which can have type `Object...` or
37+
* `Map<String, ?>`) which contains variables to be expanded into the URL
38+
* template in parameter 0.
39+
*/
40+
private class SpringRestTemplateMethodWithUriVariablesParameter extends Method {
41+
int pos;
42+
43+
SpringRestTemplateMethodWithUriVariablesParameter() {
3744
this.getDeclaringType() instanceof SpringRestTemplate and
38-
this.hasName("getForObject")
45+
this.getParameter(pos).getName() = "uriVariables"
3946
}
40-
}
4147

42-
/** A call to the method `getForObject` on `org.springframework.web.reactive.function.client.RestTemplate`. */
43-
class SpringRestTemplateGetForObjectMethodCall extends MethodCall {
44-
SpringRestTemplateGetForObjectMethodCall() {
45-
this.getMethod() instanceof SpringRestTemplateGetForObjectMethod
46-
}
48+
int getUriVariablesPosition() { result = pos }
49+
}
4750

48-
/** Gets the first argument, if it is a compile time constant. */
49-
CompileTimeConstantExpr getConstantUrl() { result = this.getArgument(0) }
51+
/** Gets the first argument of `mc`, if it is a compile-time constant. */
52+
pragma[inline]
53+
private CompileTimeConstantExpr getConstantUrl(MethodCall mc) { result = mc.getArgument(0) }
5054

51-
/**
52-
* Holds if the first argument is a compile time constant and it has a
53-
* placeholder at offset `offset`, and there are `idx` placeholders that
54-
* appear before it.
55-
*/
56-
predicate urlHasPlaceholderAtOffset(int idx, int offset) {
57-
exists(
58-
this.getConstantUrl()
59-
.getStringValue()
60-
.replaceAll("\\{", " ")
61-
.replaceAll("\\}", " ")
62-
.regexpFind("\\{[^}]*\\}", idx, offset)
63-
)
64-
}
55+
/**
56+
* Holds if the first argument of `mc` is a compile-time constant URL template
57+
* which has its `idx`-th placeholder at the offset `offset`.
58+
*/
59+
pragma[inline]
60+
private predicate urlHasPlaceholderAtOffset(MethodCall mc, int idx, int offset) {
61+
exists(
62+
getConstantUrl(mc)
63+
.getStringValue()
64+
.replaceAll("\\{", " ")
65+
.replaceAll("\\}", " ")
66+
.regexpFind("\\{[^}]*\\}", idx, offset)
67+
)
6568
}
6669

67-
private class SpringWebClientRestTemplateGetForObject extends RequestForgerySink {
68-
SpringWebClientRestTemplateGetForObject() {
69-
exists(SpringRestTemplateGetForObjectMethodCall mc, int i |
70-
// Note that the first argument is modeled as a request forgery sink
71-
// separately. This model is for arguments beyond the first two. There
72-
// are two relevant overloads, one with third parameter type `Object...`
73-
// and one with third parameter type `Map<String, ?>`. For the latter we
74-
// cannot deal with MapValue content easily but there is a default
75-
// implicit taint read at sinks that will catch it.
70+
private class SpringWebClientRestTemplateUriVariable extends RequestForgerySink {
71+
SpringWebClientRestTemplateUriVariable() {
72+
exists(SpringRestTemplateMethodWithUriVariablesParameter m, MethodCall mc, int i |
73+
// Note that the first argument of `m` is modeled as a request forgery
74+
// sink separately. This model is for arguments corresponding to the
75+
// `uriVariables` parameter. There are always two relevant overloads, one
76+
// with parameter type `Object...` and one with parameter type
77+
// `Map<String, ?>`. For the latter we cannot deal with MapValue content
78+
// easily but there is a default implicit taint read at sinks that will
79+
// catch it.
80+
mc.getMethod() = m and
7681
i >= 0 and
77-
this.asExpr() = mc.getArgument(i + 2)
82+
this.asExpr() = mc.getArgument(m.getUriVariablesPosition() + i)
7883
|
7984
// If we can determine that part of mc.getArgument(0) is a hostname
8085
// sanitizing prefix, then we count how many placeholders occur before it
@@ -83,8 +88,8 @@ private class SpringWebClientRestTemplateGetForObject extends RequestForgerySink
8388
// considering the map values as sinks if there is at least one
8489
// placeholder in the URL before the hostname sanitizing prefix.
8590
exists(int offset |
86-
mc.urlHasPlaceholderAtOffset(i, offset) and
87-
offset < mc.getConstantUrl().(HostnameSanitizingPrefix).getOffset()
91+
urlHasPlaceholderAtOffset(mc, i, offset) and
92+
offset < getConstantUrl(mc).(HostnameSanitizingPrefix).getOffset()
8893
)
8994
or
9095
// If we cannot determine that part of mc.getArgument(0) is a hostname
@@ -94,12 +99,12 @@ private class SpringWebClientRestTemplateGetForObject extends RequestForgerySink
9499
// For the `Map<String, ?>` overload this has the effect of only
95100
// considering the map values as sinks if there is at least one
96101
// placeholder in the URL.
97-
not mc.getConstantUrl() instanceof HostnameSanitizingPrefix and
98-
mc.urlHasPlaceholderAtOffset(i, _)
102+
not getConstantUrl(mc) instanceof HostnameSanitizingPrefix and
103+
urlHasPlaceholderAtOffset(mc, i, _)
99104
or
100105
// If we cannot determine the string value of mc.getArgument(0), then we
101106
// conservatively consider all arguments as sinks.
102-
not exists(mc.getConstantUrl().getStringValue())
107+
not exists(getConstantUrl(mc).getStringValue())
103108
)
104109
}
105110
}

0 commit comments

Comments
 (0)