Skip to content

Commit 8239758

Browse files
committed
JS: Add more features to AdditionalFlowInternal
This lets us place some more logic outside of the giant DataFlowPrivate file
1 parent 6c1ec8c commit 8239758

File tree

2 files changed

+60
-12
lines changed

2 files changed

+60
-12
lines changed

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

+18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
99
result = TGenericSynthesizedNode(node, tag, _)
1010
}
1111

12+
DataFlowCallable getSynthesizedCallable(AstNode node, string tag) {
13+
result = MkGenericSynthesizedCallable(node, tag)
14+
}
15+
16+
DataFlowCall getSynthesizedCall(AstNode node, string tag) {
17+
result = MkGenericSynthesizedCall(node, tag, _)
18+
}
19+
1220
/**
1321
* An extension to `AdditionalFlowStep` with additional internal-only predicates.
1422
*/
@@ -22,6 +30,10 @@ class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
2230
*/
2331
predicate needsSynthesizedNode(AstNode node, string tag, DataFlowCallable container) { none() }
2432

33+
predicate needsSynthesizedCallable(AstNode node, string tag) { none() }
34+
35+
predicate needsSynthesizedCall(AstNode node, string tag, DataFlowCallable container) { none() }
36+
2537
/**
2638
* Holds if `node` should only permit flow of values stored in `contents`.
2739
*/
@@ -31,4 +43,10 @@ class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
3143
* Holds if `node` should not permit flow of values stored in `contents`.
3244
*/
3345
predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
46+
47+
predicate argument(DataFlowCall call, ArgumentPosition pos, DataFlow::Node value) { none() }
48+
49+
predicate postUpdate(DataFlow::Node pre, DataFlow::Node post) { none() }
50+
51+
predicate viableCallable(DataFlowCall call, DataFlowCallable target) { none() }
3452
}

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

+42-12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ private import sharedlib.FlowSummaryImpl as FlowSummaryImpl
1313
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
1414
private import semmle.javascript.dataflow.FlowSummary as FlowSummary
1515
private import semmle.javascript.dataflow.internal.BarrierGuards
16+
private import codeql.util.Boolean
1617

1718
class DataFlowSecondLevelScope = Unit;
1819

@@ -381,6 +382,8 @@ predicate postUpdatePair(Node pre, Node post) {
381382
pre.(FlowSummaryNode).getSummaryNode())
382383
or
383384
VariableCaptureOutput::capturePostUpdateNode(getClosureNode(post), getClosureNode(pre))
385+
or
386+
any(AdditionalFlowInternal f).postUpdate(pre, post)
384387
}
385388

