Merge pull request #3196 from asger-semmle/js/unnecessary-source-node-range

Approved by esbena
This commit is contained in:
semmle-qlci
2020-04-08 18:44:02 +01:00
committed by GitHub
9 changed files with 88 additions and 25 deletions

View File

@@ -56,10 +56,16 @@ class AmdModuleDefinition extends CallExpr {
*/ */
pragma[nomagic] pragma[nomagic]
DataFlow::SourceNode getFactoryNode() { DataFlow::SourceNode getFactoryNode() {
result.flowsToExpr(getLastArgument()) and result = getFactoryNodeInternal() and
result instanceof DataFlow::ValueNode result instanceof DataFlow::ValueNode
} }
private DataFlow::Node getFactoryNodeInternal() {
// To avoid recursion, this should not depend on `SourceNode`.
result = DataFlow::valueNode(getLastArgument()) or
result = getFactoryNodeInternal().getAPredecessor()
}
/** Gets the expression defining this module. */ /** Gets the expression defining this module. */
Expr getModuleExpr() { Expr getModuleExpr() {
exists(DataFlow::Node f | f = getFactoryNode() | exists(DataFlow::Node f | f = getFactoryNode() |
@@ -108,7 +114,7 @@ class AmdModuleDefinition extends CallExpr {
* Gets the `i`th parameter of the factory function of this module. * Gets the `i`th parameter of the factory function of this module.
*/ */
private SimpleParameter getFactoryParameter(int i) { private SimpleParameter getFactoryParameter(int i) {
getFactoryNode().(DataFlow::FunctionNode).getParameter(i) = DataFlow::parameterNode(result) getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
} }
/** /**

View File

@@ -1369,18 +1369,18 @@ module DataFlow {
*/ */
private predicate lvalueDefaultFlowStep(Node pred, Node succ) { private predicate lvalueDefaultFlowStep(Node pred, Node succ) {
exists(PropertyPattern pattern | exists(PropertyPattern pattern |
pred = valueNode(pattern.getDefault()) and pred = TValueNode(pattern.getDefault()) and
succ = lvalueNode(pattern.getValuePattern()) succ = lvalueNode(pattern.getValuePattern())
) )
or or
exists(ArrayPattern array, int i | exists(ArrayPattern array, int i |
pred = valueNode(array.getDefault(i)) and pred = TValueNode(array.getDefault(i)) and
succ = lvalueNode(array.getElement(i)) succ = lvalueNode(array.getElement(i))
) )
or or
exists(Parameter param | exists(Parameter param |
pred = valueNode(param.getDefault()) and pred = TValueNode(param.getDefault()) and
succ = parameterNode(param) parameterNode(succ, param)
) )
} }

View File

@@ -23,14 +23,33 @@ DataFlow::SourceNode angular() {
result = DataFlow::moduleImport("angular") result = DataFlow::moduleImport("angular")
} }
pragma[noopt] /**
* Holds if `tl` appears to be a top-level using the AngularJS library.
*
* Should not depend on the `SourceNode` class.
*/
pragma[nomagic]
private predicate isAngularTopLevel(TopLevel tl) {
exists(Import imprt |
imprt.getTopLevel() = tl and
imprt.getImportedPath().getValue() = "angular"
)
or
exists(GlobalVarAccess global |
global.getName() = "angular" and
global.getTopLevel() = tl
)
}
/**
* Holds if `s` is a string in a top-level using the AngularJS library.
*
* Should not depend on the `SourceNode` class.
*/
pragma[nomagic]
private predicate isAngularString(Expr s) { private predicate isAngularString(Expr s) {
exists(DataFlow::SourceNode angular, StmtContainer sc, TopLevel tl | isAngularTopLevel(s.getTopLevel()) and
angular = angular() and (
sc = angular.getContainer() and
tl = sc.getTopLevel() and
tl = s.getTopLevel()
|
s instanceof StringLiteral or s instanceof StringLiteral or
s instanceof TemplateLiteral s instanceof TemplateLiteral
) )

View File

@@ -467,10 +467,6 @@ module HTTP {
abstract DataFlow::Node getASecretKey(); abstract DataFlow::Node getASecretKey();
} }
private class CookieMiddlewareInstanceAsSourceNode extends DataFlow::SourceNode::Range {
CookieMiddlewareInstanceAsSourceNode() { this instanceof CookieMiddlewareInstance }
}
/** /**
* A key used for signed cookies, viewed as a `CryptographicKey`. * A key used for signed cookies, viewed as a `CryptographicKey`.
*/ */

View File

@@ -6,9 +6,11 @@ import javascript
module LazyCache { module LazyCache {
/** /**
* DEPRECATED. DO NOT USE.
*
* A lazy-cache object, usually created through an expression of form `require('lazy-cache')(require)`. * A lazy-cache object, usually created through an expression of form `require('lazy-cache')(require)`.
*/ */
class LazyCacheObject extends DataFlow::SourceNode { deprecated class LazyCacheObject extends DataFlow::SourceNode {
LazyCacheObject() { LazyCacheObject() {
// Use `require` directly instead of `moduleImport` to avoid recursion. // Use `require` directly instead of `moduleImport` to avoid recursion.
// For the same reason, avoid `Import.getImportedPath`. // For the same reason, avoid `Import.getImportedPath`.
@@ -19,13 +21,26 @@ module LazyCache {
} }
} }
/**
* A variable containing a lazy-cache object.
*/
class LazyCacheVariable extends LocalVariable {
LazyCacheVariable() {
// To avoid recursion, this should not depend on `SourceNode`.
exists(Require req |
req.getArgument(0).getStringValue() = "lazy-cache" and
getAnAssignedExpr().(CallExpr).getCallee() = req
)
}
}
/** /**
* An import through `lazy-cache`. * An import through `lazy-cache`.
*/ */
class LazyCacheImport extends CallExpr, Import { class LazyCacheImport extends CallExpr, Import {
LazyCacheObject cache; LazyCacheVariable cache;
LazyCacheImport() { this = cache.getACall().asExpr() } LazyCacheImport() { getCallee() = cache.getAnAccess() }
/** Gets the name of the package as it's exposed on the lazy-cache object. */ /** Gets the name of the package as it's exposed on the lazy-cache object. */
string getLocalAlias() { string getLocalAlias() {
@@ -39,10 +54,22 @@ module LazyCache {
override PathExpr getImportedPath() { result = getArgument(0) } override PathExpr getImportedPath() { result = getArgument(0) }
private LazyCacheVariable getVariable() { result = cache }
pragma[noopt]
override DataFlow::Node getImportedModuleNode() { override DataFlow::Node getImportedModuleNode() {
this instanceof LazyCacheImport and
result = this.flow() result = this.flow()
or or
result = cache.getAPropertyRead(getLocalAlias()) exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName |
// To avoid recursion, this should not depend on `SourceNode`.
variable = getVariable() and
base = variable.getAnAccess() and
access.getBase() = base and
localName = getLocalAlias() and
access.getPropertyName() = localName and
result = access.flow()
)
} }
} }

View File

@@ -14,10 +14,6 @@ module LodashUnderscore {
abstract string getName(); abstract string getName();
} }
private class MemberAsSourceNode extends DataFlow::SourceNode::Range {
MemberAsSourceNode() { this instanceof Member }
}
/** /**
* An import of `lodash` or `underscore` accessing a given member of that package. * An import of `lodash` or `underscore` accessing a given member of that package.
*/ */

View File

@@ -0,0 +1 @@
| Success |

View File

@@ -0,0 +1,16 @@
/**
* Test that fails to compile if the domain of `SourceNode` depends on `SourceNode.flowsTo` (recursively).
*
* This tests adds a negative dependency `flowsTo --!--> SourceNode`
* so that the undesired edge `SourceNode --> flowsTo` completes a negative cycle.
*/
import javascript
class BadSourceNode extends DataFlow::SourceNode {
BadSourceNode() { this.(DataFlow::PropRead).getPropertyName() = "foo" }
override predicate flowsTo(DataFlow::Node node) { not node instanceof DataFlow::SourceNode }
}
select "Success"

View File

@@ -0,0 +1,2 @@
// The contents of this file don't matter
let x = 1;