Skip to content

Commit 6b35a76

Browse files
committed
Migrate to shared FlowSummary library
1 parent dd7aff5 commit 6b35a76

File tree

7 files changed

+72
-1603
lines changed

7 files changed

+72
-1603
lines changed

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

Lines changed: 8 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,25 @@
22

33
private import javascript
44
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl
5-
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImplSpecific
5+
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate
66
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
77
private import semmle.javascript.dataflow.internal.DataFlowPrivate
88

9-
class SummaryComponent = Impl::Public::SummaryComponent;
10-
11-
/** Provides predicates for constructing summary components. */
12-
module SummaryComponent {
13-
private import Impl::Public::SummaryComponent as SC
14-
15-
predicate parameter = SC::parameter/1;
16-
17-
predicate argument = SC::argument/1;
18-
19-
predicate content = SC::content/1;
20-
21-
predicate withoutContent = SC::withoutContent/1;
22-
23-
predicate withContent = SC::withContent/1;
24-
25-
class SyntheticGlobal = SC::SyntheticGlobal;
26-
27-
/** Gets a summary component that represents a receiver. */
28-
SummaryComponent receiver() { result = argument(MkThisParameter()) }
29-
30-
/** Gets a summary component that represents the return value of a call. */
31-
SummaryComponent return() { result = SC::return(MkNormalReturnKind()) }
32-
33-
/** Gets a summary component that represents the exception thrown from a call. */
34-
SummaryComponent exceptionalReturn() { result = SC::return(MkExceptionalReturnKind()) }
35-
}
36-
37-
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
38-
39-
/** Provides predicates for constructing stacks of summary components. */
40-
module SummaryComponentStack {
41-
private import Impl::Public::SummaryComponentStack as SCS
42-
43-
predicate singleton = SCS::singleton/1;
44-
45-
predicate push = SCS::push/2;
46-
47-
predicate argument = SCS::argument/1;
48-
49-
/** Gets a singleton stack representing a receiver. */
50-
SummaryComponentStack receiver() { result = singleton(SummaryComponent::receiver()) }
51-
52-
/** Gets a singleton stack representing the return value of a call. */
53-
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
54-
55-
/** Gets a singleton stack representing the exception thrown from a call. */
56-
SummaryComponentStack exceptionalReturn() {
57-
result = singleton(SummaryComponent::exceptionalReturn())
58-
}
59-
}
60-
619
/** A callable with a flow summary, identified by a unique string. */
6210
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
6311
bindingset[this]
6412
SummarizedCallable() { any() }
6513

66-
/**
67-
* Same as
68-
*
69-
* ```ql
70-
* propagatesFlow(
71-
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
72-
* )
73-
* ```
74-
*
75-
* but uses an external (string) representation of the input and output stacks.
76-
*/
14+
// TODO: rename 'propagatesFlowExt' and/or override 'propagatesFlow' directly
7715
pragma[nomagic]
7816
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
7917

