Skip to content

Commit 5c222f7

Browse files
authored
Merge pull request #14412 from asgerf/js/shared-dataflow
[Feature branch] JS: Migrate to shared dataflow library
2 parents 6a3bb4d + 1d267ef commit 5c222f7

File tree

394 files changed

+21998
-31036
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

394 files changed

+21998
-31036
lines changed

javascript/ql/examples/queries/dataflow/BackendIdor/BackendIdor.ql

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,42 @@
99
*/
1010

1111
import javascript
12-
import DataFlow
13-
import DataFlow::PathGraph
1412

1513
/**
1614
* A taint-tracking configuration that tracks user-controlled values into a 'userId' property sent to a backend service.
1715
*/
18-
class IdorTaint extends TaintTracking::Configuration {
19-
IdorTaint() { this = "IdorTaint" }
16+
module IdorTaintConfig implements DataFlow::ConfigSig {
17+
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
2018

21-
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
19+
predicate isSink(DataFlow::Node node) { exists(ClientRequest req | node = req.getADataNode()) }
2220

23-
override predicate isSink(Node node) { exists(ClientRequest req | node = req.getADataNode()) }
24-
25-
override predicate isAdditionalTaintStep(Node pred, Node succ) {
21+
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
2622
// Step from x -> { userId: x }
27-
succ.(SourceNode).getAPropertyWrite("userId").getRhs() = pred
23+
succ.(DataFlow::SourceNode).getAPropertyWrite("userId").getRhs() = pred
2824
}
2925

30-
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
26+
predicate isBarrier(DataFlow::Node node) {
3127
// After a check like `if (userId === session.user.id)`, the userId is considered safe.
32-
node instanceof EqualityGuard
28+
node = DataFlow::MakeBarrierGuard<EqualityGuard>::getABarrierNode()
3329
}
3430
}
3531

3632
/**
3733
* A sanitizer for values that have successfully been compared to another value.
3834
*/
39-
class EqualityGuard extends TaintTracking::SanitizerGuardNode, ValueNode {
35+
class EqualityGuard extends DataFlow::ValueNode {
4036
override EqualityTest astNode;
4137

42-
override predicate sanitizes(boolean outcome, Expr e) {
38+
predicate blocksExpr(boolean outcome, Expr e) {
4339
e = astNode.getAnOperand() and
4440
outcome = astNode.getPolarity()
4541
}
4642
}
4743

48-
from IdorTaint cfg, PathNode source, PathNode sink
49-
where cfg.hasFlowPath(source, sink)
44+
module IdorTaintFlow = TaintTracking::Global<IdorTaintConfig>;
45+
46+
import IdorTaintFlow::PathGraph
47+
48+
from IdorTaintFlow::PathNode source, IdorTaintFlow::PathNode sink
49+
where IdorTaintFlow::flowPath(source, sink)
5050
select sink.getNode(), source, sink, "Unauthenticated user ID from $@.", source.getNode(), "here"

javascript/ql/examples/queries/dataflow/DecodingAfterSanitization/DecodingAfterSanitization.ql

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,25 @@
99
*/
1010

1111
import javascript
12-
import DataFlow
13-
import DataFlow::PathGraph
1412

15-
class DecodingAfterSanitization extends TaintTracking::Configuration {
16-
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
17-
18-
override predicate isSource(Node node) { node.(CallNode).getCalleeName() = "escapeHtml" }
13+
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
14+
predicate isSource(DataFlow::Node node) {
15+
node.(DataFlow::CallNode).getCalleeName() = "escapeHtml"
16+
}
1917

20-
override predicate isSink(Node node) {
21-
exists(CallNode call |
18+
predicate isSink(DataFlow::Node node) {
19+
exists(DataFlow::CallNode call |
2220
call.getCalleeName().matches("decodeURI%") and
2321
node = call.getArgument(0)
2422
)
2523
}
2624
}
2725

28-
from DecodingAfterSanitization cfg, PathNode source, PathNode sink
29-
where cfg.hasFlowPath(source, sink)
26+
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
27+
28+
import DecodingAfterSanitizationFlow::PathGraph
29+
30+
from DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink
31+
where DecodingAfterSanitizationFlow::flowPath(source, sink)
3032
select sink.getNode(), source, sink, "URI decoding invalidates the HTML sanitization performed $@.",
3133
source.getNode(), "here"

javascript/ql/examples/queries/dataflow/DecodingAfterSanitization/DecodingAfterSanitizationGeneralized.ql

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@
99
*/
1010

1111
import javascript
12-
import DataFlow
13-
import DataFlow::PathGraph
1412

1513
/**
1614
* A call to a function that may introduce HTML meta-characters by
1715
* replacing `%3C` or `\u003C` with `<`.
1816
*/
19-
class DecodingCall extends CallNode {
17+
class DecodingCall extends DataFlow::CallNode {
2018
string kind;
21-
Node input;
19+
DataFlow::Node input;
2220

2321
DecodingCall() {
2422
this.getCalleeName().matches("decodeURI%") and
@@ -33,20 +31,24 @@ class DecodingCall extends CallNode {
3331
string getKind() { result = kind }
3432

3533
/** Gets the input being decoded. */
36-
Node getInput() { result = input }
34+
DataFlow::Node getInput() { result = input }
3735
}
3836

39-
class DecodingAfterSanitization extends TaintTracking::Configuration {
40-
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
37+
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
38+
predicate isSource(DataFlow::Node node) { node instanceof HtmlSanitizerCall }
4139

42-
override predicate isSource(Node node) { node instanceof HtmlSanitizerCall }
43-
44-
override predicate isSink(Node node) { node = any(DecodingCall c).getInput() }
40+
predicate isSink(DataFlow::Node node) { node = any(DecodingCall c).getInput() }
4541
}
4642

47-
from DecodingAfterSanitization cfg, PathNode source, PathNode sink, DecodingCall decoder
43+
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
44+
45+
import DecodingAfterSanitizationFlow::PathGraph
46+
47+
from
48+
DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink,
49+
DecodingCall decoder
4850
where
49-
cfg.hasFlowPath(source, sink) and
51+
DecodingAfterSanitizationFlow::flowPath(source, sink) and
5052
decoder.getInput() = sink.getNode()
5153
select sink.getNode(), source, sink, decoder.getKind() + " invalidates $@.", source.getNode(),
5254
"this HTML sanitization"

javascript/ql/examples/queries/dataflow/EvalTaint/EvalTaint.ql

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@
88
*/
99

1010
import javascript
11-
import DataFlow
1211

13-
class EvalTaint extends TaintTracking::Configuration {
14-
EvalTaint() { this = "EvalTaint" }
12+
module EvalTaintConfig implements DataFlow::ConfigSig {
13+
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
1514

16-
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
17-
18-
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
15+
predicate isSink(DataFlow::Node node) {
16+
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
17+
}
1918
}
2019

21-
from EvalTaint cfg, Node source, Node sink
22-
where cfg.hasFlow(source, sink)
20+
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
21+
22+
from DataFlow::Node source, DataFlow::Node sink
23+
where EvalTaintFlow::flow(source, sink)
2324
select sink, "Eval with user-controlled input from $@.", source, "here"

javascript/ql/examples/queries/dataflow/EvalTaint/EvalTaintPath.ql

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@
99
*/
1010

1111
import javascript
12-
import DataFlow
13-
import DataFlow::PathGraph
1412

15-
class EvalTaint extends TaintTracking::Configuration {
16-
EvalTaint() { this = "EvalTaint" }
13+
module EvalTaintConfig implements DataFlow::ConfigSig {
14+
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
1715

18-
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
19-
20-
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
16+
predicate isSink(DataFlow::Node node) {
17+
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
18+
}
2119
}
2220

23-
from EvalTaint cfg, PathNode source, PathNode sink
24-
where cfg.hasFlowPath(source, sink)
21+
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
22+
23+
import EvalTaintFlow::PathGraph
24+
25+
from EvalTaintFlow::PathNode source, EvalTaintFlow::PathNode sink
26+
where EvalTaintFlow::flowPath(source, sink)
2527
select sink.getNode(), source, sink, "Eval with user-controlled input from $@.", source.getNode(),
2628
"here"

javascript/ql/examples/queries/dataflow/InformationDisclosure/InformationDisclosure.ql

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
*/
1010

1111
import javascript
12-
import DataFlow
13-
import DataFlow::PathGraph
1412

1513
/**
1614
* A dataflow configuration that tracks authentication tokens ("authKey")
@@ -26,33 +24,37 @@ import DataFlow::PathGraph
2624
* }), '*');
2725
* ```
2826
*/
29-
class AuthKeyTracking extends DataFlow::Configuration {
30-
AuthKeyTracking() { this = "AuthKeyTracking" }
31-
32-
override predicate isSource(Node node) { node.(PropRead).getPropertyName() = "authKey" }
27+
module AuthKeyTrackingConfig implements DataFlow::ConfigSig {
28+
predicate isSource(DataFlow::Node node) {
29+
node.(DataFlow::PropRead).getPropertyName() = "authKey"
30+
}
3331

34-
override predicate isSink(Node node) {
35-
exists(MethodCallNode call |
32+
predicate isSink(DataFlow::Node node) {
33+
exists(DataFlow::MethodCallNode call |
3634
call.getMethodName() = "postMessage" and
3735
call.getArgument(1).getStringValue() = "*" and // no restriction on target origin
3836
call.getArgument(0) = node
3937
)
4038
}
4139

42-
override predicate isAdditionalFlowStep(Node pred, Node succ) {
40+
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
4341
// Step into objects: x -> { f: x }
44-
succ.(SourceNode).getAPropertyWrite().getRhs() = pred
42+
succ.(DataFlow::SourceNode).getAPropertyWrite().getRhs() = pred
4543
or
4644
// Step through JSON serialization: x -> JSON.stringify(x)
4745
// Note: TaintTracking::Configuration includes this step by default, but not DataFlow::Configuration
48-
exists(CallNode call |
49-
call = globalVarRef("JSON").getAMethodCall("stringify") and
46+
exists(DataFlow::CallNode call |
47+
call = DataFlow::globalVarRef("JSON").getAMethodCall("stringify") and
5048
pred = call.getArgument(0) and
5149
succ = call
5250
)
5351
}
5452
}
5553

56-
from AuthKeyTracking cfg, PathNode source, PathNode sink
57-
where cfg.hasFlowPath(source, sink)
54+
module AuthKeyTracking = DataFlow::Global<AuthKeyTrackingConfig>;
55+
56+
import AuthKeyTracking::PathGraph
57+
58+
from AuthKeyTracking::PathNode source, AuthKeyTracking::PathNode sink
59+
where AuthKeyTracking::flowPath(source, sink)
5860
select sink.getNode(), source, sink, "Message leaks the authKey from $@.", source.getNode(), "here"

javascript/ql/examples/queries/dataflow/StoredXss/StoredXss.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import javascript
1111
import semmle.javascript.security.dataflow.StoredXssQuery
12-
import DataFlow::PathGraph
12+
import StoredXssFlow::PathGraph
1313

1414
/**
1515
* The data returned from a MySQL query, such as the `data` parameter in this example:
@@ -31,6 +31,6 @@ class MysqlSource extends Source {
3131
}
3232
}
3333

34-
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
35-
where cfg.hasFlowPath(source, sink)
34+
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
35+
where StoredXssFlow::flowPath(source, sink)
3636
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."

javascript/ql/examples/queries/dataflow/StoredXss/StoredXssTypeTracking.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import javascript
1212
import semmle.javascript.security.dataflow.StoredXssQuery
13-
import DataFlow::PathGraph
13+
import StoredXssFlow::PathGraph
1414

1515
/**
1616
* Gets an instance of `mysql.createConnection()`, tracked globally.
@@ -45,6 +45,6 @@ class MysqlSource extends Source {
4545
MysqlSource() { this = mysqlConnection().getAMethodCall("query").getCallback(1).getParameter(1) }
4646
}
4747

48-
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
49-
where cfg.hasFlowPath(source, sink)
48+
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
49+
where StoredXssFlow::flowPath(source, sink)
5050
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."

javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
*/
99

1010
import javascript
11-
import DataFlow
12-
import DataFlow::PathGraph
1311

1412
/**
1513
* Gets the name of an unescaped placeholder in a lodash template.
@@ -21,21 +19,23 @@ string getAPlaceholderInString(string s) {
2119
result = s.regexpCapture(".*<%=\\s*([a-zA-Z0-9_]+)\\s*%>.*", 1)
2220
}
2321

24-
class TemplateInjection extends TaintTracking::Configuration {
25-
TemplateInjection() { this = "TemplateInjection" }
22+
module TemplateInjectionConfig implements DataFlow::ConfigSig {
23+
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
2624

27-
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
28-
29-
override predicate isSink(Node node) {
30-
exists(CallNode call, string placeholder |
25+
predicate isSink(DataFlow::Node node) {
26+
exists(DataFlow::CallNode call, string placeholder |
3127
call = LodashUnderscore::member("template").getACall() and
3228
placeholder = getAPlaceholderInString(call.getArgument(0).getStringValue()) and
3329
node = call.getOptionArgument(1, placeholder)
3430
)
3531
}
3632
}
3733

38-
from TemplateInjection cfg, PathNode source, PathNode sink
39-
where cfg.hasFlowPath(source, sink)
34+
module TemplateInjectionFlow = TaintTracking::Global<TemplateInjectionConfig>;
35+
36+
import TemplateInjectionFlow::PathGraph
37+
38+
from TemplateInjectionFlow::PathNode source, TemplateInjectionFlow::PathNode sink
39+
where TemplateInjectionFlow::flowPath(source, sink)
4040
select sink.getNode(), source, sink,
4141
"User-controlled value from $@ occurs unescaped in a lodash template.", source.getNode(), "here."

javascript/ql/lib/semmle/javascript/AMD.qll

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import javascript
77
private import semmle.javascript.internal.CachedStages
88
private import Expressions.ExprHasNoEffect
9+
private import semmle.javascript.dataflow.internal.DataFlowNode
910

1011
/**
1112
* Companion module to the `AmdModuleDefinition` class.
@@ -84,10 +85,15 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
8485
result instanceof DataFlow::ValueNode
8586
}
8687

87-
private DataFlow::Node getFactoryNodeInternal() {
88-
// To avoid recursion, this should not depend on `SourceNode`.
89-
result = DataFlow::valueNode(this.getLastArgument()) or
90-
result = this.getFactoryNodeInternal().getAPredecessor()
88+
/**
89+
* Gets the factory function of this module definition.
90+
*/
91+
Function getFactoryFunction() { TValueNode(result) = this.getFactoryNodeInternal() }
92+
93+
private EarlyStageNode getFactoryNodeInternal() {
94+
result = TValueNode(this.getLastArgument())
95+
or
96+
DataFlow::localFlowStep(result, this.getFactoryNodeInternal())
9197
}
9298

9399
/** Gets the expression defining this module. */
@@ -139,7 +145,10 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
139145
* Gets the `i`th parameter of the factory function of this module.
140146
*/
141147
private Parameter getFactoryParameter(int i) {
142-
this.getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
148+
exists(Function fun |
149+
this.getFactoryNodeInternal() = TValueNode(fun) and
150+
result = fun.getParameter(i)
151+
)
143152
}
144153

145154
/**

0 commit comments

Comments
 (0)