Skip to content

Commit 1b85feb

Browse files
committed
JS: Add imprecise post-update steps for when a captured var/this is not tracked precisely
With the capture library we sometimes bails out of handling certain functions for scalability reasons. This means we have a notion of "captured but imprecisely-tracked" variables and 'this'. In these cases we go back to propagating flow from a post-update node to the local source.
1 parent d557c76 commit 1b85feb

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,28 @@ private predicate isThisNodeTrackedByVariableCapture(DataFlow::ThisNode thisNode
10491049
)
10501050
}
10511051

1052+
/**
1053+
* Holds if there should be flow from `postUpdate` to `target` because of a variable/this value
1054+
* that is captured but not tracked precisely by the variable-capture library.
1055+
*/
1056+
pragma[nomagic]
1057+
private predicate imprecisePostUpdateStep(DataFlow::PostUpdateNode postUpdate, DataFlow::Node target) {
1058+
exists(LocalVariableOrThis var, DataFlow::Node use |
1059+
// 'var' is captured but not tracked precisely
1060+
var.isCaptured() and
1061+
not var instanceof VariableCaptureConfig::CapturedVariable and
1062+
(
1063+
use = TValueNode(var.asLocalVariable().getAnAccess())
1064+
or
1065+
use = TValueNode(var.getAThisExpr())
1066+
or
1067+
use = TImplicitThisUse(var.getAThisUse(), false)
1068+
) and
1069+
postUpdate.getPreUpdateNode() = use and
1070+
target = use.getALocalSource()
1071+
)
1072+
}
1073+
10521074
/**
10531075
* Holds if there is a value-preserving steps `node1` -> `node2` that might
10541076
* be cross function boundaries.
@@ -1059,6 +1081,8 @@ private predicate valuePreservingStep(Node node1, Node node2) {
10591081
not isBlockedLegacyNode(node2) and
10601082
not isThisNodeTrackedByVariableCapture(node1)
10611083
or
1084+
imprecisePostUpdateStep(node1, node2)
1085+
or
10621086
FlowSteps::propertyFlowStep(node1, node2)
10631087
or
10641088
FlowSteps::globalFlowStep(node1, node2)

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ legacyDataFlowDifference
1717
| callbacks.js:44:17:44:24 | source() | callbacks.js:38:35:38:35 | x | only flow with NEW data flow library |
1818
| capture-flow.js:89:13:89:20 | source() | capture-flow.js:89:6:89:21 | test3c(source()) | only flow with NEW data flow library |
1919
| capture-flow.js:101:12:101:19 | source() | capture-flow.js:102:6:102:20 | test5("safe")() | only flow with OLD data flow library |
20-
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:272:10:272:17 | this.foo | only flow with OLD data flow library |
21-
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:274:6:274:45 | new Cap ... ()).foo | only flow with OLD data flow library |
22-
| capture-flow.js:283:34:283:41 | source() | capture-flow.js:284:6:284:44 | new Cap ... e').foo | only flow with NEW data flow library |
2320
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:40:8:40:14 | e.taint | only flow with NEW data flow library |
2421
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:44:8:44:19 | f_safe.taint | only flow with NEW data flow library |
2522
| constructor-calls.js:20:15:20:22 | source() | constructor-calls.js:39:8:39:14 | e.param | only flow with NEW data flow library |
@@ -126,8 +123,9 @@ flow
126123
| capture-flow.js:259:23:259:30 | source() | capture-flow.js:252:14:252:36 | objectW ... s.field |
127124
| capture-flow.js:259:23:259:30 | source() | capture-flow.js:253:14:253:23 | this.field |
128125
| capture-flow.js:262:16:262:23 | source() | capture-flow.js:264:14:264:21 | this.foo |
126+
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:272:10:272:17 | this.foo |
127+
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:274:6:274:45 | new Cap ... ()).foo |
129128
| capture-flow.js:283:34:283:41 | source() | capture-flow.js:283:6:283:46 | new Cap ... ()).foo |
130-
| capture-flow.js:283:34:283:41 | source() | capture-flow.js:284:6:284:44 | new Cap ... e').foo |
131129
| captured-sanitizer.js:25:3:25:10 | source() | captured-sanitizer.js:15:10:15:10 | x |
132130
| case.js:2:16:2:23 | source() | case.js:5:8:5:35 | changeC ... source) |
133131
| case.js:2:16:2:23 | source() | case.js:8:8:8:24 | camelCase(source) |

javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ legacyDataFlowDifference
1111
| callbacks.js:44:17:44:24 | source() | callbacks.js:38:35:38:35 | x | only flow with NEW data flow library |
1212
| capture-flow.js:89:13:89:20 | source() | capture-flow.js:89:6:89:21 | test3c(source()) | only flow with NEW data flow library |
1313
| capture-flow.js:101:12:101:19 | source() | capture-flow.js:102:6:102:20 | test5("safe")() | only flow with OLD data flow library |
14-
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:272:10:272:17 | this.foo | only flow with OLD data flow library |
15-
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:274:6:274:45 | new Cap ... ()).foo | only flow with OLD data flow library |
1614
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:40:8:40:14 | e.taint | only flow with NEW data flow library |
1715
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:44:8:44:19 | f_safe.taint | only flow with NEW data flow library |
1816
| constructor-calls.js:20:15:20:22 | source() | constructor-calls.js:39:8:39:14 | e.param | only flow with NEW data flow library |
@@ -100,6 +98,8 @@ flow
10098
| capture-flow.js:259:23:259:30 | source() | capture-flow.js:252:14:252:36 | objectW ... s.field |
10199
| capture-flow.js:259:23:259:30 | source() | capture-flow.js:253:14:253:23 | this.field |
102100
| capture-flow.js:262:16:262:23 | source() | capture-flow.js:264:14:264:21 | this.foo |
101+
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:272:10:272:17 | this.foo |
102+
| capture-flow.js:274:33:274:40 | source() | capture-flow.js:274:6:274:45 | new Cap ... ()).foo |
103103
| capture-flow.js:283:34:283:41 | source() | capture-flow.js:283:6:283:46 | new Cap ... ()).foo |
104104
| captured-sanitizer.js:25:3:25:10 | source() | captured-sanitizer.js:15:10:15:10 | x |
105105
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:24:8:24:14 | c.taint |

javascript/ql/test/library-tests/TaintTracking/capture-flow.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,9 @@ function CaptureThisWithoutJump(x) {
269269
[1].forEach(() => {
270270
this.foo = x;
271271
});
272-
sink(this.foo); // NOT OK [INCONSISTENCY]
272+
sink(this.foo); // NOT OK
273273
}
274-
sink(new CaptureThisWithoutJump(source()).foo); // NOT OK [INCONSISTENCY]
274+
sink(new CaptureThisWithoutJump(source()).foo); // NOT OK
275275
sink(new CaptureThisWithoutJump('safe').foo); // OK
276276

277277
function CaptureThisWithoutJump2(x) {
@@ -281,4 +281,4 @@ function CaptureThisWithoutJump2(x) {
281281
return y;
282282
}
283283
sink(new CaptureThisWithoutJump2(source()).foo); // NOT OK
284-
sink(new CaptureThisWithoutJump2('safe').foo); // OK [INCONSISTENCY]
284+
sink(new CaptureThisWithoutJump2('safe').foo); // OK

0 commit comments

Comments
 (0)