@@ -13,17 +13,36 @@ private import semmle.code.java.dataflow.ExternalFlow
13
13
private import semmle.code.java.dataflow.FlowSources
14
14
private import semmle.code.java.dataflow.FlowSinks
15
15
16
- /** A sink that represent a method that outputs data without applying contextual output encoding. */
17
- abstract class XssSink extends ApiSinkNode { }
18
-
19
16
/** A sanitizer that neutralizes dangerous characters that can be used to perform a XSS attack. */
20
17
abstract class XssSanitizer extends DataFlow:: Node { }
21
18
19
+ /**
20
+ * A sink that represent a method that outputs data without applying contextual
21
+ * output encoding. Extend this class to add more sinks that should be
22
+ * considered XSS sinks by every query. To find the full set of XSS sinks, use
23
+ * `XssSink` instead.
24
+ */
25
+ abstract class AbstractXssSink extends DataFlow:: Node { }
26
+
27
+ /** A default sink representing methods susceptible to XSS attacks. */
28
+ private class DefaultXssSink extends AbstractXssSink {
29
+ DefaultXssSink ( ) { sinkNode ( this , [ "html-injection" , "js-injection" ] ) }
30
+ }
31
+
32
+ /**
33
+ * A sink that represent a method that outputs data without applying contextual
34
+ * output encoding. To add more sinks, extend `AbstractXssSink` rather than
35
+ * this class. To find XSS sinks efficiently for a diff-informed query, use the
36
+ * `XssDiffInformed` module instead.
37
+ */
38
+ final class XssSink extends ApiSinkNode instanceof XssDiffInformed< xssNotDiffInformed / 0 > :: XssSink {
39
+ }
40
+
22
41
/**
23
42
* A sink that represent a method that outputs data without applying contextual output encoding,
24
43
* and which should truncate flow paths such that downstream sinks are not flagged as well.
25
44
*/
26
- abstract class XssSinkBarrier extends XssSink { }
45
+ abstract class XssSinkBarrier extends AbstractXssSink { }
27
46
28
47
/**
29
48
* A unit class for adding additional taint steps.
@@ -39,19 +58,6 @@ class XssAdditionalTaintStep extends Unit {
39
58
abstract predicate step ( DataFlow:: Node node1 , DataFlow:: Node node2 ) ;
40
59
}
41
60
42
- /** A default sink representing methods susceptible to XSS attacks. */
43
- private class DefaultXssSink extends XssSink {
44
- DefaultXssSink ( ) {
45
- sinkNode ( this , [ "html-injection" , "js-injection" ] )
46
- or
47
- exists ( MethodCall ma |
48
- ma .getMethod ( ) instanceof WritingMethod and
49
- XssVulnerableWriterSourceToWritingMethodFlow:: flowToExpr ( ma .getQualifier ( ) ) and
50
- this .asExpr ( ) = ma .getArgument ( _)
51
- )
52
- }
53
- }
54
-
55
61
/** A default sanitizer that considers numeric and boolean typed data safe for writing to output. */
56
62
private class DefaultXssSanitizer extends XssSanitizer {
57
63
DefaultXssSanitizer ( ) {
@@ -62,20 +68,6 @@ private class DefaultXssSanitizer extends XssSanitizer {
62
68
}
63
69
}
64
70
65
- /** A configuration that tracks data from a servlet writer to an output method. */
66
- private module XssVulnerableWriterSourceToWritingMethodFlowConfig implements DataFlow:: ConfigSig {
67
- predicate isSource ( DataFlow:: Node src ) { src instanceof XssVulnerableWriterSourceNode }
68
-
69
- predicate isSink ( DataFlow:: Node sink ) {
70
- exists ( MethodCall ma |
71
- sink .asExpr ( ) = ma .getQualifier ( ) and ma .getMethod ( ) instanceof WritingMethod
72
- )
73
- }
74
- }
75
-
76
- private module XssVulnerableWriterSourceToWritingMethodFlow =
77
- TaintTracking:: Global< XssVulnerableWriterSourceToWritingMethodFlowConfig > ;
78
-
79
71
/** A method that can be used to output data to an output stream or writer. */
80
72
private class WritingMethod extends Method {
81
73
WritingMethod ( ) {
@@ -113,6 +105,77 @@ class XssVulnerableWriterSourceNode extends ApiSourceNode {
113
105
XssVulnerableWriterSourceNode ( ) { this .asExpr ( ) instanceof XssVulnerableWriterSource }
114
106
}
115
107
108
+ /** A nullary predicate. */
109
+ signature predicate xssNullaryPredicate ( ) ;
110
+
111
+ /** Holds always. Use this predicate as parameter to `XssDiffInformed` to disable diff-informed mode. */
112
+ predicate xssNotDiffInformed ( ) { any ( ) }
113
+
114
+ /**
115
+ * A module for finding XSS sinks faster in a diff-informed query. The
116
+ * `hasSourceInDiffRange` parameter should hold if the overall data-flow
117
+ * configuration of the query has any sources in the diff range.
118
+ */
119
+ module XssDiffInformed< xssNullaryPredicate / 0 hasSourceInDiffRange> {
120
+ final private class Node = DataFlow:: Node ;
121
+
122
+ /**
123
+ * A diff-informed replacement for the top-level `XssSink`, omitting for
124
+ * efficiency some sinks that would never be reported by a diff-informed
125
+ * query.
126
+ */
127
+ final class XssSink extends Node {
128
+ XssSink ( ) {
129
+ this instanceof AbstractXssSink
130
+ or
131
+ isResponseWriterSink ( this )
132
+ }
133
+ }
134
+
135
+ predicate isResponseWriterSink ( DataFlow:: Node sink ) {
136
+ exists ( MethodCall ma |
137
+ // This code mirrors `getASelectedSinkLocation`.
138
+ ma .getMethod ( ) instanceof WritingMethod and
139
+ XssVulnerableWriterSourceToWritingMethodFlow:: flowToExpr ( ma .getQualifier ( ) ) and
140
+ sink .asExpr ( ) = ma .getArgument ( _)
141
+ )
142
+ }
143
+
144
+ /** A configuration that tracks data from a servlet writer to an output method. */
145
+ private module XssVulnerableWriterSourceToWritingMethodFlowConfig implements DataFlow:: ConfigSig {
146
+ predicate isSource ( DataFlow:: Node src ) { src instanceof XssVulnerableWriterSourceNode }
147
+
148
+ predicate isSink ( DataFlow:: Node sink ) {
149
+ exists ( MethodCall ma |
150
+ sink .asExpr ( ) = ma .getQualifier ( ) and ma .getMethod ( ) instanceof WritingMethod
151
+ )
152
+ }
153
+
154
+ predicate observeDiffInformedIncrementalMode ( ) {
155
+ // Since this configuration is for finding sinks to be used in a main
156
+ // data-flow configuration, this configuration should only restrict the
157
+ // sinks to be found if there are no main-configuration sources in the
158
+ // diff range. That's because if there is such a source, we need to
159
+ // report query results for it even with sinks outside the diff range.
160
+ not hasSourceInDiffRange ( )
161
+ }
162
+
163
+ // The sources are not exposed outside this file module, so we know the
164
+ // query will not select them.
165
+ Location getASelectedSourceLocation ( DataFlow:: Node source ) { none ( ) }
166
+
167
+ Location getASelectedSinkLocation ( DataFlow:: Node sink ) {
168
+ // This code mirrors `isResponseWriterSink`.
169
+ exists ( MethodCall ma | result = ma .getAnArgument ( ) .getLocation ( ) |
170
+ sink .asExpr ( ) = ma .getQualifier ( ) and ma .getMethod ( ) instanceof WritingMethod
171
+ )
172
+ }
173
+ }
174
+
175
+ private module XssVulnerableWriterSourceToWritingMethodFlow =
176
+ TaintTracking:: Global< XssVulnerableWriterSourceToWritingMethodFlowConfig > ;
177
+ }
178
+
116
179
/**
117
180
* Holds if `s` is an HTTP Content-Type vulnerable to XSS.
118
181
*/
0 commit comments