Skip to content

Commit 7600a73

Browse files
committed
DataFlow: primary and secondary configurations
For now I've only implemented what XSS.qll needs
1 parent f8922f1 commit 7600a73

File tree

5 files changed

+180
-20
lines changed

5 files changed

+180
-20
lines changed

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

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,25 @@ private module XssVulnerableWriterSourceToWritingMethodFlowConfig implements Dat
7171
sink.asExpr() = ma.getQualifier() and ma.getMethod() instanceof WritingMethod
7272
)
7373
}
74+
}
7475

75-
predicate observeDiffInformedIncrementalMode() {
76-
// Since this configuration is for finding sinks to be used in a main
77-
// data-flow configuration, this configuration should only restrict the
78-
// sinks to be found if there are no main-configuration sources in the
79-
// diff range. That's because if there is such a source, we need to
80-
// report query results for it even with sinks outside the diff range.
81-
exists(DataFlow::DiffInformedQuery q | not q.hasSourceInDiffRange())
82-
}
83-
84-
// The sources are not exposed outside this file module, so we know the
85-
// query will not select them.
86-
Location getASelectedSourceLocation(DataFlow::Node source) { none() }
87-
88-
Location getASelectedSinkLocation(DataFlow::Node sink) {
76+
private module XssVulnerableWriterSourceToWritingMethodFlowSecondaryConfig implements
77+
DataFlow::SecondaryConfig
78+
{
79+
DataFlow::Node getPrimaryOfSecondaryNode(
80+
DataFlow::IsSourceOrSink sourceOrSink, DataFlow::Node sink
81+
) {
82+
sourceOrSink instanceof DataFlow::IsSink and
8983
// This code mirrors `DefaultXssSink()`.
90-
exists(MethodCall ma | result = ma.getAnArgument().getLocation() |
84+
exists(MethodCall ma | result.asExpr() = ma.getAnArgument() |
9185
sink.asExpr() = ma.getQualifier() and ma.getMethod() instanceof WritingMethod
9286
)
9387
}
9488
}
9589

9690
private module XssVulnerableWriterSourceToWritingMethodFlow =
97-
TaintTracking::Global<XssVulnerableWriterSourceToWritingMethodFlowConfig>;
91+
TaintTracking::FindSinks<XssVulnerableWriterSourceToWritingMethodFlowConfig,
92+
XssVulnerableWriterSourceToWritingMethodFlowSecondaryConfig>;
9893

9994
/** A method that can be used to output data to an output stream or writer. */
10095
private class WritingMethod extends Method {

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ module XssConfig implements DataFlow::ConfigSig {
2020
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
2121
any(XssAdditionalTaintStep s).step(node1, node2)
2222
}
23-
24-
predicate observeDiffInformedIncrementalMode() { exists(DataFlow::DiffInformedQuery q) }
2523
}
2624

2725
/** Tracks flow from remote sources to cross site scripting vulnerabilities. */
28-
module XssFlow = TaintTracking::Global<XssConfig>;
26+
module XssFlow = TaintTracking::Primary<XssConfig>;

shared/dataflow/codeql/dataflow/DataFlow.qll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ module DataFlowMake<LocationSig Location, InputSig<Location> Lang> {
691691

692692
class DiffInformedQuery = DiffInformedQueryImpl;
693693

694+
import SecondaryConfigHelpers
695+
694696
/**
695697
* Constructs a global data flow computation.
696698
*/
@@ -744,6 +746,33 @@ module DataFlowMake<LocationSig Location, InputSig<Location> Lang> {
744746
import Flow
745747
}
746748

749+
module Primary<ConfigSig Config> implements GlobalFlowSig {
750+
private module Config0 implements FullStateConfigSig {
751+
import DefaultState<Config>
752+
import Config
753+
754+
predicate accessPathLimit = Config::accessPathLimit/0;
755+
756+
predicate isAdditionalFlowStep(Node node1, Node node2, string model) {
757+
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
758+
}
759+
}
760+
761+
private module C implements FullStateConfigSig {
762+
import MakePrimaryDiffInformed<Config0>
763+
764+
predicate accessPathLimit = Config0::accessPathLimit/0;
765+
}
766+
767+
private module Stage1 = ImplStage1<C>;
768+
769+
import Stage1::PartialFlow
770+
771+
private module Flow = Impl<C, Stage1::Stage1NoState>;
772+
773+
import Flow
774+
}
775+
747776
signature class PathNodeSig {
748777
/** Gets a textual representation of this element. */
749778
string toString();

shared/dataflow/codeql/dataflow/TaintTracking.qll

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,61 @@ module TaintFlowMake<
143143
import Flow
144144
}
145145

146+
/**
147+
* Constructs a global taint tracking computation.
148+
*/
149+
module Primary<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
150+
private module Config0 implements DataFlowInternal::FullStateConfigSig {
151+
import DataFlowInternal::DefaultState<Config>
152+
import Config
153+
154+
predicate isAdditionalFlowStep(
155+
DataFlowLang::Node node1, DataFlowLang::Node node2, string model
156+
) {
157+
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
158+
}
159+
}
160+
161+
private module C implements DataFlowInternal::FullStateConfigSig {
162+
import DataFlowInternal::MakePrimaryDiffInformed<AddTaintDefaults<Config0>>
163+
}
164+
165+
private module Stage1 = DataFlowInternalStage1::ImplStage1<C>;
166+
167+
import Stage1::PartialFlow
168+
169+
private module Flow = DataFlowInternal::Impl<C, Stage1::Stage1NoState>;
170+
171+
import Flow
172+
}
173+
174+
module FindSinks<DataFlow::ConfigSig Config, DataFlow::SecondaryConfig SC> implements
175+
DataFlow::GlobalFlowSig
176+
{
177+
private module Config0 implements DataFlowInternal::FullStateConfigSig {
178+
import DataFlowInternal::DefaultState<Config>
179+
import Config
180+
181+
predicate isAdditionalFlowStep(
182+
DataFlowLang::Node node1, DataFlowLang::Node node2, string model
183+
) {
184+
Config::isAdditionalFlowStep(node1, node2) and model = "Config"
185+
}
186+
}
187+
188+
private module C implements DataFlowInternal::FullStateConfigSig {
189+
import DataFlowInternal::MakeSinkFinder<AddTaintDefaults<Config0>, SC>
190+
}
191+
192+
private module Stage1 = DataFlowInternalStage1::ImplStage1<C>;
193+
194+
import Stage1::PartialFlow
195+
196+
private module Flow = DataFlowInternal::Impl<C, Stage1::Stage1NoState>;
197+
198+
import Flow
199+
}
200+
146201
signature int speculationLimitSig();
147202

148203
private module AddSpeculativeTaintSteps<

shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
184184

185185
string toString() { result = "DataFlow::DiffInformedQuery" }
186186

187-
// TODO: how to wire this up? It overlaps with the data-flow configuration signature!
188187
Location getASelectedSourceLocation(Node source) { result = source.getLocation() }
189188

190189
Location getASelectedSinkLocation(Node sink) { result = sink.getLocation() }
@@ -200,6 +199,90 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
200199
}
201200
}
202201

