|
5 | 5 | private import javascript |
6 | 6 | private import semmle.javascript.dataflow.internal.DataFlowPrivate |
7 | 7 | private import semmle.javascript.dataflow.internal.Contents::Private |
8 | | -private import semmle.javascript.dataflow.FlowSummary as FlowSummary |
9 | 8 | private import sharedlib.DataFlowImplCommon |
10 | 9 | private import sharedlib.FlowSummaryImpl::Private as Private |
11 | 10 | private import sharedlib.FlowSummaryImpl::Public |
12 | 11 | private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax |
13 | 12 |
|
14 | | -private class Node = DataFlow::Node; |
15 | | - |
16 | 13 | /** |
17 | 14 | * A class of callables that are candidates for flow summary modeling. |
18 | 15 | */ |
19 | 16 | class SummarizedCallableBase = string; |
20 | 17 |
|
21 | | -/** |
22 | | - * A class of callables that are candidates for neutral modeling. |
23 | | - */ |
24 | | -class NeutralCallableBase = string; |
25 | | - |
26 | | -/** |
27 | | - * Holds if a neutral model exists for `c` of kind `kind` and with provenance `provenance`. |
28 | | - * Note: Neutral models have not been implemented for Javascript. |
29 | | - */ |
30 | | -predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() } |
31 | | - |
32 | | -DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c } |
33 | | - |
34 | 18 | /** Gets the parameter position representing a callback itself, if any. */ |
35 | 19 | ArgumentPosition callbackSelfParameterPosition() { result.isFunctionSelfReference() } |
36 | 20 |
|
37 | | -/** Gets the synthesized data-flow call for `receiver`. */ |
38 | | -SummaryCall summaryDataFlowCall(Private::SummaryNode receiver) { receiver = result.getReceiver() } |
39 | | - |
40 | | -/** Gets the type of content `c`. */ |
41 | | -DataFlowType getContentType(ContentSet c) { result = TAnyType() and exists(c) } |
42 | | - |
43 | | -/** Gets the type of the parameter at the given position. */ |
44 | | -bindingset[c, pos] |
45 | | -DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { |
46 | | - // TODO: we could assign a more precise type to the function self-reference parameter |
47 | | - result = TAnyType() and exists(c) and exists(pos) |
48 | | -} |
49 | | - |
50 | | -/** Gets the return type of kind `rk` for callable `c`. */ |
51 | | -bindingset[c, rk] |
52 | | -DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { |
53 | | - result = TAnyType() and exists(c) and exists(rk) |
54 | | -} |
55 | | - |
56 | | -/** |
57 | | - * Gets the type of the `i`th parameter in a synthesized call that targets a |
58 | | - * callback of type `t`. |
59 | | - */ |
60 | | -bindingset[t, pos] |
61 | | -DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { |
62 | | - result = TAnyType() and exists(t) and exists(pos) |
63 | | -} |
64 | | - |
65 | | -/** |
66 | | - * Gets the return type of kind `rk` in a synthesized call that targets a |
67 | | - * callback of type `t`. |
68 | | - */ |
69 | | -DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { |
70 | | - result = TAnyType() and exists(t) and exists(rk) |
71 | | -} |
72 | | - |
73 | | -/** |
74 | | - * Holds if an external flow summary exists for `c` with input specification |
75 | | - * `input`, output specification `output`, kind `kind`, and provenance `provenance`. |
76 | | - */ |
77 | | -predicate summaryElement( |
78 | | - FlowSummary::SummarizedCallable c, string input, string output, string kind, string provenance |
79 | | -) { |
80 | | - exists(boolean preservesValue | |
81 | | - c.propagatesFlowExt(input, output, preservesValue) and |
82 | | - (if preservesValue = true then kind = "value" else kind = "taint") and |
83 | | - provenance = "manual" |
84 | | - ) |
85 | | -} |
86 | | - |
87 | | -/** |
88 | | - * Holds if a neutral summary model exists for `c` with provenance `provenance`, |
89 | | - * which means that there is no flow through `c`. |
90 | | - * Note. Neutral models have not been implemented for JS. |
91 | | - */ |
92 | | -predicate neutralSummaryElement(FlowSummary::SummarizedCallable c, string provenance) { none() } |
93 | | - |
94 | | -pragma[inline] |
95 | | -private Private::SummaryComponent makeContentComponents( |
96 | | - Private::AccessPathToken token, string name, ContentSet contents |
97 | | -) { |
98 | | - token.getName() = name and |
99 | | - result = Private::SummaryComponent::content(contents) |
100 | | - or |
101 | | - token.getName() = "With" + name and |
102 | | - result = Private::SummaryComponent::withContent(contents) |
103 | | - or |
104 | | - token.getName() = "Without" + name and |
105 | | - result = Private::SummaryComponent::withoutContent(contents) |
106 | | -} |
107 | | - |
108 | | -pragma[inline] |
109 | | -private Private::SummaryComponent makePropertyContentComponents( |
110 | | - Private::AccessPathToken token, string name, PropertyName content |
111 | | -) { |
112 | | - result = makeContentComponents(token, name, ContentSet::property(content)) |
113 | | -} |
114 | | - |
115 | 21 | /** |
116 | 22 | * Gets the content set corresponding to `Awaited[arg]`. |
117 | 23 | */ |
@@ -145,82 +51,6 @@ private predicate desugaredPositionName(ParameterPosition pos, string operand) { |
145 | 51 | pos.asPositional() = AccessPathSyntax::parseInt(operand) // parse closed intervals |
146 | 52 | } |
147 | 53 |
|
148 | | -bindingset[operand] |
149 | | -private ParameterPosition parsePosition(string operand) { |
150 | | - positionName(result, operand) or desugaredPositionName(result, operand) |
151 | | -} |
152 | | - |
153 | | -/** |
154 | | - * Gets the summary component for specification component `c`, if any. |
155 | | - * |
156 | | - * This covers all the JS-specific components of a flow summary. |
157 | | - */ |
158 | | -Private::SummaryComponent interpretComponentSpecific(Private::AccessPathToken c) { |
159 | | - c.getName() = "Argument" and |
160 | | - result = Private::SummaryComponent::argument(parsePosition(c.getAnArgument())) |
161 | | - or |
162 | | - c.getName() = "Parameter" and |
163 | | - result = Private::SummaryComponent::parameter(parsePosition(c.getAnArgument())) |
164 | | - or |
165 | | - result = makePropertyContentComponents(c, "Member", c.getAnArgument()) |
166 | | - or |
167 | | - result = makeContentComponents(c, "Awaited", getPromiseContent(c.getAnArgument())) |
168 | | - or |
169 | | - c.getNumArgument() = 0 and |
170 | | - result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElement()) |
171 | | - or |
172 | | - c.getAnArgument() = "?" and |
173 | | - result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementUnknown()) |
174 | | - or |
175 | | - exists(int n | |
176 | | - n = c.getAnArgument().toInt() and |
177 | | - result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementKnown(n)) |
178 | | - or |
179 | | - // ArrayElement[n!] refers to index n, and never the unknown content |
180 | | - c.getAnArgument().regexpCapture("(\\d+)!", 1).toInt() = n and |
181 | | - result = makePropertyContentComponents(c, "ArrayElement", n.toString()) |
182 | | - or |
183 | | - // ArrayElement[n..] refers to index n or greater |
184 | | - n = AccessPathSyntax::parseLowerBound(c.getAnArgument()) and |
185 | | - result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementLowerBoundFromInt(n)) |
186 | | - ) |
187 | | - or |
188 | | - c.getNumArgument() = 0 and |
189 | | - result = makeContentComponents(c, "SetElement", ContentSet::setElement()) |
190 | | - or |
191 | | - c.getNumArgument() = 0 and |
192 | | - result = makeContentComponents(c, "IteratorElement", ContentSet::iteratorElement()) |
193 | | - or |
194 | | - c.getNumArgument() = 0 and |
195 | | - result = makeContentComponents(c, "IteratorError", ContentSet::iteratorError()) |
196 | | - or |
197 | | - c.getNumArgument() = 0 and |
198 | | - result = makeContentComponents(c, "MapKey", ContentSet::mapKey()) |
199 | | - or |
200 | | - // |
201 | | - // Note: although it is supported internally, we currently do not expose a syntax for MapValue with a known key |
202 | | - // |
203 | | - c.getNumArgument() = 0 and |
204 | | - result = makeContentComponents(c, "MapValue", ContentSet::mapValueAll()) |
205 | | - or |
206 | | - c.getName() = "ReturnValue" and |
207 | | - c.getAnArgument() = "exception" and |
208 | | - result = Private::SummaryComponent::return(MkExceptionalReturnKind()) |
209 | | - or |
210 | | - // Awaited is mapped down to a combination steps that handle coercion and promise-flattening. |
211 | | - c.getName() = "Awaited" and |
212 | | - c.getNumArgument() = 0 and |
213 | | - result = Private::SummaryComponent::content(MkAwaited()) |
214 | | - or |
215 | | - c.getName() = "AnyMemberDeep" and |
216 | | - c.getNumArgument() = 0 and |
217 | | - result = Private::SummaryComponent::content(MkAnyPropertyDeep()) |
218 | | - or |
219 | | - c.getName() = "ArrayElementDeep" and |
220 | | - c.getNumArgument() = 0 and |
221 | | - result = Private::SummaryComponent::content(MkArrayElementDeep()) |
222 | | -} |
223 | | - |
224 | 54 | private string encodeContentAux(ContentSet cs, string arg) { |
225 | 55 | cs = ContentSet::arrayElement() and |
226 | 56 | result = "ArrayElement" and |
@@ -296,78 +126,9 @@ string encodeArgumentPosition(ArgumentPosition pos) { |
296 | 126 | /** Gets the return kind corresponding to specification `"ReturnValue"`. */ |
297 | 127 | ReturnKind getStandardReturnValueKind() { result = MkNormalReturnKind() } |
298 | 128 |
|
299 | | -/** Holds if input specification component `c` needs a reference. */ |
300 | | -predicate inputNeedsReferenceSpecific(string c) { none() } |
301 | | - |
302 | | -/** Holds if output specification component `c` needs a reference. */ |
303 | | -predicate outputNeedsReferenceSpecific(string c) { none() } |
304 | | - |
305 | 129 | /** Gets the return kind corresponding to specification `"ReturnValue"`. */ |
306 | 130 | MkNormalReturnKind getReturnValueKind() { any() } |
307 | 131 |
|
308 | | -/** |
309 | | - * All definitions in this module are required by the shared implementation |
310 | | - * (for source/sink interpretation), but they are unused for JS, where |
311 | | - * we rely on API graphs instead. |
312 | | - */ |
313 | | -private module UnusedSourceSinkInterpretation { |
314 | | - /** |
315 | | - * Holds if an external source specification exists for `n` with output specification |
316 | | - * `output`, kind `kind`, and provenance `provenance`. |
317 | | - */ |
318 | | - predicate sourceElement(AstNode n, string output, string kind, string provenance) { none() } |
319 | | - |
320 | | - /** |
321 | | - * Holds if an external sink specification exists for `n` with input specification |
322 | | - * `input`, kind `kind` and provenance `provenance`. |
323 | | - */ |
324 | | - predicate sinkElement(AstNode n, string input, string kind, string provenance) { none() } |
325 | | - |
326 | | - class SourceOrSinkElement = AstNode; |
327 | | - |
328 | | - /** An entity used to interpret a source/sink specification. */ |
329 | | - class InterpretNode extends AstNode { |
330 | | - /** Gets the element that this node corresponds to, if any. */ |
331 | | - SourceOrSinkElement asElement() { none() } |
332 | | - |
333 | | - /** Gets the data-flow node that this node corresponds to, if any. */ |
334 | | - Node asNode() { none() } |
335 | | - |
336 | | - /** Gets the call that this node corresponds to, if any. */ |
337 | | - DataFlowCall asCall() { none() } |
338 | | - |
339 | | - /** Gets the callable that this node corresponds to, if any. */ |
340 | | - DataFlowCallable asCallable() { none() } |
341 | | - |
342 | | - /** Gets the target of this call, if any. */ |
343 | | - StmtContainer getCallTarget() { none() } |
344 | | - } |
345 | | - |
346 | | - /** Provides additional sink specification logic. */ |
347 | | - predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } |
348 | | - |
349 | | - /** Provides additional source specification logic. */ |
350 | | - predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } |
351 | | -} |
352 | | - |
353 | | -import UnusedSourceSinkInterpretation |
354 | | - |
355 | | -/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */ |
356 | | -bindingset[s] |
357 | | -ArgumentPosition parseParamBody(string s) { |
358 | | - s = "this" and result.isThis() |
359 | | - or |
360 | | - s = "function" and result.isFunctionSelfReference() |
361 | | - or |
362 | | - result.asPositional() = AccessPathSyntax::parseInt(s) |
363 | | -} |
364 | | - |
365 | | -/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */ |
366 | | -bindingset[s] |
367 | | -ParameterPosition parseArgBody(string s) { |
368 | | - result = parseParamBody(s) // Currently these are identical |
369 | | -} |
370 | | - |
371 | 132 | private module FlowSummaryStepInput implements Private::StepsInputSig { |
372 | 133 | DataFlowCall getACall(SummarizedCallable sc) { |
373 | 134 | exists(LibraryCallable callable | callable = sc | |
|
0 commit comments