18+
override predicate propagatesFlow(
19+
string input, string output, boolean preservesValue, string model
20+
) {
21+
this.propagatesFlowExt(input, output, preservesValue) and model = this
22+
}
23+
8024
/**
8125
* Gets the synthesized parameter that results from an input specification
8226
* that starts with `Argument[s]` for this library callable.
@@ -88,5 +32,3 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
8832
)
8933
}
9034
}
91-
92-
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import semmle.javascript.dataflow.internal.Contents::Private
1010
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
1111
private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPrivate
1212
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
13+
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
1314
private import semmle.javascript.dataflow.internal.VariableCapture as VariableCapture
1415

1516
cached
@@ -58,7 +59,10 @@ private module Cached {
5859
TConstructorThisPostUpdate(Constructor ctor) or
5960
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
6061
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
61-
FlowSummaryImpl::Private::Steps::summaryStoreStep(sn, MkAwaited(), _)
62+
// NOTE: This dependency goes through the 'Steps' module whose instantiation depends on the call graph,
63+
// but the specific predicate we're referering to does not use that information.
64+
// So it doesn't cause negative recursion but it might look a bit surprising.
65+
FlowSummaryPrivate::Steps::summaryStoreStep(sn, MkAwaited(), _)
6266
} or
6367
TSynthCaptureNode(VariableCapture::VariableCaptureOutput::SynthesizedCaptureNode node) or
6468
TGenericSynthesizedNode(AstNode node, string tag, DataFlowPrivate::DataFlowCallable container) {

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

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ private import semmle.javascript.dataflow.internal.VariableCapture
88
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
99
private import semmle.javascript.internal.flow_summaries.AllFlowSummaries
1010
private import sharedlib.FlowSummaryImpl as FlowSummaryImpl
11+
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
12+
private import semmle.javascript.dataflow.FlowSummary as FlowSummary
1113
private import semmle.javascript.dataflow.internal.BarrierGuards
1214

1315
class DataFlowSecondLevelScope = Unit;
@@ -117,7 +119,8 @@ private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
117119
or
118120
kind = MkNormalReturnKind() and result = call.asAccessorCall().(DataFlow::PropRead)
119121
or
120-
FlowSummaryImpl::Private::summaryOutNode(call, result.(FlowSummaryNode).getSummaryNode(), kind)
122+
FlowSummaryImpl::Private::summaryOutNode(call.(SummaryCall).getReceiver(),
123+
result.(FlowSummaryNode).getSummaryNode(), kind)
121124
}
122125

123126
class ReturnNode extends DataFlow::Node {
@@ -275,7 +278,8 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
275278
// argument to setter (TODO: this has no post-update node)
276279
pos.asPositional() = 0 and n = call.asAccessorCall().(DataFlow::PropWrite).getRhs()
277280
or
278-
FlowSummaryImpl::Private::summaryArgumentNode(call, n.(FlowSummaryNode).getSummaryNode(), pos)
281+
FlowSummaryImpl::Private::summaryArgumentNode(call.(SummaryCall).getReceiver(),
282+
n.(FlowSummaryNode).getSummaryNode(), pos)
279283
}
280284

281285
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
@@ -802,8 +806,8 @@ private predicate valuePreservingStep(Node node1, Node node2) {
802806
or
803807
node2 = FlowSteps::getThrowTarget(node1)
804808
or
805-
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
806-
node2.(FlowSummaryNode).getSummaryNode(), true)
809+
FlowSummaryPrivate::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
810+
node2.(FlowSummaryNode).getSummaryNode(), true, _) // TODO: preserve 'model'
807811
or
808812
// Step from post-update nodes to local sources of the pre-update node. This emulates how JS usually tracks side effects.
809813
exists(PostUpdateNode postUpdate |
@@ -828,7 +832,7 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
828832
nodeGetEnclosingCallable(pragma[only_bind_out](node2))
829833
or
830834
exists(FlowSummaryImpl::Private::SummaryNode input, FlowSummaryImpl::Private::SummaryNode output |
831-
FlowSummaryImpl::Private::Steps::summaryStoreStep(input, MkAwaited(), output) and
835+
FlowSummaryPrivate::Steps::summaryStoreStep(input, MkAwaited(), output) and
832836
node1 = TFlowSummaryNode(input) and
833837
(
834838
node2 = TFlowSummaryNode(output) and
@@ -837,7 +841,7 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
837841
node2 = TFlowSummaryIntermediateAwaitStoreNode(input)
838842
)
839843
or
840-
FlowSummaryImpl::Private::Steps::summaryReadStep(input, MkAwaited(), output) and
844+
FlowSummaryPrivate::Steps::summaryReadStep(input, MkAwaited(), output) and
841845
node1 = TFlowSummaryNode(input) and
842846
node2 = TFlowSummaryNode(output)
843847
)
@@ -859,7 +863,7 @@ predicate jumpStep(Node node1, Node node2) {
859863
valuePreservingStep(node1, node2) and
860864
node1.getContainer() != node2.getContainer()
861865
or
862-
FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(),
866+
FlowSummaryPrivate::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(),
863867
node2.(FlowSummaryNode).getSummaryNode())
864868
or
865869
DataFlow::AdditionalFlowStep::jumpStep(node1, node2)
@@ -882,8 +886,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
882886
)
883887
or
884888
exists(ContentSet contentSet |
885-
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(),
886-
contentSet, node2.(FlowSummaryNode).getSummaryNode())
889+
FlowSummaryPrivate::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), contentSet,
890+
node2.(FlowSummaryNode).getSummaryNode())
887891
|
888892
not isSpecialContentSet(contentSet) and
889893
c = contentSet
@@ -894,7 +898,7 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
894898
or
895899
// For deep reads, generate read edges with a self-loop
896900
exists(Node origin, ContentSet contentSet |
897-
FlowSummaryImpl::Private::Steps::summaryReadStep(origin.(FlowSummaryNode).getSummaryNode(),
901+
FlowSummaryPrivate::Steps::summaryReadStep(origin.(FlowSummaryNode).getSummaryNode(),
898902
contentSet, node2.(FlowSummaryNode).getSummaryNode()) and
899903
node1 = [origin, node2]
900904
|
@@ -938,13 +942,13 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
938942
node2 = tryGetPostUpdate(write.getBase())
939943
)
940944
or
941-
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
945+
FlowSummaryPrivate::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
942946
node2.(FlowSummaryNode).getSummaryNode()) and
943947
not isSpecialContentSet(c)
944948
or
945949
// Store into Awaited
946950
exists(FlowSummaryImpl::Private::SummaryNode input, FlowSummaryImpl::Private::SummaryNode output |
947-
FlowSummaryImpl::Private::Steps::summaryStoreStep(input, MkAwaited(), output) and
951+
FlowSummaryPrivate::Steps::summaryStoreStep(input, MkAwaited(), output) and
948952
node1 = TFlowSummaryIntermediateAwaitStoreNode(input) and
949953
node2 = TFlowSummaryNode(output) and
950954
c = ContentSet::promiseValue()
@@ -964,15 +968,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
964968
* in `x.f = newValue`.
965969
*/
966970
predicate clearsContent(Node n, ContentSet c) {
967-
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
971+
FlowSummaryPrivate::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
968972
or
969973
// Clear promise content before storing into promise value, to avoid creating nested promises
970974
n = TFlowSummaryIntermediateAwaitStoreNode(_) and
971975
c = MkPromiseFilter()
972976
or
973977
// After reading from Awaited, the output must not be stored in a promise content
974-
FlowSummaryImpl::Private::Steps::summaryReadStep(_, MkAwaited(),
975-
n.(FlowSummaryNode).getSummaryNode()) and
978+
FlowSummaryPrivate::Steps::summaryReadStep(_, MkAwaited(), n.(FlowSummaryNode).getSummaryNode()) and
976979
c = MkPromiseFilter()
977980
or
978981
any(AdditionalFlowInternal flow).clearsContent(n, c)
@@ -998,12 +1001,11 @@ predicate clearsContent(Node n, ContentSet c) {
9981001
* at node `n`.
9991002
*/
10001003
predicate expectsContent(Node n, ContentSet c) {
1001-
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
1004+
FlowSummaryPrivate::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
10021005
or
10031006
// After storing into Awaited, the result must be stored in a promise-content.
10041007
// There is a value step from the input directly to this node, hence the need for expectsContent.
1005-
FlowSummaryImpl::Private::Steps::summaryStoreStep(_, MkAwaited(),
1006-
n.(FlowSummaryNode).getSummaryNode()) and
1008+
FlowSummaryPrivate::Steps::summaryStoreStep(_, MkAwaited(), n.(FlowSummaryNode).getSummaryNode()) and
10071009
c = MkPromiseFilter()
10081010
or
10091011
any(AdditionalFlowInternal flow).expectsContent(n, c)
@@ -1035,7 +1037,10 @@ int accessPathLimit() { result = 2 }
10351037
* by default as a heuristic.
10361038
*/
10371039
predicate allowParameterReturnInSelf(ParameterNode p) {
1038-
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
1040+
exists(DataFlowCallable callable, ParameterPosition pos |
1041+
isParameterNodeImpl(p, callable, pos) and
1042+
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(callable.asLibraryCallable(), pos)
1043+
)
10391044
or
10401045
exists(Function f |
10411046
VariableCaptureOutput::heuristicAllowInstanceParameterReturnInSelf(f) and

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

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,6 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
7070
result = TAnyType() and exists(t) and exists(rk)
7171
}
7272

73-
/** Gets the type of synthetic global `sg`. */
74-
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) {
75-
result = TAnyType() and exists(sg)
76-
}
77-
7873
/**
7974
* Holds if an external flow summary exists for `c` with input specification
8075
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
@@ -97,21 +92,21 @@ predicate summaryElement(
9792
predicate neutralSummaryElement(FlowSummary::SummarizedCallable c, string provenance) { none() }
9893

9994
pragma[inline]
100-
private SummaryComponent makeContentComponents(
95+
private Private::SummaryComponent makeContentComponents(
10196
Private::AccessPathToken token, string name, ContentSet contents
10297
) {
10398
token.getName() = name and
104-
result = FlowSummary::SummaryComponent::content(contents)
99+
result = Private::SummaryComponent::content(contents)
105100
or
106101
token.getName() = "With" + name and
107-
result = FlowSummary::SummaryComponent::withContent(contents)
102+
result = Private::SummaryComponent::withContent(contents)
108103
or
109104
token.getName() = "Without" + name and
110-
result = FlowSummary::SummaryComponent::withoutContent(contents)
105+
result = Private::SummaryComponent::withoutContent(contents)
111106
}
112107

113108
pragma[inline]
114-
private SummaryComponent makePropertyContentComponents(
109+
private Private::SummaryComponent makePropertyContentComponents(
115110
Private::AccessPathToken token, string name, PropertyName content
116111
) {
117112
result = makeContentComponents(token, name, ContentSet::property(content))
@@ -160,12 +155,12 @@ private ParameterPosition parsePosition(string operand) {
160155
*
161156
* This covers all the JS-specific components of a flow summary.
162157
*/
163-
SummaryComponent interpretComponentSpecific(Private::AccessPathToken c) {
158+
Private::SummaryComponent interpretComponentSpecific(Private::AccessPathToken c) {
164159
c.getName() = "Argument" and
165-
result = FlowSummary::SummaryComponent::argument(parsePosition(c.getAnArgument()))
160+
result = Private::SummaryComponent::argument(parsePosition(c.getAnArgument()))
166161
or
167162
c.getName() = "Parameter" and
168-
result = FlowSummary::SummaryComponent::parameter(parsePosition(c.getAnArgument()))
163+
result = Private::SummaryComponent::parameter(parsePosition(c.getAnArgument()))
169164
or
170165
result = makePropertyContentComponents(c, "Member", c.getAnArgument())
171166
or
@@ -210,20 +205,20 @@ SummaryComponent interpretComponentSpecific(Private::AccessPathToken c) {
210205
or
211206
c.getName() = "ReturnValue" and
212207
c.getAnArgument() = "exception" and
213-
result = SummaryComponent::return(MkExceptionalReturnKind())
208+
result = Private::SummaryComponent::return(MkExceptionalReturnKind())
214209
or
215210
// Awaited is mapped down to a combination steps that handle coercion and promise-flattening.
216211
c.getName() = "Awaited" and
217212
c.getNumArgument() = 0 and
218-
result = SummaryComponent::content(MkAwaited())
213+
result = Private::SummaryComponent::content(MkAwaited())
219214
or
220215
c.getName() = "AnyMemberDeep" and
221216
c.getNumArgument() = 0 and
222-
result = SummaryComponent::content(MkAnyPropertyDeep())
217+
result = Private::SummaryComponent::content(MkAnyPropertyDeep())
223218
or
224219
c.getName() = "ArrayElementDeep" and
225220
c.getNumArgument() = 0 and
226-
result = SummaryComponent::content(MkArrayElementDeep())
221+
result = Private::SummaryComponent::content(MkArrayElementDeep())
227222
}
228223

229224
private string getMadStringFromContentSetAux(ContentSet cs) {
@@ -272,13 +267,14 @@ private string getMadStringFromContentSet(ContentSet cs) {
272267
}
273268

274269
/** Gets the textual representation of a summary component in the format used for MaD models. */
275-
string getMadRepresentationSpecific(SummaryComponent sc) {
270+
string getMadRepresentationSpecific(Private::SummaryComponent sc) {
276271
exists(ContentSet cs |
277-
sc = Private::TContentSummaryComponent(cs) and result = getMadStringFromContentSet(cs)
272+
sc = Private::SummaryComponent::content(cs) and
273+
result = getMadStringFromContentSet(cs)
278274
)
279275
or
280276
exists(ReturnKind rk |
281-
sc = Private::TReturnSummaryComponent(rk) and
277+
sc = Private::SummaryComponent::return(rk) and
282278
not rk = getReturnValueKind() and
283279
result = "ReturnValue[" + rk + "]"
284280
)
@@ -368,3 +364,13 @@ bindingset[s]
368364
ParameterPosition parseArgBody(string s) {
369365
result = parseParamBody(s) // Currently these are identical
370366
}
367+
368+
private module FlowSummaryStepInput implements Private::StepsInputSig {
369+
DataFlowCall getACall(SummarizedCallable sc) {
370+
exists(LibraryCallable callable | callable = sc |
371+
result.asOrdinaryCall() = [callable.getACall(), callable.getACallSimple()]
372+
)
373+
}
374+
}
375+
376+
module Steps = Private::Steps<FlowSummaryStepInput>;

0 commit comments

Comments
 (0)