Skip to content

Commit b9b34eb

Browse files
committed
Move Spring XSS sink definition into SpringHttp.qll
1 parent 3b6cc97 commit b9b34eb

File tree

2 files changed

+48
-42
lines changed

2 files changed

+48
-42
lines changed

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
*/
55

66
import java
7+
78
private import semmle.code.java.dataflow.ExternalFlow
89
private import semmle.code.java.dataflow.DataFlow
10+
private import semmle.code.java.frameworks.spring.SpringController
911
private import semmle.code.java.security.XSS as XSS
1012

1113
/** The class `org.springframework.http.HttpEntity` or an instantiation of it. */
@@ -143,6 +145,52 @@ private class SpringHttpFlowStep extends SummaryModelCsv {
143145
}
144146
}
145147

148+
private class SpringXssSink extends XSS::XssSink {
149+
SpringXssSink() {
150+
exists(SpringRequestMappingMethod requestMappingMethod, ReturnStmt rs |
151+
requestMappingMethod = rs.getEnclosingCallable() and
152+
this.asExpr() = rs.getResult() and
153+
(
154+
not exists(requestMappingMethod.getProduces()) or
155+
requestMappingMethod.getProduces().matches("text/%")
156+
)
157+
|
158+
// If a Spring request mapping method is either annotated with @ResponseBody (or equivalent),
159+
// or returns a HttpEntity or sub-type, then the return value of the method is converted into
160+
// a HTTP reponse using a HttpMessageConverter implementation. The implementation is chosen
161+
// based on the return type of the method, and the Accept header of the request.
162+
//
163+
// By default, the only message converter which produces a response which is vulnerable to
164+
// XSS is the StringHttpMessageConverter, which "Accept"s all text/* content types, including
165+
// text/html. Therefore, if a browser request includes "text/html" in the "Accept" header,
166+
// any String returned will be converted into a text/html response.
167+
requestMappingMethod.isResponseBody() and
168+
requestMappingMethod.getReturnType() instanceof TypeString
169+
or
170+
exists(Type returnType |
171+
// A return type of HttpEntity<T> or ResponseEntity<T> represents an HTTP response with both
172+
// a body and a set of headers. The body is subject to the same HttpMessageConverter
173+
// process as above.
174+
returnType = requestMappingMethod.getReturnType() and
175+
(
176+
returnType instanceof SpringHttpEntity
177+
or
178+
returnType instanceof SpringResponseEntity
179+
)
180+
|
181+
// The type argument, representing the type of the body, is type String
182+
returnType.(ParameterizedClass).getTypeArgument(0) instanceof TypeString
183+
or
184+
// Return type is a Raw class, which means no static type information on the body. In this
185+
// case we will still treat this as an XSS sink, but rely on our taint flow steps for
186+
// HttpEntity/ResponseEntity to only pass taint into those instances if the body type was
187+
// String.
188+
returnType instanceof RawClass
189+
)
190+
)
191+
}
192+
}
193+
146194
private string getSpringConstantContentType(FieldAccess e) {
147195
e.getQualifier().getType().(RefType).hasQualifiedName("org.springframework.http", "MediaType") and
148196
exists(string fieldName | e.getField().hasName(fieldName) |

java/ql/lib/semmle/code/java/security/XSS.qll

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -45,48 +45,6 @@ private class DefaultXssSink extends XssSink {
4545
writer.hasFlowToExpr(ma.getQualifier()) and
4646
this.asExpr() = ma.getArgument(_)
4747
)
48-
or
49-
exists(SpringRequestMappingMethod requestMappingMethod, ReturnStmt rs |
50-
requestMappingMethod = rs.getEnclosingCallable() and
51-
this.asExpr() = rs.getResult() and
52-
(
53-
not exists(requestMappingMethod.getProduces()) or
54-
requestMappingMethod.getProduces().matches("text/%")
55-
)
56-
|
57-
// If a Spring request mapping method is either annotated with @ResponseBody (or equivalent),
58-
// or returns a HttpEntity or sub-type, then the return value of the method is converted into
59-
// a HTTP reponse using a HttpMessageConverter implementation. The implementation is chosen
60-
// based on the return type of the method, and the Accept header of the request.
61-
//
62-
// By default, the only message converter which produces a response which is vulnerable to
63-
// XSS is the StringHttpMessageConverter, which "Accept"s all text/* content types, including
64-
// text/html. Therefore, if a browser request includes "text/html" in the "Accept" header,
65-
// any String returned will be converted into a text/html response.
66-
requestMappingMethod.isResponseBody() and
67-
requestMappingMethod.getReturnType() instanceof TypeString
68-
or
69-
exists(Type returnType |
70-
// A return type of HttpEntity<T> or ResponseEntity<T> represents an HTTP response with both
71-
// a body and a set of headers. The body is subject to the same HttpMessageConverter
72-
// process as above.
73-
returnType = requestMappingMethod.getReturnType() and
74-
(
75-
returnType instanceof SpringHttpEntity
76-
or
77-
returnType instanceof SpringResponseEntity
78-
)
79-
|
80-
// The type argument, representing the type of the body, is type String
81-
returnType.(ParameterizedClass).getTypeArgument(0) instanceof TypeString
82-
or
83-
// Return type is a Raw class, which means no static type information on the body. In this
84-
// case we will still treat this as an XSS sink, but rely on our taint flow steps for
85-
// HttpEntity/ResponseEntity to only pass taint into those instances if the body type was
86-
// String.
87-
returnType instanceof RawClass
88-
)
89-
)
9048
}
9149
}
9250

0 commit comments

Comments
 (0)