Skip to content

Commit acdc896

Browse files
committed
JS: Support for dynamic args to flow summaries
1 parent 53a2a66 commit acdc896

7 files changed

Lines changed: 59 additions & 28 deletions

File tree

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ private module ConsistencyConfig implements InputSig<Location, JSDataFlow> {
2424
or
2525
n instanceof FlowSummaryIntermediateAwaitStoreNode
2626
or
27+
n instanceof FlowSummaryDynamicParameterArrayNode
28+
or
2729
n instanceof GenericSynthesizedNode
2830
or
2931
n = DataFlow::globalAccessPathRootPseudoNode()
@@ -43,6 +45,10 @@ private module ConsistencyConfig implements InputSig<Location, JSDataFlow> {
4345
node instanceof TStaticArgumentArrayNode or
4446
node instanceof TDynamicArgumentArrayNode
4547
}
48+
49+
predicate reverseReadExclude(DataFlow::Node node) {
50+
node instanceof FlowSummaryDynamicParameterArrayNode
51+
}
4652
}
4753

4854
module Consistency = MakeConsistency<Location, JSDataFlow, JSTaintFlow, ConsistencyConfig>;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ private module Cached {
8080
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
8181
TConstructorThisPostUpdate(Constructor ctor) or
8282
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
83+
TFlowSummaryDynamicParameterArrayNode(FlowSummaryImpl::Public::SummarizedCallable callable) or
8384
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
8485
// NOTE: This dependency goes through the 'Steps' module whose instantiation depends on the call graph,
8586
// but the specific predicate we're referering to does not use that information.

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ class FlowSummaryNode extends DataFlow::Node, TFlowSummaryNode {
3030
override string toString() { result = this.getSummaryNode().toString() }
3131
}
3232

33+
class FlowSummaryDynamicParameterArrayNode extends DataFlow::Node,
34+
TFlowSummaryDynamicParameterArrayNode
35+
{
36+
private FlowSummaryImpl::Public::SummarizedCallable callable;
37+
38+
FlowSummaryDynamicParameterArrayNode() { this = TFlowSummaryDynamicParameterArrayNode(callable) }
39+
40+
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { result = callable }
41+
42+
cached
43+
override string toString() { result = "[dynamic parameter array] " + callable }
44+
}
45+
3346
class FlowSummaryIntermediateAwaitStoreNode extends DataFlow::Node,
3447
TFlowSummaryIntermediateAwaitStoreNode
3548
{
@@ -342,6 +355,12 @@ private predicate isParameterNodeImpl(Node p, DataFlowCallable c, ParameterPosit
342355
FlowSummaryImpl::Private::summaryParameterNode(summaryNode.getSummaryNode(), pos) and
343356
c.asLibraryCallable() = summaryNode.getSummarizedCallable()
344357
)
358+
or
359+
exists(FlowSummaryImpl::Public::SummarizedCallable callable |
360+
c.asLibraryCallable() = callable and
361+
pos.isDynamicArgumentArray() and
362+
p = TFlowSummaryDynamicParameterArrayNode(callable)
363+
)
345364
}
346365

347366
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
@@ -410,6 +429,8 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
410429
or
411430
result.asLibraryCallable() = node.(FlowSummaryNode).getSummarizedCallable()
412431
or
432+
result.asLibraryCallable() = node.(FlowSummaryDynamicParameterArrayNode).getSummarizedCallable()
433+
or
413434
result.asLibraryCallable() = node.(FlowSummaryIntermediateAwaitStoreNode).getSummarizedCallable()
414435
or
415436
node = TGenericSynthesizedNode(_, _, result)
@@ -1118,6 +1139,17 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
11181139
storeContent.isUnknownArrayElement()
11191140
else storeContent.asArrayIndex() = n + c.asArrayIndex()
11201141
)
1142+
or
1143+
exists(FlowSummaryNode parameter, ParameterPosition pos |
1144+
FlowSummaryImpl::Private::summaryParameterNode(parameter.getSummaryNode(), pos) and
1145+
node1 = TFlowSummaryDynamicParameterArrayNode(parameter.getSummarizedCallable()) and
1146+
node2 = parameter and
1147+
(
1148+
c.asArrayIndex() = pos.asPositional()
1149+
or
1150+
c = ContentSet::arrayElementLowerBound(pos.asPositionalLowerBound())
1151+
)
1152+
)
11211153
}
11221154

11231155
/** Gets the post-update node for which `node` is the corresponding pre-update node. */

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ private predicate positionName(ParameterPosition pos, string operand) {
3535
or
3636
pos.isFunctionSelfReference() and operand = "function"
3737
or
38-
pos.isDynamicArgumentArray() and operand = "arguments-array" // TODO: remove and handle automatically
39-
or
4038
operand = pos.asPositionalLowerBound() + ".."
4139
}
4240

