Skip to content

Commit e525cf0

Browse files
committed
generalize isAdditionalLoadStoreStep such that it loads and stores different properties
1 parent 8d37c03 commit e525cf0

File tree

4 files changed

+60
-12
lines changed

4 files changed

+60
-12
lines changed

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

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,15 @@ abstract class Configuration extends string {
246246
predicate isAdditionalLoadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
247247
none()
248248
}
249+
250+
/**
251+
* EXPERIMENTAL. This API may change in the future.
252+
*
253+
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
254+
*/
255+
predicate isAdditionalLoadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp) {
256+
none()
257+
}
249258
}
250259

251260
/**
@@ -515,6 +524,17 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
515524
*/
516525
cached
517526
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
527+
528+
/**
529+
* EXPERIMENTAL. This API may change in the future.
530+
*
531+
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
532+
*/
533+
cached
534+
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp) {
535+
loadProp = storeProp and
536+
loadStoreStep(pred, succ, loadProp)
537+
}
518538
}
519539

520540
/**
@@ -619,7 +639,7 @@ private predicate exploratoryFlowStep(
619639
basicLoadStep(pred, succ, _) or
620640
isAdditionalStoreStep(pred, succ, _, cfg) or
621641
isAdditionalLoadStep(pred, succ, _, cfg) or
622-
isAdditionalLoadStoreStep(pred, succ, _, cfg) or
642+
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
623643
// the following two disjuncts taken together over-approximate flow through
624644
// higher-order calls
625645
callback(pred, succ) or
@@ -859,14 +879,21 @@ private predicate isAdditionalStoreStep(
859879
}
860880

861881
/**
862-
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
882+
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
863883
*/
864884
private predicate isAdditionalLoadStoreStep(
865-
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
885+
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp, DataFlow::Configuration cfg
866886
) {
867-
any(AdditionalFlowStep s).loadStoreStep(pred, succ, prop)
887+
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
888+
or
889+
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp, storeProp)
868890
or
869-
cfg.isAdditionalLoadStoreStep(pred, succ, prop)
891+
loadProp = storeProp and
892+
(
893+
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp)
894+
or
895+
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp)
896+
)
870897
}
871898

872899
/**
@@ -904,12 +931,14 @@ private predicate reachableFromStoreBase(
904931
or
905932
exists(DataFlow::Node mid, PathSummary oldSummary, PathSummary newSummary |
906933
reachableFromStoreBase(prop, rhs, mid, cfg, oldSummary) and
907-
(
908-
flowStep(mid, cfg, nd, newSummary)
909-
or
910-
isAdditionalLoadStoreStep(mid, nd, prop, cfg) and
911-
newSummary = PathSummary::level()
912-
) and
934+
flowStep(mid, cfg, nd, newSummary) and
935+
summary = oldSummary.appendValuePreserving(newSummary)
936+
)
937+
or
938+
exists(DataFlow::Node mid, PathSummary oldSummary, PathSummary newSummary, string midProp |
939+
reachableFromStoreBase(midProp, rhs, mid, cfg, oldSummary) and
940+
isAdditionalLoadStoreStep(mid, nd, midProp, prop, cfg) and
941+
newSummary = PathSummary::level() and
913942
summary = oldSummary.appendValuePreserving(newSummary)
914943
)
915944
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
| tst.js:4:15:4:22 | "source" | tst.js:9:7:9:24 | readTaint(tainted) |
2+
| tst.js:4:15:4:22 | "source" | tst.js:15:7:15:20 | tainted3.other |

javascript/ql/test/library-tests/CustomLoadStoreSteps/test.ql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ class Configuration extends TaintTracking::Configuration {
1919
) and
2020
prop = "bar"
2121
}
22+
23+
// calling .copy("foo", "bar") actually moves a property from "foo" to "bar".
24+
override predicate isAdditionalLoadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp) {
25+
exists(DataFlow::MethodCallNode call | call.getMethodName() = "copy" and call = succ and pred = call.getReceiver() |
26+
call.getArgument(0).mayHaveStringValue(loadProp) and
27+
call.getArgument(1).mayHaveStringValue(storeProp)
28+
)
29+
}
2230
}
2331

2432
from DataFlow::Node pred, DataFlow::Node succ, Configuration cfg

javascript/ql/test/library-tests/CustomLoadStoreSteps/tst.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,15 @@
66
function readTaint(x) {
77
return x.foo;
88
}
9-
sink(readTaint(tainted));
9+
sink(readTaint(tainted)); // NOT OK
10+
11+
12+
var tainted2 = {myProp: source};
13+
14+
var tainted3 = tainted2.copy("myProp", "other");
15+
sink(tainted3.other); // NOT OK.
16+
17+
var tainted4 = tainted2.copy("other", "myProp"); // does nothing, there is no "other" on tainted2.
18+
sink(tainted4.other); // OK.
19+
1020
})();

0 commit comments

Comments
 (0)