202+
module MakePrimaryDiffInformed<FullStateConfigSig Config> implements FullStateConfigSig {
203+
// Workaround for name clash
204+
predicate accessPathLimit = Config::accessPathLimit/0;
205+
206+
import Config
207+
208+
predicate observeDiffInformedIncrementalMode() {
209+
// Add to existing configuration to support composition of config transformers
210+
Config::observeDiffInformedIncrementalMode()
211+
or
212+
exists(DiffInformedQueryImpl q)
213+
}
214+
215+
Location getASelectedSourceLocation(Node source) {
216+
result = Config::getASelectedSourceLocation(source)
217+
or
218+
exists(DiffInformedQueryImpl q | result = q.getASelectedSourceLocation(source))
219+
}
220+
221+
Location getASelectedSinkLocation(Node sink) {
222+
result = Config::getASelectedSinkLocation(sink)
223+
or
224+
exists(DiffInformedQueryImpl q | result = q.getASelectedSinkLocation(sink))
225+
}
226+
}
227+
228+
module SecondaryConfigHelpers {
229+
newtype IsSourceOrSink =
230+
IsSource() or
231+
IsSink()
232+
233+
signature module SecondaryConfig {
234+
/**
235+
* Gets the source/sink node from the primary configuration that is
236+
* informed by a given source/sink node from the secondary configuration.
237+
* Whether the secondary node is a source or a sink is determined by
238+
* `sourceOrSink`.
239+
*/
240+
bindingset[sourceOrSink, secondaryNode]
241+
Node getPrimaryOfSecondaryNode(IsSourceOrSink sourceOrSink, Node secondaryNode);
242+
}
243+
}
244+
245+
module MakeSinkFinder<FullStateConfigSig Config, SecondaryConfig SC> implements FullStateConfigSig
246+
{
247+
// Workaround for name clash
248+
predicate accessPathLimit = Config::accessPathLimit/0;
249+
250+
import Config
251+
252+
predicate observeDiffInformedIncrementalMode() {
253+
// Add to existing configuration to support composition of config transformers
254+
Config::observeDiffInformedIncrementalMode()
255+
or
256+
// Because this configuration is for finding sinks to be used in a main
257+
// data-flow configuration, this configuration should only restrict the
258+
// sinks to be found if there are no main-configuration sources in the
259+
// diff range. That's because if there is such a source, we need to
260+
// report query results for it even with sinks outside the diff range.
261+
//
262+
// The `MakeSinkFinder` and `MakeSourceFinder` modules are separate
263+
// because each can only call one of `hasSourceInDiffRange` or
264+
// `hasSinkInDiffRange`. Otherwise it would look like a non-monotonic
265+
// recursion.
266+
exists(DiffInformedQuery q | not q.hasSourceInDiffRange())
267+
}
268+
269+
Location getASelectedSourceLocation(Node source) {
270+
result = Config::getASelectedSourceLocation(source)
271+
or
272+
exists(DiffInformedQueryImpl q, IsSource s |
273+
result = q.getASelectedSourceLocation(SC::getPrimaryOfSecondaryNode(s, source))
274+
)
275+
}
276+
277+
Location getASelectedSinkLocation(Node sink) {
278+
result = Config::getASelectedSinkLocation(sink)
279+
or
280+
exists(DiffInformedQueryImpl q, IsSink s |
281+
result = q.getASelectedSinkLocation(SC::getPrimaryOfSecondaryNode(s, sink))
282+
)
283+
}
284+
}
285+
203286
/**
204287
* Constructs a data flow computation given a full input configuration, and
205288
* an initial stage 1 pruning.

0 commit comments

Comments
 (0)