386389
class CastNode extends DataFlow::Node {
@@ -391,17 +394,12 @@ cached
391394
newtype TDataFlowCallable =
392395
MkSourceCallable(StmtContainer container) or
393396
MkLibraryCallable(LibraryCallable callable) or
394-
MkClassHarnessCallable(Function f) {
395-
// We only need a class harness for functions that act as classes (i.e. constructors),
396-
// but since DataFlow::Node has not been materialised at this stage, we can't use DataFlow::ClassNode.
397-
// Exclude arrow functions as they can't be called with 'new'.
398-
not f instanceof ArrowFunctionExpr and
399-
// We also don't need harnesses for externs
400-
not f.getTopLevel().isExterns()
397+
MkGenericSynthesizedCallable(AstNode node, string tag) {
398+
any(AdditionalFlowInternal f).needsSynthesizedCallable(node, tag)
401399
}
402400

403401
/**
404-
* A callable entity. This is a wrapper around either a `StmtContainer` or a `LibraryCallable`.
402+
* A callable entity.
405403
*/
406404
class DataFlowCallable extends TDataFlowCallable {
407405
/** Gets a string representation of this callable. */
@@ -410,19 +408,26 @@ class DataFlowCallable extends TDataFlowCallable {
410408
or
411409
result = this.asLibraryCallable()
412410
or
413-
result = this.asClassHarness().toString()
411+
this.isGenericSynthesizedCallable(_, result)
414412
}
415413

416414
/** Gets the location of this callable, if it is present in the source code. */
417415
Location getLocation() {
418-
result = this.asSourceCallable().getLocation() or result = this.asClassHarness().getLocation()
416+
result = this.asSourceCallable().getLocation()
417+
or
418+
exists(AstNode node |
419+
this.isGenericSynthesizedCallable(node, _) and
420+
result = node.getLocation()
421+
)
419422
}
420423

421424
/** Gets the corresponding `StmtContainer` if this is a source callable. */
422425
StmtContainer asSourceCallable() { this = MkSourceCallable(result) }
423426

424427
/** Gets the class constructor for which this is a class harness. */
425-
Function asClassHarness() { this = MkClassHarnessCallable(result) }
428+
predicate isGenericSynthesizedCallable(AstNode node, string tag) {
429+
this = MkGenericSynthesizedCallable(node, tag)
430+
}
426431

427432
/** Gets the corresponding `StmtContainer` if this is a source callable. */
428433
pragma[nomagic]
@@ -552,6 +557,8 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
552557
n = TDynamicArgumentArrayNode(invoke) and
553558
pos.isDynamicArgumentArray()
554559
)
560+
or
561+
any(AdditionalFlowInternal f).argument(call, pos, n)
555562
}
556563

557564
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
@@ -749,7 +756,7 @@ ContentApprox getContentApprox(Content c) {
749756
}
750757

751758
cached
752-
private newtype TDataFlowCall =
759+
newtype TDataFlowCall =
753760
MkOrdinaryCall(DataFlow::InvokeNode node) or
754761
MkPartialCall(DataFlow::PartialInvokeNode node, DataFlow::Node callback) {
755762
callback = node.getACallbackNode()
@@ -770,6 +777,9 @@ private newtype TDataFlowCall =
770777
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
771778
) {
772779
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
780+
} or
781+
MkGenericSynthesizedCall(AstNode node, string tag, DataFlowCallable container) {
782+
any(AdditionalFlowInternal f).needsSynthesizedCall(node, tag, container)
773783
}
774784

775785
private module TotalOrdering {
@@ -835,6 +845,10 @@ class DataFlowCall extends TDataFlowCall {
835845
this = MkSummaryCall(enclosingCallable, receiver)
836846
}
837847

848+
predicate isGenericSynthesizedCall(AstNode node, string tag, DataFlowCallable container) {
849+
this = MkGenericSynthesizedCall(node, tag, container)
850+
}
851+
838852
Location getLocation() { none() } // Overridden in subclass
839853

840854
int totalorder() {
@@ -953,6 +967,20 @@ private class ImpliedLambdaCall extends DataFlowCall, MkImpliedLambdaCall {
953967
}
954968
}
955969

970+
class GenericSynthesizedCall extends DataFlowCall, MkGenericSynthesizedCall {
971+
private AstNode node;
972+
private string tag;
973+
private DataFlowCallable container;
974+
975+
GenericSynthesizedCall() { this = MkGenericSynthesizedCall(node, tag, container) }
976+
977+
override string toString() { result = tag }
978+
979+
override Location getLocation() { result = node.getLocation() }
980+
981+
override DataFlowCallable getEnclosingCallable() { result = container }
982+
}
983+
956984
private int getMaxArity() {
957985
// TODO: account for flow summaries
958986
result =
@@ -1050,6 +1078,8 @@ DataFlowCallable viableCallable(DataFlowCall node) {
10501078
)
10511079
or
10521080
result.asSourceCallableNotExterns() = node.asImpliedLambdaCall()
1081+
or
1082+
any(AdditionalFlowInternal f).viableCallable(node, result)
10531083
}
10541084

10551085
/**

0 commit comments

Comments
 (0)