Skip to content

Commit ecc52a1

Browse files
authored
Merge pull request #4541 from RasmusWL/python-port-reflected-xss
Python: Port reflected XSS query
2 parents 146787b + 8036045 commit ecc52a1

File tree

19 files changed

+1581
-78
lines changed

19 files changed

+1581
-78
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @name Reflected server-side cross-site scripting
3+
* @description Writing user input directly to a web page
4+
* allows for a cross-site scripting vulnerability.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @sub-severity high
8+
* @precision high
9+
* @id py/reflective-xss
10+
* @tags security
11+
* external/cwe/cwe-079
12+
* external/cwe/cwe-116
13+
*/
14+
15+
import python
16+
import experimental.dataflow.DataFlow
17+
import experimental.dataflow.TaintTracking
18+
import experimental.semmle.python.Concepts
19+
import experimental.dataflow.RemoteFlowSources
20+
import DataFlow::PathGraph
21+
22+
class ReflectedXssConfiguration extends TaintTracking::Configuration {
23+
ReflectedXssConfiguration() { this = "ReflectedXssConfiguration" }
24+
25+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
26+
27+
override predicate isSink(DataFlow::Node sink) {
28+
exists(HTTP::Server::HttpResponse response |
29+
response.getMimetype().toLowerCase() = "text/html" and
30+
sink = response.getBody()
31+
)
32+
}
33+
}
34+
35+
from ReflectedXssConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
36+
where config.hasFlowPath(source, sink)
37+
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",
38+
source.getNode(), "a user-provided value"

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ module HTTP {
228228
/** Provides classes for modeling HTTP servers. */
229229
module Server {
230230
/**
231-
* An data-flow node that sets up a route on a server.
231+
* A data-flow node that sets up a route on a server.
232232
*
233233
* Extend this class to refine existing API models. If you want to model new APIs,
234234
* extend `RouteSetup::Range` instead.
@@ -254,7 +254,7 @@ module HTTP {
254254
/** Provides a class for modeling new HTTP routing APIs. */
255255
module RouteSetup {
256256
/**
257-
* An data-flow node that sets up a route on a server.
257+
* A data-flow node that sets up a route on a server.
258258
*
259259
* Extend this class to model new APIs. If you want to refine existing API models,
260260
* extend `RouteSetup` instead.
@@ -287,5 +287,60 @@ module HTTP {
287287

288288
override string getSourceType() { result = "RoutedParameter" }
289289
}
290+
291+
/**
292+
* A data-flow node that creates a HTTP response on a server.
293+
*
294+
* Note: we don't require that this response must be sent to a client (a kind of
295+
* "if a tree falls in a forest and nobody hears it" situation).
296+
*
297+
* Extend this class to refine existing API models. If you want to model new APIs,
298+
* extend `HttpResponse::Range` instead.
299+
*/
300+
class HttpResponse extends DataFlow::Node {
301+
HttpResponse::Range range;
302+
303+
HttpResponse() { this = range }
304+
305+
/** Gets the data-flow node that specifies the body of this HTTP response. */
306+
DataFlow::Node getBody() { result = range.getBody() }
307+
308+
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
309+
string getMimetype() { result = range.getMimetype() }
310+
}
311+
312+
/** Provides a class for modeling new HTTP response APIs. */
313+
module HttpResponse {
314+
/**
315+
* A data-flow node that creates a HTTP response on a server.
316+
*
317+
* Note: we don't require that this response must be sent to a client (a kind of
318+
* "if a tree falls in a forest and nobody hears it" situation).
319+
*
320+
* Extend this class to model new APIs. If you want to refine existing API models,
321+
* extend `HttpResponse` instead.
322+
*/
323+
abstract class Range extends DataFlow::Node {
324+
/** Gets the data-flow node that specifies the body of this HTTP response. */
325+
abstract DataFlow::Node getBody();
326+
327+
/** Gets the data-flow node that specifies the content-type/mimetype of this HTTP response, if any. */
328+
abstract DataFlow::Node getMimetypeOrContentTypeArg();
329+
330+
/** Gets the default mimetype that should be used if `getMimetypeOrContentTypeArg` has no results. */
331+
abstract string getMimetypeDefault();
332+
333+
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
334+
string getMimetype() {
335+
exists(StrConst str |
336+
DataFlow::localFlow(DataFlow::exprNode(str), this.getMimetypeOrContentTypeArg()) and
337+
result = str.getText().splitAt(";", 0)
338+
)
339+
or
340+
not exists(this.getMimetypeOrContentTypeArg()) and
341+
result = this.getMimetypeDefault()
342+
}
343+
}
344+
}
290345
}
291346
}

0 commit comments

Comments
 (0)