Merge pull request #20424 from asgerf/js/overlay-manual-v4

JS: Add overlay annotations
This commit is contained in:
Asger F
2025-11-20 11:10:46 +01:00
committed by GitHub
110 changed files with 780 additions and 62 deletions

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes and predicates for the 'js/useless-expression' query. * Provides classes and predicates for the 'js/useless-expression' query.
*/ */
overlay[local]
module;
import javascript import javascript
import DOMProperties import DOMProperties
@@ -60,6 +62,7 @@ predicate isDeclaration(Expr e) {
/** /**
* Holds if there exists a getter for a property called `name` anywhere in the program. * Holds if there exists a getter for a property called `name` anywhere in the program.
*/ */
overlay[global]
predicate isGetterProperty(string name) { predicate isGetterProperty(string name) {
// there is a call of the form `Object.defineProperty(..., name, descriptor)` ... // there is a call of the form `Object.defineProperty(..., name, descriptor)` ...
exists(CallToObjectDefineProperty defProp | name = defProp.getPropertyName() | exists(CallToObjectDefineProperty defProp | name = defProp.getPropertyName() |
@@ -85,6 +88,7 @@ predicate isGetterProperty(string name) {
/** /**
* A property access that may invoke a getter. * A property access that may invoke a getter.
*/ */
overlay[global]
class GetterPropertyAccess extends PropAccess { class GetterPropertyAccess extends PropAccess {
override predicate isImpure() { isGetterProperty(this.getPropertyName()) } override predicate isImpure() { isGetterProperty(this.getPropertyName()) }
} }
@@ -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 * even if they do, the call itself is useless and should be flagged by this
* query. * query.
*/ */
overlay[global]
predicate noSideEffects(Expr e) { predicate noSideEffects(Expr e) {
e.isPure() e.isPure()
or or
@@ -148,6 +153,7 @@ predicate isCompoundExpression(Expr e) {
/** /**
* Holds if the expression `e` should be reported as having no effect. * Holds if the expression `e` should be reported as having no effect.
*/ */
overlay[global]
predicate hasNoEffect(Expr e) { predicate hasNoEffect(Expr e) {
noSideEffects(e) and noSideEffects(e) and
inVoidContext(e) and inVoidContext(e) and

View File

@@ -1,6 +1,8 @@
/** /**
* Provides a predicate for identifying unused index variables in loops. * Provides a predicate for identifying unused index variables in loops.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -2,6 +2,8 @@
* Provides classes for working with * Provides classes for working with
* [Asynchronous Module Definitions](https://github.com/amdjs/amdjs-api/wiki/AMD). * [Asynchronous Module Definitions](https://github.com/amdjs/amdjs-api/wiki/AMD).
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -62,9 +64,11 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
} }
/** DEPRECATED. Use `getDependencyExpr` instead. */ /** DEPRECATED. Use `getDependencyExpr` instead. */
overlay[global]
deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) } deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) }
/** DEPRECATED. Use `getADependencyExpr` instead. */ /** DEPRECATED. Use `getADependencyExpr` instead. */
overlay[global]
deprecated PathExpr getADependency() { result = this.getADependencyExpr() } deprecated PathExpr getADependency() { result = this.getADependencyExpr() }
/** Gets the `i`th dependency of this module definition. */ /** Gets the `i`th dependency of this module definition. */
@@ -194,16 +198,19 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
* Gets an abstract value representing one or more values that may flow * Gets an abstract value representing one or more values that may flow
* into this module's `module.exports` property. * into this module's `module.exports` property.
*/ */
overlay[global]
DefiniteAbstractValue getAModuleExportsValue() { DefiniteAbstractValue getAModuleExportsValue() {
result = [this.getAnImplicitExportsValue(), this.getAnExplicitExportsValue()] result = [this.getAnImplicitExportsValue(), this.getAnExplicitExportsValue()]
} }
overlay[global]
pragma[noinline, nomagic] pragma[noinline, nomagic]
private AbstractValue getAnImplicitExportsValue() { private AbstractValue getAnImplicitExportsValue() {
// implicit exports: anything that is returned from the factory function // implicit exports: anything that is returned from the factory function
result = this.getModuleExpr().analyze().getAValue() result = this.getModuleExpr().analyze().getAValue()
} }
overlay[global]
pragma[noinline] pragma[noinline]
private AbstractValue getAnExplicitExportsValue() { private AbstractValue getAnExplicitExportsValue() {
// explicit exports: anything assigned to `module.exports` // explicit exports: anything assigned to `module.exports`
@@ -227,6 +234,7 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
private predicate isPseudoDependency(string s) { s = ["exports", "require", "module"] } private predicate isPseudoDependency(string s) { s = ["exports", "require", "module"] }
/** An AMD dependency, considered as a path expression. */ /** An AMD dependency, considered as a path expression. */
overlay[global]
private class AmdDependencyPath extends PathExprCandidate { private class AmdDependencyPath extends PathExprCandidate {
AmdDependencyPath() { AmdDependencyPath() {
exists(AmdModuleDefinition amd | exists(AmdModuleDefinition amd |
@@ -239,6 +247,7 @@ private class AmdDependencyPath extends PathExprCandidate {
} }
/** A constant path element appearing in an AMD dependency expression. */ /** A constant path element appearing in an AMD dependency expression. */
overlay[global]
deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString { deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString {
ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() } ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() }
@@ -281,6 +290,7 @@ private class AmdDependencyImport extends Import {
* Specifically, we look for files whose absolute path ends with the imported path, possibly * Specifically, we look for files whose absolute path ends with the imported path, possibly
* adding well-known JavaScript file extensions like `.js`. * adding well-known JavaScript file extensions like `.js`.
*/ */
overlay[global]
private File guessTarget() { private File guessTarget() {
exists(FilePath imported, string abspath, string dirname, string basename | exists(FilePath imported, string abspath, string dirname, string basename |
this.targetCandidate(result, abspath, imported, dirname, basename) this.targetCandidate(result, abspath, imported, dirname, basename)
@@ -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 * 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`. * `dirname` and `basename` to the dirname and basename (respectively) of `imported`.
*/ */
overlay[global]
private predicate targetCandidate( private predicate targetCandidate(
File f, string abspath, FilePath imported, string dirname, string basename File f, string abspath, FilePath imported, string dirname, string basename
) { ) {
@@ -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. * Gets the module whose absolute path matches this import, if there is only a single such module.
*/ */
overlay[global]
private Module resolveByAbsolutePath() { private Module resolveByAbsolutePath() {
result.getFile() = unique(File file | file = this.guessTarget()) result.getFile() = unique(File file | file = this.guessTarget())
} }
overlay[global]
override Module getImportedModule() { override Module getImportedModule() {
result = super.getImportedModule() result = super.getImportedModule()
or or
@@ -348,14 +361,12 @@ private class AmdDependencyImport extends Import {
*/ */
class AmdModule extends Module { class AmdModule extends Module {
cached cached
AmdModule() { AmdModule() { exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this))) }
Stages::DataFlowStage::ref() and
exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this)))
}
/** Gets the definition of this module. */ /** Gets the definition of this module. */
AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) } AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) }
overlay[global]
override DataFlow::Node getAnExportedValue(string name) { override DataFlow::Node getAnExportedValue(string name) {
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() | exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
pwn.getBase().analyze().getAValue() = this.getDefine().getAModuleExportsValue() and pwn.getBase().analyze().getAValue() = this.getDefine().getAModuleExportsValue() and
@@ -363,6 +374,7 @@ class AmdModule extends Module {
) )
} }
overlay[global]
override DataFlow::Node getABulkExportedNode() { override DataFlow::Node getABulkExportedNode() {
// Assigned to `module.exports` via the factory's `module` parameter // Assigned to `module.exports` via the factory's `module` parameter
exists(AbstractModuleObject m, DataFlow::PropWrite write | exists(AbstractModuleObject m, DataFlow::PropWrite write |

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with the AST-based representation of JavaScript programs. * Provides classes for working with the AST-based representation of JavaScript programs.
*/ */
overlay[local]
module;
import javascript import javascript
private import internal.StmtContainers private import internal.StmtContainers
@@ -172,6 +174,7 @@ class AstNode extends @ast_node, NodeInStmtContainer {
* The TypeScript compiler emits no code for ambient declarations, but they * The TypeScript compiler emits no code for ambient declarations, but they
* can affect name resolution and type checking at compile-time. * can affect name resolution and type checking at compile-time.
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
predicate isAmbient() { predicate isAmbient() {
this.isAmbientInternal() this.isAmbientInternal()
@@ -470,9 +473,12 @@ module AST {
*/ */
class ValueNode extends AstNode, @dataflownode { class ValueNode extends AstNode, @dataflownode {
/** Gets type inference results for this element. */ /** Gets type inference results for this element. */
overlay[global]
DataFlow::AnalyzedNode analyze() { result = DataFlow::valueNode(this).analyze() } DataFlow::AnalyzedNode analyze() { result = DataFlow::valueNode(this).analyze() }
/** Gets the data flow node associated with this program element. */ /** Gets the data flow node associated with this program element. */
overlay[caller]
pragma[inline]
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) } DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
/** /**
@@ -481,6 +487,7 @@ module AST {
* This can be used to map an expression to the class it refers to, or * 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. * associate it with a named value coming from an dependency.
*/ */
overlay[global]
ExprNameBindingNode getNameBinding() { result = this } ExprNameBindingNode getNameBinding() { result = this }
/** /**
@@ -490,6 +497,7 @@ module AST {
* (according to the type system), or to associate it with a named type coming * (according to the type system), or to associate it with a named type coming
* from a dependency. * from a dependency.
*/ */
overlay[global]
TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) } TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) }
} }
} }

View File

@@ -272,6 +272,8 @@
* Note that the `import` statement as a whole is part of the CFG of the body, while its single * 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. * import specifier `x as y` forms part of the preamble.
*/ */
overlay[local]
module;
import javascript import javascript
private import internal.StmtContainers private import internal.StmtContainers

View File

@@ -4,6 +4,8 @@
* Class declarations and class expressions are modeled by (QL) classes `ClassDeclaration` * Class declarations and class expressions are modeled by (QL) classes `ClassDeclaration`
* and `ClassExpression`, respectively, which are both subclasses of `ClassDefinition`. * and `ClassExpression`, respectively, which are both subclasses of `ClassDefinition`.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -119,6 +121,7 @@ class ClassOrInterface extends @class_or_interface, TypeParameterized {
* *
* Anonymous classes and interfaces do not have a canonical name. * Anonymous classes and interfaces do not have a canonical name.
*/ */
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this } deprecated TypeName getTypeName() { result.getADefinition() = this }
/** /**
@@ -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. * Gets the definition of the super class of this class, if it can be determined.
*/ */
overlay[global]
ClassDefinition getSuperClassDefinition() { ClassDefinition getSuperClassDefinition() {
result = this.getSuperClass().analyze().getAValue().(AbstractClass).getClass() result = this.getSuperClass().analyze().getAValue().(AbstractClass).getClass()
} }
@@ -580,6 +584,7 @@ class MemberDeclaration extends @property, Documentable {
int getMemberIndex() { properties(this, _, result, _, _) } int getMemberIndex() { properties(this, _, result, _, _) }
/** Holds if the name of this member is computed by an impure expression. */ /** Holds if the name of this member is computed by an impure expression. */
overlay[global]
predicate hasImpureNameExpr() { this.isComputed() and this.getNameExpr().isImpure() } predicate hasImpureNameExpr() { this.isComputed() and this.getNameExpr().isImpure() }
/** /**

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with the Closure-Library module system. * Provides classes for working with the Closure-Library module system.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -40,6 +42,7 @@ module Closure {
/** /**
* A reference to a Closure namespace. * A reference to a Closure namespace.
*/ */
overlay[global]
deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range { deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {
/** /**
* Gets the namespace being referenced. * Gets the namespace being referenced.
@@ -47,6 +50,7 @@ module Closure {
string getClosureNamespace() { result = super.getClosureNamespace() } string getClosureNamespace() { result = super.getClosureNamespace() }
} }
overlay[global]
deprecated module ClosureNamespaceRef { deprecated module ClosureNamespaceRef {
/** /**
* A reference to a Closure namespace. * A reference to a Closure namespace.
@@ -64,9 +68,11 @@ module Closure {
/** /**
* A data flow node that returns the value of a closure namespace. * A data flow node that returns the value of a closure namespace.
*/ */
overlay[global]
deprecated class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range deprecated class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range
{ } { }
overlay[global]
deprecated module ClosureNamespaceAccess { deprecated module ClosureNamespaceAccess {
/** /**
* A data flow node that returns the value of a closure namespace. * A data flow node that returns the value of a closure namespace.
@@ -79,6 +85,7 @@ module Closure {
/** /**
* A call to a method on the `goog.` namespace, as a closure reference. * A call to a method on the `goog.` namespace, as a closure reference.
*/ */
overlay[global]
abstract deprecated private class DefaultNamespaceRef extends DataFlow::MethodCallNode, abstract deprecated private class DefaultNamespaceRef extends DataFlow::MethodCallNode,
ClosureNamespaceRef::Range ClosureNamespaceRef::Range
{ {
@@ -91,6 +98,7 @@ module Closure {
* Holds if `node` is the data flow node corresponding to the expression in * Holds if `node` is the data flow node corresponding to the expression in
* a top-level expression statement. * a top-level expression statement.
*/ */
overlay[global]
deprecated private predicate isTopLevelExpr(DataFlow::Node node) { deprecated private predicate isTopLevelExpr(DataFlow::Node node) {
any(TopLevel tl).getAChildStmt().(ExprStmt).getExpr().flow() = node any(TopLevel tl).getAChildStmt().(ExprStmt).getExpr().flow() = node
} }
@@ -98,6 +106,7 @@ module Closure {
/** /**
* A top-level call to `goog.provide`. * A top-level call to `goog.provide`.
*/ */
overlay[global]
deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef { deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef {
DefaultClosureProvideCall() { DefaultClosureProvideCall() {
this.getMethodName() = "provide" and this.getMethodName() = "provide" and
@@ -108,12 +117,14 @@ module Closure {
/** /**
* A top-level call to `goog.provide`. * A top-level call to `goog.provide`.
*/ */
overlay[global]
deprecated class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall deprecated class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall
{ } { }
/** /**
* A call to `goog.require`. * A call to `goog.require`.
*/ */
overlay[global]
deprecated private class DefaultClosureRequireCall extends DefaultNamespaceRef, deprecated private class DefaultClosureRequireCall extends DefaultNamespaceRef,
ClosureNamespaceAccess::Range ClosureNamespaceAccess::Range
{ {
@@ -123,12 +134,14 @@ module Closure {
/** /**
* A call to `goog.require`. * A call to `goog.require`.
*/ */
overlay[global]
deprecated class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall deprecated class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall
{ } { }
/** /**
* A top-level call to `goog.module` or `goog.declareModuleId`. * A top-level call to `goog.module` or `goog.declareModuleId`.
*/ */
overlay[global]
deprecated private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef { deprecated private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef {
DefaultClosureModuleDeclaration() { DefaultClosureModuleDeclaration() {
(this.getMethodName() = "module" or this.getMethodName() = "declareModuleId") and (this.getMethodName() = "module" or this.getMethodName() = "declareModuleId") and
@@ -139,6 +152,7 @@ module Closure {
/** /**
* A top-level call to `goog.module` or `goog.declareModuleId`. * A top-level call to `goog.module` or `goog.declareModuleId`.
*/ */
overlay[global]
deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration
{ } { }
@@ -156,6 +170,7 @@ module Closure {
/** /**
* Gets the call to `goog.module` or `goog.declareModuleId` in this module. * Gets the call to `goog.module` or `goog.declareModuleId` in this module.
*/ */
overlay[global]
deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this } deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this }
/** /**
@@ -181,6 +196,7 @@ module Closure {
result = this.getScope().getVariable("exports") result = this.getScope().getVariable("exports")
} }
overlay[global]
override DataFlow::Node getAnExportedValue(string name) { override DataFlow::Node getAnExportedValue(string name) {
exists(DataFlow::PropWrite write, Expr base | exists(DataFlow::PropWrite write, Expr base |
result = write.getRhs() and result = write.getRhs() and
@@ -193,6 +209,7 @@ module Closure {
) )
} }
overlay[global]
override DataFlow::Node getABulkExportedNode() { override DataFlow::Node getABulkExportedNode() {
result = this.getExportsVariable().getAnAssignedExpr().flow() result = this.getExportsVariable().getAnAssignedExpr().flow()
} }
@@ -232,6 +249,7 @@ module Closure {
/** /**
* Holds if `name` is a closure namespace, including proper namespace prefixes. * Holds if `name` is a closure namespace, including proper namespace prefixes.
*/ */
overlay[global]
pragma[noinline] pragma[noinline]
predicate isClosureNamespace(string name) { predicate isClosureNamespace(string name) {
exists(string namespace | exists(string namespace |
@@ -253,6 +271,7 @@ module Closure {
* Holds if a prefix of `name` is a closure namespace. * Holds if a prefix of `name` is a closure namespace.
*/ */
bindingset[name] bindingset[name]
overlay[global]
private predicate hasClosureNamespacePrefix(string name) { private predicate hasClosureNamespacePrefix(string name) {
isClosureNamespace(name.substring(0, name.indexOf("."))) isClosureNamespace(name.substring(0, name.indexOf(".")))
or or
@@ -262,6 +281,7 @@ module Closure {
/** /**
* Gets the closure namespace path addressed by the given data flow node, if any. * Gets the closure namespace path addressed by the given data flow node, if any.
*/ */
overlay[global]
string getClosureNamespaceFromSourceNode(DataFlow::SourceNode node) { string getClosureNamespaceFromSourceNode(DataFlow::SourceNode node) {
node = AccessPath::getAReferenceOrAssignmentTo(result) and node = AccessPath::getAReferenceOrAssignmentTo(result) and
hasClosureNamespacePrefix(result) hasClosureNamespacePrefix(result)
@@ -270,6 +290,7 @@ module Closure {
/** /**
* Gets the closure namespace path written to by the given property write, if any. * Gets the closure namespace path written to by the given property write, if any.
*/ */
overlay[global]
string getWrittenClosureNamespace(DataFlow::PropWrite node) { string getWrittenClosureNamespace(DataFlow::PropWrite node) {
node.getRhs() = AccessPath::getAnAssignmentTo(result) and node.getRhs() = AccessPath::getAnAssignmentTo(result) and
hasClosureNamespacePrefix(result) hasClosureNamespacePrefix(result)
@@ -278,6 +299,7 @@ module Closure {
/** /**
* Gets a data flow node that refers to the given value exported from a Closure module. * Gets a data flow node that refers to the given value exported from a Closure module.
*/ */
overlay[global]
DataFlow::SourceNode moduleImport(string moduleName) { DataFlow::SourceNode moduleImport(string moduleName) {
getClosureNamespaceFromSourceNode(result) = moduleName getClosureNamespaceFromSourceNode(result) = moduleName
} }
@@ -285,6 +307,7 @@ module Closure {
/** /**
* A call to `goog.bind`, as a partial function invocation. * A call to `goog.bind`, as a partial function invocation.
*/ */
overlay[global]
private class BindCall extends DataFlow::PartialInvokeNode::Range, DataFlow::CallNode { private class BindCall extends DataFlow::PartialInvokeNode::Range, DataFlow::CallNode {
BindCall() { this = moduleImport("goog.bind").getACall() } BindCall() { this = moduleImport("goog.bind").getACall() }

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with JavaScript comments. */ /** Provides classes for working with JavaScript comments. */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with expressions that evaluate to constant values. * Provides classes for working with expressions that evaluate to constant values.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages

View File

@@ -1,4 +1,6 @@
/** Provides classes and predicates for working with variable definitions and uses. */ /** Provides classes and predicates for working with variable definitions and uses. */
overlay[local]
module;
import javascript import javascript
@@ -231,6 +233,7 @@ class VarUse extends ControlFlowNode, @varref instanceof RValue {
* *
* For global variables, each definition is considered to reach each use. * For global variables, each definition is considered to reach each use.
*/ */
overlay[global]
VarDef getADef() { VarDef getADef() {
result = this.getSsaVariable().getDefinition().getAContributingVarDef() or result = this.getSsaVariable().getDefinition().getAContributingVarDef() or
result.getAVariable() = this.getVariable().(GlobalVariable) result.getAVariable() = this.getVariable().(GlobalVariable)
@@ -241,5 +244,6 @@ class VarUse extends ControlFlowNode, @varref instanceof RValue {
* *
* This predicate is only defined for variables that can be SSA-converted. * This predicate is only defined for variables that can be SSA-converted.
*/ */
overlay[global]
SsaVariable getSsaVariable() { result.getAUse() = this } SsaVariable getSsaVariable() { result.getAUse() = this }
} }

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with E4X. * Provides classes for working with E4X.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with ECMAScript 2015 modules. */ /** Provides classes for working with ECMAScript 2015 modules. */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -29,11 +31,13 @@ class ES2015Module extends Module {
/** Gets an export declaration in this module. */ /** Gets an export declaration in this module. */
ExportDeclaration getAnExport() { result.getTopLevel() = this } ExportDeclaration getAnExport() { result.getTopLevel() = this }
overlay[global]
override DataFlow::Node getAnExportedValue(string name) { override DataFlow::Node getAnExportedValue(string name) {
exists(ExportDeclaration ed | ed = this.getAnExport() and result = ed.getSourceNode(name)) exists(ExportDeclaration ed | ed = this.getAnExport() and result = ed.getSourceNode(name))
} }
/** Holds if this module exports variable `v` under the name `name`. */ /** Holds if this module exports variable `v` under the name `name`. */
overlay[global]
predicate exportsAs(LexicalName v, string name) { this.getAnExport().exportsAs(v, name) } predicate exportsAs(LexicalName v, string name) { this.getAnExport().exportsAs(v, name) }
override predicate isStrict() { override predicate isStrict() {
@@ -50,6 +54,7 @@ class ES2015Module extends Module {
* When a module has both named and `default` exports, the non-standard interpretation can lead to * When a module has both named and `default` exports, the non-standard interpretation can lead to
* ambiguities, so we only allow the standard interpretation in that case. * ambiguities, so we only allow the standard interpretation in that case.
*/ */
overlay[global]
predicate hasBothNamedAndDefaultExports() { predicate hasBothNamedAndDefaultExports() {
hasNamedExports(this) and hasNamedExports(this) and
hasDefaultExport(this) hasDefaultExport(this)
@@ -59,6 +64,7 @@ class ES2015Module extends Module {
/** /**
* Holds if `mod` contains one or more named export declarations other than `default`. * Holds if `mod` contains one or more named export declarations other than `default`.
*/ */
overlay[global]
private predicate hasNamedExports(ES2015Module mod) { private predicate hasNamedExports(ES2015Module mod) {
mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() != "default" mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() != "default"
or or
@@ -71,6 +77,7 @@ private predicate hasNamedExports(ES2015Module mod) {
/** /**
* Holds if this module contains a default export. * Holds if this module contains a default export.
*/ */
overlay[global]
private predicate hasDefaultExport(ES2015Module mod) { private predicate hasDefaultExport(ES2015Module mod) {
// export default foo; // export default foo;
mod.getAnExport() instanceof ExportDefaultDeclaration mod.getAnExport() instanceof ExportDefaultDeclaration
@@ -172,6 +179,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
} }
/** A literal path expression appearing in an `import` declaration. */ /** A literal path expression appearing in an `import` declaration. */
overlay[global]
deprecated private class LiteralImportPath extends PathExpr, ConstantString { deprecated private class LiteralImportPath extends PathExpr, ConstantString {
LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) } LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) }
@@ -198,6 +206,7 @@ deprecated private class LiteralImportPath extends PathExpr, ConstantString {
*/ */
class ImportSpecifier extends Expr, @import_specifier { class ImportSpecifier extends Expr, @import_specifier {
/** Gets the import declaration in which this specifier appears. */ /** Gets the import declaration in which this specifier appears. */
overlay[global]
ImportDeclaration getImportDeclaration() { result.getASpecifier() = this } ImportDeclaration getImportDeclaration() { result.getASpecifier() = this }
/** Gets the imported symbol; undefined for default and namespace import specifiers. */ /** Gets the imported symbol; undefined for default and namespace import specifiers. */
@@ -297,6 +306,7 @@ class BulkImportDeclaration extends ImportDeclaration {
* import console, { log } from 'console'; * import console, { log } from 'console';
* ``` * ```
*/ */
overlay[global]
class SelectiveImportDeclaration extends ImportDeclaration { class SelectiveImportDeclaration extends ImportDeclaration {
SelectiveImportDeclaration() { not this instanceof BulkImportDeclaration } SelectiveImportDeclaration() { not this instanceof BulkImportDeclaration }
@@ -330,9 +340,11 @@ class SelectiveImportDeclaration extends ImportDeclaration {
*/ */
abstract class ExportDeclaration extends Stmt, @export_declaration { abstract class ExportDeclaration extends Stmt, @export_declaration {
/** Gets the module to which this export declaration belongs. */ /** Gets the module to which this export declaration belongs. */
overlay[global]
ES2015Module getEnclosingModule() { this = result.getAnExport() } ES2015Module getEnclosingModule() { this = result.getAnExport() }
/** Holds if this export declaration exports variable `v` under the name `name`. */ /** Holds if this export declaration exports variable `v` under the name `name`. */
overlay[global]
abstract predicate exportsAs(LexicalName v, string name); abstract predicate exportsAs(LexicalName v, string name);
/** /**
@@ -356,6 +368,7 @@ abstract class ExportDeclaration extends Stmt, @export_declaration {
* exports under the same name. In particular, its source node belongs * exports under the same name. In particular, its source node belongs
* to module `a` or possibly to some other module from which `a` re-exports. * to module `a` or possibly to some other module from which `a` re-exports.
*/ */
overlay[global]
abstract DataFlow::Node getSourceNode(string name); abstract DataFlow::Node getSourceNode(string name);
/** Holds if is declared with the `type` keyword, so only types are exported. */ /** Holds if is declared with the `type` keyword, so only types are exported. */
@@ -407,11 +420,13 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati
/** Gets the name of the module from which this declaration re-exports. */ /** Gets the name of the module from which this declaration re-exports. */
override ConstantString getImportedPath() { result = this.getChildExpr(0) } override ConstantString getImportedPath() { result = this.getChildExpr(0) }
overlay[global]
override predicate exportsAs(LexicalName v, string name) { override predicate exportsAs(LexicalName v, string name) {
this.getReExportedES2015Module().exportsAs(v, name) and this.getReExportedES2015Module().exportsAs(v, name) and
not isShadowedFromBulkExport(this, name) not isShadowedFromBulkExport(this, name)
} }
overlay[global]
override DataFlow::Node getSourceNode(string name) { override DataFlow::Node getSourceNode(string name) {
result = this.getReExportedES2015Module().getAnExport().getSourceNode(name) result = this.getReExportedES2015Module().getAnExport().getSourceNode(name)
} }
@@ -430,6 +445,7 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati
* At runtime, the interface `X` will have been removed, so `X` is actually re-exported anyway, * At runtime, the interface `X` will have been removed, so `X` is actually re-exported anyway,
* but we ignore this subtlety. * but we ignore this subtlety.
*/ */
overlay[global]
private predicate isShadowedFromBulkExport(BulkReExportDeclaration reExport, string name) { private predicate isShadowedFromBulkExport(BulkReExportDeclaration reExport, string name) {
exists(ExportNamedDeclaration other | other.getTopLevel() = reExport.getEnclosingModule() | exists(ExportNamedDeclaration other | other.getTopLevel() = reExport.getEnclosingModule() |
other.getAnExportedDecl().getName() = name other.getAnExportedDecl().getName() = name
@@ -452,6 +468,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declar
/** Gets the operand statement or expression that is exported by this declaration. */ /** Gets the operand statement or expression that is exported by this declaration. */
ExprOrStmt getOperand() { result = this.getChild(0) } ExprOrStmt getOperand() { result = this.getChild(0) }
overlay[global]
override predicate exportsAs(LexicalName v, string name) { override predicate exportsAs(LexicalName v, string name) {
name = "default" and v = this.getADecl().getVariable() name = "default" and v = this.getADecl().getVariable()
} }
@@ -464,6 +481,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declar
) )
} }
overlay[global]
override DataFlow::Node getSourceNode(string name) { override DataFlow::Node getSourceNode(string name) {
name = "default" and result = DataFlow::valueNode(this.getOperand()) name = "default" and result = DataFlow::valueNode(this.getOperand())
} }
@@ -506,6 +524,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
/** Gets the variable declaration, if any, exported by this named export. */ /** Gets the variable declaration, if any, exported by this named export. */
VarDecl getADecl() { result = this.getAnExportedDecl() } VarDecl getADecl() { result = this.getAnExportedDecl() }
overlay[global]
override predicate exportsAs(LexicalName v, string name) { override predicate exportsAs(LexicalName v, string name) {
exists(LexicalDecl vd | vd = this.getAnExportedDecl() | exists(LexicalDecl vd | vd = this.getAnExportedDecl() |
name = vd.getName() and v = vd.getALexicalName() name = vd.getName() and v = vd.getALexicalName()
@@ -518,6 +537,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
) )
} }
overlay[global]
override DataFlow::Node getSourceNode(string name) { override DataFlow::Node getSourceNode(string name) {
exists(VarDef d | d.getTarget() = this.getADecl() | exists(VarDef d | d.getTarget() = this.getADecl() |
name = d.getTarget().(VarDecl).getName() and name = d.getTarget().(VarDecl).getName() and
@@ -555,6 +575,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
private import semmle.javascript.dataflow.internal.PreCallGraphStep private import semmle.javascript.dataflow.internal.PreCallGraphStep
overlay[global]
private class ExportNamespaceStep extends PreCallGraphStep { private class ExportNamespaceStep extends PreCallGraphStep {
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
exists(ExportNamedDeclaration exprt, ExportNamespaceSpecifier spec | exists(ExportNamedDeclaration exprt, ExportNamespaceSpecifier spec |
@@ -572,6 +593,7 @@ private class ExportNamespaceStep extends PreCallGraphStep {
private class TypeOnlyExportDeclaration extends ExportNamedDeclaration { private class TypeOnlyExportDeclaration extends ExportNamedDeclaration {
TypeOnlyExportDeclaration() { this.isTypeOnly() } TypeOnlyExportDeclaration() { this.isTypeOnly() }
overlay[global]
override predicate exportsAs(LexicalName v, string name) { override predicate exportsAs(LexicalName v, string name) {
super.exportsAs(v, name) and super.exportsAs(v, name) and
not v instanceof Variable not v instanceof Variable
@@ -745,9 +767,11 @@ abstract class ReExportDeclaration extends ExportDeclaration {
abstract ConstantString getImportedPath(); abstract ConstantString getImportedPath();
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */ /** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
overlay[global]
ES2015Module getReExportedES2015Module() { result = this.getReExportedModule() } ES2015Module getReExportedES2015Module() { result = this.getReExportedModule() }
/** Gets the module from which this declaration re-exports. */ /** Gets the module from which this declaration re-exports. */
overlay[global]
cached cached
Module getReExportedModule() { Module getReExportedModule() {
Stages::Imports::ref() and Stages::Imports::ref() and
@@ -756,6 +780,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
} }
/** A literal path expression appearing in a re-export declaration. */ /** A literal path expression appearing in a re-export declaration. */
overlay[global]
deprecated private class LiteralReExportPath extends PathExpr, ConstantString { deprecated private class LiteralReExportPath extends PathExpr, ConstantString {
LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) } LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) }
@@ -795,11 +820,13 @@ class SelectiveReExportDeclaration extends ReExportDeclaration, ExportNamedDecla
class OriginalExportDeclaration extends ExportDeclaration { class OriginalExportDeclaration extends ExportDeclaration {
OriginalExportDeclaration() { not this instanceof ReExportDeclaration } OriginalExportDeclaration() { not this instanceof ReExportDeclaration }
overlay[global]
override predicate exportsAs(LexicalName v, string name) { override predicate exportsAs(LexicalName v, string name) {
this.(ExportDefaultDeclaration).exportsAs(v, name) or this.(ExportDefaultDeclaration).exportsAs(v, name) or
this.(ExportNamedDeclaration).exportsAs(v, name) this.(ExportNamedDeclaration).exportsAs(v, name)
} }
overlay[global]
override DataFlow::Node getSourceNode(string name) { override DataFlow::Node getSourceNode(string name) {
result = this.(ExportDefaultDeclaration).getSourceNode(name) or result = this.(ExportDefaultDeclaration).getSourceNode(name) or
result = this.(ExportNamedDeclaration).getSourceNode(name) result = this.(ExportNamedDeclaration).getSourceNode(name)

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with syntax errors. */ /** Provides classes for working with syntax errors. */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with expressions. * Provides classes for working with expressions.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -115,12 +117,14 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
string getStringValue() { Stages::Ast::ref() and result = getStringValue(this) } string getStringValue() { Stages::Ast::ref() and result = getStringValue(this) }
/** Holds if this expression is impure, that is, its evaluation could have side effects. */ /** Holds if this expression is impure, that is, its evaluation could have side effects. */
overlay[global]
predicate isImpure() { any() } predicate isImpure() { any() }
/** /**
* Holds if this expression is pure, that is, its evaluation is guaranteed * Holds if this expression is pure, that is, its evaluation is guaranteed
* to be side-effect free. * to be side-effect free.
*/ */
overlay[global]
predicate isPure() { not this.isImpure() } predicate isPure() { not this.isImpure() }
/** /**
@@ -153,21 +157,25 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
* Holds if this expression accesses the global variable `g`, either directly * Holds if this expression accesses the global variable `g`, either directly
* or through the `window` object. * or through the `window` object.
*/ */
overlay[global]
predicate accessesGlobal(string g) { this.flow().accessesGlobal(g) } predicate accessesGlobal(string g) { this.flow().accessesGlobal(g) }
/** /**
* Holds if this expression may evaluate to `s`. * Holds if this expression may evaluate to `s`.
*/ */
overlay[global]
predicate mayHaveStringValue(string s) { this.flow().mayHaveStringValue(s) } predicate mayHaveStringValue(string s) { this.flow().mayHaveStringValue(s) }
/** /**
* Holds if this expression may evaluate to `b`. * Holds if this expression may evaluate to `b`.
*/ */
overlay[global]
predicate mayHaveBooleanValue(boolean b) { this.flow().mayHaveBooleanValue(b) } predicate mayHaveBooleanValue(boolean b) { this.flow().mayHaveBooleanValue(b) }
/** /**
* Holds if this expression may refer to the initial value of parameter `p`. * Holds if this expression may refer to the initial value of parameter `p`.
*/ */
overlay[global]
predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) } predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) }
/** /**
@@ -178,6 +186,7 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
* Has no result if the expression is in a JavaScript file or in a TypeScript * Has no result if the expression is in a JavaScript file or in a TypeScript
* file that was extracted without type information. * file that was extracted without type information.
*/ */
overlay[global]
deprecated Type getType() { ast_node_type(this, result) } deprecated Type getType() { ast_node_type(this, result) }
/** /**
@@ -240,21 +249,16 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
) )
} }
pragma[inline]
private Stmt getRawEnclosingStmt(Expr e) {
// For performance reasons, we need the enclosing statement without overrides
enclosing_stmt(e, result)
}
/** /**
* Gets the data-flow node where exceptions thrown by this expression will * Gets the data-flow node where exceptions thrown by this expression will
* propagate if this expression causes an exception to be thrown. * propagate if this expression causes an exception to be thrown.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getExceptionTarget() { DataFlow::Node getExceptionTarget() {
result = getCatchParameterFromStmt(this.getRawEnclosingStmt(this)) result = getCatchParameterFromStmt(getRawEnclosingStmt(this))
or or
not exists(getCatchParameterFromStmt(this.getRawEnclosingStmt(this))) and not exists(getCatchParameterFromStmt(getRawEnclosingStmt(this))) and
result = result =
any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn() any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn()
} }
@@ -267,6 +271,13 @@ private DataFlow::Node getCatchParameterFromStmt(Stmt stmt) {
DataFlow::parameterNode(stmt.getEnclosingTryCatchStmt().getACatchClause().getAParameter()) DataFlow::parameterNode(stmt.getEnclosingTryCatchStmt().getACatchClause().getAParameter())
} }
overlay[caller]
pragma[inline]
private Stmt getRawEnclosingStmt(Expr e) {
// For performance reasons, we need the enclosing statement without overrides
enclosing_stmt(e, result)
}
/** /**
* An identifier. * An identifier.
* *
@@ -301,6 +312,7 @@ class Identifier extends @identifier, ExprOrType {
* ``` * ```
*/ */
class Label extends @label, Identifier, Expr { class Label extends @label, Identifier, Expr {
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "Label" } override string getAPrimaryQlClass() { result = "Label" }
@@ -330,6 +342,7 @@ class Literal extends @literal, Expr {
*/ */
string getRawValue() { literals(_, result, this) } string getRawValue() { literals(_, result, this) }
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "Literal" } override string getAPrimaryQlClass() { result = "Literal" }
@@ -352,6 +365,7 @@ class ParExpr extends @par_expr, Expr {
override int getIntValue() { result = this.getExpression().getIntValue() } override int getIntValue() { result = this.getExpression().getIntValue() }
overlay[global]
override predicate isImpure() { this.getExpression().isImpure() } override predicate isImpure() { this.getExpression().isImpure() }
override Expr getUnderlyingValue() { result = this.getExpression().getUnderlyingValue() } override Expr getUnderlyingValue() { result = this.getExpression().getUnderlyingValue() }
@@ -500,6 +514,7 @@ class RegExpLiteral extends @regexp_literal, Literal, RegExpParent {
* ``` * ```
*/ */
class ThisExpr extends @this_expr, Expr { class ThisExpr extends @this_expr, Expr {
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
/** /**
@@ -555,6 +570,7 @@ class ArrayExpr extends @array_expr, Expr {
/** Holds if this array literal has an omitted element. */ /** Holds if this array literal has an omitted element. */
predicate hasOmittedElement() { this.elementIsOmitted(_) } predicate hasOmittedElement() { this.elementIsOmitted(_) }
overlay[global]
override predicate isImpure() { this.getAnElement().isImpure() } override predicate isImpure() { this.getAnElement().isImpure() }
override string getAPrimaryQlClass() { result = "ArrayExpr" } override string getAPrimaryQlClass() { result = "ArrayExpr" }
@@ -597,6 +613,7 @@ class ObjectExpr extends @obj_expr, Expr {
*/ */
predicate hasTrailingComma() { this.getLastToken().getPreviousToken().getValue() = "," } predicate hasTrailingComma() { this.getLastToken().getPreviousToken().getValue() = "," }
overlay[global]
override predicate isImpure() { this.getAProperty().isImpure() } override predicate isImpure() { this.getAProperty().isImpure() }
override string getAPrimaryQlClass() { result = "ObjectExpr" } override string getAPrimaryQlClass() { result = "ObjectExpr" }
@@ -664,6 +681,7 @@ class Property extends @property, Documentable {
* Holds if this property is impure, that is, the evaluation of its name or * Holds if this property is impure, that is, the evaluation of its name or
* its initializer expression could have side effects. * its initializer expression could have side effects.
*/ */
overlay[global]
predicate isImpure() { predicate isImpure() {
this.isComputed() and this.getNameExpr().isImpure() this.isComputed() and this.getNameExpr().isImpure()
or or
@@ -826,6 +844,7 @@ class FunctionExpr extends @function_expr, Expr, Function {
Stages::Ast::ref() and result = Expr.super.getContainer() Stages::Ast::ref() and result = Expr.super.getContainer()
} }
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "FunctionExpr" } override string getAPrimaryQlClass() { result = "FunctionExpr" }
@@ -846,6 +865,7 @@ class ArrowFunctionExpr extends @arrow_function_expr, Expr, Function {
override StmtContainer getEnclosingContainer() { result = Expr.super.getContainer() } override StmtContainer getEnclosingContainer() { result = Expr.super.getContainer() }
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override Function getThisBinder() { override Function getThisBinder() {
@@ -877,6 +897,7 @@ class SeqExpr extends @seq_expr, Expr {
/** Gets the last expression in this sequence. */ /** Gets the last expression in this sequence. */
Expr getLastOperand() { result = this.getOperand(this.getNumOperands() - 1) } Expr getLastOperand() { result = this.getOperand(this.getNumOperands() - 1) }
overlay[global]
override predicate isImpure() { this.getAnOperand().isImpure() } override predicate isImpure() { this.getAnOperand().isImpure() }
override Expr getUnderlyingValue() { result = this.getLastOperand().getUnderlyingValue() } override Expr getUnderlyingValue() { result = this.getLastOperand().getUnderlyingValue() }
@@ -906,6 +927,7 @@ class ConditionalExpr extends @conditional_expr, Expr {
/** Gets either the 'then' or the 'else' expression of this conditional. */ /** Gets either the 'then' or the 'else' expression of this conditional. */
Expr getABranch() { result = this.getConsequent() or result = this.getAlternate() } Expr getABranch() { result = this.getConsequent() or result = this.getAlternate() }
overlay[global]
override predicate isImpure() { override predicate isImpure() {
this.getCondition().isImpure() or this.getCondition().isImpure() or
this.getABranch().isImpure() this.getABranch().isImpure()
@@ -985,6 +1007,7 @@ class InvokeExpr extends @invokeexpr, Expr {
* *
* This predicate is an approximation, computed using only local data flow. * This predicate is an approximation, computed using only local data flow.
*/ */
overlay[global]
predicate hasOptionArgument(int i, string name, Expr value) { predicate hasOptionArgument(int i, string name, Expr value) {
value = this.flow().(DataFlow::InvokeNode).getOptionArgument(i, name).asExpr() value = this.flow().(DataFlow::InvokeNode).getOptionArgument(i, name).asExpr()
} }
@@ -997,6 +1020,7 @@ class InvokeExpr extends @invokeexpr, Expr {
* *
* This predicate is only populated for files extracted with full TypeScript extraction. * This predicate is only populated for files extracted with full TypeScript extraction.
*/ */
overlay[global]
deprecated CallSignatureType getResolvedSignature() { invoke_expr_signature(this, result) } deprecated CallSignatureType getResolvedSignature() { invoke_expr_signature(this, result) }
/** /**
@@ -1014,6 +1038,7 @@ class InvokeExpr extends @invokeexpr, Expr {
* *
* This predicate is only populated for files extracted with full TypeScript extraction. * This predicate is only populated for files extracted with full TypeScript extraction.
*/ */
overlay[global]
deprecated CanonicalFunctionName getResolvedCalleeName() { ast_node_symbol(this, result) } deprecated CanonicalFunctionName getResolvedCalleeName() { ast_node_symbol(this, result) }
/** /**
@@ -1022,6 +1047,7 @@ class InvokeExpr extends @invokeexpr, Expr {
* Note that the resolved function may be overridden in a subclass and thus is not * Note that the resolved function may be overridden in a subclass and thus is not
* necessarily the actual target of this invocation at runtime. * necessarily the actual target of this invocation at runtime.
*/ */
overlay[global]
Function getResolvedCallee() { TypeResolution::callTarget(this, result) } Function getResolvedCallee() { TypeResolution::callTarget(this, result) }
} }
@@ -1156,6 +1182,7 @@ class DotExpr extends @dot_expr, PropAccess {
/** Gets the identifier specifying the name of the accessed property. */ /** Gets the identifier specifying the name of the accessed property. */
Identifier getProperty() { result = this.getChildExpr(1) } Identifier getProperty() { result = this.getChildExpr(1) }
overlay[global]
override predicate isImpure() { this.getBase().isImpure() } override predicate isImpure() { this.getBase().isImpure() }
override string getAPrimaryQlClass() { result = "DotExpr" } override string getAPrimaryQlClass() { result = "DotExpr" }
@@ -1176,6 +1203,7 @@ class IndexExpr extends @index_expr, PropAccess {
override string getPropertyName() { result = this.getIndex().(Literal).getValue() } override string getPropertyName() { result = this.getIndex().(Literal).getValue() }
overlay[global]
override predicate isImpure() { override predicate isImpure() {
this.getBase().isImpure() or this.getBase().isImpure() or
this.getIndex().isImpure() this.getIndex().isImpure()
@@ -1201,6 +1229,7 @@ class UnaryExpr extends @unaryexpr, Expr {
/** Gets the operator of this expression. */ /** Gets the operator of this expression. */
string getOperator() { none() } string getOperator() { none() }
overlay[global]
override predicate isImpure() { this.getOperand().isImpure() } override predicate isImpure() { this.getOperand().isImpure() }
override ControlFlowNode getFirstControlFlowNode() { override ControlFlowNode getFirstControlFlowNode() {
@@ -1302,6 +1331,7 @@ class VoidExpr extends @void_expr, UnaryExpr {
class DeleteExpr extends @delete_expr, UnaryExpr { class DeleteExpr extends @delete_expr, UnaryExpr {
override string getOperator() { result = "delete" } override string getOperator() { result = "delete" }
overlay[global]
override predicate isImpure() { any() } override predicate isImpure() { any() }
} }
@@ -1352,6 +1382,7 @@ class BinaryExpr extends @binaryexpr, Expr {
/** Gets the operator of this expression. */ /** Gets the operator of this expression. */
string getOperator() { none() } string getOperator() { none() }
overlay[global]
override predicate isImpure() { this.getAnOperand().isImpure() } override predicate isImpure() { this.getAnOperand().isImpure() }
override ControlFlowNode getFirstControlFlowNode() { override ControlFlowNode getFirstControlFlowNode() {
@@ -1617,13 +1648,19 @@ private string getConstantString(Expr e) {
result = e.(TemplateElement).getValue() result = e.(TemplateElement).getValue()
} }
pragma[nomagic]
private predicate hasConstantStringValue(Expr e) {
exists(getConstantString(e))
or
hasAllConstantLeafs(e.getUnderlyingValue())
}
/** /**
* Holds if `add` is a string-concatenation where all the transitive leafs have a constant string value. * Holds if `add` is a string-concatenation where all the transitive leafs have a constant string value.
*/ */
private predicate hasAllConstantLeafs(AddExpr add) { private predicate hasAllConstantLeafs(AddExpr add) {
forex(Expr leaf | leaf = getAnAddOperand*(add) and not exists(getAnAddOperand(leaf)) | hasConstantStringValue(add.getLeftOperand()) and
exists(getConstantString(leaf)) hasConstantStringValue(add.getRightOperand())
)
} }
/** /**
@@ -2233,6 +2270,7 @@ class YieldExpr extends @yield_expr, Expr {
/** Holds if this is a `yield*` expression. */ /** Holds if this is a `yield*` expression. */
predicate isDelegating() { is_delegating(this) } predicate isDelegating() { is_delegating(this) }
overlay[global]
override predicate isImpure() { any() } override predicate isImpure() { any() }
override ControlFlowNode getFirstControlFlowNode() { override ControlFlowNode getFirstControlFlowNode() {
@@ -2289,6 +2327,7 @@ class ComprehensionExpr extends @comprehension_expr, Expr {
/** Gets the body expression of this comprehension. */ /** Gets the body expression of this comprehension. */
Expr getBody() { result = this.getChildExpr(0) } Expr getBody() { result = this.getChildExpr(0) }
overlay[global]
override predicate isImpure() { override predicate isImpure() {
this.getABlock().isImpure() or this.getABlock().isImpure() or
this.getAFilter().isImpure() or this.getAFilter().isImpure() or
@@ -2349,6 +2388,7 @@ class ComprehensionBlock extends @comprehension_block, Expr {
/** Gets the domain over which this comprehension block iterates. */ /** Gets the domain over which this comprehension block iterates. */
Expr getDomain() { result = this.getChildExpr(1) } Expr getDomain() { result = this.getChildExpr(1) }
overlay[global]
override predicate isImpure() { override predicate isImpure() {
this.getIterator().isImpure() or this.getIterator().isImpure() or
this.getDomain().isImpure() this.getDomain().isImpure()
@@ -2675,6 +2715,7 @@ class AwaitExpr extends @await_expr, Expr {
/** Gets the operand of this `await` expression. */ /** Gets the operand of this `await` expression. */
Expr getOperand() { result = this.getChildExpr(0) } Expr getOperand() { result = this.getChildExpr(0) }
overlay[global]
override predicate isImpure() { any() } override predicate isImpure() { any() }
override ControlFlowNode getFirstControlFlowNode() { override ControlFlowNode getFirstControlFlowNode() {
@@ -2698,6 +2739,7 @@ class AwaitExpr extends @await_expr, Expr {
* ``` * ```
*/ */
class FunctionSentExpr extends @function_sent_expr, Expr { class FunctionSentExpr extends @function_sent_expr, Expr {
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "FunctionSentExpr" } override string getAPrimaryQlClass() { result = "FunctionSentExpr" }
@@ -2857,6 +2899,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import {
} }
/** A literal path expression appearing in a dynamic import. */ /** A literal path expression appearing in a dynamic import. */
overlay[global]
deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString { deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString {
LiteralDynamicImportPath() { LiteralDynamicImportPath() {
exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource()) exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource())
@@ -2919,6 +2962,7 @@ class OptionalChainRoot extends ChainElem {
* ``` * ```
*/ */
class ImportMetaExpr extends @import_meta_expr, Expr { class ImportMetaExpr extends @import_meta_expr, Expr {
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "ImportMetaExpr" } override string getAPrimaryQlClass() { result = "ImportMetaExpr" }

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for reasoning about `extend`-like functions. * Provides classes for reasoning about `extend`-like functions.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -169,6 +171,7 @@ private class FunctionalExtendCallShallow extends ExtendCall {
* *
* Since all object properties are preserved, we model this as a value-preserving step. * Since all object properties are preserved, we model this as a value-preserving step.
*/ */
overlay[global]
private class ExtendCallStep extends PreCallGraphStep { private class ExtendCallStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(ExtendCall extend | exists(ExtendCall extend |
@@ -184,6 +187,7 @@ private import semmle.javascript.dataflow.internal.PreCallGraphStep
/** /**
* A step through a cloning library, such as `clone` or `fclone`. * A step through a cloning library, such as `clone` or `fclone`.
*/ */
overlay[global]
private class CloneStep extends PreCallGraphStep { private class CloneStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | exists(DataFlow::CallNode call |

View File

@@ -36,6 +36,8 @@
* Array.prototype.length; * Array.prototype.length;
* </pre> * </pre>
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with files and folders. */ /** Provides classes for working with files and folders. */
overlay[local]
module;
import javascript import javascript
private import NodeModuleResolutionImpl private import NodeModuleResolutionImpl
@@ -33,12 +35,14 @@ module Folder = Impl::Folder;
/** A folder. */ /** A folder. */
class Folder extends Container, Impl::Folder { class Folder extends Container, Impl::Folder {
/** Gets the file or subfolder in this folder that has the given `name`, if any. */ /** Gets the file or subfolder in this folder that has the given `name`, if any. */
overlay[global]
Container getChildContainer(string name) { Container getChildContainer(string name) {
result = this.getAChildContainer() and result = this.getAChildContainer() and
result.getBaseName() = name result.getBaseName() = name
} }
/** Gets the file in this folder that has the given `stem` and `extension`, if any. */ /** Gets the file in this folder that has the given `stem` and `extension`, if any. */
overlay[global]
File getFile(string stem, string extension) { File getFile(string stem, string extension) {
result = this.getAChildContainer() and result = this.getAChildContainer() and
result.getStem() = stem and result.getStem() = stem and
@@ -46,6 +50,7 @@ class Folder extends Container, Impl::Folder {
} }
/** Like `getFile` except `d.ts` is treated as a single extension. */ /** Like `getFile` except `d.ts` is treated as a single extension. */
overlay[global]
private File getFileLongExtension(string stem, string extension) { private File getFileLongExtension(string stem, string extension) {
not (stem.matches("%.d") and extension = "ts") and not (stem.matches("%.d") and extension = "ts") and
result = this.getFile(stem, extension) result = this.getFile(stem, extension)
@@ -65,6 +70,7 @@ class Folder extends Container, Impl::Folder {
* *
* HTML files will not be found by this method. * HTML files will not be found by this method.
*/ */
overlay[global]
File getJavaScriptFile(string stem) { File getJavaScriptFile(string stem) {
result = result =
min(int p, string ext | min(int p, string ext |
@@ -78,6 +84,7 @@ class Folder extends Container, Impl::Folder {
* Gets an implementation file and/or a typings file from this folder that has the given `stem`. * Gets an implementation file and/or a typings file from this folder that has the given `stem`.
* This could be a single `.ts` file or a pair of `.js` and `.d.ts` files. * This could be a single `.ts` file or a pair of `.js` and `.d.ts` files.
*/ */
overlay[global]
File getJavaScriptFileOrTypings(string stem) { File getJavaScriptFileOrTypings(string stem) {
exists(File jsFile | jsFile = this.getJavaScriptFile(stem) | exists(File jsFile | jsFile = this.getJavaScriptFile(stem) |
result = jsFile result = jsFile
@@ -88,6 +95,7 @@ class Folder extends Container, Impl::Folder {
} }
/** Gets a subfolder contained in this folder. */ /** Gets a subfolder contained in this folder. */
overlay[global]
Folder getASubFolder() { result = this.getAChildContainer() } Folder getASubFolder() { result = this.getAChildContainer() }
} }

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with functions. */ /** Provides classes for working with functions. */
overlay[local]
module;
import javascript import javascript
@@ -434,11 +436,13 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
* *
* This predicate is only populated for files extracted with full TypeScript extraction. * This predicate is only populated for files extracted with full TypeScript extraction.
*/ */
overlay[global]
deprecated CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) } deprecated CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
/** /**
* Gets the call signature of this function, as determined by the TypeScript compiler, if any. * Gets the call signature of this function, as determined by the TypeScript compiler, if any.
*/ */
overlay[global]
deprecated CallSignatureType getCallSignature() { declared_function_signature(this, result) } deprecated CallSignatureType getCallSignature() { declared_function_signature(this, result) }
} }

View File

@@ -1,6 +1,8 @@
/** /**
* Provides predicates for associating qualified names with data flow nodes. * Provides predicates for associating qualified names with data flow nodes.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes
@@ -204,6 +206,7 @@ module AccessPath {
* Holds if the global `accessPath` is only assigned to from one file, not counting * Holds if the global `accessPath` is only assigned to from one file, not counting
* self-assignments. * self-assignments.
*/ */
overlay[global]
predicate isAssignedInUniqueFile(string accessPath) { predicate isAssignedInUniqueFile(string accessPath) {
strictcount(File f | isAssignedInFile(accessPath, f)) = 1 strictcount(File f | isAssignedInFile(accessPath, f)) = 1
} }
@@ -354,6 +357,7 @@ module AccessPath {
* Gets a variable that is relevant for the computations in the `GetLaterAccess` module. * Gets a variable that is relevant for the computations in the `GetLaterAccess` module.
* This predicate restricts as much as it can, but without depending on `getAVariableRef`. * This predicate restricts as much as it can, but without depending on `getAVariableRef`.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
private SsaVariable getARelevantVariableSimple() { private SsaVariable getARelevantVariableSimple() {
// The variable might be used where `getLaterBaseAccess()` is called. // The variable might be used where `getLaterBaseAccess()` is called.
@@ -405,6 +409,7 @@ module AccessPath {
* } * }
* ``` * ```
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAReferenceTo(Root root, string path) { DataFlow::Node getAReferenceTo(Root root, string path) {
path = fromReference(result, root) and path = fromReference(result, root) and
@@ -428,6 +433,7 @@ module AccessPath {
* })(NS = NS || {}); * })(NS = NS || {});
* ``` * ```
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAReferenceTo(string path) { DataFlow::Node getAReferenceTo(string path) {
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode()) path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
@@ -449,6 +455,7 @@ module AccessPath {
* } * }
* ``` * ```
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAnAssignmentTo(Root root, string path) { DataFlow::Node getAnAssignmentTo(Root root, string path) {
path = fromRhs(result, root) and path = fromRhs(result, root) and
@@ -470,6 +477,7 @@ module AccessPath {
* })(foo = foo || {}); * })(foo = foo || {});
* ``` * ```
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAnAssignmentTo(string path) { DataFlow::Node getAnAssignmentTo(string path) {
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode()) path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
@@ -480,6 +488,7 @@ module AccessPath {
* *
* See `getAReferenceTo` and `getAnAssignmentTo` for more details. * See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(string path) { DataFlow::Node getAReferenceOrAssignmentTo(string path) {
result = getAReferenceTo(path) result = getAReferenceTo(path)
@@ -492,6 +501,7 @@ module AccessPath {
* *
* See `getAReferenceTo` and `getAnAssignmentTo` for more details. * See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) { DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) {
result = getAReferenceTo(root, path) result = getAReferenceTo(root, path)
@@ -502,6 +512,7 @@ module AccessPath {
/** /**
* Holds if there is a step from `pred` to `succ` through an assignment to an access path. * Holds if there is a step from `pred` to `succ` through an assignment to an access path.
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
predicate step(DataFlow::Node pred, DataFlow::Node succ) { predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, Root root | exists(string name, Root root |
@@ -519,6 +530,7 @@ module AccessPath {
/** /**
* Gets a `SourceNode` that refers to the same value or access path as the given node. * Gets a `SourceNode` that refers to the same value or access path as the given node.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) { DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
exists(DataFlow::SourceNode root, string accessPath | exists(DataFlow::SourceNode root, string accessPath |
@@ -657,7 +669,7 @@ module AccessPath {
*/ */
cached cached
predicate hasDominatingWrite(DataFlow::PropRead read) { predicate hasDominatingWrite(DataFlow::PropRead read) {
Stages::TypeTracking::ref() and Stages::DataFlowStage::ref() and
// within the same basic block. // within the same basic block.
exists(ReachableBasicBlock bb, Root root, string path, int ranking | exists(ReachableBasicBlock bb, Root root, string path, int ranking |
read.asExpr() = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and read.asExpr() = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with HTML documents. */ /** Provides classes for working with HTML documents. */
overlay[local]
module;
import javascript import javascript
@@ -283,6 +285,7 @@ module HTML {
/** /**
* A path string arising from the `src` attribute of a `script` tag. * A path string arising from the `src` attribute of a `script` tag.
*/ */
overlay[global]
deprecated private class ScriptSrcPath extends PathString { deprecated private class ScriptSrcPath extends PathString {
ScriptSrcPath() { scriptSrc(this, _) } ScriptSrcPath() { scriptSrc(this, _) }

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with JSDoc comments. */ /** Provides classes for working with JSDoc comments. */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -627,6 +629,7 @@ module JSDoc {
/** /**
* A statement container which may declare JSDoc name aliases. * A statement container which may declare JSDoc name aliases.
*/ */
overlay[global]
deprecated class Environment extends StmtContainer { deprecated class Environment extends StmtContainer {
/** /**
* Gets the fully qualified name aliased by the given unqualified name * Gets the fully qualified name aliased by the given unqualified name

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with JSON data. * Provides classes for working with JSON data.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with JSX code. * Provides classes for working with JSX code.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -4,6 +4,8 @@
* This information is only available for snapshots that have been extracted with * This information is only available for snapshots that have been extracted with
* the `--extract-program-text` flag. * the `--extract-program-text` flag.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with locations and program elements that have locations. */ /** Provides classes for working with locations and program elements that have locations. */
overlay[local]
module;
import javascript import javascript
@@ -30,6 +32,7 @@ final class Location extends @location_default {
int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 }
/** Holds if this location starts before location `that`. */ /** Holds if this location starts before location `that`. */
overlay[caller]
pragma[inline] pragma[inline]
predicate startsBefore(Location that) { predicate startsBefore(Location that) {
exists(string f, int sl1, int sc1, int sl2, int sc2 | exists(string f, int sl1, int sc1, int sl2, int sc2 |
@@ -43,6 +46,7 @@ final class Location extends @location_default {
} }
/** Holds if this location ends after location `that`. */ /** Holds if this location ends after location `that`. */
overlay[caller]
pragma[inline] pragma[inline]
predicate endsAfter(Location that) { predicate endsAfter(Location that) {
exists(string f, int el1, int ec1, int el2, int ec2 | exists(string f, int el1, int ec1, int el2, int ec2 |

View File

@@ -3,6 +3,8 @@
* ECMAScript 2015-style modules, and the older CommonJS and AMD-style * ECMAScript 2015-style modules, and the older CommonJS and AMD-style
* modules. * modules.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -23,9 +25,11 @@ abstract class Module extends TopLevel {
Import getAnImport() { result.getTopLevel() = this } Import getAnImport() { result.getTopLevel() = this }
/** Gets a module from which this module imports. */ /** Gets a module from which this module imports. */
overlay[global]
Module getAnImportedModule() { result = this.getAnImport().getImportedModule() } Module getAnImportedModule() { result = this.getAnImport().getImportedModule() }
/** Gets a symbol exported by this module. */ /** Gets a symbol exported by this module. */
overlay[global]
string getAnExportedSymbol() { exists(this.getAnExportedValue(result)) } string getAnExportedSymbol() { exists(this.getAnExportedValue(result)) }
/** /**
@@ -39,12 +43,14 @@ abstract class Module extends TopLevel {
* Symbols defined in another module that are re-exported by * Symbols defined in another module that are re-exported by
* this module are only sometimes considered. * this module are only sometimes considered.
*/ */
overlay[global]
cached cached
abstract DataFlow::Node getAnExportedValue(string name); abstract DataFlow::Node getAnExportedValue(string name);
/** /**
* Gets a value that is exported as the whole exports object of this module. * Gets a value that is exported as the whole exports object of this module.
*/ */
overlay[global]
cached cached
DataFlow::Node getABulkExportedNode() { none() } // overridden in subclasses DataFlow::Node getABulkExportedNode() { none() } // overridden in subclasses
@@ -55,6 +61,7 @@ abstract class Module extends TopLevel {
* This can be used to determine which value a default-import will likely refer to, * This can be used to determine which value a default-import will likely refer to,
* as the interaction between different module types is not standardized. * as the interaction between different module types is not standardized.
*/ */
overlay[global]
DataFlow::Node getDefaultOrBulkExport() { DataFlow::Node getDefaultOrBulkExport() {
result = [this.getAnExportedValue("default"), this.getABulkExportedNode()] result = [this.getAnExportedValue("default"), this.getABulkExportedNode()]
} }
@@ -69,6 +76,7 @@ abstract class Module extends TopLevel {
* This predicate is not part of the public API, it is only exposed to allow * This predicate is not part of the public API, it is only exposed to allow
* overriding by subclasses. * overriding by subclasses.
*/ */
overlay[global]
deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this and path.getEnclosingModule() = this and
priority = 0 and priority = 0 and
@@ -90,6 +98,7 @@ abstract class Module extends TopLevel {
* resolves to a folder containing a main module (such as `index.js`), then * resolves to a folder containing a main module (such as `index.js`), then
* that file is the result. * that file is the result.
*/ */
overlay[global]
deprecated File resolve(PathExpr path) { deprecated File resolve(PathExpr path) {
path.getEnclosingModule() = this and path.getEnclosingModule() = this and
( (
@@ -124,6 +133,7 @@ abstract class Import extends AstNode {
abstract Module getEnclosingModule(); abstract Module getEnclosingModule();
/** DEPRECATED. Use `getImportedPathExpr` instead. */ /** DEPRECATED. Use `getImportedPathExpr` instead. */
overlay[global]
deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() } deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() }
/** Gets the (unresolved) path that this import refers to. */ /** Gets the (unresolved) path that this import refers to. */
@@ -138,6 +148,7 @@ abstract class Import extends AstNode {
* Any externs module whose name exactly matches the imported * Any externs module whose name exactly matches the imported
* path is assumed to be a possible target of the import. * path is assumed to be a possible target of the import.
*/ */
overlay[global]
Module resolveExternsImport() { Module resolveExternsImport() {
result.isExterns() and result.getName() = this.getImportedPathString() result.isExterns() and result.getName() = this.getImportedPathString()
} }
@@ -145,16 +156,19 @@ abstract class Import extends AstNode {
/** /**
* Gets the module the path of this import resolves to. * Gets the module the path of this import resolves to.
*/ */
overlay[global]
Module resolveImportedPath() { result.getFile() = this.getImportedFile() } Module resolveImportedPath() { result.getFile() = this.getImportedFile() }
/** /**
* Gets the module the path of this import resolves to. * Gets the module the path of this import resolves to.
*/ */
overlay[global]
File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) } File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) }
/** /**
* DEPRECATED. Use `getImportedModule()` instead. * DEPRECATED. Use `getImportedModule()` instead.
*/ */
overlay[global]
deprecated Module resolveFromTypeScriptSymbol() { deprecated Module resolveFromTypeScriptSymbol() {
exists(CanonicalName symbol | exists(CanonicalName symbol |
ast_node_symbol(this, symbol) and ast_node_symbol(this, symbol) and
@@ -170,6 +184,7 @@ abstract class Import extends AstNode {
* behavior of Node.js imports, which prefer core modules such as `fs` over any * behavior of Node.js imports, which prefer core modules such as `fs` over any
* source module of the same name. * source module of the same name.
*/ */
overlay[global]
cached cached
Module getImportedModule() { Module getImportedModule() {
Stages::Imports::ref() and Stages::Imports::ref() and
@@ -210,6 +225,7 @@ abstract class Import extends AstNode {
* in cases where it would cause ambiguity between named exports and properties * in cases where it would cause ambiguity between named exports and properties
* of a default export. * of a default export.
*/ */
overlay[global]
final DataFlow::Node getImportedModuleNodeStrict() { final DataFlow::Node getImportedModuleNodeStrict() {
result = this.getImportedModuleNode() and result = this.getImportedModuleNode() and
not ( not (

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with NPM module definitions and dependencies. * Provides classes for working with NPM module definitions and dependencies.
*/ */
overlay[local?]
module;
import javascript import javascript
private import NodeModuleResolutionImpl private import NodeModuleResolutionImpl

View File

@@ -17,6 +17,7 @@ private import semmle.javascript.dataflow.internal.DataFlowNode
* process.stdout.write(fs.readFileSync(process.argv[i], 'utf8')); * process.stdout.write(fs.readFileSync(process.argv[i], 'utf8'));
* ``` * ```
*/ */
overlay[local]
class NodeModule extends Module { class NodeModule extends Module {
NodeModule() { NodeModule() {
is_module(this) and is_module(this) and
@@ -36,11 +37,13 @@ class NodeModule extends Module {
* Gets an abstract value representing one or more values that may flow * Gets an abstract value representing one or more values that may flow
* into this module's `module.exports` property. * into this module's `module.exports` property.
*/ */
overlay[global]
pragma[noinline] pragma[noinline]
DefiniteAbstractValue getAModuleExportsValue() { DefiniteAbstractValue getAModuleExportsValue() {
result = this.getAModuleExportsProperty().getAValue() result = this.getAModuleExportsProperty().getAValue()
} }
overlay[global]
pragma[noinline] pragma[noinline]
private AbstractProperty getAModuleExportsProperty() { private AbstractProperty getAModuleExportsProperty() {
result.getBase().(AbstractModuleObject).getModule() = this and result.getBase().(AbstractModuleObject).getModule() = this and
@@ -52,12 +55,14 @@ class NodeModule extends Module {
* For performance this predicate only computes relevant expressions (in `getAModuleExportsCandidate`). * For performance this predicate only computes relevant expressions (in `getAModuleExportsCandidate`).
* So if using this predicate - consider expanding the list of relevant expressions. * So if using this predicate - consider expanding the list of relevant expressions.
*/ */
overlay[global]
DataFlow::AnalyzedNode getAModuleExportsNode() { DataFlow::AnalyzedNode getAModuleExportsNode() {
result = getAModuleExportsCandidate() and result = getAModuleExportsCandidate() and
result.getAValue() = this.getAModuleExportsValue() result.getAValue() = this.getAModuleExportsValue()
} }
/** Gets a symbol exported by this module. */ /** Gets a symbol exported by this module. */
overlay[global]
override string getAnExportedSymbol() { override string getAnExportedSymbol() {
result = super.getAnExportedSymbol() result = super.getAnExportedSymbol()
or or
@@ -70,6 +75,7 @@ class NodeModule extends Module {
) )
} }
overlay[global]
override DataFlow::Node getAnExportedValue(string name) { override DataFlow::Node getAnExportedValue(string name) {
// a property write whose base is `exports` or `module.exports` // a property write whose base is `exports` or `module.exports`
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() | exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
@@ -114,6 +120,7 @@ class NodeModule extends Module {
) )
} }
overlay[global]
override DataFlow::Node getABulkExportedNode() { override DataFlow::Node getABulkExportedNode() {
Stages::Imports::ref() and Stages::Imports::ref() and
exists(DataFlow::PropWrite write | exists(DataFlow::PropWrite write |
@@ -124,6 +131,7 @@ class NodeModule extends Module {
} }
/** Gets a symbol that the module object inherits from its prototypes. */ /** Gets a symbol that the module object inherits from its prototypes. */
overlay[global]
private string getAnImplicitlyExportedSymbol() { private string getAnImplicitlyExportedSymbol() {
exists(ExternalConstructor ec | ec = this.getPrototypeOfExportedExpr() | exists(ExternalConstructor ec | ec = this.getPrototypeOfExportedExpr() |
result = ec.getAMember().getName() result = ec.getAMember().getName()
@@ -136,6 +144,7 @@ class NodeModule extends Module {
} }
/** Gets an externs declaration of the prototype object of a value exported by this module. */ /** Gets an externs declaration of the prototype object of a value exported by this module. */
overlay[global]
private ExternalConstructor getPrototypeOfExportedExpr() { private ExternalConstructor getPrototypeOfExportedExpr() {
exists(AbstractValue exported | exported = this.getAModuleExportsValue() | exists(AbstractValue exported | exported = this.getAModuleExportsValue() |
result instanceof ObjectExternal result instanceof ObjectExternal
@@ -146,6 +155,7 @@ class NodeModule extends Module {
) )
} }
overlay[global]
deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) { deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this and path.getEnclosingModule() = this and
exists(string pathval | pathval = path.getValue() | exists(string pathval | pathval = path.getValue() |
@@ -224,6 +234,7 @@ predicate findNodeModulesFolder(Folder f, Folder nodeModules, int distance) {
/** /**
* A Node.js `require` variable. * A Node.js `require` variable.
*/ */
overlay[local]
private class RequireVariable extends Variable { private class RequireVariable extends Variable {
RequireVariable() { RequireVariable() {
this = any(ModuleScope m).getVariable("require") this = any(ModuleScope m).getVariable("require")
@@ -236,6 +247,7 @@ private class RequireVariable extends Variable {
} }
} }
overlay[local]
private predicate isModuleModule(EarlyStageNode nd) { private predicate isModuleModule(EarlyStageNode nd) {
exists(ImportDeclaration imp | imp.getRawImportPath() = "module" | exists(ImportDeclaration imp | imp.getRawImportPath() = "module" |
nd = TDestructuredModuleImportNode(imp) nd = TDestructuredModuleImportNode(imp)
@@ -249,6 +261,7 @@ private predicate isModuleModule(EarlyStageNode nd) {
) )
} }
overlay[local]
private predicate isCreateRequire(EarlyStageNode nd) { private predicate isCreateRequire(EarlyStageNode nd) {
exists(PropAccess prop | exists(PropAccess prop |
isModuleModule(TValueNode(prop.getBase())) and isModuleModule(TValueNode(prop.getBase())) and
@@ -278,6 +291,7 @@ private predicate isCreateRequire(EarlyStageNode nd) {
/** /**
* Holds if `nd` may refer to `require`, either directly or modulo local data flow. * Holds if `nd` may refer to `require`, either directly or modulo local data flow.
*/ */
overlay[local]
cached cached
private predicate isRequire(EarlyStageNode nd) { private predicate isRequire(EarlyStageNode nd) {
exists(VarAccess access | exists(VarAccess access |
@@ -320,6 +334,7 @@ private predicate isRequire(EarlyStageNode nd) {
* require('fs') * require('fs')
* ``` * ```
*/ */
overlay[local]
class Require extends CallExpr, Import { class Require extends CallExpr, Import {
Require() { isRequire(TValueNode(this.getCallee())) } Require() { isRequire(TValueNode(this.getCallee())) }

View File

@@ -186,11 +186,13 @@ module Promises {
/** /**
* Gets the pseudo-field used to describe resolved values in a promise. * Gets the pseudo-field used to describe resolved values in a promise.
*/ */
overlay[local]
string valueProp() { result = "$PromiseResolveField$" } string valueProp() { result = "$PromiseResolveField$" }
/** /**
* Gets the pseudo-field used to describe rejected values in a promise. * Gets the pseudo-field used to describe rejected values in a promise.
*/ */
overlay[local]
string errorProp() { result = "$PromiseRejectField$" } string errorProp() { result = "$PromiseRejectField$" }
/** A property set containing the pseudo-properites of a promise object. */ /** A property set containing the pseudo-properites of a promise object. */
@@ -236,6 +238,7 @@ module PromiseTypeTracking {
* *
* These type-tracking steps are already included in the default type-tracking steps (through `PreCallGraphStep`). * These type-tracking steps are already included in the default type-tracking steps (through `PreCallGraphStep`).
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
DataFlow::Node promiseStep(DataFlow::Node pred, StepSummary summary) { DataFlow::Node promiseStep(DataFlow::Node pred, StepSummary summary) {
exists(string field | field = Promises::valueProp() | exists(string field | field = Promises::valueProp() |
@@ -254,6 +257,7 @@ module PromiseTypeTracking {
* Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`. * Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another. * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
DataFlow::SourceNode promiseStep( DataFlow::SourceNode promiseStep(
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2 DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2

View File

@@ -4,6 +4,8 @@
* Regular expression literals are represented as an abstract syntax tree of regular expression * Regular expression literals are represented as an abstract syntax tree of regular expression
* terms. * terms.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes
@@ -150,6 +152,7 @@ class RegExpTerm extends Locatable, @regexpterm {
* /[a-z]+/g; // YES - Regexp literals are always used as regexp * /[a-z]+/g; // YES - Regexp literals are always used as regexp
* ``` * ```
*/ */
overlay[global]
predicate isUsedAsRegExp() { predicate isUsedAsRegExp() {
exists(RegExpParent parent | parent = this.getRootTerm().getParent() | exists(RegExpParent parent | parent = this.getRootTerm().getParent() |
parent instanceof RegExpLiteral parent instanceof RegExpLiteral
@@ -964,6 +967,7 @@ class RegExpParseError extends Error, @regexp_parse_error {
/** /**
* Holds if `func` is a method defined on `String.prototype` with name `name`. * Holds if `func` is a method defined on `String.prototype` with name `name`.
*/ */
overlay[global]
private predicate isNativeStringMethod(Function func, string name) { private predicate isNativeStringMethod(Function func, string name) {
exists(ExternalInstanceMemberDecl decl | exists(ExternalInstanceMemberDecl decl |
decl.hasQualifiedName("String", name) and decl.hasQualifiedName("String", name) and
@@ -975,6 +979,7 @@ private predicate isNativeStringMethod(Function func, string name) {
* Holds if `name` is the name of a property on a Match object returned by `String.prototype.match`, * Holds if `name` is the name of a property on a Match object returned by `String.prototype.match`,
* not including array indices. * not including array indices.
*/ */
overlay[global]
private predicate isMatchObjectProperty(string name) { private predicate isMatchObjectProperty(string name) {
any(ExternalInstanceMemberDecl decl).hasQualifiedName("Array", name) any(ExternalInstanceMemberDecl decl).hasQualifiedName("Array", name)
or or
@@ -982,6 +987,7 @@ private predicate isMatchObjectProperty(string name) {
} }
/** Holds if `call` is a call to `match` whose result is used in a way that is incompatible with Match objects. */ /** Holds if `call` is a call to `match` whose result is used in a way that is incompatible with Match objects. */
overlay[global]
private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) { private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
call.getMethodName() = ["match", "matchAll"] and call.getMethodName() = ["match", "matchAll"] and
call.getNumArgument() = 1 and call.getNumArgument() = 1 and
@@ -1006,10 +1012,11 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
/** /**
* Holds if `value` is used in a way that suggests it returns a number. * Holds if `value` is used in a way that suggests it returns a number.
*/ */
overlay[global]
pragma[inline] pragma[inline]
private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) { private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
any(Comparison compare) any(Comparison compare)
.hasOperands(value.getALocalUse().asExpr(), any(Expr e | e.analyze().getAType() = TTNumber())) .hasOperands(value.getALocalUse().asExpr(), any(Expr e | canBeNumber(e.analyze())))
or or
value.flowsToExpr(any(ArithmeticExpr e).getAnOperand()) value.flowsToExpr(any(ArithmeticExpr e).getAnOperand())
or or
@@ -1024,20 +1031,31 @@ private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
) )
} }
bindingset[node]
overlay[global]
pragma[inline_late]
private predicate canBeString(DataFlow::AnalyzedNode node) { node.getAType() = TTString() }
bindingset[node]
overlay[global]
pragma[inline_late]
private predicate canBeNumber(DataFlow::AnalyzedNode node) { node.getAType() = TTNumber() }
/** /**
* Holds if `source` may be interpreted as a regular expression. * Holds if `source` may be interpreted as a regular expression.
*/ */
overlay[global]
cached cached
predicate isInterpretedAsRegExp(DataFlow::Node source) { predicate isInterpretedAsRegExp(DataFlow::Node source) {
Stages::Taint::ref() and Stages::Taint::ref() and
source.analyze().getAType() = TTString() and canBeString(source) and
( (
// The first argument to an invocation of `RegExp` (with or without `new`). // The first argument to an invocation of `RegExp` (with or without `new`).
source = DataFlow::globalVarRef("RegExp").getAnInvocation().getArgument(0) source = DataFlow::globalVarRef("RegExp").getAnInvocation().getArgument(0)
or or
// The argument of a call that coerces the argument to a regular expression. // The argument of a call that coerces the argument to a regular expression.
exists(DataFlow::MethodCallNode mce, string methodName | exists(DataFlow::MethodCallNode mce, string methodName |
mce.getReceiver().analyze().getAType() = TTString() and canBeString(mce.getReceiver()) and
mce.getMethodName() = methodName and mce.getMethodName() = methodName and
not exists(Function func | func = mce.getACallee() | not exists(Function func | func = mce.getACallee() |
not isNativeStringMethod(func, methodName) not isNativeStringMethod(func, methodName)
@@ -1073,6 +1091,7 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
* Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted * Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
* as a part of a regular expression. * as a part of a regular expression.
*/ */
overlay[global]
private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker t) { private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker t) {
t.start() and t.start() and
re = result and re = result and
@@ -1090,6 +1109,7 @@ private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker
* Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted * Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
* as a part of a regular expression. * as a part of a regular expression.
*/ */
overlay[global]
private DataFlow::Node regExpSource(DataFlow::Node re) { private DataFlow::Node regExpSource(DataFlow::Node re) {
result = regExpSource(re, DataFlow::TypeBackTracker::end()) result = regExpSource(re, DataFlow::TypeBackTracker::end())
} }
@@ -1098,6 +1118,7 @@ private DataFlow::Node regExpSource(DataFlow::Node re) {
* A node whose value may flow to a position where it is interpreted * A node whose value may flow to a position where it is interpreted
* as a part of a regular expression. * as a part of a regular expression.
*/ */
overlay[global]
abstract class RegExpPatternSource extends DataFlow::Node { abstract class RegExpPatternSource extends DataFlow::Node {
/** /**
* Gets a node where the pattern of this node is parsed as a part of * Gets a node where the pattern of this node is parsed as a part of
@@ -1126,6 +1147,7 @@ abstract class RegExpPatternSource extends DataFlow::Node {
/** /**
* A regular expression literal, viewed as the pattern source for itself. * A regular expression literal, viewed as the pattern source for itself.
*/ */
overlay[global]
private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::ValueNode { private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::ValueNode {
override RegExpLiteral astNode; override RegExpLiteral astNode;
@@ -1145,6 +1167,7 @@ private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::
* A node whose string value may flow to a position where it is interpreted * A node whose string value may flow to a position where it is interpreted
* as a part of a regular expression. * as a part of a regular expression.
*/ */
overlay[global]
private class StringRegExpPatternSource extends RegExpPatternSource { private class StringRegExpPatternSource extends RegExpPatternSource {
DataFlow::Node parse; DataFlow::Node parse;
@@ -1169,6 +1192,7 @@ private class StringRegExpPatternSource extends RegExpPatternSource {
* A node whose string value may flow to a position where it is interpreted * A node whose string value may flow to a position where it is interpreted
* as a part of a regular expression. * as a part of a regular expression.
*/ */
overlay[global]
private class StringConcatRegExpPatternSource extends RegExpPatternSource { private class StringConcatRegExpPatternSource extends RegExpPatternSource {
DataFlow::Node parse; DataFlow::Node parse;
@@ -1331,6 +1355,7 @@ module RegExp {
/** /**
* Gets the AST of a regular expression object that can flow to `node`. * Gets the AST of a regular expression object that can flow to `node`.
*/ */
overlay[global]
RegExpTerm getRegExpObjectFromNode(DataFlow::Node node) { RegExpTerm getRegExpObjectFromNode(DataFlow::Node node) {
exists(DataFlow::RegExpCreationNode regexp | exists(DataFlow::RegExpCreationNode regexp |
regexp.getAReference().flowsTo(node) and regexp.getAReference().flowsTo(node) and
@@ -1342,6 +1367,7 @@ module RegExp {
* Gets the AST of a regular expression that can flow to `node`, * Gets the AST of a regular expression that can flow to `node`,
* including `RegExp` objects as well as strings interpreted as regular expressions. * including `RegExp` objects as well as strings interpreted as regular expressions.
*/ */
overlay[global]
RegExpTerm getRegExpFromNode(DataFlow::Node node) { RegExpTerm getRegExpFromNode(DataFlow::Node node) {
result = getRegExpObjectFromNode(node) result = getRegExpObjectFromNode(node)
or or

View File

@@ -73,6 +73,8 @@
* expression in `k` induces a re-capture of `x` to reflect the fact that `x` * expression in `k` induces a re-capture of `x` to reflect the fact that `x`
* is incremented between the two `console.log` calls. * is incremented between the two `console.log` calls.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.dataflow.Refinements private import semmle.javascript.dataflow.Refinements

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with statements. */ /** Provides classes for working with statements. */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with ECMAScript 2015-style template expressions. */ /** Provides classes for working with ECMAScript 2015-style template expressions. */
overlay[local]
module;
import javascript import javascript
@@ -58,6 +60,7 @@ class TemplateLiteral extends Expr, @template_literal {
*/ */
int getNumElement() { result = count(this.getAnElement()) } int getNumElement() { result = count(this.getAnElement()) }
overlay[global]
override predicate isImpure() { this.getAnElement().isImpure() } override predicate isImpure() { this.getAnElement().isImpure() }
override string getAPrimaryQlClass() { result = "TemplateLiteral" } override string getAPrimaryQlClass() { result = "TemplateLiteral" }

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for working with the token-based representation of JavaScript programs. * Provides classes for working with the token-based representation of JavaScript programs.
*/ */
overlay[local]
module;
import javascript import javascript

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes for reasoning about type annotations independently of dialect. * Provides classes for reasoning about type annotations independently of dialect.
*/ */
overlay[local]
module;
import javascript import javascript
private import internal.StmtContainers private import internal.StmtContainers
@@ -18,6 +20,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* This can be used to map a type name to the class/interface it refers to, or * This can be used to map a type name to the class/interface it refers to, or
* associate it with a named type coming from an dependency. * associate it with a named type coming from an dependency.
*/ */
overlay[global]
TypeNameBindingNode getTypeBinding() { result = this } TypeNameBindingNode getTypeBinding() { result = this }
/** Holds if this is the `any` type. */ /** Holds if this is the `any` type. */
@@ -90,6 +93,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* *
* Holds if this is a reference to the type with qualified name `globalName` relative to the global scope. * Holds if this is a reference to the type with qualified name `globalName` relative to the global scope.
*/ */
overlay[global]
deprecated predicate hasQualifiedName(string globalName) { deprecated predicate hasQualifiedName(string globalName) {
UnderlyingTypes::nodeHasUnderlyingType(this, globalName) UnderlyingTypes::nodeHasUnderlyingType(this, globalName)
} }
@@ -99,6 +103,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* *
* Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`. * Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`.
*/ */
overlay[global]
deprecated predicate hasQualifiedName(string moduleName, string exportedName) { deprecated predicate hasQualifiedName(string moduleName, string exportedName) {
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName) UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName)
} }
@@ -107,6 +112,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* Holds if this is a reference to the type with qualified name `globalName` relative to the global scope, * Holds if this is a reference to the type with qualified name `globalName` relative to the global scope,
* or is declared as a subtype thereof, or is a union or intersection containing such a type. * or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/ */
overlay[global]
final predicate hasUnderlyingType(string globalName) { final predicate hasUnderlyingType(string globalName) {
UnderlyingTypes::nodeHasUnderlyingType(this, globalName) UnderlyingTypes::nodeHasUnderlyingType(this, globalName)
} }
@@ -115,6 +121,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`, * Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`,
* or is declared as a subtype thereof, or is a union or intersection containing such a type. * or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/ */
overlay[global]
final predicate hasUnderlyingType(string moduleName, string exportedName) { final predicate hasUnderlyingType(string moduleName, string exportedName) {
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName) UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName)
} }
@@ -135,6 +142,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* *
* Note that this has no result for JSDoc type annotations. * Note that this has no result for JSDoc type annotations.
*/ */
overlay[global]
deprecated Type getType() { none() } deprecated Type getType() { none() }
/** /**
@@ -142,5 +150,6 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
* *
* This unfolds nullability modifiers and generic type applications. * This unfolds nullability modifiers and generic type applications.
*/ */
overlay[global]
final DataFlow::ClassNode getClass() { UnderlyingTypes::nodeHasUnderlyingClassType(this, result) } final DataFlow::ClassNode getClass() { UnderlyingTypes::nodeHasUnderlyingClassType(this, result) }
} }

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
import javascript import javascript
/** /**
@@ -31,6 +34,7 @@ class NamespaceDefinition extends Stmt, @namespace_definition, AST::ValueNode {
/** /**
* Gets the canonical name of the namespace being defined. * Gets the canonical name of the namespace being defined.
*/ */
overlay[global]
deprecated Namespace getNamespace() { result.getADefinition() = this } deprecated Namespace getNamespace() { result.getADefinition() = this }
} }
@@ -111,11 +115,13 @@ class TypeDefinition extends AstNode, @type_definition {
/** /**
* Gets the canonical name of the type being defined. * Gets the canonical name of the type being defined.
*/ */
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this } deprecated TypeName getTypeName() { result.getADefinition() = this }
/** /**
* Gets the type defined by this declaration. * Gets the type defined by this declaration.
*/ */
overlay[global]
deprecated Type getType() { ast_node_type(this.getIdentifier(), result) } deprecated Type getType() { ast_node_type(this.getIdentifier(), result) }
override string getAPrimaryQlClass() { result = "TypeDefinition" } override string getAPrimaryQlClass() { result = "TypeDefinition" }
@@ -221,6 +227,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference {
} }
/** A literal path expression appearing in an external module reference. */ /** A literal path expression appearing in an external module reference. */
overlay[global]
deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString { deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString {
LiteralExternalModulePath() { LiteralExternalModulePath() {
exists(ExternalModuleReference emr | this.getParentExpr*() = emr.getExpression()) exists(ExternalModuleReference emr | this.getParentExpr*() = emr.getExpression())
@@ -268,6 +275,7 @@ class TypeAliasDeclaration extends @type_alias_declaration, TypeParameterized, S
/** /**
* Gets the canonical name of the type being defined. * Gets the canonical name of the type being defined.
*/ */
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this } deprecated TypeName getTypeName() { result.getADefinition() = this }
override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" } override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" }
@@ -548,6 +556,7 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName {
/** /**
* Gets the canonical name of the namespace referenced by this name. * Gets the canonical name of the namespace referenced by this name.
*/ */
overlay[global]
deprecated Namespace getNamespace() { result = this.getADeclaration().getNamespace() } deprecated Namespace getNamespace() { result = this.getADeclaration().getNamespace() }
override DeclarationSpace getDeclarationSpace() { result = "namespace" } override DeclarationSpace getDeclarationSpace() { result = "namespace" }
@@ -568,6 +577,7 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation {
* Has no result if this occurs in a TypeScript file that was extracted * Has no result if this occurs in a TypeScript file that was extracted
* without type information. * without type information.
*/ */
overlay[global]
deprecated override Type getType() { ast_node_type(this, result) } deprecated override Type getType() { ast_node_type(this, result) }
override Stmt getEnclosingStmt() { result = ExprOrType.super.getEnclosingStmt() } override Stmt getEnclosingStmt() { result = ExprOrType.super.getEnclosingStmt() }
@@ -692,6 +702,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
/** /**
* Gets the canonical name of the type being accessed. * Gets the canonical name of the type being accessed.
*/ */
overlay[global]
deprecated TypeName getTypeName() { ast_node_symbol(this, result) } deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "TypeAccess" } override string getAPrimaryQlClass() { result = "TypeAccess" }
@@ -1379,6 +1390,7 @@ class LocalNamespaceDecl extends VarDecl, NamespaceRef {
/** /**
* Gets the canonical name of the namespace being defined or aliased by this name. * Gets the canonical name of the namespace being defined or aliased by this name.
*/ */
overlay[global]
deprecated Namespace getNamespace() { ast_node_symbol(this, result) } deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
} }
@@ -1397,6 +1409,7 @@ class NamespaceAccess extends TypeExpr, NamespaceRef, @namespace_access {
/** /**
* Gets the canonical name of the namespace being accessed. * Gets the canonical name of the namespace being accessed.
*/ */
overlay[global]
deprecated Namespace getNamespace() { ast_node_symbol(this, result) } deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "NamespaceAccess" } override string getAPrimaryQlClass() { result = "NamespaceAccess" }
@@ -1506,6 +1519,7 @@ class EnumDeclaration extends NamespaceDefinition, @enum_declaration, AST::Value
/** /**
* Gets the canonical name of the type being defined. * Gets the canonical name of the type being defined.
*/ */
overlay[global]
deprecated TypeName getTypeName() { ast_node_symbol(this, result) } deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
/** /**
@@ -1594,6 +1608,7 @@ class EnumMember extends AstNode, @enum_member {
/** /**
* Gets the canonical name of the type defined by this enum member. * Gets the canonical name of the type defined by this enum member.
*/ */
overlay[global]
deprecated TypeName getTypeName() { ast_node_symbol(this, result) } deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
override string getAPrimaryQlClass() { result = "EnumMember" } override string getAPrimaryQlClass() { result = "EnumMember" }
@@ -1762,6 +1777,7 @@ class TypeRootFolder extends Folder {
/** /**
* Gets the priority with which this type root folder should be used from within the given search root. * Gets the priority with which this type root folder should be used from within the given search root.
*/ */
overlay[global]
int getSearchPriority(Folder searchRoot) { int getSearchPriority(Folder searchRoot) {
findNodeModulesFolder(searchRoot, this.getNodeModulesFolder(), result) findNodeModulesFolder(searchRoot, this.getNodeModulesFolder(), result)
} }
@@ -1780,6 +1796,7 @@ class TypeRootFolder extends Folder {
* For instance, there may be many AST nodes representing different uses of the * For instance, there may be many AST nodes representing different uses of the
* `number` keyword, but there only exists one `number` type. * `number` keyword, but there only exists one `number` type.
*/ */
overlay[global]
deprecated class Type extends @type { deprecated class Type extends @type {
/** /**
* Gets a string representation of this type. * Gets a string representation of this type.
@@ -1984,6 +2001,7 @@ deprecated class Type extends @type {
* *
* A union type or intersection type, such as `string | number` or `T & U`. * A union type or intersection type, such as `string | number` or `T & U`.
*/ */
overlay[global]
deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_type { deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_type {
/** /**
* Gets the `i`th member of this union or intersection, starting at 0. * Gets the `i`th member of this union or intersection, starting at 0.
@@ -2012,6 +2030,7 @@ deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_ty
* Note that the `boolean` type is represented as the union `true | false`, * Note that the `boolean` type is represented as the union `true | false`,
* but is still displayed as `boolean` in string representations. * but is still displayed as `boolean` in string representations.
*/ */
overlay[global]
deprecated class UnionType extends UnionOrIntersectionType, @union_type { } deprecated class UnionType extends UnionOrIntersectionType, @union_type { }
/** /**
@@ -2022,6 +2041,7 @@ deprecated class UnionType extends UnionOrIntersectionType, @union_type { }
* *
* An intersection type, such as `T & {x: number}`. * An intersection type, such as `T & {x: number}`.
*/ */
overlay[global]
deprecated class IntersectionType extends UnionOrIntersectionType, @intersection_type { } deprecated class IntersectionType extends UnionOrIntersectionType, @intersection_type { }
/** /**
@@ -2040,6 +2060,7 @@ deprecated class IntersectionType extends UnionOrIntersectionType, @intersection
* Foreign array-like objects such as `HTMLCollection` are not normal JavaScript arrays, * Foreign array-like objects such as `HTMLCollection` are not normal JavaScript arrays,
* and their corresponding types are not considered array types either. * and their corresponding types are not considered array types either.
*/ */
overlay[global]
deprecated class ArrayType extends Type { deprecated class ArrayType extends Type {
ArrayType() { ArrayType() {
this instanceof @tuple_type or this instanceof @tuple_type or
@@ -2061,6 +2082,7 @@ deprecated class ArrayType extends Type {
* *
* An array type such as `Array<string>`, or equivalently, `string[]`. * An array type such as `Array<string>`, or equivalently, `string[]`.
*/ */
overlay[global]
deprecated class PlainArrayType extends ArrayType, TypeReference { deprecated class PlainArrayType extends ArrayType, TypeReference {
PlainArrayType() { this.hasQualifiedName("Array") } PlainArrayType() { this.hasQualifiedName("Array") }
@@ -2075,6 +2097,7 @@ deprecated class PlainArrayType extends ArrayType, TypeReference {
* *
* A read-only array type such as `ReadonlyArray<string>`. * A read-only array type such as `ReadonlyArray<string>`.
*/ */
overlay[global]
deprecated class ReadonlyArrayType extends ArrayType, TypeReference { deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
ReadonlyArrayType() { this.hasQualifiedName("ReadonlyArray") } ReadonlyArrayType() { this.hasQualifiedName("ReadonlyArray") }
} }
@@ -2087,6 +2110,7 @@ deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
* *
* A tuple type, such as `[number, string]`. * A tuple type, such as `[number, string]`.
*/ */
overlay[global]
deprecated class TupleType extends ArrayType, @tuple_type { deprecated class TupleType extends ArrayType, @tuple_type {
/** /**
* Gets the `i`th member of this tuple type, starting at 0. * Gets the `i`th member of this tuple type, starting at 0.
@@ -2148,6 +2172,7 @@ deprecated class TupleType extends ArrayType, @tuple_type {
* *
* The predefined `any` type. * The predefined `any` type.
*/ */
overlay[global]
deprecated class AnyType extends Type, @any_type { } deprecated class AnyType extends Type, @any_type { }
/** /**
@@ -2158,6 +2183,7 @@ deprecated class AnyType extends Type, @any_type { }
* *
* The predefined `unknown` type. * The predefined `unknown` type.
*/ */
overlay[global]
deprecated class UnknownType extends Type, @unknown_type { } deprecated class UnknownType extends Type, @unknown_type { }
/** /**
@@ -2168,6 +2194,7 @@ deprecated class UnknownType extends Type, @unknown_type { }
* *
* The predefined `string` type. * The predefined `string` type.
*/ */
overlay[global]
deprecated class StringType extends Type, @string_type { } deprecated class StringType extends Type, @string_type { }
/** /**
@@ -2178,6 +2205,7 @@ deprecated class StringType extends Type, @string_type { }
* *
* The predefined `number` type. * The predefined `number` type.
*/ */
overlay[global]
deprecated class NumberType extends Type, @number_type { } deprecated class NumberType extends Type, @number_type { }
/** /**
@@ -2188,6 +2216,7 @@ deprecated class NumberType extends Type, @number_type { }
* *
* The predefined `bigint` type. * The predefined `bigint` type.
*/ */
overlay[global]
deprecated class BigIntType extends Type, @bigint_type { } deprecated class BigIntType extends Type, @bigint_type { }
/** /**
@@ -2198,6 +2227,7 @@ deprecated class BigIntType extends Type, @bigint_type { }
* *
* A boolean, number, or string literal type. * A boolean, number, or string literal type.
*/ */
overlay[global]
deprecated class LiteralType extends Type, @literal_type { deprecated class LiteralType extends Type, @literal_type {
/** /**
* Gets the string value of this literal. * Gets the string value of this literal.
@@ -2213,6 +2243,7 @@ deprecated class LiteralType extends Type, @literal_type {
* *
* The boolean literal type `true` or `false`. * The boolean literal type `true` or `false`.
*/ */
overlay[global]
deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type { deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type {
/** /**
* Gets the boolean value represented by this type. * Gets the boolean value represented by this type.
@@ -2227,6 +2258,7 @@ deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type {
/** /**
* A number literal as a static type. * A number literal as a static type.
*/ */
overlay[global]
deprecated class NumberLiteralType extends LiteralType, @number_literal_type { deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
override string getStringValue() { type_literal_value(this, result) } override string getStringValue() { type_literal_value(this, result) }
@@ -2249,6 +2281,7 @@ deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
* *
* A string literal as a static type. * A string literal as a static type.
*/ */
overlay[global]
deprecated class StringLiteralType extends LiteralType, @string_literal_type { deprecated class StringLiteralType extends LiteralType, @string_literal_type {
override string getStringValue() { type_literal_value(this, result) } override string getStringValue() { type_literal_value(this, result) }
} }
@@ -2261,6 +2294,7 @@ deprecated class StringLiteralType extends LiteralType, @string_literal_type {
* *
* A bigint literal as a static type. * A bigint literal as a static type.
*/ */
overlay[global]
deprecated class BigIntLiteralType extends LiteralType { deprecated class BigIntLiteralType extends LiteralType {
override string getStringValue() { type_literal_value(this, result) } override string getStringValue() { type_literal_value(this, result) }
@@ -2283,6 +2317,7 @@ deprecated class BigIntLiteralType extends LiteralType {
* *
* The `boolean` type, internally represented as the union type `true | false`. * The `boolean` type, internally represented as the union type `true | false`.
*/ */
overlay[global]
deprecated class BooleanType extends UnionType { deprecated class BooleanType extends UnionType {
BooleanType() { BooleanType() {
this.getAnElementType() instanceof @true_type and this.getAnElementType() instanceof @true_type and
@@ -2299,6 +2334,7 @@ deprecated class BooleanType extends UnionType {
* *
* The `string` type or a string literal type. * The `string` type or a string literal type.
*/ */
overlay[global]
deprecated class StringLikeType extends Type { deprecated class StringLikeType extends Type {
StringLikeType() { StringLikeType() {
this instanceof StringType or this instanceof StringType or
@@ -2314,6 +2350,7 @@ deprecated class StringLikeType extends Type {
* *
* The `number` type or a number literal type. * The `number` type or a number literal type.
*/ */
overlay[global]
deprecated class NumberLikeType extends Type { deprecated class NumberLikeType extends Type {
NumberLikeType() { NumberLikeType() {
this instanceof NumberType or this instanceof NumberType or
@@ -2329,6 +2366,7 @@ deprecated class NumberLikeType extends Type {
* *
* The `boolean`, `true,` or `false` type. * The `boolean`, `true,` or `false` type.
*/ */
overlay[global]
deprecated class BooleanLikeType extends Type { deprecated class BooleanLikeType extends Type {
BooleanLikeType() { BooleanLikeType() {
this instanceof BooleanType or this instanceof BooleanType or
@@ -2344,6 +2382,7 @@ deprecated class BooleanLikeType extends Type {
* *
* The `void` type. * The `void` type.
*/ */
overlay[global]
deprecated class VoidType extends Type, @void_type { } deprecated class VoidType extends Type, @void_type { }
/** /**
@@ -2354,6 +2393,7 @@ deprecated class VoidType extends Type, @void_type { }
* *
* The `undefined` type. * The `undefined` type.
*/ */
overlay[global]
deprecated class UndefinedType extends Type, @undefined_type { } deprecated class UndefinedType extends Type, @undefined_type { }
/** /**
@@ -2364,6 +2404,7 @@ deprecated class UndefinedType extends Type, @undefined_type { }
* *
* The `null` type. * The `null` type.
*/ */
overlay[global]
deprecated class NullType extends Type, @null_type { } deprecated class NullType extends Type, @null_type { }
/** /**
@@ -2374,6 +2415,7 @@ deprecated class NullType extends Type, @null_type { }
* *
* The `never` type. * The `never` type.
*/ */
overlay[global]
deprecated class NeverType extends Type, @never_type { } deprecated class NeverType extends Type, @never_type { }
/** /**
@@ -2384,6 +2426,7 @@ deprecated class NeverType extends Type, @never_type { }
* *
* The `symbol` type or a specific `unique symbol` type. * The `symbol` type or a specific `unique symbol` type.
*/ */
overlay[global]
deprecated class SymbolType extends Type, @symbol_type { } deprecated class SymbolType extends Type, @symbol_type { }
/** /**
@@ -2394,6 +2437,7 @@ deprecated class SymbolType extends Type, @symbol_type { }
* *
* The `symbol` type. * The `symbol` type.
*/ */
overlay[global]
deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { } deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
/** /**
@@ -2404,6 +2448,7 @@ deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
* *
* A `unique symbol` type. * A `unique symbol` type.
*/ */
overlay[global]
deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type { deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
/** /**
* Gets the canonical name of the variable exposing the symbol. * Gets the canonical name of the variable exposing the symbol.
@@ -2438,6 +2483,7 @@ deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
* *
* The `object` type. * The `object` type.
*/ */
overlay[global]
deprecated class ObjectKeywordType extends Type, @objectkeyword_type { } deprecated class ObjectKeywordType extends Type, @objectkeyword_type { }
/** /**
@@ -2448,6 +2494,7 @@ deprecated class ObjectKeywordType extends Type, @objectkeyword_type { }
* *
* A type that refers to a class, interface, enum, or enum member. * A type that refers to a class, interface, enum, or enum member.
*/ */
overlay[global]
deprecated class TypeReference extends Type, @type_reference { deprecated class TypeReference extends Type, @type_reference {
/** /**
* Gets the canonical name of the type being referenced. * Gets the canonical name of the type being referenced.
@@ -2506,6 +2553,7 @@ deprecated class TypeReference extends Type, @type_reference {
* *
* A type that refers to a class, possibly with type arguments. * A type that refers to a class, possibly with type arguments.
*/ */
overlay[global]
deprecated class ClassType extends TypeReference { deprecated class ClassType extends TypeReference {
ClassDefinition declaration; ClassDefinition declaration;
@@ -2525,6 +2573,7 @@ deprecated class ClassType extends TypeReference {
* *
* A type that refers to an interface, possibly with type arguents. * A type that refers to an interface, possibly with type arguents.
*/ */
overlay[global]
deprecated class InterfaceType extends TypeReference { deprecated class InterfaceType extends TypeReference {
InterfaceDeclaration declaration; InterfaceDeclaration declaration;
@@ -2544,6 +2593,7 @@ deprecated class InterfaceType extends TypeReference {
* *
* A type that refers to an enum. * A type that refers to an enum.
*/ */
overlay[global]
deprecated class EnumType extends TypeReference { deprecated class EnumType extends TypeReference {
EnumDeclaration declaration; EnumDeclaration declaration;
@@ -2563,6 +2613,7 @@ deprecated class EnumType extends TypeReference {
* *
* A type that refers to the value of an enum member. * A type that refers to the value of an enum member.
*/ */
overlay[global]
deprecated class EnumLiteralType extends TypeReference { deprecated class EnumLiteralType extends TypeReference {
EnumMember declaration; EnumMember declaration;
@@ -2582,6 +2633,7 @@ deprecated class EnumLiteralType extends TypeReference {
* *
* A type that refers to a type alias. * A type that refers to a type alias.
*/ */
overlay[global]
deprecated class TypeAliasReference extends TypeReference { deprecated class TypeAliasReference extends TypeReference {
TypeAliasReference() { type_alias(this, _) } TypeAliasReference() { type_alias(this, _) }
@@ -2601,6 +2653,7 @@ deprecated class TypeAliasReference extends TypeReference {
* *
* An anonymous interface type, such as `{ x: number }`. * An anonymous interface type, such as `{ x: number }`.
*/ */
overlay[global]
deprecated class AnonymousInterfaceType extends Type, @object_type { } deprecated class AnonymousInterfaceType extends Type, @object_type { }
/** /**
@@ -2611,6 +2664,7 @@ deprecated class AnonymousInterfaceType extends Type, @object_type { }
* *
* A type that refers to a type variable. * A type that refers to a type variable.
*/ */
overlay[global]
deprecated class TypeVariableType extends Type, @typevariable_type { deprecated class TypeVariableType extends Type, @typevariable_type {
/** /**
* Gets a syntactic declaration of this type variable. * Gets a syntactic declaration of this type variable.
@@ -2656,6 +2710,7 @@ deprecated class TypeVariableType extends Type, @typevariable_type {
* *
* A type that refers to a type variable declared on a class, interface or function. * A type that refers to a type variable declared on a class, interface or function.
*/ */
overlay[global]
deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type { deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type {
override TypeName getHostType() { result = this.getCanonicalName().getParent() } override TypeName getHostType() { result = this.getCanonicalName().getParent() }
@@ -2681,6 +2736,7 @@ deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_
* - `<T>(x: T) => T` * - `<T>(x: T) => T`
* - `<S, T>(x: S, y: T) => T`. * - `<S, T>(x: S, y: T) => T`.
*/ */
overlay[global]
deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type { deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type {
override string getName() { override string getName() {
types(this, _, result) // The toString value contains the name. types(this, _, result) // The toString value contains the name.
@@ -2703,6 +2759,7 @@ deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type
* } * }
* ``` * ```
*/ */
overlay[global]
deprecated class ThisType extends Type, @this_type { deprecated class ThisType extends Type, @this_type {
/** /**
* Gets the type containing the `this` type. * Gets the type containing the `this` type.
@@ -2721,6 +2778,7 @@ deprecated class ThisType extends Type, @this_type {
* The type of a named value, `typeof X`, typically denoting the type of * The type of a named value, `typeof X`, typically denoting the type of
* a class constructor, namespace object, enum object, or module object. * a class constructor, namespace object, enum object, or module object.
*/ */
overlay[global]
deprecated class TypeofType extends Type, @typeof_type { deprecated class TypeofType extends Type, @typeof_type {
/** /**
* Gets the canonical name of the named value. * Gets the canonical name of the named value.
@@ -2801,6 +2859,7 @@ module SignatureKind {
* *
* A function or constructor signature in a TypeScript type. * A function or constructor signature in a TypeScript type.
*/ */
overlay[global]
deprecated class CallSignatureType extends @signature_type { deprecated class CallSignatureType extends @signature_type {
/** /**
* Gets a value indicating if this is a function or constructor signature. * Gets a value indicating if this is a function or constructor signature.
@@ -2955,6 +3014,7 @@ deprecated class CallSignatureType extends @signature_type {
* *
* A function call signature in a type, that is, a signature without the `new` keyword. * A function call signature in a type, that is, a signature without the `new` keyword.
*/ */
overlay[global]
deprecated class FunctionCallSignatureType extends CallSignatureType, @function_signature_type { } deprecated class FunctionCallSignatureType extends CallSignatureType, @function_signature_type { }
/** /**
@@ -2965,6 +3025,7 @@ deprecated class FunctionCallSignatureType extends CallSignatureType, @function_
* *
* A constructor call signature in a type, that is, a signature with the `new` keyword. * A constructor call signature in a type, that is, a signature with the `new` keyword.
*/ */
overlay[global]
deprecated class ConstructorCallSignatureType extends CallSignatureType, @constructor_signature_type deprecated class ConstructorCallSignatureType extends CallSignatureType, @constructor_signature_type
{ } { }
@@ -2976,6 +3037,7 @@ deprecated class ConstructorCallSignatureType extends CallSignatureType, @constr
* - It has one type parameter, say, `T` * - It has one type parameter, say, `T`
* - It has a `then` method whose first argument is a callback that takes a `T` as argument. * - It has a `then` method whose first argument is a callback that takes a `T` as argument.
*/ */
overlay[global]
deprecated private class PromiseTypeName extends TypeName { deprecated private class PromiseTypeName extends TypeName {
PromiseTypeName() { PromiseTypeName() {
// The name must suggest it is a promise. // The name must suggest it is a promise.
@@ -3005,6 +3067,7 @@ deprecated private class PromiseTypeName extends TypeName {
* This includes types whose name and `then` method signature suggest it is a promise, * This includes types whose name and `then` method signature suggest it is a promise,
* such as `PromiseLike<T>` and `Thenable<T>`. * such as `PromiseLike<T>` and `Thenable<T>`.
*/ */
overlay[global]
deprecated class PromiseType extends TypeReference { deprecated class PromiseType extends TypeReference {
PromiseType() { PromiseType() {
this.getNumTypeArgument() = 1 and this.getNumTypeArgument() = 1 and

View File

@@ -1,4 +1,6 @@
/** Provides classes for modeling program variables. */ /** Provides classes for modeling program variables. */
overlay[local]
module;
import javascript import javascript
@@ -62,6 +64,7 @@ class LocalScope extends Scope {
*/ */
class ModuleScope extends Scope, @module_scope { class ModuleScope extends Scope, @module_scope {
/** Gets the module that induces this scope. */ /** Gets the module that induces this scope. */
overlay[global]
Module getModule() { result = this.getScopeElement() } Module getModule() { result = this.getScopeElement() }
override string toString() { result = "module scope" } override string toString() { result = "module scope" }
@@ -256,6 +259,7 @@ class VarRef extends @varref, Identifier, BindingPattern, LexicalRef {
override VarRef getABindingVarRef() { result = this } override VarRef getABindingVarRef() { result = this }
overlay[global]
override predicate isImpure() { none() } override predicate isImpure() { none() }
override Expr getUnderlyingReference() { result = this } override Expr getUnderlyingReference() { result = this }
@@ -543,6 +547,7 @@ class ArrayPattern extends DestructuringPattern, @array_pattern {
/** Holds if this array pattern has an omitted element. */ /** Holds if this array pattern has an omitted element. */
predicate hasOmittedElement() { this.elementIsOmitted(_) } predicate hasOmittedElement() { this.elementIsOmitted(_) }
overlay[global]
override predicate isImpure() { this.getAnElement().isImpure() } override predicate isImpure() { this.getAnElement().isImpure() }
override VarRef getABindingVarRef() { override VarRef getABindingVarRef() {
@@ -583,6 +588,7 @@ class ObjectPattern extends DestructuringPattern, @object_pattern {
/** Gets the rest property pattern of this object pattern, if any. */ /** Gets the rest property pattern of this object pattern, if any. */
override Expr getRest() { result = this.getChildExpr(-1) } override Expr getRest() { result = this.getChildExpr(-1) }
overlay[global]
override predicate isImpure() { this.getAPropertyPattern().isImpure() } override predicate isImpure() { this.getAPropertyPattern().isImpure() }
override VarRef getABindingVarRef() { override VarRef getABindingVarRef() {
@@ -640,6 +646,7 @@ class PropertyPattern extends @property, AstNode {
ObjectPattern getObjectPattern() { properties(this, result, _, _, _) } ObjectPattern getObjectPattern() { properties(this, result, _, _, _) }
/** Holds if this pattern is impure, that is, if its evaluation could have side effects. */ /** Holds if this pattern is impure, that is, if its evaluation could have side effects. */
overlay[global]
predicate isImpure() { predicate isImpure() {
this.isComputed() and this.getNameExpr().isImpure() this.isComputed() and this.getNameExpr().isImpure()
or or
@@ -844,6 +851,7 @@ class SimpleParameter extends Parameter, VarDecl {
* Gets a use of this parameter that refers to its initial value as * Gets a use of this parameter that refers to its initial value as
* passed in from the caller. * passed in from the caller.
*/ */
overlay[global]
VarUse getAnInitialUse() { VarUse getAnInitialUse() {
exists(SsaDefinition ssa | exists(SsaDefinition ssa |
ssa.getAContributingVarDef() = this and ssa.getAContributingVarDef() = this and

View File

@@ -1,6 +1,8 @@
/** /**
* Provides classes and predicates for working with XML files and their content. * Provides classes and predicates for working with XML files and their content.
*/ */
overlay[local]
module;
import semmle.files.FileSystem import semmle.files.FileSystem
private import codeql.xml.Xml private import codeql.xml.Xml

View File

@@ -4,6 +4,8 @@
* YAML documents are represented as abstract syntax trees whose nodes * YAML documents are represented as abstract syntax trees whose nodes
* are either YAML values or alias nodes referring to another YAML value. * are either YAML values or alias nodes referring to another YAML value.
*/ */
overlay[local]
module;
import javascript import javascript
private import codeql.yaml.Yaml as LibYaml private import codeql.yaml.Yaml as LibYaml

View File

@@ -37,6 +37,8 @@
* they represent; additionally, indefinite abstract values record * they represent; additionally, indefinite abstract values record
* the source of imprecision that caused them to arise. * the source of imprecision that caused them to arise.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.AbstractValuesImpl private import semmle.javascript.dataflow.internal.AbstractValuesImpl
@@ -57,6 +59,7 @@ class AbstractValue extends TAbstractValue {
* Gets the Boolean value some concrete value represented by this * Gets the Boolean value some concrete value represented by this
* abstract value coerces to. * abstract value coerces to.
*/ */
pragma[nomagic]
abstract boolean getBooleanValue(); abstract boolean getBooleanValue();
/** /**
@@ -97,6 +100,7 @@ class AbstractValue extends TAbstractValue {
* In all cases, purely local flow tracking is used to find prototype objects, so * In all cases, purely local flow tracking is used to find prototype objects, so
* this predicate cannot be relied on to compute all possible prototype objects. * this predicate cannot be relied on to compute all possible prototype objects.
*/ */
overlay[global]
DefiniteAbstractValue getAPrototype() { DefiniteAbstractValue getAPrototype() {
exists(AbstractProtoProperty proto | exists(AbstractProtoProperty proto |
proto.getBase() = this and proto.getBase() = this and

View File

@@ -33,21 +33,25 @@ private import semmle.javascript.internal.CachedStages
* Note: For performance reasons, all subclasses of this class should be part * Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Use `isAdditionalFlowStep` for query-specific flow steps. * of the standard library. Use `isAdditionalFlowStep` for query-specific flow steps.
*/ */
overlay[local]
class AdditionalFlowStep extends Unit { class AdditionalFlowStep extends Unit {
/** /**
* Holds if `pred` &rarr; `succ` should be considered a value-preserving data flow edge.f * Holds if `pred` &rarr; `succ` should be considered a value-preserving data flow edge.f
*/ */
overlay[global]
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() } predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/** /**
* Holds if `pred` &rarr; `succ` should be considered a value-preserving data flow edge that * Holds if `pred` &rarr; `succ` should be considered a value-preserving data flow edge that
* crosses calling contexts. * crosses calling contexts.
*/ */
overlay[global]
predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) { none() } predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
/** /**
* Holds if `pred` should be stored in the given `content` of the object `succ`. * Holds if `pred` should be stored in the given `content` of the object `succ`.
*/ */
overlay[global]
predicate storeStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) { predicate storeStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
none() none()
} }
@@ -55,6 +59,7 @@ class AdditionalFlowStep extends Unit {
/** /**
* Holds if the given `content` of the object in `pred` should be read into `succ`. * Holds if the given `content` of the object in `pred` should be read into `succ`.
*/ */
overlay[global]
predicate readStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) { predicate readStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
none() none()
} }

View File

@@ -625,15 +625,19 @@ abstract deprecated class LabeledBarrierGuardNode extends BarrierGuardNode {
* *
* For use with load/store steps in `DataFlow::SharedFlowStep` and TypeTracking. * For use with load/store steps in `DataFlow::SharedFlowStep` and TypeTracking.
*/ */
overlay[local]
module PseudoProperties { module PseudoProperties {
/** Holds if `s` is a pseudo-property. */ /** Holds if `s` is a pseudo-property. */
bindingset[s] bindingset[s]
overlay[caller]
predicate isPseudoProperty(string s) { s.matches("$%$") } predicate isPseudoProperty(string s) { s.matches("$%$") }
bindingset[s] bindingset[s]
overlay[caller]
private string pseudoProperty(string s) { result = "$" + s + "$" } private string pseudoProperty(string s) { result = "$" + s + "$" }
bindingset[s, v] bindingset[s, v]
overlay[caller]
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" } private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
/** /**
@@ -680,6 +684,7 @@ module PseudoProperties {
* Gets a pseudo-property for the location of a map value where the key is `key`. * Gets a pseudo-property for the location of a map value where the key is `key`.
* The string value of the `key` is encoded in the result, and there is only a result if the string value of `key` is known. * The string value of the `key` is encoded in the result, and there is only a result if the string value of `key` is known.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
string mapValueKnownKey(DataFlow::Node key) { string mapValueKnownKey(DataFlow::Node key) {
result = mapValueKey(any(string s | key.mayHaveStringValue(s))) result = mapValueKey(any(string s | key.mayHaveStringValue(s)))
@@ -689,17 +694,20 @@ module PseudoProperties {
* Gets a pseudo-property for the location of a map value where the key is `key`. * Gets a pseudo-property for the location of a map value where the key is `key`.
*/ */
bindingset[key] bindingset[key]
overlay[caller]
string mapValueKey(string key) { result = pseudoProperty("mapValue", key) } string mapValueKey(string key) { result = pseudoProperty("mapValue", key) }
/** /**
* Holds if `prop` equals `mapValueKey(key)` for some value of `key`. * Holds if `prop` equals `mapValueKey(key)` for some value of `key`.
*/ */
bindingset[prop] bindingset[prop]
overlay[caller]
predicate isMapValueKey(string prop) { prop.matches("$mapValue|%$") } predicate isMapValueKey(string prop) { prop.matches("$mapValue|%$") }
/** /**
* Gets a pseudo-property for the location of a map value where the key is `key`. * Gets a pseudo-property for the location of a map value where the key is `key`.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
string mapValue(DataFlow::Node key) { string mapValue(DataFlow::Node key) {
result = mapValueKnownKey(key) result = mapValueKnownKey(key)

View File

@@ -7,6 +7,8 @@
* For performance reasons, all subclasses of `CustomAbstractValueDefinition` * For performance reasons, all subclasses of `CustomAbstractValueDefinition`
* should be part of the standard library. * should be part of the standard library.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import internal.AbstractValuesImpl private import internal.AbstractValuesImpl
@@ -32,6 +34,7 @@ class CustomAbstractValueFromDefinition extends AbstractValue, TCustomAbstractVa
override predicate isIndefinite(DataFlow::Incompleteness cause) { def.isIndefinite(cause) } override predicate isIndefinite(DataFlow::Incompleteness cause) { def.isIndefinite(cause) }
overlay[global]
override DefiniteAbstractValue getAPrototype() { result = def.getAPrototype() } override DefiniteAbstractValue getAPrototype() { result = def.getAPrototype() }
override predicate hasLocationInfo( override predicate hasLocationInfo(
@@ -98,6 +101,7 @@ abstract class CustomAbstractValueDefinition extends Locatable {
* Gets an abstract value that represents a prototype object of the * Gets an abstract value that represents a prototype object of the
* induced abstract value. * induced abstract value.
*/ */
overlay[global]
AbstractValue getAPrototype() { AbstractValue getAPrototype() {
exists(AbstractProtoProperty proto | exists(AbstractProtoProperty proto |
proto.getBase() = this.getAbstractValue() and proto.getBase() = this.getAbstractValue() and
@@ -119,6 +123,7 @@ abstract class CustomAbstractValueDefinition extends Locatable {
/** /**
* Flow analysis for custom abstract values. * Flow analysis for custom abstract values.
*/ */
overlay[global]
class CustomAbstractValueFromDefinitionNode extends DataFlow::AnalyzedNode, DataFlow::ValueNode { class CustomAbstractValueFromDefinitionNode extends DataFlow::AnalyzedNode, DataFlow::ValueNode {
CustomAbstractValueFromDefinition val; CustomAbstractValueFromDefinition val;

View File

@@ -17,6 +17,8 @@
* Flow through global variables, object properties or function calls is not * Flow through global variables, object properties or function calls is not
* modeled (except for immediately invoked functions as explained above). * modeled (except for immediately invoked functions as explained above).
*/ */
overlay[local]
module;
import javascript import javascript
private import internal.CallGraphs private import internal.CallGraphs
@@ -64,9 +66,11 @@ module DataFlow {
* `p.getALocalSource()` does _not_ return the corresponding argument, and * `p.getALocalSource()` does _not_ return the corresponding argument, and
* `p.isIncomplete("call")` holds. * `p.isIncomplete("call")` holds.
*/ */
overlay[global]
predicate isIncomplete(Incompleteness cause) { isIncomplete(this, cause) } predicate isIncomplete(Incompleteness cause) { isIncomplete(this, cause) }
/** Gets type inference results for this data flow node. */ /** Gets type inference results for this data flow node. */
overlay[global]
AnalyzedNode analyze() { result = this } AnalyzedNode analyze() { result = this }
/** Gets the expression corresponding to this data flow node, if any. */ /** Gets the expression corresponding to this data flow node, if any. */
@@ -124,11 +128,13 @@ module DataFlow {
int getIntValue() { result = this.asExpr().getIntValue() } int getIntValue() { result = this.asExpr().getIntValue() }
/** Gets a function value that may reach this node. */ /** Gets a function value that may reach this node. */
overlay[global]
final FunctionNode getAFunctionValue() { final FunctionNode getAFunctionValue() {
CallGraph::getAFunctionReference(result, 0).flowsTo(this) CallGraph::getAFunctionReference(result, 0).flowsTo(this)
} }
/** Gets a function value that may reach this node with the given `imprecision` level. */ /** Gets a function value that may reach this node with the given `imprecision` level. */
overlay[global]
final FunctionNode getAFunctionValue(int imprecision) { final FunctionNode getAFunctionValue(int imprecision) {
CallGraph::getAFunctionReference(result, imprecision).flowsTo(this) CallGraph::getAFunctionReference(result, imprecision).flowsTo(this)
} }
@@ -137,6 +143,7 @@ module DataFlow {
* Gets a function value that may reach this node, * Gets a function value that may reach this node,
* possibly derived from a partial function invocation. * possibly derived from a partial function invocation.
*/ */
overlay[global]
final FunctionNode getABoundFunctionValue(int boundArgs) { final FunctionNode getABoundFunctionValue(int boundArgs) {
result = this.getAFunctionValue() and boundArgs = 0 result = this.getAFunctionValue() and boundArgs = 0
or or
@@ -192,6 +199,7 @@ module DataFlow {
FlowSteps::identityFunctionStep(result, this) FlowSteps::identityFunctionStep(result, this)
} }
overlay[global]
private NameResolution::Node getNameResolutionNode() { private NameResolution::Node getNameResolutionNode() {
this = valueNode(result) this = valueNode(result)
or or
@@ -205,6 +213,7 @@ module DataFlow {
* Holds if this node is annotated with the given named type, * Holds if this node is annotated with the given named type,
* or is declared as a subtype thereof, or is a union or intersection containing such a type. * or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/ */
overlay[global]
cached cached
predicate hasUnderlyingType(string globalName) { predicate hasUnderlyingType(string globalName) {
Stages::TypeTracking::ref() and Stages::TypeTracking::ref() and
@@ -218,6 +227,7 @@ module DataFlow {
* Holds if this node is annotated with the given named type, * Holds if this node is annotated with the given named type,
* or is declared as a subtype thereof, or is a union or intersection containing such a type. * or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/ */
overlay[global]
cached cached
predicate hasUnderlyingType(string moduleName, string typeName) { predicate hasUnderlyingType(string moduleName, string typeName) {
Stages::TypeTracking::ref() and Stages::TypeTracking::ref() and
@@ -466,6 +476,7 @@ module DataFlow {
/** /**
* Gets an accessor (`get` or `set` method) that may be invoked by this property reference. * Gets an accessor (`get` or `set` method) that may be invoked by this property reference.
*/ */
overlay[global]
final DataFlow::FunctionNode getAnAccessorCallee() { final DataFlow::FunctionNode getAnAccessorCallee() {
result = CallGraph::getAnAccessorCallee(this) result = CallGraph::getAnAccessorCallee(this)
} }
@@ -1419,11 +1430,13 @@ module DataFlow {
* This predicate is only defined for expressions, properties, and for statements that declare * This predicate is only defined for expressions, properties, and for statements that declare
* a function, a class, or a TypeScript namespace or enum. * a function, a class, or a TypeScript namespace or enum.
*/ */
pragma[nomagic]
ValueNode valueNode(AstNode nd) { result.getAstNode() = nd } ValueNode valueNode(AstNode nd) { result.getAstNode() = nd }
/** /**
* Gets the data flow node corresponding to `e`. * Gets the data flow node corresponding to `e`.
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
ExprNode exprNode(Expr e) { result = valueNode(e) } ExprNode exprNode(Expr e) { result = valueNode(e) }
@@ -1762,6 +1775,7 @@ module DataFlow {
) )
} }
overlay[global]
private class ReflectiveParamsStep extends LegacyPreCallGraphStep { private class ReflectiveParamsStep extends LegacyPreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i | exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i |
@@ -1774,6 +1788,7 @@ module DataFlow {
} }
/** A taint step from the reflective parameters node to any parameter. */ /** A taint step from the reflective parameters node to any parameter. */
overlay[global]
private class ReflectiveParamsTaintStep extends TaintTracking::LegacyTaintStep { private class ReflectiveParamsTaintStep extends TaintTracking::LegacyTaintStep {
override predicate step(DataFlow::Node obj, DataFlow::Node element) { override predicate step(DataFlow::Node obj, DataFlow::Node element) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f | exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f |
@@ -1787,6 +1802,7 @@ module DataFlow {
/** /**
* Holds if there is a step from `pred` to `succ` through a field accessed through `this` in a class. * Holds if there is a step from `pred` to `succ` through a field accessed through `this` in a class.
*/ */
overlay[global]
predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) { predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(ClassNode cls, string prop | exists(ClassNode cls, string prop |
pred = AccessPath::getAnAssignmentTo(cls.getADirectSuperClass*().getAReceiverNode(), prop) or pred = AccessPath::getAnAssignmentTo(cls.getADirectSuperClass*().getAReceiverNode(), prop) or
@@ -1819,6 +1835,7 @@ module DataFlow {
* `p.getALocalSource()` does _not_ return the corresponding argument, and * `p.getALocalSource()` does _not_ return the corresponding argument, and
* `p.isIncomplete("call")` holds. * `p.isIncomplete("call")` holds.
*/ */
overlay[global]
predicate isIncomplete(Node nd, Incompleteness cause) { predicate isIncomplete(Node nd, Incompleteness cause) {
exists(SsaVariable ssa | nd = TSsaDefNode(ssa.getDefinition()) | exists(SsaVariable ssa | nd = TSsaDefNode(ssa.getDefinition()) |
defIsIncomplete(ssa.(SsaExplicitDefinition).getDef(), cause) defIsIncomplete(ssa.(SsaExplicitDefinition).getDef(), cause)

View File

@@ -1,4 +1,6 @@
/** Provides classes and predicates for defining flow summaries. */ /** Provides classes and predicates for defining flow summaries. */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
/** /**
* Types inferred by the flow analysis, represented as type tags. * Types inferred by the flow analysis, represented as type tags.
* *

View File

@@ -3,6 +3,8 @@
* as nodes corresponding to function definitions or nodes corresponding to * as nodes corresponding to function definitions or nodes corresponding to
* parameters. * parameters.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dependencies.Dependencies private import semmle.javascript.dependencies.Dependencies
@@ -158,6 +160,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* addEventHandler("click", foo.bind(this, "value of x")) * addEventHandler("click", foo.bind(this, "value of x"))
* ``` * ```
*/ */
overlay[global]
ParameterNode getABoundCallbackParameter(int callback, int param) { ParameterNode getABoundCallbackParameter(int callback, int param) {
exists(int boundArgs | exists(int boundArgs |
result = result =
@@ -178,6 +181,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
private ObjectLiteralNode getOptionsArgument(int i) { result.flowsTo(this.getArgument(i)) } private ObjectLiteralNode getOptionsArgument(int i) { result.flowsTo(this.getArgument(i)) }
/** Gets an abstract value representing possible callees of this call site. */ /** Gets an abstract value representing possible callees of this call site. */
overlay[global]
final AbstractValue getACalleeValue() { final AbstractValue getACalleeValue() {
exists(DataFlow::Node callee, DataFlow::AnalyzedNode analyzed | exists(DataFlow::Node callee, DataFlow::AnalyzedNode analyzed |
pragma[only_bind_into](callee) = this.getCalleeNode() and pragma[only_bind_into](callee) = this.getCalleeNode() and
@@ -192,6 +196,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* To alter the call graph as seen by the interprocedural data flow libraries, override * To alter the call graph as seen by the interprocedural data flow libraries, override
* the `getACallee(int imprecision)` predicate instead. * the `getACallee(int imprecision)` predicate instead.
*/ */
overlay[global]
final Function getACallee() { result = this.getACallee(0) } final Function getACallee() { result = this.getACallee(0) }
/** /**
@@ -206,6 +211,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* This predicate can be overridden to alter the call graph used by the interprocedural * This predicate can be overridden to alter the call graph used by the interprocedural
* data flow libraries. * data flow libraries.
*/ */
overlay[global]
Function getACallee(int imprecision) { Function getACallee(int imprecision) {
result = CallGraph::getACallee(this, imprecision).getFunction() result = CallGraph::getACallee(this, imprecision).getFunction()
} }
@@ -214,6 +220,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* Holds if the approximation of possible callees for this call site is * Holds if the approximation of possible callees for this call site is
* affected by the given analysis incompleteness `cause`. * affected by the given analysis incompleteness `cause`.
*/ */
overlay[global]
predicate isIndefinite(DataFlow::Incompleteness cause) { predicate isIndefinite(DataFlow::Incompleteness cause) {
this.getACalleeValue().isIndefinite(cause) this.getACalleeValue().isIndefinite(cause)
} }
@@ -229,6 +236,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* independent contexts, so tracking flow through it leads to * independent contexts, so tracking flow through it leads to
* imprecision. * imprecision.
*/ */
overlay[global]
predicate isImprecise() { predicate isImprecise() {
this.isIndefinite("global") and this.isIndefinite("global") and
exists(DefiniteAbstractValue v | v = this.getACalleeValue() | not v instanceof AbstractCallable) exists(DefiniteAbstractValue v | v = this.getACalleeValue() | not v instanceof AbstractCallable)
@@ -238,6 +246,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* Holds if our approximation of possible callees for this call site is * Holds if our approximation of possible callees for this call site is
* likely to be incomplete. * likely to be incomplete.
*/ */
overlay[global]
predicate isIncomplete() { predicate isIncomplete() {
// the flow analysis identifies a source of incompleteness other than // the flow analysis identifies a source of incompleteness other than
// global flow (which usually leads to imprecision rather than incompleteness) // global flow (which usually leads to imprecision rather than incompleteness)
@@ -248,6 +257,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
* Holds if our approximation of possible callees for this call site is * Holds if our approximation of possible callees for this call site is
* likely to be imprecise or incomplete. * likely to be imprecise or incomplete.
*/ */
overlay[global]
predicate isUncertain() { this.isImprecise() or this.isIncomplete() } predicate isUncertain() { this.isImprecise() or this.isIncomplete() }
/** /**
@@ -763,7 +773,7 @@ module ModuleImportNode {
cached cached
ModuleImportNode moduleImport(string path) { ModuleImportNode moduleImport(string path) {
// NB. internal modules may be imported with a "node:" prefix // NB. internal modules may be imported with a "node:" prefix
Stages::Imports::ref() and result.getPath() = ["node:" + path, path] result.getPath() = ["node:" + path, path]
} }
/** /**
@@ -771,6 +781,7 @@ ModuleImportNode moduleImport(string path) {
* `require("lodash")` in a context where a package.json file includes * `require("lodash")` in a context where a package.json file includes
* `"lodash"` as a dependency. * `"lodash"` as a dependency.
*/ */
overlay[global]
ModuleImportNode dependencyModuleImport(Dependency dep) { ModuleImportNode dependencyModuleImport(Dependency dep) {
result = dep.getAUse("import").(Import).getImportedModuleNode() result = dep.getAUse("import").(Import).getImportedModuleNode()
} }
@@ -861,6 +872,7 @@ module MemberKind {
* *
* Additional patterns can be recognized as class nodes, by extending `DataFlow::ClassNode::Range`. * Additional patterns can be recognized as class nodes, by extending `DataFlow::ClassNode::Range`.
*/ */
overlay[global]
class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode { class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
override AST::ValueNode astNode; override AST::ValueNode astNode;
AbstractCallable function; AbstractCallable function;
@@ -1329,6 +1341,7 @@ class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
/** /**
* Helper predicate to get a prototype reference in a file. * Helper predicate to get a prototype reference in a file.
*/ */
overlay[global]
private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) { private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) and result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) and
result.getPropertyName() = "prototype" and result.getPropertyName() = "prototype" and
@@ -1338,6 +1351,7 @@ private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
/** /**
* Helper predicate to get an instantiation in a file. * Helper predicate to get an instantiation in a file.
*/ */
overlay[global]
private DataFlow::NewNode getAnInstantiationInFile(string name, File f) { private DataFlow::NewNode getAnInstantiationInFile(string name, File f) {
result = AccessPath::getAReferenceTo(name).(DataFlow::LocalSourceNode).getAnInstantiation() and result = AccessPath::getAReferenceTo(name).(DataFlow::LocalSourceNode).getAnInstantiation() and
result.getFile() = f result.getFile() = f
@@ -1346,6 +1360,7 @@ private DataFlow::NewNode getAnInstantiationInFile(string name, File f) {
/** /**
* Gets a reference to the function `func`, where there exists a read/write of the "prototype" property on that reference. * Gets a reference to the function `func`, where there exists a read/write of the "prototype" property on that reference.
*/ */
overlay[global]
pragma[noinline] pragma[noinline]
private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func) { private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func) {
exists(result.getAPropertyReference("prototype")) and exists(result.getAPropertyReference("prototype")) and
@@ -1353,6 +1368,7 @@ private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func)
func instanceof AbstractCallable // the join-order goes bad if `func` has type `AbstractFunction`. func instanceof AbstractCallable // the join-order goes bad if `func` has type `AbstractFunction`.
} }
overlay[global]
module ClassNode { module ClassNode {
/** /**
* A dataflow node that should be considered a class node. * A dataflow node that should be considered a class node.
@@ -1435,6 +1451,7 @@ module ClassNode {
* _.partial(fn, x, y, z) * _.partial(fn, x, y, z)
* ``` * ```
*/ */
overlay[global]
class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Range { class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Range {
/** Gets a node holding a callback invoked by this partial invocation node. */ /** Gets a node holding a callback invoked by this partial invocation node. */
DataFlow::Node getACallbackNode() { DataFlow::Node getACallbackNode() {
@@ -1470,6 +1487,7 @@ class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Ran
} }
} }
overlay[global]
module PartialInvokeNode { module PartialInvokeNode {
/** /**
* A data flow node that performs a partial function application. * A data flow node that performs a partial function application.
@@ -1717,6 +1735,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
predicate maybeGlobal() { RegExp::maybeGlobal(this.tryGetFlags()) } predicate maybeGlobal() { RegExp::maybeGlobal(this.tryGetFlags()) }
/** Gets a data flow node referring to this regular expression. */ /** Gets a data flow node referring to this regular expression. */
overlay[global]
private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) { private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) {
t.start() and t.start() and
result = this result = this
@@ -1725,6 +1744,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
} }
/** Gets a data flow node referring to this regular expression. */ /** Gets a data flow node referring to this regular expression. */
overlay[global]
cached cached
DataFlow::SourceNode getAReference() { DataFlow::SourceNode getAReference() {
Stages::FlowSteps::ref() and Stages::FlowSteps::ref() and
@@ -1736,6 +1756,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
* A guard node for a variable in a negative condition, such as `x` in `if(!x)`. * A guard node for a variable in a negative condition, such as `x` in `if(!x)`.
* Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks. * Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks.
*/ */
overlay[global]
class VarAccessBarrier extends DataFlow::Node { class VarAccessBarrier extends DataFlow::Node {
VarAccessBarrier() { VarAccessBarrier() {
exists(ConditionGuardNode guard, SsaRefinementNode refinement | exists(ConditionGuardNode guard, SsaRefinementNode refinement |

View File

@@ -27,6 +27,8 @@
* so the refinement can evaluate to both `true` and `false` for the same * so the refinement can evaluate to both `true` and `false` for the same
* candidate value. * candidate value.
*/ */
overlay[local]
module;
import javascript import javascript
private import AbstractValues private import AbstractValues
@@ -45,6 +47,7 @@ abstract class RefinementCandidate extends Expr {
/** /**
* Gets a refinement value inferred for this expression in context `ctxt`. * Gets a refinement value inferred for this expression in context `ctxt`.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
abstract RefinementValue eval(RefinementContext ctxt); abstract RefinementValue eval(RefinementContext ctxt);
} }
@@ -64,6 +67,7 @@ class Refinement extends Expr instanceof RefinementCandidate {
/** /**
* Gets a refinement value inferred for this expression in context `ctxt`. * Gets a refinement value inferred for this expression in context `ctxt`.
*/ */
overlay[global]
RefinementValue eval(RefinementContext ctxt) { result = super.eval(ctxt) } RefinementValue eval(RefinementContext ctxt) { result = super.eval(ctxt) }
} }
@@ -71,6 +75,7 @@ class Refinement extends Expr instanceof RefinementCandidate {
abstract private class LiteralRefinement extends RefinementCandidate, Literal { abstract private class LiteralRefinement extends RefinementCandidate, Literal {
override SsaSourceVariable getARefinedVar() { none() } override SsaSourceVariable getARefinedVar() { none() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
ctxt.appliesTo(this) and result = this.eval() ctxt.appliesTo(this) and result = this.eval()
} }
@@ -78,16 +83,19 @@ abstract private class LiteralRefinement extends RefinementCandidate, Literal {
/** /**
* Gets the refinement value that represents this literal. * Gets the refinement value that represents this literal.
*/ */
overlay[global]
RefinementValue eval() { result = TAny() } RefinementValue eval() { result = TAny() }
} }
/** A `null` literal, viewed as a refinement expression. */ /** A `null` literal, viewed as a refinement expression. */
private class NullLiteralRefinement extends LiteralRefinement, NullLiteral { private class NullLiteralRefinement extends LiteralRefinement, NullLiteral {
overlay[global]
override RefinementValue eval() { result = TValueWithType(TTNull()) } override RefinementValue eval() { result = TValueWithType(TTNull()) }
} }
/** A Boolean literal, viewed as a refinement expression. */ /** A Boolean literal, viewed as a refinement expression. */
private class BoolRefinement extends LiteralRefinement, BooleanLiteral { private class BoolRefinement extends LiteralRefinement, BooleanLiteral {
overlay[global]
override RefinementValue eval() { override RefinementValue eval() {
exists(boolean b | b.toString() = this.getValue() | result = TBoolConstant(b)) exists(boolean b | b.toString() = this.getValue() | result = TBoolConstant(b))
} }
@@ -95,11 +103,13 @@ private class BoolRefinement extends LiteralRefinement, BooleanLiteral {
/** A constant string, viewed as a refinement expression. */ /** A constant string, viewed as a refinement expression. */
private class StringRefinement extends LiteralRefinement, ConstantString { private class StringRefinement extends LiteralRefinement, ConstantString {
overlay[global]
override RefinementValue eval() { result = TStringConstant(this.getStringValue()) } override RefinementValue eval() { result = TStringConstant(this.getStringValue()) }
} }
/** A numeric literal, viewed as a refinement expression. */ /** A numeric literal, viewed as a refinement expression. */
abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral { abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral {
overlay[global]
override RefinementValue eval() { result = TValueWithType(TTNumber()) } override RefinementValue eval() { result = TValueWithType(TTNumber()) }
} }
@@ -112,6 +122,7 @@ abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral
private class IntRefinement extends NumberRefinement, NumberLiteral { private class IntRefinement extends NumberRefinement, NumberLiteral {
IntRefinement() { this.getValue().toInt() = 0 } IntRefinement() { this.getValue().toInt() = 0 }
overlay[global]
override RefinementValue eval() { result = TIntConstant(this.getValue().toInt()) } override RefinementValue eval() { result = TIntConstant(this.getValue().toInt()) }
} }
@@ -123,6 +134,7 @@ private class UndefinedInRefinement extends RefinementCandidate,
{ {
override SsaSourceVariable getARefinedVar() { none() } override SsaSourceVariable getARefinedVar() { none() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
ctxt.appliesTo(this) and ctxt.appliesTo(this) and
result = TValueWithType(TTUndefined()) result = TValueWithType(TTUndefined())
@@ -135,6 +147,7 @@ private class VariableRefinement extends RefinementCandidate, VarUse {
override SsaSourceVariable getARefinedVar() { result = this.getVariable() } override SsaSourceVariable getARefinedVar() { result = this.getVariable() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
ctxt.appliesTo(this) and ctxt.appliesTo(this) and
result = ctxt.(VarRefinementContext).getAValue() result = ctxt.(VarRefinementContext).getAValue()
@@ -149,6 +162,7 @@ private class ParRefinement extends RefinementCandidate, ParExpr {
result = this.getExpression().(RefinementCandidate).getARefinedVar() result = this.getExpression().(RefinementCandidate).getARefinedVar()
} }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
result = this.getExpression().(RefinementCandidate).eval(ctxt) result = this.getExpression().(RefinementCandidate).eval(ctxt)
} }
@@ -162,6 +176,7 @@ private class TypeofRefinement extends RefinementCandidate, TypeofExpr {
result = this.getOperand().(RefinementCandidate).getARefinedVar() result = this.getOperand().(RefinementCandidate).getARefinedVar()
} }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
exists(RefinementValue opVal | exists(RefinementValue opVal |
opVal = this.getOperand().(RefinementCandidate).eval(ctxt) and opVal = this.getOperand().(RefinementCandidate).eval(ctxt) and
@@ -182,6 +197,7 @@ private class EqRefinement extends RefinementCandidate, EqualityTest {
result = this.getRightOperand().(RefinementCandidate).getARefinedVar() result = this.getRightOperand().(RefinementCandidate).getARefinedVar()
} }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
exists(RefinementCandidate l, RefinementValue lv, RefinementCandidate r, RefinementValue rv | exists(RefinementCandidate l, RefinementValue lv, RefinementCandidate r, RefinementValue rv |
l = this.getLeftOperand() and l = this.getLeftOperand() and
@@ -220,6 +236,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
result = this.getIndex().(RefinementCandidate).getARefinedVar() result = this.getIndex().(RefinementCandidate).getARefinedVar()
} }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) { override RefinementValue eval(RefinementContext ctxt) {
exists( exists(
RefinementCandidate base, RefinementValue baseVal, RefinementCandidate index, RefinementCandidate base, RefinementValue baseVal, RefinementCandidate index,
@@ -242,6 +259,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
* if any. * if any.
*/ */
bindingset[s, i] bindingset[s, i]
overlay[global]
private RefinementValue evalIndex(StringConstant s, IntConstant i) { private RefinementValue evalIndex(StringConstant s, IntConstant i) {
result = TStringConstant(s.getValue().charAt(i.getValue())) result = TStringConstant(s.getValue().charAt(i.getValue()))
} }
@@ -249,6 +267,7 @@ private RefinementValue evalIndex(StringConstant s, IntConstant i) {
/** /**
* A context in which a refinement expression is analyzed. * A context in which a refinement expression is analyzed.
*/ */
overlay[global]
newtype TRefinementContext = newtype TRefinementContext =
/** /**
* A refinement context associated with refinement `ref`, specifying that variable `var` * A refinement context associated with refinement `ref`, specifying that variable `var`
@@ -266,6 +285,7 @@ newtype TRefinementContext =
/** /**
* A context in which a refinement expression is analyzed. * A context in which a refinement expression is analyzed.
*/ */
overlay[global]
class RefinementContext extends TRefinementContext { class RefinementContext extends TRefinementContext {
/** /**
* Holds if refinement expression `cand` might be analyzed in this context. * Holds if refinement expression `cand` might be analyzed in this context.
@@ -280,6 +300,7 @@ class RefinementContext extends TRefinementContext {
* A refinement context specifying that some variable is assumed to have one particular * A refinement context specifying that some variable is assumed to have one particular
* abstract value. * abstract value.
*/ */
overlay[global]
class VarRefinementContext extends RefinementContext, TVarRefinementContext { class VarRefinementContext extends RefinementContext, TVarRefinementContext {
override predicate appliesTo(RefinementCandidate cand) { override predicate appliesTo(RefinementCandidate cand) {
exists(AnalyzedRefinement ref, SsaSourceVariable var | exists(AnalyzedRefinement ref, SsaSourceVariable var |

View File

@@ -5,6 +5,8 @@
* Note that unlike `TypeTracking.qll`, this library only performs * Note that unlike `TypeTracking.qll`, this library only performs
* local tracking within a function. * local tracking within a function.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.TypeTracking private import semmle.javascript.dataflow.TypeTracking
@@ -192,6 +194,7 @@ class SourceNode extends DataFlow::Node instanceof SourceNode::Range {
* *
* See `TypeTracker` for more details about how to use this. * See `TypeTracker` for more details about how to use this.
*/ */
overlay[global]
pragma[inline] pragma[inline]
DataFlow::SourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) } DataFlow::SourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
@@ -200,6 +203,7 @@ class SourceNode extends DataFlow::Node instanceof SourceNode::Range {
* *
* See `TypeBackTracker` for more details about how to use this. * See `TypeBackTracker` for more details about how to use this.
*/ */
overlay[global]
pragma[inline] pragma[inline]
DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
t2 = t.step(result, this) t2 = t.step(result, this)

View File

@@ -92,10 +92,15 @@ class AnalyzedNode extends DataFlow::Node {
PrimitiveType getAPrimitiveType() { result = this.getAValue().toPrimitive().getType() } PrimitiveType getAPrimitiveType() { result = this.getAValue().toPrimitive().getType() }
/** Gets a Boolean value that this node evaluates to. */ /** Gets a Boolean value that this node evaluates to. */
bindingset[this]
overlay[caller?]
pragma[inline_late]
boolean getABooleanValue() { result = this.getAValue().getBooleanValue() } boolean getABooleanValue() { result = this.getAValue().getBooleanValue() }
/** Gets the unique Boolean value that this node evaluates to, if any. */ /** Gets the unique Boolean value that this node evaluates to, if any. */
boolean getTheBooleanValue() { forex(boolean bv | bv = this.getABooleanValue() | result = bv) } overlay[caller?]
pragma[inline]
boolean getTheBooleanValue() { result = unique( | | this.getABooleanValue()) }
/** Gets the unique type inferred for this node, if any. */ /** Gets the unique type inferred for this node, if any. */
InferredType getTheType() { result = unique(InferredType t | t = this.getAType()) } InferredType getTheType() { result = unique(InferredType t | t = this.getAType()) }

View File

@@ -3,6 +3,8 @@
* *
* Provides a representation for abstract values. * Provides a representation for abstract values.
*/ */
overlay[local]
module;
private import javascript private import javascript
import semmle.javascript.dataflow.AbstractValues import semmle.javascript.dataflow.AbstractValues

View File

@@ -14,6 +14,8 @@
* to the same value have the same access paths, so access paths are neither sound nor * to the same value have the same access paths, so access paths are neither sound nor
* complete as an approximation of expression semantics. * complete as an approximation of expression semantics.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages

View File

@@ -5,6 +5,7 @@ private import semmle.javascript.dataflow.internal.DataFlowPrivate
/** /**
* Gets a data-flow node synthesized using `AdditionalFlowInternal#needsSynthesizedNode`. * Gets a data-flow node synthesized using `AdditionalFlowInternal#needsSynthesizedNode`.
*/ */
overlay[local]
DataFlow::Node getSynthesizedNode(AstNode node, string tag) { DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
result = TGenericSynthesizedNode(node, tag, _) result = TGenericSynthesizedNode(node, tag, _)
} }
@@ -12,6 +13,7 @@ DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
/** /**
* An extension to `AdditionalFlowStep` with additional internal-only predicates. * An extension to `AdditionalFlowStep` with additional internal-only predicates.
*/ */
overlay[local]
class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep { class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
/** /**
* Holds if a data-flow node should be synthesized for the pair `(node, tag)`. * Holds if a data-flow node should be synthesized for the pair `(node, tag)`.
@@ -25,10 +27,12 @@ class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
/** /**
* Holds if `node` should only permit flow of values stored in `contents`. * Holds if `node` should only permit flow of values stored in `contents`.
*/ */
overlay[global]
predicate expectsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() } predicate expectsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
/** /**
* Holds if `node` should not permit flow of values stored in `contents`. * Holds if `node` should not permit flow of values stored in `contents`.
*/ */
overlay[global]
predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() } predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
} }

View File

@@ -358,25 +358,18 @@ module MakeStateBarrierGuard<
} }
/** /**
* Gets a logical `and` expression, or parenthesized expression, that contains `guard`. * Gets any of the ancestors of `guard` that preserves the value of `possibleOutcome`. Includes the guard itself.
*/ */
private Expr getALogicalAndParent(BarrierGuard guard) { private Expr getALogicalOperatorParent(BarrierGuard guard, boolean possibleOutcome) {
barrierGuardIsRelevant(guard) and result = guard.asExpr() barrierGuardIsRelevant(guard) and result = guard.asExpr() and possibleOutcome = [true, false]
or or
result.(LogAndExpr).getAnOperand() = getALogicalAndParent(guard) result.(LogOrExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
possibleOutcome = false
or or
result.getUnderlyingValue() = getALogicalAndParent(guard) result.(LogAndExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
} possibleOutcome = true
/**
* Gets a logical `or` expression, or parenthesized expression, that contains `guard`.
*/
private Expr getALogicalOrParent(BarrierGuard guard) {
barrierGuardIsRelevant(guard) and result = guard.asExpr()
or or
result.(LogOrExpr).getAnOperand() = getALogicalOrParent(guard) result.getUnderlyingValue() = getALogicalOperatorParent(guard, possibleOutcome)
or
result.getUnderlyingValue() = getALogicalOrParent(guard)
} }
final private class FinalFunction = Function; final private class FinalFunction = Function;
@@ -394,15 +387,7 @@ module MakeStateBarrierGuard<
exists(BarrierGuard guard | exists(BarrierGuard guard |
barrierGuardIsRelevant(guard) and barrierGuardIsRelevant(guard) and
exists(Expr e | exists(Expr e |
exists(Expr returnExpr | exists(Expr returnExpr | returnExpr = getALogicalOperatorParent(guard, guardOutcome) |
returnExpr = guard.asExpr()
or
// ad hoc support for conjunctions:
getALogicalAndParent(guard) = returnExpr and guardOutcome = true
or
// ad hoc support for disjunctions:
getALogicalOrParent(guard) = returnExpr and guardOutcome = false
|
exists(SsaExplicitDefinition ssa | exists(SsaExplicitDefinition ssa |
ssa.getDef().getSource() = returnExpr and ssa.getDef().getSource() = returnExpr and
ssa.getVariable().getAUse() = this.getAReturnedExpr() ssa.getVariable().getAUse() = this.getAReturnedExpr()

View File

@@ -97,9 +97,14 @@ module CallGraph {
not exists(read.getPropertyName()) and not exists(read.getPropertyName()) and
result = read and result = read and
// there exists only local reads of the object, nothing else. // there exists only local reads of the object, nothing else.
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) | objectOnlyUsedForPropRead(obj)
ref = [obj, any(DataFlow::PropRead r).getBase()] )
) }
pragma[nomagic]
private predicate objectOnlyUsedForPropRead(DataFlow::ObjectLiteralNode obj) {
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) |
ref = [obj, any(DataFlow::PropRead r).getBase()]
) )
} }

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels private import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
@@ -194,6 +197,7 @@ module Public {
*/ */
class ContentSet extends TContentSet { class ContentSet extends TContentSet {
/** Gets a content that may be stored into when storing into this set. */ /** Gets a content that may be stored into when storing into this set. */
overlay[caller?]
pragma[inline] pragma[inline]
Content getAStoreContent() { Content getAStoreContent() {
result = this.asSingleton() result = this.asSingleton()
@@ -333,12 +337,14 @@ module Public {
/** /**
* A content set containing only the given content. * A content set containing only the given content.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
ContentSet singleton(Content content) { result.asSingleton() = content } ContentSet singleton(Content content) { result.asSingleton() = content }
/** /**
* A content set corresponding to the given property name. * A content set corresponding to the given property name.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
ContentSet property(PropertyName name) { result.asSingleton().asPropertyName() = name } ContentSet property(PropertyName name) { result.asSingleton().asPropertyName() = name }
@@ -399,6 +405,7 @@ module Public {
* If `bound` is too large, it is truncated to the greatest lower bound we can represent. * If `bound` is too large, it is truncated to the greatest lower bound we can represent.
*/ */
bindingset[bound] bindingset[bound]
overlay[caller]
ContentSet arrayElementLowerBoundFromInt(int bound) { ContentSet arrayElementLowerBoundFromInt(int bound) {
result = arrayElementLowerBound(bound.minimum(getMaxPreciseArrayIndex() + 1)) result = arrayElementLowerBound(bound.minimum(getMaxPreciseArrayIndex() + 1))
} }
@@ -409,6 +416,7 @@ module Public {
* If `n` is too large, it is truncated to the greatest lower bound we can represent. * If `n` is too large, it is truncated to the greatest lower bound we can represent.
*/ */
bindingset[n] bindingset[n]
overlay[caller]
ContentSet arrayElementFromInt(int n) { ContentSet arrayElementFromInt(int n) {
result = arrayElementKnown(n) result = arrayElementKnown(n)
or or
@@ -448,6 +456,7 @@ module Public {
* If `key` is not one of the keys we track precisely, this is mapped to the unknown key instead. * If `key` is not one of the keys we track precisely, this is mapped to the unknown key instead.
*/ */
bindingset[key] bindingset[key]
overlay[caller]
ContentSet mapValueFromKey(string key) { ContentSet mapValueFromKey(string key) {
result = mapValueWithKnownKey(key) result = mapValueWithKnownKey(key)
or or
@@ -510,6 +519,7 @@ module Public {
* are mapped to their corresponding content sets (which are no longer seen as property names). * are mapped to their corresponding content sets (which are no longer seen as property names).
*/ */
bindingset[propertyName] bindingset[propertyName]
overlay[caller]
ContentSet fromLegacyProperty(string propertyName) { ContentSet fromLegacyProperty(string propertyName) {
result = fromLegacyPseudoProperty(propertyName) result = fromLegacyPseudoProperty(propertyName)
or or

View File

@@ -3,6 +3,8 @@
* *
* Contains the raw data type underlying `DataFlow::Node`. * Contains the raw data type underlying `DataFlow::Node`.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import codeql.util.Boolean private import codeql.util.Boolean

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.CallGraphs private import semmle.javascript.dataflow.internal.CallGraphs
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode
@@ -310,6 +313,7 @@ private predicate returnNodeImpl(DataFlow::Node node, ReturnKind kind) {
kind = MkExceptionalReturnKind() kind = MkExceptionalReturnKind()
} }
overlay[global]
private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) { private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
kind = MkNormalReturnKind() and result = call.asOrdinaryCall() kind = MkNormalReturnKind() and result = call.asOrdinaryCall()
or or
@@ -336,10 +340,12 @@ class ReturnNode extends DataFlow::Node {
} }
/** A node that receives an output from a call. */ /** A node that receives an output from a call. */
overlay[global]
class OutNode extends DataFlow::Node { class OutNode extends DataFlow::Node {
OutNode() { this = getAnOutNodeImpl(_, _) } OutNode() { this = getAnOutNodeImpl(_, _) }
} }
overlay[global]
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { result = getAnOutNodeImpl(call, kind) } OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { result = getAnOutNodeImpl(call, kind) }
cached cached
@@ -416,9 +422,11 @@ abstract class LibraryCallable extends string {
LibraryCallable() { any() } LibraryCallable() { any() }
/** Gets a call to this library callable. */ /** Gets a call to this library callable. */
overlay[global]
DataFlow::InvokeNode getACall() { none() } DataFlow::InvokeNode getACall() { none() }
/** Same as `getACall()` except this does not depend on the call graph or API graph. */ /** Same as `getACall()` except this does not depend on the call graph or API graph. */
overlay[global]
DataFlow::InvokeNode getACallSimple() { none() } DataFlow::InvokeNode getACallSimple() { none() }
} }
@@ -432,6 +440,7 @@ abstract class LibraryCallableInternal extends LibraryCallable {
* *
* Same as `getACall()` but is evaluated later and may depend negatively on `getACall()`. * Same as `getACall()` but is evaluated later and may depend negatively on `getACall()`.
*/ */
overlay[global]
DataFlow::InvokeNode getACallStage2() { none() } DataFlow::InvokeNode getACallStage2() { none() }
} }
@@ -467,6 +476,7 @@ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition
isParameterNodeImpl(p, c, pos) isParameterNodeImpl(p, c, pos)
} }
overlay[global]
private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition pos) { private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition pos) {
n = call.asOrdinaryCall().getArgument(pos.asPositional()) n = call.asOrdinaryCall().getArgument(pos.asPositional())
or or
@@ -523,6 +533,7 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
) )
} }
overlay[global]
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) { predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
isArgumentNodeImpl(n, call, pos) isArgumentNodeImpl(n, call, pos)
} }
@@ -545,11 +556,13 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
node instanceof DataFlow::XmlAttributeNode and result.asFileCallable() = node.getFile() node instanceof DataFlow::XmlAttributeNode and result.asFileCallable() = node.getFile()
} }
overlay[global]
newtype TDataFlowType = newtype TDataFlowType =
TFunctionType(Function f) or TFunctionType(Function f) or
TInstanceType(DataFlow::ClassNode cls) or TInstanceType(DataFlow::ClassNode cls) or
TAnyType() TAnyType()
overlay[global]
class DataFlowType extends TDataFlowType { class DataFlowType extends TDataFlowType {
string toDebugString() { string toDebugString() {
result = result =
@@ -575,6 +588,7 @@ class DataFlowType extends TDataFlowType {
/** /**
* Holds if `t1` is strictly stronger than `t2`. * Holds if `t1` is strictly stronger than `t2`.
*/ */
overlay[global]
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
// 't1' is a subclass of 't2' // 't1' is a subclass of 't2'
t1.asInstanceOfClass() = t2.asInstanceOfClass().getADirectSubClass+() t1.asInstanceOfClass() = t2.asInstanceOfClass().getADirectSubClass+()
@@ -584,6 +598,7 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
t2 = TAnyType() t2 = TAnyType()
} }
overlay[global]
private DataFlowType getPreciseType(Node node) { private DataFlowType getPreciseType(Node node) {
exists(Function f | exists(Function f |
(node = TValueNode(f) or node = TFunctionSelfReferenceNode(f)) and (node = TValueNode(f) or node = TFunctionSelfReferenceNode(f)) and
@@ -598,6 +613,7 @@ private DataFlowType getPreciseType(Node node) {
result = getPreciseType(node.(PostUpdateNode).getPreUpdateNode()) result = getPreciseType(node.(PostUpdateNode).getPreUpdateNode())
} }
overlay[global]
DataFlowType getNodeType(Node node) { DataFlowType getNodeType(Node node) {
result = getPreciseType(node) result = getPreciseType(node)
or or
@@ -681,19 +697,23 @@ predicate neverSkipInPathGraph(Node node) {
node.asExpr() instanceof VarRef node.asExpr() instanceof VarRef
} }
overlay[global]
string ppReprType(DataFlowType t) { none() } string ppReprType(DataFlowType t) { none() }
overlay[global]
pragma[inline] pragma[inline]
private predicate compatibleTypesWithAny(DataFlowType t1, DataFlowType t2) { private predicate compatibleTypesWithAny(DataFlowType t1, DataFlowType t2) {
t1 != TAnyType() and t1 != TAnyType() and
t2 = TAnyType() t2 = TAnyType()
} }
overlay[global]
pragma[nomagic] pragma[nomagic]
private predicate compatibleTypes1(DataFlowType t1, DataFlowType t2) { private predicate compatibleTypes1(DataFlowType t1, DataFlowType t2) {
t1.asInstanceOfClass().getADirectSubClass+() = t2.asInstanceOfClass() t1.asInstanceOfClass().getADirectSubClass+() = t2.asInstanceOfClass()
} }
overlay[global]
pragma[inline] pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 = t2 t1 = t2
@@ -742,6 +762,7 @@ class ContentApprox extends TContentApprox {
} }
} }
overlay[global]
pragma[inline] pragma[inline]
ContentApprox getContentApprox(Content c) { ContentApprox getContentApprox(Content c) {
c instanceof MkPropertyContent and result = TApproxPropertyContent() c instanceof MkPropertyContent and result = TApproxPropertyContent()
@@ -767,6 +788,7 @@ ContentApprox getContentApprox(Content c) {
c instanceof MkCapturedContent and result = TApproxCapturedContent() c instanceof MkCapturedContent and result = TApproxCapturedContent()
} }
overlay[global]
cached cached
private newtype TDataFlowCall = private newtype TDataFlowCall =
MkOrdinaryCall(DataFlow::InvokeNode node) or MkOrdinaryCall(DataFlow::InvokeNode node) or
@@ -791,6 +813,7 @@ private newtype TDataFlowCall =
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
} }
overlay[global]
class DataFlowCall extends TDataFlowCall { class DataFlowCall extends TDataFlowCall {
DataFlowCallable getEnclosingCallable() { none() } // Overridden in subclass DataFlowCallable getEnclosingCallable() { none() } // Overridden in subclass
@@ -816,6 +839,7 @@ class DataFlowCall extends TDataFlowCall {
Location getLocation() { none() } // Overridden in subclass Location getLocation() { none() } // Overridden in subclass
} }
overlay[global]
private class OrdinaryCall extends DataFlowCall, MkOrdinaryCall { private class OrdinaryCall extends DataFlowCall, MkOrdinaryCall {
private DataFlow::InvokeNode node; private DataFlow::InvokeNode node;
@@ -832,6 +856,7 @@ private class OrdinaryCall extends DataFlowCall, MkOrdinaryCall {
override Location getLocation() { result = node.getLocation() } override Location getLocation() { result = node.getLocation() }
} }
overlay[global]
private class PartialCall extends DataFlowCall, MkPartialCall { private class PartialCall extends DataFlowCall, MkPartialCall {
private DataFlow::PartialInvokeNode node; private DataFlow::PartialInvokeNode node;
private DataFlow::Node callback; private DataFlow::Node callback;
@@ -851,6 +876,7 @@ private class PartialCall extends DataFlowCall, MkPartialCall {
override Location getLocation() { result = node.getLocation() } override Location getLocation() { result = node.getLocation() }
} }
overlay[global]
private class BoundCall extends DataFlowCall, MkBoundCall { private class BoundCall extends DataFlowCall, MkBoundCall {
private DataFlow::InvokeNode node; private DataFlow::InvokeNode node;
private int boundArgs; private int boundArgs;
@@ -868,6 +894,7 @@ private class BoundCall extends DataFlowCall, MkBoundCall {
override Location getLocation() { result = node.getLocation() } override Location getLocation() { result = node.getLocation() }
} }
overlay[global]
private class AccessorCall extends DataFlowCall, MkAccessorCall { private class AccessorCall extends DataFlowCall, MkAccessorCall {
private DataFlow::PropRef ref; private DataFlow::PropRef ref;
@@ -882,6 +909,7 @@ private class AccessorCall extends DataFlowCall, MkAccessorCall {
override Location getLocation() { result = ref.getLocation() } override Location getLocation() { result = ref.getLocation() }
} }
overlay[global]
class SummaryCall extends DataFlowCall, MkSummaryCall { class SummaryCall extends DataFlowCall, MkSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable enclosingCallable; private FlowSummaryImpl::Public::SummarizedCallable enclosingCallable;
private FlowSummaryImpl::Private::SummaryNode receiver; private FlowSummaryImpl::Private::SummaryNode receiver;
@@ -908,6 +936,7 @@ class SummaryCall extends DataFlowCall, MkSummaryCall {
* This is to help ensure captured variables can flow into the lambda in cases where * This is to help ensure captured variables can flow into the lambda in cases where
* we can't find its call sites. * we can't find its call sites.
*/ */
overlay[global]
private class ImpliedLambdaCall extends DataFlowCall, MkImpliedLambdaCall { private class ImpliedLambdaCall extends DataFlowCall, MkImpliedLambdaCall {
private Function function; private Function function;
@@ -981,6 +1010,7 @@ class DataFlowExpr = Expr;
Node exprNode(DataFlowExpr expr) { result = DataFlow::exprNode(expr) } Node exprNode(DataFlowExpr expr) { result = DataFlow::exprNode(expr) }
overlay[global]
pragma[nomagic] pragma[nomagic]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
ppos = apos ppos = apos
@@ -993,6 +1023,7 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
// are only using these in cases where either the call or callee is generated by a flow summary. // are only using these in cases where either the call or callee is generated by a flow summary.
} }
overlay[global]
pragma[inline] pragma[inline]
DataFlowCallable viableCallable(DataFlowCall node) { DataFlowCallable viableCallable(DataFlowCall node) {
// Note: we never include call edges externs here, as it negatively affects the field-flow branch limit, // Note: we never include call edges externs here, as it negatively affects the field-flow branch limit,
@@ -1021,6 +1052,7 @@ DataFlowCallable viableCallable(DataFlowCall node) {
result.asSourceCallableNotExterns() = node.asImpliedLambdaCall() result.asSourceCallableNotExterns() = node.asImpliedLambdaCall()
} }
overlay[global]
private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) { private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
result.asOrdinaryCall() = cls.getAReceiverNode().getAPropertyRead().getACall() result.asOrdinaryCall() = cls.getAReceiverNode().getAPropertyRead().getACall()
or or
@@ -1029,6 +1061,7 @@ private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
result.asPartialCall().getACallbackNode() = cls.getAReceiverNode().getAPropertyRead() result.asPartialCall().getACallbackNode() = cls.getAReceiverNode().getAPropertyRead()
} }
overlay[global]
private predicate downwardCall(DataFlowCall call) { private predicate downwardCall(DataFlowCall call) {
exists(DataFlow::ClassNode cls | exists(DataFlow::ClassNode cls |
call = getACallOnThis(cls) and call = getACallOnThis(cls) and
@@ -1041,9 +1074,11 @@ private predicate downwardCall(DataFlowCall call) {
* Holds if the set of viable implementations that can be called by `call` * Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. * might be improved by knowing the call context.
*/ */
overlay[global]
predicate mayBenefitFromCallContext(DataFlowCall call) { downwardCall(call) } predicate mayBenefitFromCallContext(DataFlowCall call) { downwardCall(call) }
/** Gets the type of the receiver of `call`. */ /** Gets the type of the receiver of `call`. */
overlay[global]
private DataFlowType getThisArgumentType(DataFlowCall call) { private DataFlowType getThisArgumentType(DataFlowCall call) {
exists(DataFlow::Node node | exists(DataFlow::Node node |
isArgumentNodeImpl(node, call, MkThisParameter()) and isArgumentNodeImpl(node, call, MkThisParameter()) and
@@ -1052,6 +1087,7 @@ private DataFlowType getThisArgumentType(DataFlowCall call) {
} }
/** Gets the type of the 'this' parameter of `call`. */ /** Gets the type of the 'this' parameter of `call`. */
overlay[global]
private DataFlowType getThisParameterType(DataFlowCallable callable) { private DataFlowType getThisParameterType(DataFlowCallable callable) {
exists(DataFlow::Node node | exists(DataFlow::Node node |
isParameterNodeImpl(node, callable, MkThisParameter()) and isParameterNodeImpl(node, callable, MkThisParameter()) and
@@ -1063,6 +1099,7 @@ private DataFlowType getThisParameterType(DataFlowCallable callable) {
* Gets a viable dispatch target of `call` in the context `ctx`. This is * Gets a viable dispatch target of `call` in the context `ctx`. This is
* restricted to those `call`s for which a context might make a difference. * restricted to those `call`s for which a context might make a difference.
*/ */
overlay[global]
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
mayBenefitFromCallContext(call) and mayBenefitFromCallContext(call) and
result = viableCallable(call) and result = viableCallable(call) and
@@ -1071,16 +1108,19 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
} }
bindingset[node, fun] bindingset[node, fun]
overlay[caller]
pragma[inline_late] pragma[inline_late]
private predicate sameContainerAsEnclosingContainer(Node node, Function fun) { private predicate sameContainerAsEnclosingContainer(Node node, Function fun) {
node.getContainer() = fun.getEnclosingContainer() node.getContainer() = fun.getEnclosingContainer()
} }
overlay[global]
abstract private class BarrierGuardAdapter extends DataFlow::Node { abstract private class BarrierGuardAdapter extends DataFlow::Node {
// Note: avoid depending on DataFlow::FlowLabel here as it will cause these barriers to be re-evaluated // Note: avoid depending on DataFlow::FlowLabel here as it will cause these barriers to be re-evaluated
predicate blocksExpr(boolean outcome, Expr e) { none() } predicate blocksExpr(boolean outcome, Expr e) { none() }
} }
overlay[global]
deprecated private class BarrierGuardAdapterSubclass extends BarrierGuardAdapter instanceof DataFlow::AdditionalBarrierGuardNode deprecated private class BarrierGuardAdapterSubclass extends BarrierGuardAdapter instanceof DataFlow::AdditionalBarrierGuardNode
{ {
override predicate blocksExpr(boolean outcome, Expr e) { super.blocks(outcome, e) } override predicate blocksExpr(boolean outcome, Expr e) { super.blocks(outcome, e) }
@@ -1092,6 +1132,7 @@ deprecated private class BarrierGuardAdapterSubclass extends BarrierGuardAdapter
* *
* The standard library contains no subclasses of that class; this is for backwards compatibility only. * The standard library contains no subclasses of that class; this is for backwards compatibility only.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private predicate legacyBarrier(DataFlow::Node node) { private predicate legacyBarrier(DataFlow::Node node) {
node = MakeBarrierGuard<BarrierGuardAdapter>::getABarrierNode() node = MakeBarrierGuard<BarrierGuardAdapter>::getABarrierNode()
@@ -1100,6 +1141,7 @@ private predicate legacyBarrier(DataFlow::Node node) {
/** /**
* Holds if `node` should be removed from the local data flow graph, for compatibility with legacy code. * Holds if `node` should be removed from the local data flow graph, for compatibility with legacy code.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private predicate isBlockedLegacyNode(Node node) { private predicate isBlockedLegacyNode(Node node) {
// Ignore captured variable nodes for those variables that are handled by the captured-variable library. // Ignore captured variable nodes for those variables that are handled by the captured-variable library.
@@ -1155,6 +1197,7 @@ private predicate imprecisePostUpdateStep(DataFlow::PostUpdateNode postUpdate, D
* Holds if there is a value-preserving steps `node1` -> `node2` that might * Holds if there is a value-preserving steps `node1` -> `node2` that might
* be cross function boundaries. * be cross function boundaries.
*/ */
overlay[global]
private predicate valuePreservingStep(Node node1, Node node2) { private predicate valuePreservingStep(Node node1, Node node2) {
node1.getASuccessor() = node2 and node1.getASuccessor() = node2 and
not isBlockedLegacyNode(node1) and not isBlockedLegacyNode(node1) and
@@ -1223,10 +1266,12 @@ private predicate useUseFlow(Node node1, Node node2) {
) )
} }
overlay[global]
predicate simpleLocalFlowStep(Node node1, Node node2, string model) { predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
simpleLocalFlowStep(node1, node2) and model = "" simpleLocalFlowStep(node1, node2) and model = ""
} }
overlay[global]
predicate simpleLocalFlowStep(Node node1, Node node2) { predicate simpleLocalFlowStep(Node node1, Node node2) {
valuePreservingStep(node1, node2) and valuePreservingStep(node1, node2) and
nodeGetEnclosingCallable(pragma[only_bind_out](node1)) = nodeGetEnclosingCallable(pragma[only_bind_out](node1)) =
@@ -1314,6 +1359,7 @@ private predicate excludedJumpStep(Node node1, Node node2) {
* that does not follow a call edge. For example, a step through a global * that does not follow a call edge. For example, a step through a global
* variable. * variable.
*/ */
overlay[global]
predicate jumpStep(Node node1, Node node2) { predicate jumpStep(Node node1, Node node2) {
valuePreservingStep(node1, node2) and valuePreservingStep(node1, node2) and
node1.getContainer() != node2.getContainer() and node1.getContainer() != node2.getContainer() and
@@ -1330,6 +1376,7 @@ predicate jumpStep(Node node1, Node node2) {
* `node1` references an object with a content `c.getAReadContent()` whose * `node1` references an object with a content `c.getAReadContent()` whose
* value ends up in `node2`. * value ends up in `node2`.
*/ */
overlay[global]
predicate readStep(Node node1, ContentSet c, Node node2) { predicate readStep(Node node1, ContentSet c, Node node2) {
exists(DataFlow::PropRead read | exists(DataFlow::PropRead read |
node1 = read.getBase() and node1 = read.getBase() and
@@ -1447,6 +1494,7 @@ private predicate stringifiedNode(Node node) {
} }
/** Gets the post-update node for which `node` is the corresponding pre-update node. */ /** Gets the post-update node for which `node` is the corresponding pre-update node. */
pragma[nomagic]
private Node getPostUpdateForStore(Node base) { private Node getPostUpdateForStore(Node base) {
exists(Expr expr | exists(Expr expr |
base = TValueNode(expr) and base = TValueNode(expr) and
@@ -1469,6 +1517,7 @@ private Node getPostUpdateForStore(Node base) {
} }
/** Gets node to target with a store to the given `base` object.. */ /** Gets node to target with a store to the given `base` object.. */
overlay[caller]
pragma[inline] pragma[inline]
private Node getStoreTarget(DataFlow::Node base) { private Node getStoreTarget(DataFlow::Node base) {
result = getPostUpdateForStore(base) result = getPostUpdateForStore(base)
@@ -1487,6 +1536,7 @@ private int firstSpreadArgumentIndex(InvokeExpr expr) {
* `node2` references an object with a content `c.getAStoreContent()` that * `node2` references an object with a content `c.getAStoreContent()` that
* contains the value of `node1`. * contains the value of `node1`.
*/ */
overlay[global]
predicate storeStep(Node node1, ContentSet c, Node node2) { predicate storeStep(Node node1, ContentSet c, Node node2) {
exists(DataFlow::PropWrite write | exists(DataFlow::PropWrite write |
node1 = write.getRhs() and node1 = write.getRhs() and
@@ -1545,6 +1595,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
* any value stored inside `f` is cleared at the pre-update node associated with `x` * any value stored inside `f` is cleared at the pre-update node associated with `x`
* in `x.f = newValue`. * in `x.f = newValue`.
*/ */
overlay[global]
predicate clearsContent(Node n, ContentSet c) { predicate clearsContent(Node n, ContentSet c) {
FlowSummaryPrivate::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c) FlowSummaryPrivate::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or or
@@ -1578,6 +1629,7 @@ predicate clearsContent(Node n, ContentSet c) {
* Holds if the value that is being tracked is expected to be stored inside content `c` * Holds if the value that is being tracked is expected to be stored inside content `c`
* at node `n`. * at node `n`.
*/ */
overlay[global]
predicate expectsContent(Node n, ContentSet c) { predicate expectsContent(Node n, ContentSet c) {
FlowSummaryPrivate::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c) FlowSummaryPrivate::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or or
@@ -1602,6 +1654,7 @@ abstract class NodeRegion extends Unit {
/** /**
* Holds if the node `n` is unreachable when the call context is `call`. * Holds if the node `n` is unreachable when the call context is `call`.
*/ */
overlay[global]
predicate isUnreachableInCall(NodeRegion n, DataFlowCall call) { predicate isUnreachableInCall(NodeRegion n, DataFlowCall call) {
none() // TODO: could be useful, but not currently implemented for JS none() // TODO: could be useful, but not currently implemented for JS
} }
@@ -1635,6 +1688,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
} }
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ /** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
overlay[global]
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode()) and exists(kind) call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode()) and exists(kind)
or or
@@ -1646,6 +1700,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
/** Extra data-flow steps needed for lambda flow analysis. */ /** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
overlay[global]
class ArgumentNode extends DataFlow::Node { class ArgumentNode extends DataFlow::Node {
ArgumentNode() { isArgumentNodeImpl(this, _, _) } ArgumentNode() { isArgumentNodeImpl(this, _, _) }

View File

@@ -48,6 +48,7 @@ private predicate legacyPostUpdateStep(DataFlow::Node pred, DataFlow::Node succ)
* Holds if data can flow in one step from `pred` to `succ`, taking * Holds if data can flow in one step from `pred` to `succ`, taking
* additional steps from the configuration into account. * additional steps from the configuration into account.
*/ */
overlay[caller?]
pragma[inline] pragma[inline]
deprecated predicate localFlowStep( deprecated predicate localFlowStep(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration configuration, DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration configuration,
@@ -523,6 +524,7 @@ private module CachedSteps {
/** /**
* Holds if there is a step from `pred` to `succ` through a call to an identity function. * Holds if there is a step from `pred` to `succ` through a call to an identity function.
*/ */
overlay[local]
cached cached
predicate identityFunctionStep(DataFlow::Node pred, DataFlow::CallNode succ) { predicate identityFunctionStep(DataFlow::Node pred, DataFlow::CallNode succ) {
exists(DataFlow::GlobalVarRefNode global | exists(DataFlow::GlobalVarRefNode global |

View File

@@ -1,6 +1,8 @@
/** /**
* Provides JS specific classes and predicates for defining flow summaries. * Provides JS specific classes and predicates for defining flow summaries.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowPrivate private import semmle.javascript.dataflow.internal.DataFlowPrivate
@@ -140,6 +142,7 @@ string encodeArgumentPosition(ArgumentPosition pos) {
ReturnKind getStandardReturnValueKind() { result = MkNormalReturnKind() and Stage::ref() } ReturnKind getStandardReturnValueKind() { result = MkNormalReturnKind() and Stage::ref() }
private module FlowSummaryStepInput implements Private::StepsInputSig { private module FlowSummaryStepInput implements Private::StepsInputSig {
overlay[global]
DataFlowCall getACall(SummarizedCallable sc) { DataFlowCall getACall(SummarizedCallable sc) {
exists(LibraryCallable callable | callable = sc | exists(LibraryCallable callable | callable = sc |
result.asOrdinaryCall() = result.asOrdinaryCall() =

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import javascript as js private import javascript as js
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode
private import semmle.javascript.dataflow.internal.VariableOrThis private import semmle.javascript.dataflow.internal.VariableOrThis

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import javascript private import javascript
private import DataFlowNode private import DataFlowNode

View File

@@ -3,6 +3,8 @@
* *
* JavaScript's old SSA library is still responsible for the ordinary SSA flow. * JavaScript's old SSA library is still responsible for the ordinary SSA flow.
*/ */
overlay[local]
module;
private import javascript as js private import javascript as js
private import codeql.ssa.Ssa private import codeql.ssa.Ssa
@@ -94,6 +96,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig {
} }
} }
overlay[caller?]
pragma[inline] pragma[inline]
predicate guardDirectlyControlsBlock(Guard guard, js::Cfg::BasicBlock bb, GuardValue branch) { predicate guardDirectlyControlsBlock(Guard guard, js::Cfg::BasicBlock bb, GuardValue branch) {
exists(js::ConditionGuardNode g | exists(js::ConditionGuardNode g |

View File

@@ -15,6 +15,7 @@ private import AngularJS
/** /**
* Holds if `nd` is a reference to the `angular` variable. * Holds if `nd` is a reference to the `angular` variable.
*/ */
overlay[local]
DataFlow::SourceNode angular() { DataFlow::SourceNode angular() {
// either as a global // either as a global
result = DataFlow::globalVarRef("angular") result = DataFlow::globalVarRef("angular")

View File

@@ -172,6 +172,7 @@ module AsyncPackage {
DataFlow::FunctionNode getFinalCallback() { result = this.getCallback(finalCallbackIndex) } DataFlow::FunctionNode getFinalCallback() { result = this.getCallback(finalCallbackIndex) }
} }
overlay[local?]
private class IterationCallFlowSummary extends DataFlow::SummarizedCallable { private class IterationCallFlowSummary extends DataFlow::SummarizedCallable {
private int callbackArgIndex; private int callbackArgIndex;
@@ -219,6 +220,7 @@ module AsyncPackage {
* *
* For example: `data -> result` in `async.sortBy(data, orderingFn, (err, result) => {})`. * For example: `data -> result` in `async.sortBy(data, orderingFn, (err, result) => {})`.
*/ */
overlay[local?]
private class IterationPreserveTaintStepFlowSummary extends DataFlow::SummarizedCallable { private class IterationPreserveTaintStepFlowSummary extends DataFlow::SummarizedCallable {
IterationPreserveTaintStepFlowSummary() { this = "async.sortBy" } IterationPreserveTaintStepFlowSummary() { this = "async.sortBy" }

View File

@@ -1,6 +1,8 @@
/** /**
* Models imports through the NPM `lazy-cache` package. * Models imports through the NPM `lazy-cache` package.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -43,7 +45,7 @@ module LazyCache {
pragma[noopt] pragma[noopt]
override DataFlow::Node getImportedModuleNode() { override DataFlow::Node getImportedModuleNode() {
this instanceof LazyCacheImport and this instanceof LazyCacheImport and
result = this.flow() result = DataFlow::valueNode(this)
or or
exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName | exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName |
// To avoid recursion, this should not depend on `SourceNode`. // To avoid recursion, this should not depend on `SourceNode`.
@@ -52,12 +54,13 @@ module LazyCache {
access.getBase() = base and access.getBase() = base and
localName = this.getLocalAlias() and localName = this.getLocalAlias() and
access.getPropertyName() = localName and access.getPropertyName() = localName and
result = access.flow() result = DataFlow::valueNode(access)
) )
} }
} }
/** A constant path element appearing in a call to a lazy-cache object. */ /** A constant path element appearing in a call to a lazy-cache object. */
overlay[global]
deprecated private class LazyCachePathExpr extends PathExpr, ConstantString { deprecated private class LazyCachePathExpr extends PathExpr, ConstantString {
LazyCachePathExpr() { this = any(LazyCacheImport rp).getArgument(0) } LazyCachePathExpr() { this = any(LazyCacheImport rp).getArgument(0) }

View File

@@ -9,6 +9,7 @@ module LodashUnderscore {
/** /**
* A data flow node that accesses a given member of `lodash` or `underscore`. * A data flow node that accesses a given member of `lodash` or `underscore`.
*/ */
overlay[local]
abstract class Member extends DataFlow::SourceNode { abstract class Member extends DataFlow::SourceNode {
/** Gets the name of the accessed member. */ /** Gets the name of the accessed member. */
abstract string getName(); abstract string getName();
@@ -17,6 +18,7 @@ module LodashUnderscore {
/** /**
* 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.
*/ */
overlay[local]
private class DefaultMember extends Member { private class DefaultMember extends Member {
string name; string name;
@@ -39,12 +41,14 @@ module LodashUnderscore {
* In addition to normal imports, this supports per-method imports such as `require("lodash.map")` and `require("lodash/map")`. * In addition to normal imports, this supports per-method imports such as `require("lodash.map")` and `require("lodash/map")`.
* In addition, the global variable `_` is assumed to refer to `lodash` or `underscore`. * In addition, the global variable `_` is assumed to refer to `lodash` or `underscore`.
*/ */
overlay[local]
DataFlow::SourceNode member(string name) { result.(Member).getName() = name } DataFlow::SourceNode member(string name) { result.(Member).getName() = name }
/** /**
* Holds if `name` is the name of a member exported from the `lodash` package * Holds if `name` is the name of a member exported from the `lodash` package
* which has a corresponding `lodash.xxx` NPM package. * which has a corresponding `lodash.xxx` NPM package.
*/ */
overlay[local]
private predicate isLodashMember(string name) { private predicate isLodashMember(string name) {
// Can be generated using Object.keys(require('lodash')) // Can be generated using Object.keys(require('lodash'))
name = name =
@@ -181,9 +185,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashEach extends DataFlow::SummarizedCallable { private class LodashEach extends DataFlow::SummarizedCallable {
LodashEach() { this = "_.each-like" } LodashEach() { this = "_.each-like" }
overlay[global]
override DataFlow::CallNode getACall() { override DataFlow::CallNode getACall() {
result = member(["each", "eachRight", "forEach", "forEachRight", "every", "some"]).getACall() result = member(["each", "eachRight", "forEach", "forEachRight", "every", "some"]).getACall()
} }
@@ -195,9 +201,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashMap extends DataFlow::SummarizedCallable { private class LodashMap extends DataFlow::SummarizedCallable {
LodashMap() { this = "_.map" } LodashMap() { this = "_.map" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("map").getACall() } override DataFlow::CallNode getACall() { result = member("map").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -212,9 +220,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashFlatMap extends DataFlow::SummarizedCallable { private class LodashFlatMap extends DataFlow::SummarizedCallable {
LodashFlatMap() { this = "_.flatMap" } LodashFlatMap() { this = "_.flatMap" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("flatMap").getACall() } override DataFlow::CallNode getACall() { result = member("flatMap").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -232,9 +242,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashFlatMapDeep extends DataFlow::SummarizedCallable { private class LodashFlatMapDeep extends DataFlow::SummarizedCallable {
LodashFlatMapDeep() { this = "_.flatMapDeep" } LodashFlatMapDeep() { this = "_.flatMapDeep" }
overlay[global]
override DataFlow::CallNode getACall() { override DataFlow::CallNode getACall() {
result = member(["flatMapDeep", "flatMapDepth"]).getACall() result = member(["flatMapDeep", "flatMapDepth"]).getACall()
} }
@@ -254,9 +266,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashReduce extends DataFlow::SummarizedCallable { private class LodashReduce extends DataFlow::SummarizedCallable {
LodashReduce() { this = "_.reduce-like" } LodashReduce() { this = "_.reduce-like" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["reduce", "reduceRight"]).getACall() } override DataFlow::CallNode getACall() { result = member(["reduce", "reduceRight"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -271,9 +285,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LoashSortBy extends DataFlow::SummarizedCallable { private class LoashSortBy extends DataFlow::SummarizedCallable {
LoashSortBy() { this = "_.sortBy-like" } LoashSortBy() { this = "_.sortBy-like" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["sortBy", "orderBy"]).getACall() } override DataFlow::CallNode getACall() { result = member(["sortBy", "orderBy"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -287,9 +303,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashMinMaxBy extends DataFlow::SummarizedCallable { private class LodashMinMaxBy extends DataFlow::SummarizedCallable {
LodashMinMaxBy() { this = "_.minBy / _.maxBy" } LodashMinMaxBy() { this = "_.minBy / _.maxBy" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["minBy", "maxBy"]).getACall() } override DataFlow::CallNode getACall() { result = member(["minBy", "maxBy"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -299,9 +317,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashPartition extends DataFlow::SummarizedCallable { private class LodashPartition extends DataFlow::SummarizedCallable {
LodashPartition() { this = "_.partition" } LodashPartition() { this = "_.partition" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("partition").getACall() } override DataFlow::CallNode getACall() { result = member("partition").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -311,9 +331,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class UnderscoreMapObject extends DataFlow::SummarizedCallable { private class UnderscoreMapObject extends DataFlow::SummarizedCallable {
UnderscoreMapObject() { this = "_.mapObject" } UnderscoreMapObject() { this = "_.mapObject" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("mapObject").getACall() } override DataFlow::CallNode getACall() { result = member("mapObject").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -330,9 +352,11 @@ module LodashUnderscore {
} }
} }
overlay[local]
private class LodashTap extends DataFlow::SummarizedCallable { private class LodashTap extends DataFlow::SummarizedCallable {
LodashTap() { this = "_.tap" } LodashTap() { this = "_.tap" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("tap").getACall() } override DataFlow::CallNode getACall() { result = member("tap").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -342,6 +366,7 @@ module LodashUnderscore {
} }
} }
overlay[local?]
private class LodashGroupBy extends DataFlow::SummarizedCallable { private class LodashGroupBy extends DataFlow::SummarizedCallable {
LodashGroupBy() { this = "_.groupBy" } LodashGroupBy() { this = "_.groupBy" }

View File

@@ -8,14 +8,17 @@ import semmle.javascript.security.SensitiveActions
private import semmle.javascript.dataflow.internal.PreCallGraphStep private import semmle.javascript.dataflow.internal.PreCallGraphStep
module NodeJSLib { module NodeJSLib {
overlay[local]
private GlobalVariable processVariable() { variables(result, "process", any(GlobalScope sc)) } private GlobalVariable processVariable() { variables(result, "process", any(GlobalScope sc)) }
overlay[local]
pragma[nomagic] pragma[nomagic]
private GlobalVarAccess processExprInTopLevel(TopLevel tl) { private GlobalVarAccess processExprInTopLevel(TopLevel tl) {
result = processVariable().getAnAccess() and result = processVariable().getAnAccess() and
tl = result.getTopLevel() tl = result.getTopLevel()
} }
overlay[local]
pragma[nomagic] pragma[nomagic]
private GlobalVarAccess processExprInNodeModule() { private GlobalVarAccess processExprInNodeModule() {
result = processExprInTopLevel(any(NodeModule m)) result = processExprInTopLevel(any(NodeModule m))
@@ -25,6 +28,7 @@ module NodeJSLib {
* An access to the global `process` variable in a Node.js module, interpreted as * An access to the global `process` variable in a Node.js module, interpreted as
* an import of the `process` module. * an import of the `process` module.
*/ */
overlay[local]
private class ImplicitProcessImport extends DataFlow::ModuleImportNode::Range { private class ImplicitProcessImport extends DataFlow::ModuleImportNode::Range {
ImplicitProcessImport() { this = DataFlow::exprNode(processExprInNodeModule()) } ImplicitProcessImport() { this = DataFlow::exprNode(processExprInNodeModule()) }

View File

@@ -4,6 +4,8 @@
* Subclass `PropertyProjection` to refine the behavior of the analysis on existing property projections. * Subclass `PropertyProjection` to refine the behavior of the analysis on existing property projections.
* Subclass `CustomPropertyProjection` to introduce new kinds of property projections. * Subclass `CustomPropertyProjection` to introduce new kinds of property projections.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -137,6 +139,7 @@ private class VarArgsPropertyProjection extends PropertyProjection::Range {
/** /**
* A taint step for a property projection. * A taint step for a property projection.
*/ */
overlay[global]
private class PropertyProjectionTaintStep extends TaintTracking::SharedTaintStep { private class PropertyProjectionTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
// reading from a tainted object yields a tainted result // reading from a tainted object yields a tainted result

View File

@@ -1,6 +1,8 @@
/** /**
* Provides predicates for working with templating libraries. * Provides predicates for working with templating libraries.
*/ */
overlay[local]
module;
import javascript import javascript
@@ -45,6 +47,7 @@ module Templating {
Locatable getParent() { template_placeholder_tag_info(this, result, _) } Locatable getParent() { template_placeholder_tag_info(this, result, _) }
/** Gets a data flow node representing the value plugged into this placeholder. */ /** Gets a data flow node representing the value plugged into this placeholder. */
overlay[global]
DataFlow::TemplatePlaceholderTagNode asDataFlowNode() { result.getTag() = this } DataFlow::TemplatePlaceholderTagNode asDataFlowNode() { result.getTag() = this }
/** Gets the top-level containing the template expression to be inserted at this placeholder. */ /** Gets the top-level containing the template expression to be inserted at this placeholder. */
@@ -54,6 +57,7 @@ module Templating {
* Holds if this performs raw interpolation, that is, inserts its result * Holds if this performs raw interpolation, that is, inserts its result
* in the output without escaping it. * in the output without escaping it.
*/ */
overlay[global]
predicate isRawInterpolation() { predicate isRawInterpolation() {
this.getRawText() this.getRawText()
.regexpMatch(getLikelyTemplateSyntax(this.getFile()).getRawInterpolationRegexp()) .regexpMatch(getLikelyTemplateSyntax(this.getFile()).getRawInterpolationRegexp())
@@ -62,6 +66,7 @@ module Templating {
/** /**
* Holds if this performs HTML escaping on the result before inserting it in the template. * Holds if this performs HTML escaping on the result before inserting it in the template.
*/ */
overlay[global]
predicate isEscapingInterpolation() { predicate isEscapingInterpolation() {
this.getRawText() this.getRawText()
.regexpMatch(getLikelyTemplateSyntax(this.getFile()).getEscapingInterpolationRegexp()) .regexpMatch(getLikelyTemplateSyntax(this.getFile()).getEscapingInterpolationRegexp())
@@ -93,6 +98,7 @@ module Templating {
* Holds if this placeholder occurs in the definition of another template, which means the output * Holds if this placeholder occurs in the definition of another template, which means the output
* is susceptible to code injection. * is susceptible to code injection.
*/ */
overlay[global]
predicate isInNestedTemplateContext(string templateType) { predicate isInNestedTemplateContext(string templateType) {
templateType = "AngularJS" and templateType = "AngularJS" and
AngularJS::isInterpretedByAngularJS(this.getParent()) and AngularJS::isInterpretedByAngularJS(this.getParent()) and
@@ -135,6 +141,7 @@ module Templating {
* *
* For example, the call generated from `items | async` would be found by `getAPipeCall("async")`. * For example, the call generated from `items | async` would be found by `getAPipeCall("async")`.
*/ */
overlay[global]
DataFlow::CallNode getAPipeCall(string name) { DataFlow::CallNode getAPipeCall(string name) {
result.getCalleeNode().asExpr().(PipeRefExpr).getName() = name result.getCalleeNode().asExpr().(PipeRefExpr).getName() = name
} }
@@ -153,16 +160,19 @@ module Templating {
Expr getExpression() { result = this.getChildStmt(0).(ExprStmt).getExpr() } Expr getExpression() { result = this.getChildStmt(0).(ExprStmt).getExpr() }
/** Gets the data flow node representing the initialization of the given variable in this scope. */ /** Gets the data flow node representing the initialization of the given variable in this scope. */
overlay[global]
DataFlow::Node getVariableInit(string name) { DataFlow::Node getVariableInit(string name) {
result = DataFlow::ssaDefinitionNode(Ssa::implicitInit(this.getScope().getVariable(name))) result = DataFlow::ssaDefinitionNode(Ssa::implicitInit(this.getScope().getVariable(name)))
} }
/** Gets a data flow node corresponding to a use of the given template variable within this top-level. */ /** Gets a data flow node corresponding to a use of the given template variable within this top-level. */
overlay[global]
DataFlow::SourceNode getAVariableUse(string name) { DataFlow::SourceNode getAVariableUse(string name) {
result = this.getScope().getVariable(name).getAnAccess().flow() result = this.getScope().getVariable(name).getAnAccess().flow()
} }
/** Gets a data flow node corresponding to a use of the given template variable within this top-level. */ /** Gets a data flow node corresponding to a use of the given template variable within this top-level. */
overlay[global]
DataFlow::SourceNode getAnAccessPathUse(string accessPath) { DataFlow::SourceNode getAnAccessPathUse(string accessPath) {
result = this.getAVariableUse(accessPath) result = this.getAVariableUse(accessPath)
or or
@@ -177,6 +187,7 @@ module Templating {
/** /**
* A place where a template is instantiated or rendered. * A place where a template is instantiated or rendered.
*/ */
overlay[global]
class TemplateInstantiation extends DataFlow::Node instanceof TemplateInstantiation::Range { class TemplateInstantiation extends DataFlow::Node instanceof TemplateInstantiation::Range {
/** Gets a data flow node that refers to the instantiated template string, if any. */ /** Gets a data flow node that refers to the instantiated template string, if any. */
DataFlow::SourceNode getOutput() { result = super.getOutput() } DataFlow::SourceNode getOutput() { result = super.getOutput() }
@@ -206,6 +217,7 @@ module Templating {
} }
/** Companion module to the `TemplateInstantiation` class. */ /** Companion module to the `TemplateInstantiation` class. */
overlay[global]
module TemplateInstantiation { module TemplateInstantiation {
abstract class Range extends DataFlow::Node { abstract class Range extends DataFlow::Node {
/** Gets a data flow node that refers to the instantiated template, if any. */ /** Gets a data flow node that refers to the instantiated template, if any. */
@@ -230,6 +242,7 @@ module Templating {
} }
/** Gets an API node that may flow to `succ` through a template instantiation. */ /** Gets an API node that may flow to `succ` through a template instantiation. */
overlay[global]
private API::Node getTemplateInput(DataFlow::SourceNode succ) { private API::Node getTemplateInput(DataFlow::SourceNode succ) {
exists(TemplateInstantiation inst, API::Node base, string name | exists(TemplateInstantiation inst, API::Node base, string name |
base.asSink() = inst.getTemplateParamsNode() and base.asSink() = inst.getTemplateParamsNode() and
@@ -258,6 +271,7 @@ module Templating {
) )
} }
overlay[global]
private class TemplateInputStep extends DataFlow::SharedFlowStep { private class TemplateInputStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
getTemplateInput(succ).asSink() = pred getTemplateInput(succ).asSink() = pred
@@ -268,6 +282,7 @@ module Templating {
* A data flow step from the expression in a placeholder tag to the tag itself, * A data flow step from the expression in a placeholder tag to the tag itself,
* representing the value plugged into the template. * representing the value plugged into the template.
*/ */
overlay[global]
private class TemplatePlaceholderStep extends DataFlow::SharedFlowStep { private class TemplatePlaceholderStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(TemplatePlaceholderTag tag | exists(TemplatePlaceholderTag tag |
@@ -281,6 +296,7 @@ module Templating {
* A taint step from a `TemplatePlaceholderTag` to the enclosing expression in the * A taint step from a `TemplatePlaceholderTag` to the enclosing expression in the
* surrounding JavaScript program. * surrounding JavaScript program.
*/ */
overlay[global]
private class PlaceholderToGeneratedCodeStep extends TaintTracking::SharedTaintStep { private class PlaceholderToGeneratedCodeStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(TemplatePlaceholderTag tag | exists(TemplatePlaceholderTag tag |
@@ -296,6 +312,7 @@ module Templating {
final TemplatePlaceholderTag getAPlaceholder() { result.getFile() = this } final TemplatePlaceholderTag getAPlaceholder() { result.getFile() = this }
/** Gets a template file referenced by this one via a template inclusion tag, such as `{% include foo %}` */ /** Gets a template file referenced by this one via a template inclusion tag, such as `{% include foo %}` */
overlay[global]
TemplateFile getAnImportedFile() { TemplateFile getAnImportedFile() {
result = this.getAPlaceholder().(TemplateInclusionTag).getImportedFile() result = this.getAPlaceholder().(TemplateInclusionTag).getImportedFile()
} }
@@ -314,6 +331,7 @@ module Templating {
* - The root folder is considered unknown, and so a heuristic is used to guess the most * - The root folder is considered unknown, and so a heuristic is used to guess the most
* likely template file being referenced. * likely template file being referenced.
*/ */
overlay[global]
abstract class TemplateFileReference extends DataFlow::Node { abstract class TemplateFileReference extends DataFlow::Node {
/** Gets the value that identifies the template. */ /** Gets the value that identifies the template. */
string getValue() { string getValue() {
@@ -335,6 +353,7 @@ module Templating {
} }
/** Get file argument of a template instantiation, seen as a template file reference. */ /** Get file argument of a template instantiation, seen as a template file reference. */
overlay[global]
private class DefaultTemplateFileReference extends TemplateFileReference { private class DefaultTemplateFileReference extends TemplateFileReference {
DefaultTemplateFileReference() { this = any(TemplateInstantiation inst).getTemplateFileNode() } DefaultTemplateFileReference() { this = any(TemplateInstantiation inst).getTemplateFileNode() }
} }
@@ -352,6 +371,7 @@ module Templating {
* - The root folder is considered unknown, and so a heuristic is used to guess the most * - The root folder is considered unknown, and so a heuristic is used to guess the most
* likely template file being referenced. * likely template file being referenced.
*/ */
overlay[global]
abstract class TemplateFileReferenceString extends string { abstract class TemplateFileReferenceString extends string {
bindingset[this] bindingset[this]
TemplateFileReferenceString() { this = this } TemplateFileReferenceString() { this = this }
@@ -382,6 +402,7 @@ module Templating {
} }
/** The value of a template reference node, as a template reference string. */ /** The value of a template reference node, as a template reference string. */
overlay[global]
private class DefaultTemplateReferenceString extends TemplateFileReferenceString { private class DefaultTemplateReferenceString extends TemplateFileReferenceString {
TemplateFileReference r; TemplateFileReference r;
@@ -397,6 +418,7 @@ module Templating {
} }
/** The `X` in a path of form `../X`, treated as a separate path string with a different context folder. */ /** The `X` in a path of form `../X`, treated as a separate path string with a different context folder. */
overlay[global]
private class UpwardTraversalSuffix extends TemplateFileReferenceString { private class UpwardTraversalSuffix extends TemplateFileReferenceString {
TemplateFileReferenceString original; TemplateFileReferenceString original;
@@ -412,6 +434,7 @@ module Templating {
* Gets a "fingerprint" for the given template file, which is used to references * Gets a "fingerprint" for the given template file, which is used to references
* that might refer to it (for pruning purposes only). * that might refer to it (for pruning purposes only).
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private string getTemplateFileFingerprint(TemplateFile file) { private string getTemplateFileFingerprint(TemplateFile file) {
result = file.getStem() result = file.getStem()
@@ -424,6 +447,7 @@ module Templating {
* Gets a "fingerprint" for the given string, which must match one of the fingerprints of * Gets a "fingerprint" for the given string, which must match one of the fingerprints of
* the referenced file (for pruning purposes only). * the referenced file (for pruning purposes only).
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private string getTemplateRefFingerprint(TemplateFileReferenceString ref) { private string getTemplateRefFingerprint(TemplateFileReferenceString ref) {
result = ref.getStem() and not result = ["index", ""] result = ref.getStem() and not result = ["index", ""]
@@ -442,6 +466,7 @@ module Templating {
* *
* This is only used to speed up `getAMatchingTarget` by pruning out pairs that can't match. * This is only used to speed up `getAMatchingTarget` by pruning out pairs that can't match.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private TemplateFile getAPotentialTarget(TemplateFileReferenceString ref) { private TemplateFile getAPotentialTarget(TemplateFileReferenceString ref) {
getTemplateFileFingerprint(result) = getTemplateRefFingerprint(ref) getTemplateFileFingerprint(result) = getTemplateRefFingerprint(ref)
@@ -467,6 +492,7 @@ module Templating {
* Additionally, a file whose stem is `index` matches if `ref` would match the parent folder by * Additionally, a file whose stem is `index` matches if `ref` would match the parent folder by
* the above rules. For example: `bar` matches `src/bar/index.html`. * the above rules. For example: `bar` matches `src/bar/index.html`.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private TemplateFile getAMatchingTarget(TemplateFileReferenceString ref) { private TemplateFile getAMatchingTarget(TemplateFileReferenceString ref) {
result = getAPotentialTarget(ref) and result = getAPotentialTarget(ref) and
@@ -491,6 +517,7 @@ module Templating {
* The string `list` in `A/components/foo.js` will resolve to `A/views/list.html`, * The string `list` in `A/components/foo.js` will resolve to `A/views/list.html`,
* and vice versa in `B/components/foo.js`. * and vice versa in `B/components/foo.js`.
*/ */
overlay[global]
pragma[nomagic] pragma[nomagic]
private int getRankOfMatchingTarget( private int getRankOfMatchingTarget(
TemplateFile file, Folder baseFolder, TemplateFileReferenceString ref TemplateFile file, Folder baseFolder, TemplateFileReferenceString ref
@@ -508,6 +535,7 @@ module Templating {
/** /**
* Gets the template file referred to by `ref` when resolved from `baseFolder`. * Gets the template file referred to by `ref` when resolved from `baseFolder`.
*/ */
overlay[global]
private TemplateFile getBestMatchingTarget(Folder baseFolder, TemplateFileReferenceString ref) { private TemplateFile getBestMatchingTarget(Folder baseFolder, TemplateFileReferenceString ref) {
result = max(getAMatchingTarget(ref) as f order by getRankOfMatchingTarget(f, baseFolder, ref)) result = max(getAMatchingTarget(ref) as f order by getRankOfMatchingTarget(f, baseFolder, ref))
} }
@@ -599,6 +627,7 @@ module Templating {
override string getAPackageName() { result = "dot" } override string getAPackageName() { result = "dot" }
} }
overlay[global]
private TemplateSyntax getOwnTemplateSyntaxInFolder(Folder f) { private TemplateSyntax getOwnTemplateSyntaxInFolder(Folder f) {
exists(PackageDependencies deps | exists(PackageDependencies deps |
deps.getADependency(result.getAPackageName(), _) and deps.getADependency(result.getAPackageName(), _) and
@@ -606,6 +635,7 @@ module Templating {
) )
} }
overlay[global]
private TemplateSyntax getTemplateSyntaxInFolder(Folder f) { private TemplateSyntax getTemplateSyntaxInFolder(Folder f) {
result = getOwnTemplateSyntaxInFolder(f) result = getOwnTemplateSyntaxInFolder(f)
or or
@@ -613,6 +643,7 @@ module Templating {
result = getTemplateSyntaxInFolder(f.getParentContainer()) result = getTemplateSyntaxInFolder(f.getParentContainer())
} }
overlay[global]
private TemplateSyntax getTemplateSyntaxFromInstantiation(TemplateFile file) { private TemplateSyntax getTemplateSyntaxFromInstantiation(TemplateFile file) {
result = any(TemplateInstantiation inst | inst.getTemplateFile() = file).getTemplateSyntax() result = any(TemplateInstantiation inst | inst.getTemplateFile() = file).getTemplateSyntax()
} }
@@ -620,6 +651,7 @@ module Templating {
/** /**
* Gets a template syntax likely to be used in the given file. * Gets a template syntax likely to be used in the given file.
*/ */
overlay[global]
TemplateSyntax getLikelyTemplateSyntax(TemplateFile file) { TemplateSyntax getLikelyTemplateSyntax(TemplateFile file) {
result = getTemplateSyntaxFromInstantiation(file) result = getTemplateSyntaxFromInstantiation(file)
or or
@@ -632,6 +664,7 @@ module Templating {
} }
/** A step through the `safe` pipe, which bypasses HTML escaping. */ /** A step through the `safe` pipe, which bypasses HTML escaping. */
overlay[global]
private class SafePipeStep extends TaintTracking::SharedTaintStep { private class SafePipeStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | exists(DataFlow::CallNode call |
@@ -645,6 +678,7 @@ module Templating {
/** /**
* An EJS-style `include` call within a template tag, such as `<%- include(file, { params }) %>`. * An EJS-style `include` call within a template tag, such as `<%- include(file, { params }) %>`.
*/ */
overlay[global]
private class EjsIncludeCallInTemplate extends TemplateInstantiation::Range, DataFlow::CallNode { private class EjsIncludeCallInTemplate extends TemplateInstantiation::Range, DataFlow::CallNode {
EjsIncludeCallInTemplate() { EjsIncludeCallInTemplate() {
exists(TemplatePlaceholderTag tag | exists(TemplatePlaceholderTag tag |
@@ -669,6 +703,7 @@ module Templating {
* *
* These API nodes are used in the `getTemplateInput` predicate. * These API nodes are used in the `getTemplateInput` predicate.
*/ */
overlay[global]
private class IncludeFunctionAsEntryPoint extends API::EntryPoint { private class IncludeFunctionAsEntryPoint extends API::EntryPoint {
IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" } IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" }
@@ -703,6 +738,7 @@ module Templating {
string getPath() { result = rawPath.trim().replaceAll("\\", "/").regexpReplaceAll("^\\./", "") } string getPath() { result = rawPath.trim().replaceAll("\\", "/").regexpReplaceAll("^\\./", "") }
/** Gets the file referenced by this inclusion tag. */ /** Gets the file referenced by this inclusion tag. */
overlay[global]
TemplateFile getImportedFile() { TemplateFile getImportedFile() {
result = result =
this.getPath() this.getPath()
@@ -712,6 +748,7 @@ module Templating {
} }
/** The imported string from a template inclusion tag. */ /** The imported string from a template inclusion tag. */
overlay[global]
private class TemplateInclusionPathString extends TemplateFileReferenceString { private class TemplateInclusionPathString extends TemplateFileReferenceString {
TemplateInclusionTag tag; TemplateInclusionTag tag;
@@ -723,6 +760,7 @@ module Templating {
/** /**
* A call to a member of the `consolidate` library, seen as a template instantiation. * A call to a member of the `consolidate` library, seen as a template instantiation.
*/ */
overlay[global]
private class ConsolidateCall extends TemplateInstantiation::Range, API::CallNode { private class ConsolidateCall extends TemplateInstantiation::Range, API::CallNode {
string engine; string engine;

View File

@@ -422,9 +422,11 @@ private module ClosureLibraryUri {
} }
} }
overlay[local]
private class QueryStringStringification extends DataFlow::SummarizedCallable { private class QueryStringStringification extends DataFlow::SummarizedCallable {
QueryStringStringification() { this = "query-string stringification" } QueryStringStringification() { this = "query-string stringification" }
overlay[global]
override DataFlow::InvokeNode getACall() { override DataFlow::InvokeNode getACall() {
result = result =
API::moduleImport(["querystring", "query-string", "querystringify", "qs"]) API::moduleImport(["querystring", "query-string", "querystringify", "qs"])

View File

@@ -48,6 +48,7 @@ private class ThreatModelSourceFromDataExtension extends ThreatModelSource::Rang
} }
} }
overlay[local]
private class SummarizedCallableFromModel extends DataFlow::SummarizedCallable { private class SummarizedCallableFromModel extends DataFlow::SummarizedCallable {
string type; string type;
string path; string path;
@@ -57,6 +58,7 @@ private class SummarizedCallableFromModel extends DataFlow::SummarizedCallable {
this = type + ";" + path this = type + ";" + path
} }
overlay[global]
override DataFlow::InvokeNode getACall() { ModelOutput::resolvedSummaryBase(type, path, result) } override DataFlow::InvokeNode getACall() { ModelOutput::resolvedSummaryBase(type, path, result) }
override predicate propagatesFlow( override predicate propagatesFlow(

View File

@@ -62,6 +62,8 @@
* should be prefixed with a tilde character (`~`). For example, `~Bar` can be used to indicate that * should be prefixed with a tilde character (`~`). For example, `~Bar` can be used to indicate that
* the type is not intended to match a static type. * the type is not intended to match a static type.
*/ */
overlay[local?]
module;
private import codeql.util.Unit private import codeql.util.Unit
private import ApiGraphModelsSpecific as Specific private import ApiGraphModelsSpecific as Specific

View File

@@ -1,6 +1,8 @@
/** /**
* Defines extensible predicates for contributing library models from data extensions. * Defines extensible predicates for contributing library models from data extensions.
*/ */
overlay[local]
module;
/** /**
* Holds if the value at `(type, path)` should be seen as a flow * Holds if the value at `(type, path)` should be seen as a flow

View File

@@ -41,6 +41,7 @@ class Location = JS::Location;
* The model generator must explicitly generate the step between `(package)` and `(package).foo`, for example. * The model generator must explicitly generate the step between `(package)` and `(package).foo`, for example.
*/ */
bindingset[rawType] bindingset[rawType]
overlay[caller]
predicate parseTypeString(string rawType, string package, string qualifiedName) { predicate parseTypeString(string rawType, string package, string qualifiedName) {
exists(string regexp | exists(string regexp |
regexp = "('[^']+'|[^.]+)(.*)" and regexp = "('[^']+'|[^.]+)(.*)" and
@@ -55,6 +56,7 @@ predicate parseTypeString(string rawType, string package, string qualifiedName)
/** /**
* Holds if models describing `package` may be relevant for the analysis of this database. * Holds if models describing `package` may be relevant for the analysis of this database.
*/ */
overlay[local]
predicate isPackageUsed(string package) { predicate isPackageUsed(string package) {
package = "global" package = "global"
or or
@@ -68,6 +70,7 @@ predicate isPackageUsed(string package) {
} }
bindingset[type] bindingset[type]
overlay[local]
predicate isTypeUsed(string type) { predicate isTypeUsed(string type) {
exists(string package | exists(string package |
parseTypeString(type, package, _) and parseTypeString(type, package, _) and
@@ -79,8 +82,10 @@ predicate isTypeUsed(string type) {
* Holds if `type` can be obtained from an instance of `otherType` due to * Holds if `type` can be obtained from an instance of `otherType` due to
* language semantics modeled by `getExtraNodeFromType`. * language semantics modeled by `getExtraNodeFromType`.
*/ */
overlay[local]
predicate hasImplicitTypeModel(string type, string otherType) { none() } predicate hasImplicitTypeModel(string type, string otherType) { none() }
overlay[local]
pragma[nomagic] pragma[nomagic]
private predicate parseRelevantTypeString(string rawType, string package, string qualifiedName) { private predicate parseRelevantTypeString(string rawType, string package, string qualifiedName) {
isRelevantFullPath(rawType, _) and isRelevantFullPath(rawType, _) and
@@ -190,6 +195,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
} }
bindingset[node] bindingset[node]
overlay[caller?]
pragma[inline_late] pragma[inline_late]
private API::Node getAGuardedRouteHandlerApprox(API::Node node) { private API::Node getAGuardedRouteHandlerApprox(API::Node node) {
// For now just get any routing node with the same root (i.e. the same web app), as // For now just get any routing node with the same root (i.e. the same web app), as
@@ -230,6 +236,7 @@ private predicate blockFuzzyCall(DataFlow::CallNode call) {
isCommonBuiltinMethodName(call.getCalleeName()) isCommonBuiltinMethodName(call.getCalleeName())
} }
overlay[caller?]
pragma[inline] pragma[inline]
API::Node getAFuzzySuccessor(API::Node node) { API::Node getAFuzzySuccessor(API::Node node) {
result = node.getAMember() and result = node.getAMember() and

View File

@@ -2,6 +2,8 @@
* Provides classes for working with basic blocks, and predicates for computing * Provides classes for working with basic blocks, and predicates for computing
* liveness information for local variables. * liveness information for local variables.
*/ */
overlay[local]
module;
import javascript import javascript
private import semmle.javascript.internal.StmtContainers private import semmle.javascript.internal.StmtContainers
@@ -318,6 +320,7 @@ module Public {
/** /**
* Holds if this basic block strictly dominates `bb`. * Holds if this basic block strictly dominates `bb`.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
predicate strictlyDominates(ReachableBasicBlock bb) { this = immediateDominator+(bb) } predicate strictlyDominates(ReachableBasicBlock bb) { this = immediateDominator+(bb) }
@@ -326,12 +329,14 @@ module Public {
* *
* This predicate is reflexive: each reachable basic block dominates itself. * This predicate is reflexive: each reachable basic block dominates itself.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
predicate dominates(ReachableBasicBlock bb) { this = immediateDominator*(bb) } predicate dominates(ReachableBasicBlock bb) { this = immediateDominator*(bb) }
/** /**
* Holds if this basic block strictly post-dominates `bb`. * Holds if this basic block strictly post-dominates `bb`.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
predicate strictlyPostDominates(ReachableBasicBlock bb) { this = immediatePostDominator+(bb) } predicate strictlyPostDominates(ReachableBasicBlock bb) { this = immediatePostDominator+(bb) }
@@ -340,6 +345,7 @@ module Public {
* *
* This predicate is reflexive: each reachable basic block post-dominates itself. * This predicate is reflexive: each reachable basic block post-dominates itself.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
predicate postDominates(ReachableBasicBlock bb) { this = immediatePostDominator*(bb) } predicate postDominates(ReachableBasicBlock bb) { this = immediatePostDominator*(bb) }
} }

View File

@@ -40,6 +40,7 @@ module Stages {
/** /**
* The `ast` stage. * The `ast` stage.
*/ */
overlay[local]
cached cached
module Ast { module Ast {
/** /**
@@ -84,6 +85,7 @@ module Stages {
/** /**
* The `basicblocks` stage. * The `basicblocks` stage.
*/ */
overlay[local]
cached cached
module BasicBlocks { module BasicBlocks {
/** /**
@@ -110,6 +112,7 @@ module Stages {
/** /**
* The part of data flow computed before flow summary nodes. * The part of data flow computed before flow summary nodes.
*/ */
overlay[local]
cached cached
module EarlyDataFlowStage { module EarlyDataFlowStage {
/** /**
@@ -134,6 +137,7 @@ module Stages {
/** /**
* The `dataflow` stage. * The `dataflow` stage.
*/ */
overlay[local]
cached cached
module DataFlowStage { module DataFlowStage {
/** /**
@@ -167,8 +171,6 @@ module Stages {
or or
exists(any(DataFlow::PropRef ref).getBase()) exists(any(DataFlow::PropRef ref).getBase())
or or
exists(any(DataFlow::ClassNode cls))
or
exists(any(DataFlow::CallNode node).getArgument(_)) exists(any(DataFlow::CallNode node).getArgument(_))
or or
exists(any(DataFlow::CallNode node).getAnArgument()) exists(any(DataFlow::CallNode node).getAnArgument())
@@ -202,8 +204,6 @@ module Stages {
or or
exists(any(Import i).getImportedModule()) exists(any(Import i).getImportedModule())
or or
exists(DataFlow::moduleImport(_))
or
exists(any(ReExportDeclaration d).getReExportedModule()) exists(any(ReExportDeclaration d).getReExportedModule())
or or
exists(any(Module m).getABulkExportedNode()) exists(any(Module m).getABulkExportedNode())

View File

@@ -6,10 +6,16 @@ private predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local] overlay[local]
private string getFileFromEntity(@locatable node) { private string getFileFromEntity(@locatable node) {
exists(@location loc, @file file | exists(@location loc |
hasLocation(node, loc) and hasLocation(node, loc)
locations_default(loc, file, _, _, _, _) and or
files(file, result) json_locations(node, loc)
or
yaml_locations(node, loc)
or
xmllocations(node, loc)
|
result = getFileFromLocation(loc)
) )
} }
@@ -28,3 +34,23 @@ overlay[discard_entity]
private predicate discardEntity(@locatable node) { private predicate discardEntity(@locatable node) {
exists(string file | discardableEntity(file, node) and discardFile(file)) exists(string file | discardableEntity(file, node) and discardFile(file))
} }
overlay[local]
private string getFileFromLocation(@location loc) {
exists(@file file |
locations_default(loc, file, _, _, _, _) and
files(file, result)
)
}
/** Holds if `loc` is in the `file` and is part of the overlay base database. */
overlay[local]
private predicate discardableLocation(string file, @location node) {
not isOverlay() and file = getFileFromLocation(node)
}
/** Holds if `loc` should be discarded, because it is part of the overlay base and is in a file that was also extracted as part of the overlay database. */
overlay[discard_entity]
private predicate discardLocation(@location loc) {
exists(string file | discardableLocation(file, loc) and discardFile(file))
}

View File

@@ -4,6 +4,8 @@
* Provides predicates and classes for relating nodes to their * Provides predicates and classes for relating nodes to their
* enclosing `StmtContainer`. * enclosing `StmtContainer`.
*/ */
overlay[local]
module;
private import javascript private import javascript
private import semmle.javascript.internal.CachedStages private import semmle.javascript.internal.CachedStages
@@ -46,6 +48,7 @@ class NodeInStmtContainer extends Locatable, @node_in_stmt_container {
/** /**
* Gets the function or toplevel to which this node belongs. * Gets the function or toplevel to which this node belongs.
*/ */
overlay[caller]
pragma[inline] pragma[inline]
final StmtContainer getContainer() { result = getStmtContainer(this) } final StmtContainer getContainer() { result = getStmtContainer(this) }
} }

View File

@@ -20,6 +20,8 @@
* *
* (Promise is absent in the table above as there currently are no name clashes with Promise methods) * (Promise is absent in the table above as there currently are no name clashes with Promise methods)
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -3,6 +3,8 @@
* *
* Note that some of Array methods are modeled in `AmbiguousCoreMethods.qll`, and `toString` is special-cased elsewhere. * Note that some of Array methods are modeled in `AmbiguousCoreMethods.qll`, and `toString` is special-cased elsewhere.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow steps to model flow through `async` functions and the `await` operator. * Contains flow steps to model flow through `async` functions and the `await` operator.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow steps to model flow from a module into a dynamic `import()` expression. * Contains flow steps to model flow from a module into a dynamic `import()` expression.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -1,6 +1,8 @@
/** /**
* Contains a summary for propagating exceptions out of callbacks * Contains a summary for propagating exceptions out of callbacks
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import FlowSummaryUtil private import FlowSummaryUtil

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary
private import semmle.javascript.dataflow.internal.Contents::Private private import semmle.javascript.dataflow.internal.Contents::Private

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow steps to model flow through `for..of` loops. * Contains flow steps to model flow through `for..of` loops.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode
@@ -48,12 +50,18 @@ class ForOfLoopStep extends AdditionalFlowInternal {
) { ) {
exists(ForOfStmt stmt | exists(ForOfStmt stmt |
pred = getSynthesizedNode(stmt, "for-of-map-key") and pred = getSynthesizedNode(stmt, "for-of-map-key") and
contents.asSingleton().asArrayIndex() = 0 contents = arrayIndex0()
or or
pred = getSynthesizedNode(stmt, "for-of-map-value") and pred = getSynthesizedNode(stmt, "for-of-map-value") and
contents.asSingleton().asArrayIndex() = 1 contents = arrayIndex1()
| |
succ = DataFlow::lvalueNode(stmt.getLValue()) succ = DataFlow::lvalueNode(stmt.getLValue())
) )
} }
} }
pragma[nomagic]
private DataFlow::ContentSet arrayIndex0() { result.asSingleton().asArrayIndex() = 0 }
pragma[nomagic]
private DataFlow::ContentSet arrayIndex1() { result.asSingleton().asArrayIndex() = 1 }

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow steps to model flow through generator functions. * Contains flow steps to model flow through generator functions.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow summaries and steps modeling flow through iterators. * Contains flow summaries and steps modeling flow through iterators.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -1,6 +1,8 @@
/** /**
* Contains implicit read steps at the input to any function that converts a deep object to a string, such as `JSON.stringify`. * Contains implicit read steps at the input to any function that converts a deep object to a string, such as `JSON.stringify`.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import FlowSummaryUtil private import FlowSummaryUtil

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow summaries and steps modeling flow through `Map` objects. * Contains flow summaries and steps modeling flow through `Map` objects.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow summaries and steps modeling flow through `Promise` objects. * Contains flow summaries and steps modeling flow through `Promise` objects.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow summaries and steps modeling flow through `Set` objects. * Contains flow summaries and steps modeling flow through `Set` objects.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary

View File

@@ -1,6 +1,8 @@
/** /**
* Contains flow summaries and steps modeling flow through string methods. * Contains flow summaries and steps modeling flow through string methods.
*/ */
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import javascript private import javascript
private import semmle.javascript.dataflow.FlowSummary private import semmle.javascript.dataflow.FlowSummary
private import semmle.javascript.dataflow.InferredTypes private import semmle.javascript.dataflow.InferredTypes

View File

@@ -3,6 +3,8 @@
* *
* For now, the `URLSearchParams` object is modeled as a `Map` object. * For now, the `URLSearchParams` object is modeled as a `Map` object.
*/ */
overlay[local?]
module;
private import javascript private import javascript

View File

@@ -1,6 +1,7 @@
import javascript import javascript
import semmle.javascript.dataflow.FlowSummary import semmle.javascript.dataflow.FlowSummary
overlay[local]
class MkSummary extends SummarizedCallable { class MkSummary extends SummarizedCallable {
private CallExpr mkSummary; private CallExpr mkSummary;

View File

@@ -14,12 +14,26 @@
import Clones import Clones
bindingset[init]
pragma[inline_late]
private Property getPropertyFromInitializerStrict(Expr init) { result.getInit() = init }
pragma[nomagic]
private predicate duplicateProperties(
DuplicatePropertyInitDetector dup, Property prop1, Property prop2
) {
exists(Expr init2 |
dup.same(init2) and
prop1 = getPropertyFromInitializerStrict(dup) and
prop2 = getPropertyFromInitializerStrict(init2)
)
}
from ObjectExpr oe, int i, int j, Property p, Property q, DuplicatePropertyInitDetector dpid from ObjectExpr oe, int i, int j, Property p, Property q, DuplicatePropertyInitDetector dpid
where where
duplicateProperties(dpid, p, q) and
p = oe.getProperty(i) and p = oe.getProperty(i) and
q = oe.getProperty(j) and q = oe.getProperty(j) and
dpid = p.getInit() and
dpid.same(q.getInit()) and
i < j and i < j and
// only report the next duplicate // only report the next duplicate
not exists(int mid | mid in [i + 1 .. j - 1] | dpid.same(oe.getProperty(mid).getInit())) not exists(int mid | mid in [i + 1 .. j - 1] | dpid.same(oe.getProperty(mid).getInit()))

View File

@@ -1,5 +1,6 @@
import javascript import javascript
overlay[local]
class TestAmdModuleRange extends AmdModuleDefinition::Range { class TestAmdModuleRange extends AmdModuleDefinition::Range {
TestAmdModuleRange() { this.getCallee().(PropAccess).getQualifiedName() = "test.amd.range" } TestAmdModuleRange() { this.getCallee().(PropAccess).getQualifiedName() = "test.amd.range" }
} }

View File

@@ -4,6 +4,7 @@ import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
import semmle.javascript.dataflow.internal.AbstractPropertiesImpl as AbstractPropertiesImpl import semmle.javascript.dataflow.internal.AbstractPropertiesImpl as AbstractPropertiesImpl
import semmle.javascript.dataflow.CustomAbstractValueDefinitions import semmle.javascript.dataflow.CustomAbstractValueDefinitions
overlay[local]
class MyCustomAbstractValueDefinition extends CustomAbstractValueDefinition { class MyCustomAbstractValueDefinition extends CustomAbstractValueDefinition {
DataFlow::ValueNode node; DataFlow::ValueNode node;

Some files were not shown because too many files have changed in this diff Show More