javascript/ql/lib/semmle/javascript/internal/flow_summaries/Arrays.qll

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,11 @@ class ArrayConstructorSummary extends SummarizedCallable {
101101

102102
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
103103
preservesValue = true and
104-
(
105-
input = "Argument[0..]" and
106-
output = "ReturnValue.ArrayElement"
107-
or
108-
input = "Argument[arguments-array].WithArrayElement" and
109-
output = "ReturnValue"
110-
)
104+
input = "Argument[0..]" and
105+
output = "ReturnValue.ArrayElement"
111106
or
112-
// TODO: workaround for WithArrayElement not being converted to a taint step
113107
preservesValue = false and
114-
input = "Argument[arguments-array]" and
108+
input = "Argument[0..]" and
115109
output = "ReturnValue"
116110
}
117111
}
@@ -437,8 +431,7 @@ class PushLike extends SummarizedCallable {
437431

438432
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
439433
preservesValue = true and
440-
// TODO: make it so `arguments-array` is handled without needing to reference it explicitly in every flow-summary
441-
input = ["Argument[0..]", "Argument[arguments-array].ArrayElement"] and
434+
input = "Argument[0..]" and
442435
output = "Argument[this].ArrayElement"
443436
}
444437
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +0,0 @@
1-
| library-tests/FlowSummary/tst.js:278 | expected an alert, but found none | NOT OK | ConsistencyConfig |
2-
| library-tests/FlowSummary/tst.js:282 | expected an alert, but found none | NOT OK | ConsistencyConfig |
3-
| library-tests/FlowSummary/tst.js:283 | expected an alert, but found none | NOT OK | ConsistencyConfig |
4-
| library-tests/FlowSummary/tst.js:286 | expected an alert, but found none | NOT OK | ConsistencyConfig |
5-
| library-tests/FlowSummary/tst.js:287 | expected an alert, but found none | NOT OK | ConsistencyConfig |
6-
| library-tests/FlowSummary/tst.js:290 | expected an alert, but found none | NOT OK | ConsistencyConfig |
7-
| library-tests/FlowSummary/tst.js:291 | expected an alert, but found none | NOT OK | ConsistencyConfig |

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,27 @@ function m18() {
275275
const dynamicParam0 = mkSummary("Argument[0..]", "ReturnValue");
276276
const dynamicParam1 = mkSummary("Argument[1..]", "ReturnValue");
277277

278-
sink(staticParam0(...source())); // NOT OK
279-
sink(staticParam0("safe", ...source())); // OK
278+
sink(staticParam0(...[source()])); // NOT OK
279+
sink(staticParam0(...["safe", source()])); // OK
280+
sink(staticParam0(...[source(), "safe", ])); // NOT OK
281+
sink(staticParam0("safe", ...[source()])); // OK
280282
sink(staticParam0(source(), ...["safe"])); // NOT OK
281283

282-
sink(staticParam1(...source())); // NOT OK
283-
sink(staticParam1("safe", ...source())); // NOT OK
284+
sink(staticParam1(...[source()])); // OK
285+
sink(staticParam1(...["safe", source()])); // NOT OK
286+
sink(staticParam1(...[source(), "safe", ])); // OK
287+
sink(staticParam1("safe", ...[source()])); // NOT OK
284288
sink(staticParam1(source(), ...["safe"])); // OK
285289

286-
sink(dynamicParam0(...source())); // NOT OK
287-
sink(dynamicParam0("safe", ...source())); // NOT OK
290+
sink(dynamicParam0(...[source()])); // NOT OK
291+
sink(dynamicParam0(...["safe", source()])); // NOT OK
292+
sink(dynamicParam0(...[source(), "safe", ])); // NOT OK
293+
sink(dynamicParam0("safe", ...[source()])); // NOT OK
288294
sink(dynamicParam0(source(), ...["safe"])); // NOT OK
289295

290-
sink(dynamicParam1(...source())); // NOT OK
291-
sink(dynamicParam1("safe", ...source())); // NOT OK
296+
sink(dynamicParam1(...[source()])); // OK
297+
sink(dynamicParam1(...["safe", source()])); // NOT OK
298+
sink(dynamicParam1(...[source(), "safe", ])); // OK
299+
sink(dynamicParam1("safe", ...[source()])); // NOT OK
292300
sink(dynamicParam1(source(), ...["safe"])); // OK
293301
}

0 commit comments

Comments
 (0)