Skip to content
Draft
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
6 changes: 6 additions & 0 deletions javascript/ql/lib/Expressions/ExprHasNoEffect.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Provides classes and predicates for the 'js/useless-expression' query.
*/
overlay[local]
module;

import javascript
import DOMProperties
Expand Down Expand Up @@ -60,6 +62,7 @@ predicate isDeclaration(Expr e) {
/**
* Holds if there exists a getter for a property called `name` anywhere in the program.
*/
overlay[global]
predicate isGetterProperty(string name) {
// there is a call of the form `Object.defineProperty(..., name, descriptor)` ...
exists(CallToObjectDefineProperty defProp | name = defProp.getPropertyName() |
Expand All @@ -85,6 +88,7 @@ predicate isGetterProperty(string name) {
/**
* A property access that may invoke a getter.
*/
overlay[global]
class GetterPropertyAccess extends PropAccess {
override predicate isImpure() { isGetterProperty(this.getPropertyName()) }
}
Expand Down Expand Up @@ -123,6 +127,7 @@ predicate isReceiverSuppressingCall(CallExpr c, Expr dummy, PropAccess callee) {
* even if they do, the call itself is useless and should be flagged by this
* query.
*/
overlay[global]
predicate noSideEffects(Expr e) {
e.isPure()
or
Expand All @@ -148,6 +153,7 @@ predicate isCompoundExpression(Expr e) {
/**
* Holds if the expression `e` should be reported as having no effect.
*/
overlay[global]
predicate hasNoEffect(Expr e) {
noSideEffects(e) and
inVoidContext(e) and
Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/qlpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ dataExtensions:
- semmle/javascript/security/domains/**/*.model.yml
- ext/*.model.yml
warnOnImplicitThis: true
compileForOverlayEval: true
20 changes: 16 additions & 4 deletions javascript/ql/lib/semmle/javascript/AMD.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Provides classes for working with
* [Asynchronous Module Definitions](https://github.com/amdjs/amdjs-api/wiki/AMD).
*/
overlay[local]
module;

import javascript
private import semmle.javascript.internal.CachedStages
Expand Down Expand Up @@ -62,9 +64,11 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
}

/** DEPRECATED. Use `getDependencyExpr` instead. */
overlay[global]
deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) }

/** DEPRECATED. Use `getADependencyExpr` instead. */
overlay[global]
deprecated PathExpr getADependency() { result = this.getADependencyExpr() }

/** Gets the `i`th dependency of this module definition. */
Expand Down Expand Up @@ -194,16 +198,19 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
* Gets an abstract value representing one or more values that may flow
* into this module's `module.exports` property.
*/
overlay[global]
DefiniteAbstractValue getAModuleExportsValue() {
result = [this.getAnImplicitExportsValue(), this.getAnExplicitExportsValue()]
}

overlay[global]
pragma[noinline, nomagic]
private AbstractValue getAnImplicitExportsValue() {
// implicit exports: anything that is returned from the factory function
result = this.getModuleExpr().analyze().getAValue()
}

overlay[global]
pragma[noinline]
private AbstractValue getAnExplicitExportsValue() {
// explicit exports: anything assigned to `module.exports`
Expand All @@ -227,6 +234,7 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
private predicate isPseudoDependency(string s) { s = ["exports", "require", "module"] }

/** An AMD dependency, considered as a path expression. */
overlay[global]
private class AmdDependencyPath extends PathExprCandidate {
AmdDependencyPath() {
exists(AmdModuleDefinition amd |
Expand All @@ -239,6 +247,7 @@ private class AmdDependencyPath extends PathExprCandidate {
}

/** A constant path element appearing in an AMD dependency expression. */
overlay[global]
deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString {
ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() }

Expand Down Expand Up @@ -281,6 +290,7 @@ private class AmdDependencyImport extends Import {
* Specifically, we look for files whose absolute path ends with the imported path, possibly
* adding well-known JavaScript file extensions like `.js`.
*/
overlay[global]
private File guessTarget() {
exists(FilePath imported, string abspath, string dirname, string basename |
this.targetCandidate(result, abspath, imported, dirname, basename)
Expand All @@ -303,6 +313,7 @@ private class AmdDependencyImport extends Import {
* Additionally, `abspath` is bound to the absolute path of `f`, `imported` to the imported path, and
* `dirname` and `basename` to the dirname and basename (respectively) of `imported`.
*/
overlay[global]
private predicate targetCandidate(
File f, string abspath, FilePath imported, string dirname, string basename
) {
Expand All @@ -316,10 +327,12 @@ private class AmdDependencyImport extends Import {
/**
* Gets the module whose absolute path matches this import, if there is only a single such module.
*/
overlay[global]
private Module resolveByAbsolutePath() {
result.getFile() = unique(File file | file = this.guessTarget())
}

overlay[global]
override Module getImportedModule() {
result = super.getImportedModule()
or
Expand Down Expand Up @@ -348,21 +361,20 @@ private class AmdDependencyImport extends Import {
*/
class AmdModule extends Module {
cached
AmdModule() {
Stages::DataFlowStage::ref() and
exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this)))
}
AmdModule() { exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this))) }

/** Gets the definition of this module. */
AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) }

overlay[global]
override DataFlow::Node getAnExportedValue(string name) {
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
pwn.getBase().analyze().getAValue() = this.getDefine().getAModuleExportsValue() and
name = pwn.getPropertyName()
)
}

overlay[global]
override DataFlow::Node getABulkExportedNode() {
// Assigned to `module.exports` via the factory's `module` parameter
exists(AbstractModuleObject m, DataFlow::PropWrite write |
Expand Down
5 changes: 5 additions & 0 deletions javascript/ql/lib/semmle/javascript/AST.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Provides classes for working with the AST-based representation of JavaScript programs.
*/
overlay[local]
module;

import javascript
private import internal.StmtContainers
Expand Down Expand Up @@ -470,6 +472,7 @@ module AST {
*/
class ValueNode extends AstNode, @dataflownode {
/** Gets type inference results for this element. */
overlay[global]
DataFlow::AnalyzedNode analyze() { result = DataFlow::valueNode(this).analyze() }

/** Gets the data flow node associated with this program element. */
Expand All @@ -481,6 +484,7 @@ module AST {
* This can be used to map an expression to the class it refers to, or
* associate it with a named value coming from an dependency.
*/
overlay[global]
ExprNameBindingNode getNameBinding() { result = this }

/**
Expand All @@ -490,6 +494,7 @@ module AST {
* (according to the type system), or to associate it with a named type coming
* from a dependency.
*/
overlay[global]
TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) }
}
}
4 changes: 2 additions & 2 deletions javascript/ql/lib/semmle/javascript/ApiGraphs.qll
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ module API {
or
// special case: from `require('m')` to an export of `prop` in `m`
exists(Import imp, Module m, string prop |
pred = imp.getImportedModuleNode() and
pred = imp.getImportedModuleNodeStrict() and
m = imp.getImportedModule() and
lbl = Label::member(prop) and
rhs = m.getAnExportedValue(prop)
Expand Down Expand Up @@ -1337,7 +1337,7 @@ module API {
result = nd.getALocalSource()
or
// additional backwards step from `require('m')` to `exports` or `module.exports` in m
exists(Import imp | imp.getImportedModuleNode() = trackDefNode(nd, t.continue()) |
exists(Import imp | imp.getImportedModuleNodeStrict() = trackDefNode(nd, t.continue()) |
result = DataFlow::exportsVarNode(imp.getImportedModule())
or
result = DataFlow::moduleVarNode(imp.getImportedModule()).getAPropertyRead("exports")
Expand Down
2 changes: 2 additions & 0 deletions javascript/ql/lib/semmle/javascript/CFG.qll
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@
* Note that the `import` statement as a whole is part of the CFG of the body, while its single
* import specifier `x as y` forms part of the preamble.
*/
overlay[local]
module;

import javascript
private import internal.StmtContainers
Expand Down
5 changes: 5 additions & 0 deletions javascript/ql/lib/semmle/javascript/Classes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Class declarations and class expressions are modeled by (QL) classes `ClassDeclaration`
* and `ClassExpression`, respectively, which are both subclasses of `ClassDefinition`.
*/
overlay[local]
module;

import javascript

Expand Down Expand Up @@ -119,6 +121,7 @@ class ClassOrInterface extends @class_or_interface, TypeParameterized {
*
* Anonymous classes and interfaces do not have a canonical name.
*/
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this }

/**
Expand Down Expand Up @@ -253,6 +256,7 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod
/**
* Gets the definition of the super class of this class, if it can be determined.
*/
overlay[global]
ClassDefinition getSuperClassDefinition() {
result = this.getSuperClass().analyze().getAValue().(AbstractClass).getClass()
}
Expand Down Expand Up @@ -580,6 +584,7 @@ class MemberDeclaration extends @property, Documentable {
int getMemberIndex() { properties(this, _, result, _, _) }

/** Holds if the name of this member is computed by an impure expression. */
overlay[global]
predicate hasImpureNameExpr() { this.isComputed() and this.getNameExpr().isImpure() }

/**
Expand Down
Loading
Loading