Skip to content

Commit 4702790

Browse files
author
Max Schaefer
committed
JavaScript: Refactor AMD/CommonJS path expression analysis to avoid bad magic.
1 parent 0e0fe25 commit 4702790

File tree

3 files changed

+38
-22
lines changed

3 files changed

+38
-22
lines changed

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,20 +170,19 @@ class AMDModuleDefinition extends CallExpr {
170170
}
171171
}
172172

173-
/** A path expression appearing in the list of dependencies of an AMD module. */
174-
private class AMDDependencyPath extends PathExprInModule, ConstantString {
175-
AMDDependencyPath() {
176-
exists(AMDModuleDefinition amd | this.getParentExpr*() = amd.getDependencies().getAnElement())
173+
/** An AMD dependency, considered as a path expression. */
174+
private class AmdDependencyPath extends PathExprCandidate {
175+
AmdDependencyPath() {
176+
exists(AMDModuleDefinition amd |
177+
this = amd.getDependencies().getAnElement() or
178+
this = amd.getARequireCall().getAnArgument()
179+
)
177180
}
178-
179-
override string getValue() { result = this.(ConstantString).getStringValue() }
180181
}
181182

182-
/** A path expression appearing in a `require` call in an AMD module. */
183-
private class AMDRequirePath extends PathExprInModule, ConstantString {
184-
AMDRequirePath() {
185-
exists(AMDModuleDefinition amd | this.getParentExpr*() = amd.getARequireCall().getAnArgument())
186-
}
183+
/** A constant path element appearing in an AMD dependency expression. */
184+
private class ConstantAmdDependencyPathElement extends PathExprInModule, ConstantString {
185+
ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() }
187186

188187
override string getValue() { result = this.(ConstantString).getStringValue() }
189188
}

javascript/ql/src/semmle/javascript/NodeJS.qll

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,22 +239,22 @@ class Require extends CallExpr, Import {
239239
}
240240
}
241241

242-
/** A literal path expression appearing in a `require` import. */
243-
private class LiteralRequiredPath extends PathExprInModule, ConstantString {
244-
LiteralRequiredPath() { exists(Require req | this.getParentExpr*() = req.getArgument(0)) }
245-
246-
override string getValue() { result = this.getStringValue() }
247-
}
248-
249-
/** A literal path expression appearing in a call to `require.resolve`. */
250-
private class LiteralRequireResolvePath extends PathExprInModule, ConstantString {
251-
LiteralRequireResolvePath() {
242+
/** An argument to `require` or `require.resolve`, considered as a path expression. */
243+
private class RequirePath extends PathExprCandidate {
244+
RequirePath() {
245+
this = any(Require req).getArgument(0)
246+
or
252247
exists(RequireVariable req, MethodCallExpr reqres |
253248
reqres.getReceiver() = req.getAnAccess() and
254249
reqres.getMethodName() = "resolve" and
255-
this.getParentExpr*() = reqres.getArgument(0)
250+
this = reqres.getArgument(0)
256251
)
257252
}
253+
}
254+
255+
/** A constant path element appearing in a call to `require` or `require.resolve`. */
256+
private class ConstantRequirePathElement extends PathExprInModule, ConstantString {
257+
ConstantRequirePathElement() { this = any(RequirePath rp).getAPart() }
258258

259259
override string getValue() { result = this.getStringValue() }
260260
}

javascript/ql/src/semmle/javascript/Paths.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,20 @@ private class ConcatPath extends PathExpr {
236236
result = this.(AddExpr).getAnOperand().(PathExpr).getSearchRoot(priority)
237237
}
238238
}
239+
240+
/**
241+
* An expression that appears in a syntactic position where it may represent a path.
242+
*
243+
* Examples include arguments to the CommonJS `require` function or AMD dependency arguments.
244+
*/
245+
abstract class PathExprCandidate extends Expr {
246+
/**
247+
* Gets an expression that is nested inside this expression.
248+
*
249+
* Equivalent to `getAChildExpr*()`, but useful to enforce a better join order (in spite of
250+
* what the optimizer thinks, there are generally far fewer `PathExprCandidate`s than
251+
* `ConstantString`s).
252+
*/
253+
pragma[nomagic]
254+
Expr getAPart() { result = this or result = getAPart().getAChildExpr() }
255+
}

0 commit comments

Comments
 (0)