Skip to content

Commit 404f722

Browse files
authored
Merge pull request github#3196 from asger-semmle/js/unnecessary-source-node-range
Approved by esbena
2 parents 433794e + 5ab595d commit 404f722

File tree

9 files changed

+88
-25
lines changed

9 files changed

+88
-25
lines changed

javascript/ql/src/semmle/javascript/AMD.qll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,16 @@ class AmdModuleDefinition extends CallExpr {
5656
*/
5757
pragma[nomagic]
5858
DataFlow::SourceNode getFactoryNode() {
59-
result.flowsToExpr(getLastArgument()) and
59+
result = getFactoryNodeInternal() and
6060
result instanceof DataFlow::ValueNode
6161
}
6262

63+
private DataFlow::Node getFactoryNodeInternal() {
64+
// To avoid recursion, this should not depend on `SourceNode`.
65+
result = DataFlow::valueNode(getLastArgument()) or
66+
result = getFactoryNodeInternal().getAPredecessor()
67+
}
68+
6369
/** Gets the expression defining this module. */
6470
Expr getModuleExpr() {
6571
exists(DataFlow::Node f | f = getFactoryNode() |
@@ -108,7 +114,7 @@ class AmdModuleDefinition extends CallExpr {
108114
* Gets the `i`th parameter of the factory function of this module.
109115
*/
110116
private SimpleParameter getFactoryParameter(int i) {
111-
getFactoryNode().(DataFlow::FunctionNode).getParameter(i) = DataFlow::parameterNode(result)
117+
getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
112118
}
113119

114120
/**

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,18 +1369,18 @@ module DataFlow {
13691369
*/
13701370
private predicate lvalueDefaultFlowStep(Node pred, Node succ) {
13711371
exists(PropertyPattern pattern |
1372-
pred = valueNode(pattern.getDefault()) and
1372+
pred = TValueNode(pattern.getDefault()) and
13731373
succ = lvalueNode(pattern.getValuePattern())
13741374
)
13751375
or
13761376
exists(ArrayPattern array, int i |
1377-
pred = valueNode(array.getDefault(i)) and
1377+
pred = TValueNode(array.getDefault(i)) and
13781378
succ = lvalueNode(array.getElement(i))
13791379
)
13801380
or
13811381
exists(Parameter param |
1382-
pred = valueNode(param.getDefault()) and
1383-
succ = parameterNode(param)
1382+
pred = TValueNode(param.getDefault()) and
1383+
parameterNode(succ, param)
13841384
)
13851385
}
13861386

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,33 @@ DataFlow::SourceNode angular() {
2323
result = DataFlow::moduleImport("angular")
2424
}
2525

26-
pragma[noopt]
26+
/**
27+
* Holds if `tl` appears to be a top-level using the AngularJS library.
28+
*
29+
* Should not depend on the `SourceNode` class.
30+
*/
31+
pragma[nomagic]
32+
private predicate isAngularTopLevel(TopLevel tl) {
33+
exists(Import imprt |
34+
imprt.getTopLevel() = tl and
35+
imprt.getImportedPath().getValue() = "angular"
36+
)
37+
or
38+
exists(GlobalVarAccess global |
39+
global.getName() = "angular" and
40+
global.getTopLevel() = tl
41+
)
42+
}
43+
44+
/**
45+
* Holds if `s` is a string in a top-level using the AngularJS library.
46+
*
47+
* Should not depend on the `SourceNode` class.
48+
*/
49+
pragma[nomagic]
2750
private predicate isAngularString(Expr s) {
28-
exists(DataFlow::SourceNode angular, StmtContainer sc, TopLevel tl |
29-
angular = angular() and
30-
sc = angular.getContainer() and
31-
tl = sc.getTopLevel() and
32-
tl = s.getTopLevel()
33-
|
51+
isAngularTopLevel(s.getTopLevel()) and
52+
(
3453
s instanceof StringLiteral or
3554
s instanceof TemplateLiteral
3655
)

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,6 @@ module HTTP {
467467
abstract DataFlow::Node getASecretKey();
468468
}
469469

470-
private class CookieMiddlewareInstanceAsSourceNode extends DataFlow::SourceNode::Range {
471-
CookieMiddlewareInstanceAsSourceNode() { this instanceof CookieMiddlewareInstance }
472-
}
473-
474470
/**
475471
* A key used for signed cookies, viewed as a `CryptographicKey`.
476472
*/

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import javascript
66

77
module LazyCache {
88
/**
9+
* DEPRECATED. DO NOT USE.
10+
*
911
* A lazy-cache object, usually created through an expression of form `require('lazy-cache')(require)`.
1012
*/
11-
class LazyCacheObject extends DataFlow::SourceNode {
13+
deprecated class LazyCacheObject extends DataFlow::SourceNode {
1214
LazyCacheObject() {
1315
// Use `require` directly instead of `moduleImport` to avoid recursion.
1416
// For the same reason, avoid `Import.getImportedPath`.
@@ -19,13 +21,26 @@ module LazyCache {
1921
}
2022
}
2123

24+
/**
25+
* A variable containing a lazy-cache object.
26+
*/
27+
class LazyCacheVariable extends LocalVariable {
28+
LazyCacheVariable() {
29+
// To avoid recursion, this should not depend on `SourceNode`.
30+
exists(Require req |
31+
req.getArgument(0).getStringValue() = "lazy-cache" and
32+
getAnAssignedExpr().(CallExpr).getCallee() = req
33+
)
34+
}
35+
}
36+
2237
/**
2338
* An import through `lazy-cache`.
2439
*/
2540
class LazyCacheImport extends CallExpr, Import {
26-
LazyCacheObject cache;
41+
LazyCacheVariable cache;
2742

28-
LazyCacheImport() { this = cache.getACall().asExpr() }
43+
LazyCacheImport() { getCallee() = cache.getAnAccess() }
2944

3045
/** Gets the name of the package as it's exposed on the lazy-cache object. */
3146
string getLocalAlias() {
@@ -39,10 +54,22 @@ module LazyCache {
3954

4055
override PathExpr getImportedPath() { result = getArgument(0) }
4156

57+
private LazyCacheVariable getVariable() { result = cache }
58+
59+
pragma[noopt]
4260
override DataFlow::Node getImportedModuleNode() {
61+
this instanceof LazyCacheImport and
4362
result = this.flow()
4463
or
45-
result = cache.getAPropertyRead(getLocalAlias())
64+
exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName |
65+
// To avoid recursion, this should not depend on `SourceNode`.
66+
variable = getVariable() and
67+
base = variable.getAnAccess() and
68+
access.getBase() = base and
69+
localName = getLocalAlias() and
70+
access.getPropertyName() = localName and
71+
result = access.flow()
72+
)
4673
}
4774
}
4875

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

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

17-
private class MemberAsSourceNode extends DataFlow::SourceNode::Range {
18-
MemberAsSourceNode() { this instanceof Member }
19-
}
20-
2117
/**
2218
* An import of `lodash` or `underscore` accessing a given member of that package.
2319
*/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| Success |
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Test that fails to compile if the ___domain of `SourceNode` depends on `SourceNode.flowsTo` (recursively).
3+
*
4+
* This tests adds a negative dependency `flowsTo --!--> SourceNode`
5+
* so that the undesired edge `SourceNode --> flowsTo` completes a negative cycle.
6+
*/
7+
8+
import javascript
9+
10+
class BadSourceNode extends DataFlow::SourceNode {
11+
BadSourceNode() { this.(DataFlow::PropRead).getPropertyName() = "foo" }
12+
13+
override predicate flowsTo(DataFlow::Node node) { not node instanceof DataFlow::SourceNode }
14+
}
15+
16+
select "Success"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// The contents of this file don't matter
2+
let x = 1;

0 commit comments

Comments
 (0)