Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions javascript/ql/src/semmle/javascript/AMD.qll
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,16 @@ class AmdModuleDefinition extends CallExpr {
*/
pragma[nomagic]
DataFlow::SourceNode getFactoryNode() {
result.flowsToExpr(getLastArgument()) and
result = getFactoryNodeInternal() and
result instanceof DataFlow::ValueNode
}

private DataFlow::Node getFactoryNodeInternal() {
// To avoid recursion, this should not depend on `SourceNode`.
result = DataFlow::valueNode(getLastArgument()) or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a warning here as well?

result = getFactoryNodeInternal().getAPredecessor()
}

/** Gets the expression defining this module. */
Expr getModuleExpr() {
exists(DataFlow::Node f | f = getFactoryNode() |
Expand Down Expand Up @@ -108,7 +114,7 @@ class AmdModuleDefinition extends CallExpr {
* Gets the `i`th parameter of the factory function of this module.
*/
private SimpleParameter getFactoryParameter(int i) {
getFactoryNode().(DataFlow::FunctionNode).getParameter(i) = DataFlow::parameterNode(result)
getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
}

/**
Expand Down
8 changes: 4 additions & 4 deletions javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1369,18 +1369,18 @@ module DataFlow {
*/
private predicate lvalueDefaultFlowStep(Node pred, Node succ) {
exists(PropertyPattern pattern |
pred = valueNode(pattern.getDefault()) and
pred = TValueNode(pattern.getDefault()) and
succ = lvalueNode(pattern.getValuePattern())
)
or
exists(ArrayPattern array, int i |
pred = valueNode(array.getDefault(i)) and
pred = TValueNode(array.getDefault(i)) and
succ = lvalueNode(array.getElement(i))
)
or
exists(Parameter param |
pred = valueNode(param.getDefault()) and
succ = parameterNode(param)
pred = TValueNode(param.getDefault()) and
parameterNode(succ, param)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,33 @@ DataFlow::SourceNode 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) {
exists(DataFlow::SourceNode angular, StmtContainer sc, TopLevel tl |
angular = angular() and
sc = angular.getContainer() and
tl = sc.getTopLevel() and
tl = s.getTopLevel()
|
isAngularTopLevel(s.getTopLevel()) and
(
s instanceof StringLiteral or
s instanceof TemplateLiteral
)
Expand Down
4 changes: 0 additions & 4 deletions javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,6 @@ module HTTP {
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`.
*/
Expand Down
35 changes: 31 additions & 4 deletions javascript/ql/src/semmle/javascript/frameworks/LazyCache.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import javascript

module LazyCache {
/**
* DEPRECATED. DO NOT USE.
*
* 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() {
// Use `require` directly instead of `moduleImport` to avoid recursion.
// For the same reason, avoid `Import.getImportedPath`.
Expand All @@ -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`.
*/
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. */
string getLocalAlias() {
Expand All @@ -39,10 +54,22 @@ module LazyCache {

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

private LazyCacheVariable getVariable() { result = cache }

pragma[noopt]
override DataFlow::Node getImportedModuleNode() {
this instanceof LazyCacheImport and
result = this.flow()
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()
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ module LodashUnderscore {
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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| Success |
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 2 additions & 0 deletions javascript/ql/test/library-tests/RecursionPrevention/tst.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// The contents of this file don't matter
let x = 1;