Skip to content

Commit b04c46f

Browse files
authored
Merge pull request #8478 from asgerf/js/store-load-flow-context-sensitivity-bug
Approved by erik-krogh
2 parents 4bf35ad + 26b7edc commit b04c46f

File tree

4 files changed

+36
-11
lines changed

4 files changed

+36
-11
lines changed

javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,27 +1365,31 @@ private predicate loadStep(
13651365

13661366
/**
13671367
* Holds if there is flow to `base.startProp`, and `base.startProp` flows to `nd.endProp` under `cfg/summary`.
1368+
*
1369+
* If `onlyRelevantInCall` is true, the `base` object will not be propagated out of return edges, because
1370+
* the flow that originally reached `base.startProp` used a call edge.
13681371
*/
13691372
pragma[nomagic]
13701373
private predicate reachableFromStoreBase(
13711374
string startProp, string endProp, DataFlow::Node base, DataFlow::Node nd,
1372-
DataFlow::Configuration cfg, PathSummary summary
1375+
DataFlow::Configuration cfg, PathSummary summary, boolean onlyRelevantInCall
13731376
) {
13741377
exists(PathSummary s1, PathSummary s2, DataFlow::Node rhs |
1375-
reachableFromSource(rhs, cfg, s1)
1378+
reachableFromSource(rhs, cfg, s1) and
1379+
onlyRelevantInCall = s1.hasCall()
13761380
or
1377-
reachableFromStoreBase(_, _, _, rhs, cfg, s1)
1381+
reachableFromStoreBase(_, _, _, rhs, cfg, s1, onlyRelevantInCall)
13781382
|
13791383
storeStep(rhs, nd, startProp, cfg, s2) and
13801384
endProp = startProp and
13811385
base = nd and
13821386
summary =
1383-
MkPathSummary(false, s1.hasCall().booleanOr(s2.hasCall()), DataFlow::FlowLabel::data(),
1384-
DataFlow::FlowLabel::data())
1387+
MkPathSummary(false, s2.hasCall(), DataFlow::FlowLabel::data(), DataFlow::FlowLabel::data())
13851388
)
13861389
or
13871390
exists(PathSummary newSummary, PathSummary oldSummary |
1388-
reachableFromStoreBaseStep(startProp, endProp, base, nd, cfg, oldSummary, newSummary) and
1391+
reachableFromStoreBaseStep(startProp, endProp, base, nd, cfg, oldSummary, newSummary,
1392+
onlyRelevantInCall) and
13891393
summary = oldSummary.appendValuePreserving(newSummary)
13901394
)
13911395
}
@@ -1399,14 +1403,16 @@ private predicate reachableFromStoreBase(
13991403
pragma[noinline]
14001404
private predicate reachableFromStoreBaseStep(
14011405
string startProp, string endProp, DataFlow::Node base, DataFlow::Node nd,
1402-
DataFlow::Configuration cfg, PathSummary oldSummary, PathSummary newSummary
1406+
DataFlow::Configuration cfg, PathSummary oldSummary, PathSummary newSummary,
1407+
boolean onlyRelevantInCall
14031408
) {
14041409
exists(DataFlow::Node mid |
1405-
reachableFromStoreBase(startProp, endProp, base, mid, cfg, oldSummary) and
1406-
flowStep(mid, cfg, nd, newSummary)
1410+
reachableFromStoreBase(startProp, endProp, base, mid, cfg, oldSummary, onlyRelevantInCall) and
1411+
flowStep(mid, cfg, nd, newSummary) and
1412+
onlyRelevantInCall.booleanAnd(newSummary.hasReturn()) = false
14071413
or
14081414
exists(string midProp |
1409-
reachableFromStoreBase(startProp, midProp, base, mid, cfg, oldSummary) and
1415+
reachableFromStoreBase(startProp, midProp, base, mid, cfg, oldSummary, onlyRelevantInCall) and
14101416
isAdditionalLoadStoreStep(mid, nd, midProp, endProp, cfg) and
14111417
newSummary = PathSummary::level()
14121418
)
@@ -1446,7 +1452,7 @@ private predicate storeToLoad(
14461452
PathSummary s1, PathSummary s2
14471453
|
14481454
storeStep(pred, storeBase, storeProp, cfg, s1) and
1449-
reachableFromStoreBase(storeProp, loadProp, storeBase, loadBase, cfg, s2) and
1455+
reachableFromStoreBase(storeProp, loadProp, storeBase, loadBase, cfg, s2, _) and
14501456
oldSummary = s1.appendValuePreserving(s2) and
14511457
loadStep(loadBase, succ, loadProp, cfg, newSummary)
14521458
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Fixed an issue that would sometimes prevent the data-flow analysis from finding flow
5+
paths through a function that stores its result on an object.
6+
This may lead to more results for the security queries.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ typeInferenceMismatch
172172
| string-replace.js:3:13:3:20 | source() | string-replace.js:21:6:21:41 | safe(). ... taint) |
173173
| string-replace.js:3:13:3:20 | source() | string-replace.js:22:6:22:48 | safe(). ... taint) |
174174
| string-replace.js:3:13:3:20 | source() | string-replace.js:24:6:24:45 | taint.r ... + '!') |
175+
| summarize-store-load-in-call.js:9:15:9:22 | source() | summarize-store-load-in-call.js:9:10:9:23 | blah(source()) |
175176
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field |
176177
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
177178
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as dummy from 'dummy';
2+
3+
function blah(obj) {
4+
obj.prop = obj.prop + "x";
5+
return obj.prop;
6+
}
7+
8+
function test() {
9+
sink(blah(source())); // NOT OK
10+
11+
blah(); // ensure more than one call site exists
12+
}

0 commit comments

Comments
 (0)