@@ -13,17 +13,36 @@ private import semmle.code.java.dataflow.ExternalFlow
1313private import semmle.code.java.dataflow.FlowSources
1414private import semmle.code.java.dataflow.FlowSinks
1515
16- /** A sink that represent a method that outputs data without applying contextual output encoding. */
17- abstract class XssSink extends ApiSinkNode { }
18-
1916/** A sanitizer that neutralizes dangerous characters that can be used to perform a XSS attack. */
2017abstract class XssSanitizer extends DataFlow:: Node { }
2118
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+
2241/**
2342 * A sink that represent a method that outputs data without applying contextual output encoding,
2443 * and which should truncate flow paths such that downstream sinks are not flagged as well.
2544 */
26- abstract class XssSinkBarrier extends XssSink { }
45+ abstract class XssSinkBarrier extends AbstractXssSink { }
2746
2847/**
2948 * A unit class for adding additional taint steps.
@@ -39,19 +58,6 @@ class XssAdditionalTaintStep extends Unit {
3958 abstract predicate step ( DataFlow:: Node node1 , DataFlow:: Node node2 ) ;
4059}
4160
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-
5561/** A default sanitizer that considers numeric and boolean typed data safe for writing to output. */
5662private class DefaultXssSanitizer extends XssSanitizer {
5763 DefaultXssSanitizer ( ) {
@@ -62,20 +68,6 @@ private class DefaultXssSanitizer extends XssSanitizer {
6268 }
6369}
6470
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-
7971/** A method that can be used to output data to an output stream or writer. */
8072private class WritingMethod extends Method {
8173 WritingMethod ( ) {
@@ -113,6 +105,77 @@ class XssVulnerableWriterSourceNode extends ApiSourceNode {
113105 XssVulnerableWriterSourceNode ( ) { this .asExpr ( ) instanceof XssVulnerableWriterSource }
114106}
115107
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+
116179/**
117180 * Holds if `s` is an HTTP Content-Type vulnerable to XSS.
118181 */
0 commit comments