Skip to content

Commit 6886474

Browse files
authored
Merge pull request #727 from xiemaisi/js/restructure-sourcenode
Approved by esben-semmle
2 parents b8f53b5 + de42975 commit 6886474

File tree

10 files changed

+106
-61
lines changed

10 files changed

+106
-61
lines changed

change-notes/1.20/analysis-javascript.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@
3333
| Uncontrolled data used in path expression | Fewer false-positive results | This rule now recognizes the Express `root` option, which prevents path traversal. |
3434

3535
## Changes to QL libraries
36+
37+
* `DataFlow::SourceNode` is no longer an abstract class; to add new source nodes, extend `DataFlow::SourceNode::Range` instead.
38+
* Subclasses of `DataFlow::PropRead` are no longer automatically made source nodes; you now need to additionally define a corresponding subclass of `DataFlow::SourceNode::Range` to achieve this.

javascript/ql/src/semmle/javascript/EmailClients.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import javascript
33
/**
44
* An operation that sends an email.
55
*/
6-
abstract class EmailSender extends DataFlow::DefaultSourceNode {
6+
abstract class EmailSender extends DataFlow::SourceNode {
77
/**
88
* Gets a data flow node holding the plaintext version of the email body.
99
*/

javascript/ql/src/semmle/javascript/StandardLibrary.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private class AnalyzedThisInArrayIterationFunction extends AnalyzedNode, DataFlo
7676
/**
7777
* A definition of a `Promise` object.
7878
*/
79-
abstract class PromiseDefinition extends DataFlow::DefaultSourceNode {
79+
abstract class PromiseDefinition extends DataFlow::SourceNode {
8080
/** Gets the executor function of this promise object. */
8181
abstract DataFlow::FunctionNode getExecutor();
8282

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,20 @@ module DataFlow {
515515
*/
516516
abstract class PropRead extends PropRef, SourceNode { }
517517

518+
/**
519+
* A property read, considered as a source node.
520+
*
521+
* Note that we cannot simplify the characteristic predicate to `this instanceof PropRead`,
522+
* since `PropRead` is itself a subclass of `SourceNode`.
523+
*/
524+
private class PropReadAsSourceNode extends SourceNode::Range {
525+
PropReadAsSourceNode() {
526+
this = TPropNode(any(PropertyPattern p)) or
527+
this instanceof RestPatternNode or
528+
this instanceof ElementPatternNode
529+
}
530+
}
531+
518532
/**
519533
* A property access in rvalue position.
520534
*/

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import javascript
88

99
/** A data flow node corresponding to a parameter. */
10-
class ParameterNode extends DataFlow::DefaultSourceNode {
10+
class ParameterNode extends DataFlow::SourceNode {
1111
Parameter p;
1212

1313
ParameterNode() { DataFlow::parameterNode(this, p) }
@@ -20,7 +20,7 @@ class ParameterNode extends DataFlow::DefaultSourceNode {
2020
}
2121

2222
/** A data flow node corresponding to a function invocation (with or without `new`). */
23-
class InvokeNode extends DataFlow::DefaultSourceNode {
23+
class InvokeNode extends DataFlow::SourceNode {
2424
DataFlow::Impl::InvokeNodeDef impl;
2525

2626
InvokeNode() { this = impl }
@@ -168,7 +168,7 @@ class MethodCallNode extends CallNode {
168168
class NewNode extends InvokeNode { override DataFlow::Impl::NewNodeDef impl; }
169169

170170
/** A data flow node corresponding to the `this` parameter in a function or `this` at the top-level. */
171-
class ThisNode extends DataFlow::Node, DataFlow::DefaultSourceNode {
171+
class ThisNode extends DataFlow::Node, DataFlow::SourceNode {
172172
ThisNode() { DataFlow::thisNode(this, _) }
173173

174174
/**
@@ -202,7 +202,7 @@ class ThisNode extends DataFlow::Node, DataFlow::DefaultSourceNode {
202202
}
203203

204204
/** A data flow node corresponding to a global variable access. */
205-
class GlobalVarRefNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
205+
class GlobalVarRefNode extends DataFlow::ValueNode, DataFlow::SourceNode {
206206
override GlobalVarAccess astNode;
207207

208208
/** Gets the name of the global variable. */
@@ -216,7 +216,10 @@ class GlobalVarRefNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode
216216
*/
217217
DataFlow::SourceNode globalObjectRef() {
218218
// top-level `this`
219-
exists(ThisNode globalThis | result = globalThis | not exists(globalThis.getBinder()))
219+
exists(StmtContainer sc |
220+
sc = result.(ThisNode).getBindingContainer() and
221+
not sc instanceof Function
222+
)
220223
or
221224
// DOM
222225
result = globalVarRef("window")
@@ -244,7 +247,7 @@ DataFlow::SourceNode globalVarRef(string name) {
244247
}
245248

246249
/** A data flow node corresponding to a function definition. */
247-
class FunctionNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
250+
class FunctionNode extends DataFlow::ValueNode, DataFlow::SourceNode {
248251
override Function astNode;
249252

250253
/** Gets the `i`th parameter of this function. */
@@ -287,12 +290,12 @@ class FunctionNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
287290
}
288291

289292
/** A data flow node corresponding to an object literal expression. */
290-
class ObjectLiteralNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
293+
class ObjectLiteralNode extends DataFlow::ValueNode, DataFlow::SourceNode {
291294
override ObjectExpr astNode;
292295
}
293296

294297
/** A data flow node corresponding to an array literal expression. */
295-
class ArrayLiteralNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
298+
class ArrayLiteralNode extends DataFlow::ValueNode, DataFlow::SourceNode {
296299
override ArrayExpr astNode;
297300

298301
/** Gets the `i`th element of this array literal. */
@@ -323,7 +326,7 @@ class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
323326
* A data flow node corresponding to the creation or a new array, either through an array literal
324327
* or an invocation of the `Array` constructor.
325328
*/
326-
class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
329+
class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::SourceNode {
327330
ArrayCreationNode() {
328331
this instanceof ArrayLiteralNode or
329332
this instanceof ArrayConstructorInvokeNode
@@ -346,7 +349,7 @@ class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode
346349
* For compatibility with old transpilers, we treat `import * from '...'`
347350
* as a default import as well.
348351
*/
349-
class ModuleImportNode extends DataFlow::DefaultSourceNode {
352+
class ModuleImportNode extends DataFlow::SourceNode {
350353
string path;
351354

352355
ModuleImportNode() {

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

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
import javascript
1010

1111
/**
12-
* A source node for local data flow, that is, a node for which local
13-
* data flow cannot provide any information about its inputs.
12+
* A source node for local data flow, that is, a node from which local data flow is tracked.
1413
*
15-
* By default, functions, object and array expressions and JSX nodes
16-
* are considered sources, as well as expressions that have non-local
17-
* flow (such as calls and property accesses). Additional sources
18-
* can be modelled by extending this class with additional subclasses.
14+
* Examples include function parameters, imports and property accesses; see
15+
* `DataFlow::SourceNode::DefaultRange` for details. You can introduce new kinds of
16+
* source nodes by defining new subclasses of `DataFlow::SourceNode::Range`.
1917
*/
20-
abstract class SourceNode extends DataFlow::Node {
18+
class SourceNode extends DataFlow::Node {
19+
SourceNode() { this instanceof SourceNode::Range }
20+
2121
/**
2222
* Holds if this node flows into `sink` in zero or more local (that is,
2323
* intra-procedural) steps.
@@ -167,45 +167,62 @@ abstract class SourceNode extends DataFlow::Node {
167167
}
168168
}
169169

170-
/**
171-
* A data flow node that is considered a source node by default.
172-
*
173-
* Currently, the following nodes are source nodes:
174-
* - import specifiers
175-
* - non-destructuring function parameters
176-
* - property accesses
177-
* - function invocations
178-
* - `this` expressions
179-
* - global variable accesses
180-
* - function definitions
181-
* - class definitions
182-
* - object expressions
183-
* - array expressions
184-
* - JSX literals.
185-
*/
186-
class DefaultSourceNode extends SourceNode {
187-
DefaultSourceNode() {
188-
exists(ASTNode astNode | this = DataFlow::valueNode(astNode) |
189-
astNode instanceof PropAccess or
190-
astNode instanceof Function or
191-
astNode instanceof ClassDefinition or
192-
astNode instanceof ObjectExpr or
193-
astNode instanceof ArrayExpr or
194-
astNode instanceof JSXNode or
195-
astNode instanceof GlobalVarAccess or
196-
astNode instanceof ExternalModuleReference
197-
)
198-
or
199-
exists(SsaExplicitDefinition ssa, VarDef def |
200-
this = DataFlow::ssaDefinitionNode(ssa) and def = ssa.getDef()
201-
|
202-
def instanceof ImportSpecifier
203-
)
204-
or
205-
DataFlow::parameterNode(this, _)
206-
or
207-
this instanceof DataFlow::Impl::InvokeNodeDef
208-
or
209-
DataFlow::thisNode(this, _)
170+
module SourceNode {
171+
/**
172+
* A data flow node that should be considered a source node.
173+
*
174+
* Subclass this class to introduce new kinds of source nodes. If you want to refine
175+
* the definition of existing source nodes, subclass `DataFlow::SourceNode` instead.
176+
*/
177+
cached
178+
abstract class Range extends DataFlow::Node { }
179+
180+
/**
181+
* A data flow node that is considered a source node by default.
182+
*
183+
* Currently, the following nodes are source nodes:
184+
* - import specifiers
185+
* - function parameters
186+
* - `this` nodes
187+
* - property accesses
188+
* - function invocations
189+
* - global variable accesses
190+
* - function definitions
191+
* - class definitions
192+
* - object expressions
193+
* - array expressions
194+
* - JSX literals
195+
*
196+
* This class is for internal use only and should not normally be used directly.
197+
*/
198+
class DefaultRange extends Range {
199+
DefaultRange() {
200+
exists(ASTNode astNode | this = DataFlow::valueNode(astNode) |
201+
astNode instanceof PropAccess or
202+
astNode instanceof Function or
203+
astNode instanceof ClassDefinition or
204+
astNode instanceof ObjectExpr or
205+
astNode instanceof ArrayExpr or
206+
astNode instanceof JSXNode or
207+
astNode instanceof GlobalVarAccess or
208+
astNode instanceof ExternalModuleReference
209+
)
210+
or
211+
exists(SsaExplicitDefinition ssa, VarDef def |
212+
this = DataFlow::ssaDefinitionNode(ssa) and def = ssa.getDef()
213+
|
214+
def instanceof ImportSpecifier
215+
)
216+
or
217+
DataFlow::parameterNode(this, _)
218+
or
219+
this instanceof DataFlow::Impl::InvokeNodeDef
220+
or
221+
DataFlow::thisNode(this, _)
222+
}
210223
}
211224
}
225+
226+
deprecated class DefaultSourceNode extends SourceNode {
227+
DefaultSourceNode() { this instanceof SourceNode::DefaultRange }
228+
}

javascript/ql/src/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private predicate isAngularString(Expr s) {
4545
* String literals in Angular code are often used as identifiers or references, so we
4646
* want to track them.
4747
*/
48-
private class TrackStringsInAngularCode extends DataFlow::SourceNode, DataFlow::ValueNode {
48+
private class TrackStringsInAngularCode extends DataFlow::SourceNode::Range, DataFlow::ValueNode {
4949
TrackStringsInAngularCode() { isAngularString(astNode) }
5050
}
5151

javascript/ql/src/semmle/javascript/frameworks/HTTP.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ module HTTP {
435435
abstract DataFlow::Node getASecretKey();
436436
}
437437

438+
private class CookieMiddlewareInstanceAsSourceNode extends DataFlow::SourceNode::Range {
439+
CookieMiddlewareInstanceAsSourceNode() { this instanceof CookieMiddlewareInstance }
440+
}
441+
438442
/**
439443
* A key used for signed cookies, viewed as a `CryptographicKey`.
440444
*/

javascript/ql/src/semmle/javascript/frameworks/LodashUnderscore.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ module LodashUnderscore {
1414
abstract string getName();
1515
}
1616

17+
private class MemberAsSourceNode extends DataFlow::SourceNode::Range {
18+
MemberAsSourceNode() { this instanceof Member }
19+
}
20+
1721
/**
1822
* An import of `lodash` or `underscore` accessing a given member of that package.
1923
*/

javascript/ql/src/semmle/javascript/frameworks/ReactNative.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import javascript
66

77
module ReactNative {
88
/** A `WebView` JSX element. */
9-
class WebViewElement extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
9+
class WebViewElement extends DataFlow::ValueNode, DataFlow::SourceNode {
1010
override JSXElement astNode;
1111

1212
WebViewElement() {

0 commit comments

Comments
 (0)