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

View File

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

View File

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

View File

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

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
* import specifier `x as y` forms part of the preamble.
*/
overlay[local]
module;
import javascript
private import internal.StmtContainers

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes and predicates for working with variable definitions and uses. */
overlay[local]
module;
import javascript
@@ -231,6 +233,7 @@ class VarUse extends ControlFlowNode, @varref instanceof RValue {
*
* For global variables, each definition is considered to reach each use.
*/
overlay[global]
VarDef getADef() {
result = this.getSsaVariable().getDefinition().getAContributingVarDef() or
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.
*/
overlay[global]
SsaVariable getSsaVariable() { result.getAUse() = this }
}

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with ECMAScript 2015 modules. */
overlay[local]
module;
import javascript
private import semmle.javascript.internal.CachedStages
@@ -29,11 +31,13 @@ class ES2015Module extends Module {
/** Gets an export declaration in this module. */
ExportDeclaration getAnExport() { result.getTopLevel() = this }
overlay[global]
override DataFlow::Node getAnExportedValue(string name) {
exists(ExportDeclaration ed | ed = this.getAnExport() and result = ed.getSourceNode(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) }
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
* ambiguities, so we only allow the standard interpretation in that case.
*/
overlay[global]
predicate hasBothNamedAndDefaultExports() {
hasNamedExports(this) and
hasDefaultExport(this)
@@ -59,6 +64,7 @@ class ES2015Module extends Module {
/**
* Holds if `mod` contains one or more named export declarations other than `default`.
*/
overlay[global]
private predicate hasNamedExports(ES2015Module mod) {
mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() != "default"
or
@@ -71,6 +77,7 @@ private predicate hasNamedExports(ES2015Module mod) {
/**
* Holds if this module contains a default export.
*/
overlay[global]
private predicate hasDefaultExport(ES2015Module mod) {
// export default foo;
mod.getAnExport() instanceof ExportDefaultDeclaration
@@ -172,6 +179,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
}
/** A literal path expression appearing in an `import` declaration. */
overlay[global]
deprecated private class LiteralImportPath extends PathExpr, ConstantString {
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 {
/** Gets the import declaration in which this specifier appears. */
overlay[global]
ImportDeclaration getImportDeclaration() { result.getASpecifier() = this }
/** Gets the imported symbol; undefined for default and namespace import specifiers. */
@@ -297,6 +306,7 @@ class BulkImportDeclaration extends ImportDeclaration {
* import console, { log } from 'console';
* ```
*/
overlay[global]
class SelectiveImportDeclaration extends ImportDeclaration {
SelectiveImportDeclaration() { not this instanceof BulkImportDeclaration }
@@ -330,9 +340,11 @@ class SelectiveImportDeclaration extends ImportDeclaration {
*/
abstract class ExportDeclaration extends Stmt, @export_declaration {
/** Gets the module to which this export declaration belongs. */
overlay[global]
ES2015Module getEnclosingModule() { this = result.getAnExport() }
/** Holds if this export declaration exports variable `v` under the name `name`. */
overlay[global]
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
* to module `a` or possibly to some other module from which `a` re-exports.
*/
overlay[global]
abstract DataFlow::Node getSourceNode(string name);
/** 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. */
override ConstantString getImportedPath() { result = this.getChildExpr(0) }
overlay[global]
override predicate exportsAs(LexicalName v, string name) {
this.getReExportedES2015Module().exportsAs(v, name) and
not isShadowedFromBulkExport(this, name)
}
overlay[global]
override DataFlow::Node getSourceNode(string 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,
* but we ignore this subtlety.
*/
overlay[global]
private predicate isShadowedFromBulkExport(BulkReExportDeclaration reExport, string name) {
exists(ExportNamedDeclaration other | other.getTopLevel() = reExport.getEnclosingModule() |
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. */
ExprOrStmt getOperand() { result = this.getChild(0) }
overlay[global]
override predicate exportsAs(LexicalName v, string name) {
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) {
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. */
VarDecl getADecl() { result = this.getAnExportedDecl() }
overlay[global]
override predicate exportsAs(LexicalName v, string name) {
exists(LexicalDecl vd | vd = this.getAnExportedDecl() |
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) {
exists(VarDef d | d.getTarget() = this.getADecl() |
name = d.getTarget().(VarDecl).getName() and
@@ -555,6 +575,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
private import semmle.javascript.dataflow.internal.PreCallGraphStep
overlay[global]
private class ExportNamespaceStep extends PreCallGraphStep {
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
exists(ExportNamedDeclaration exprt, ExportNamespaceSpecifier spec |
@@ -572,6 +593,7 @@ private class ExportNamespaceStep extends PreCallGraphStep {
private class TypeOnlyExportDeclaration extends ExportNamedDeclaration {
TypeOnlyExportDeclaration() { this.isTypeOnly() }
overlay[global]
override predicate exportsAs(LexicalName v, string name) {
super.exportsAs(v, name) and
not v instanceof Variable
@@ -745,9 +767,11 @@ abstract class ReExportDeclaration extends ExportDeclaration {
abstract ConstantString getImportedPath();
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
overlay[global]
ES2015Module getReExportedES2015Module() { result = this.getReExportedModule() }
/** Gets the module from which this declaration re-exports. */
overlay[global]
cached
Module getReExportedModule() {
Stages::Imports::ref() and
@@ -756,6 +780,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
}
/** A literal path expression appearing in a re-export declaration. */
overlay[global]
deprecated private class LiteralReExportPath extends PathExpr, ConstantString {
LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) }
@@ -795,11 +820,13 @@ class SelectiveReExportDeclaration extends ReExportDeclaration, ExportNamedDecla
class OriginalExportDeclaration extends ExportDeclaration {
OriginalExportDeclaration() { not this instanceof ReExportDeclaration }
overlay[global]
override predicate exportsAs(LexicalName v, string name) {
this.(ExportDefaultDeclaration).exportsAs(v, name) or
this.(ExportNamedDeclaration).exportsAs(v, name)
}
overlay[global]
override DataFlow::Node getSourceNode(string name) {
result = this.(ExportDefaultDeclaration).getSourceNode(name) or
result = this.(ExportNamedDeclaration).getSourceNode(name)

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with expressions.
*/
overlay[local]
module;
import javascript
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) }
/** Holds if this expression is impure, that is, its evaluation could have side effects. */
overlay[global]
predicate isImpure() { any() }
/**
* Holds if this expression is pure, that is, its evaluation is guaranteed
* to be side-effect free.
*/
overlay[global]
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
* or through the `window` object.
*/
overlay[global]
predicate accessesGlobal(string g) { this.flow().accessesGlobal(g) }
/**
* Holds if this expression may evaluate to `s`.
*/
overlay[global]
predicate mayHaveStringValue(string s) { this.flow().mayHaveStringValue(s) }
/**
* Holds if this expression may evaluate to `b`.
*/
overlay[global]
predicate mayHaveBooleanValue(boolean b) { this.flow().mayHaveBooleanValue(b) }
/**
* Holds if this expression may refer to the initial value of parameter `p`.
*/
overlay[global]
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
* file that was extracted without type information.
*/
overlay[global]
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
* propagate if this expression causes an exception to be thrown.
*/
overlay[caller]
pragma[inline]
DataFlow::Node getExceptionTarget() {
result = getCatchParameterFromStmt(this.getRawEnclosingStmt(this))
result = getCatchParameterFromStmt(getRawEnclosingStmt(this))
or
not exists(getCatchParameterFromStmt(this.getRawEnclosingStmt(this))) and
not exists(getCatchParameterFromStmt(getRawEnclosingStmt(this))) and
result =
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())
}
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.
*
@@ -301,6 +312,7 @@ class Identifier extends @identifier, ExprOrType {
* ```
*/
class Label extends @label, Identifier, Expr {
overlay[global]
override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "Label" }
@@ -330,6 +342,7 @@ class Literal extends @literal, Expr {
*/
string getRawValue() { literals(_, result, this) }
overlay[global]
override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "Literal" }
@@ -352,6 +365,7 @@ class ParExpr extends @par_expr, Expr {
override int getIntValue() { result = this.getExpression().getIntValue() }
overlay[global]
override predicate isImpure() { this.getExpression().isImpure() }
override Expr getUnderlyingValue() { result = this.getExpression().getUnderlyingValue() }
@@ -500,6 +514,7 @@ class RegExpLiteral extends @regexp_literal, Literal, RegExpParent {
* ```
*/
class ThisExpr extends @this_expr, Expr {
overlay[global]
override predicate isImpure() { none() }
/**
@@ -555,6 +570,7 @@ class ArrayExpr extends @array_expr, Expr {
/** Holds if this array literal has an omitted element. */
predicate hasOmittedElement() { this.elementIsOmitted(_) }
overlay[global]
override predicate isImpure() { this.getAnElement().isImpure() }
override string getAPrimaryQlClass() { result = "ArrayExpr" }
@@ -597,6 +613,7 @@ class ObjectExpr extends @obj_expr, Expr {
*/
predicate hasTrailingComma() { this.getLastToken().getPreviousToken().getValue() = "," }
overlay[global]
override predicate isImpure() { this.getAProperty().isImpure() }
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
* its initializer expression could have side effects.
*/
overlay[global]
predicate isImpure() {
this.isComputed() and this.getNameExpr().isImpure()
or
@@ -826,6 +844,7 @@ class FunctionExpr extends @function_expr, Expr, Function {
Stages::Ast::ref() and result = Expr.super.getContainer()
}
overlay[global]
override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "FunctionExpr" }
@@ -846,6 +865,7 @@ class ArrowFunctionExpr extends @arrow_function_expr, Expr, Function {
override StmtContainer getEnclosingContainer() { result = Expr.super.getContainer() }
overlay[global]
override predicate isImpure() { none() }
override Function getThisBinder() {
@@ -877,6 +897,7 @@ class SeqExpr extends @seq_expr, Expr {
/** Gets the last expression in this sequence. */
Expr getLastOperand() { result = this.getOperand(this.getNumOperands() - 1) }
overlay[global]
override predicate isImpure() { this.getAnOperand().isImpure() }
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. */
Expr getABranch() { result = this.getConsequent() or result = this.getAlternate() }
overlay[global]
override predicate isImpure() {
this.getCondition().isImpure() or
this.getABranch().isImpure()
@@ -985,6 +1007,7 @@ class InvokeExpr extends @invokeexpr, Expr {
*
* This predicate is an approximation, computed using only local data flow.
*/
overlay[global]
predicate hasOptionArgument(int i, string name, Expr value) {
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.
*/
overlay[global]
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.
*/
overlay[global]
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
* necessarily the actual target of this invocation at runtime.
*/
overlay[global]
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. */
Identifier getProperty() { result = this.getChildExpr(1) }
overlay[global]
override predicate isImpure() { this.getBase().isImpure() }
override string getAPrimaryQlClass() { result = "DotExpr" }
@@ -1176,6 +1203,7 @@ class IndexExpr extends @index_expr, PropAccess {
override string getPropertyName() { result = this.getIndex().(Literal).getValue() }
overlay[global]
override predicate isImpure() {
this.getBase().isImpure() or
this.getIndex().isImpure()
@@ -1201,6 +1229,7 @@ class UnaryExpr extends @unaryexpr, Expr {
/** Gets the operator of this expression. */
string getOperator() { none() }
overlay[global]
override predicate isImpure() { this.getOperand().isImpure() }
override ControlFlowNode getFirstControlFlowNode() {
@@ -1302,6 +1331,7 @@ class VoidExpr extends @void_expr, UnaryExpr {
class DeleteExpr extends @delete_expr, UnaryExpr {
override string getOperator() { result = "delete" }
overlay[global]
override predicate isImpure() { any() }
}
@@ -1352,6 +1382,7 @@ class BinaryExpr extends @binaryexpr, Expr {
/** Gets the operator of this expression. */
string getOperator() { none() }
overlay[global]
override predicate isImpure() { this.getAnOperand().isImpure() }
override ControlFlowNode getFirstControlFlowNode() {
@@ -1617,13 +1648,19 @@ private string getConstantString(Expr e) {
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.
*/
private predicate hasAllConstantLeafs(AddExpr add) {
forex(Expr leaf | leaf = getAnAddOperand*(add) and not exists(getAnAddOperand(leaf)) |
exists(getConstantString(leaf))
)
hasConstantStringValue(add.getLeftOperand()) and
hasConstantStringValue(add.getRightOperand())
}
/**
@@ -2233,6 +2270,7 @@ class YieldExpr extends @yield_expr, Expr {
/** Holds if this is a `yield*` expression. */
predicate isDelegating() { is_delegating(this) }
overlay[global]
override predicate isImpure() { any() }
override ControlFlowNode getFirstControlFlowNode() {
@@ -2289,6 +2327,7 @@ class ComprehensionExpr extends @comprehension_expr, Expr {
/** Gets the body expression of this comprehension. */
Expr getBody() { result = this.getChildExpr(0) }
overlay[global]
override predicate isImpure() {
this.getABlock().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. */
Expr getDomain() { result = this.getChildExpr(1) }
overlay[global]
override predicate isImpure() {
this.getIterator().isImpure() or
this.getDomain().isImpure()
@@ -2675,6 +2715,7 @@ class AwaitExpr extends @await_expr, Expr {
/** Gets the operand of this `await` expression. */
Expr getOperand() { result = this.getChildExpr(0) }
overlay[global]
override predicate isImpure() { any() }
override ControlFlowNode getFirstControlFlowNode() {
@@ -2698,6 +2739,7 @@ class AwaitExpr extends @await_expr, Expr {
* ```
*/
class FunctionSentExpr extends @function_sent_expr, Expr {
overlay[global]
override predicate isImpure() { none() }
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. */
overlay[global]
deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString {
LiteralDynamicImportPath() {
exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource())
@@ -2919,6 +2962,7 @@ class OptionalChainRoot extends ChainElem {
* ```
*/
class ImportMetaExpr extends @import_meta_expr, Expr {
overlay[global]
override predicate isImpure() { none() }
override string getAPrimaryQlClass() { result = "ImportMetaExpr" }

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for reasoning about `extend`-like functions.
*/
overlay[local]
module;
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.
*/
overlay[global]
private class ExtendCallStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
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`.
*/
overlay[global]
private class CloneStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call |

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with functions. */
overlay[local]
module;
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.
*/
overlay[global]
deprecated CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
/**
* 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) }
}

View File

@@ -1,6 +1,8 @@
/**
* Provides predicates for associating qualified names with data flow nodes.
*/
overlay[local]
module;
import javascript
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
* self-assignments.
*/
overlay[global]
predicate isAssignedInUniqueFile(string accessPath) {
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.
* This predicate restricts as much as it can, but without depending on `getAVariableRef`.
*/
overlay[caller]
pragma[inline]
private SsaVariable getARelevantVariableSimple() {
// The variable might be used where `getLaterBaseAccess()` is called.
@@ -405,6 +409,7 @@ module AccessPath {
* }
* ```
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAReferenceTo(Root root, string path) {
path = fromReference(result, root) and
@@ -428,6 +433,7 @@ module AccessPath {
* })(NS = NS || {});
* ```
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAReferenceTo(string path) {
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
@@ -449,6 +455,7 @@ module AccessPath {
* }
* ```
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAnAssignmentTo(Root root, string path) {
path = fromRhs(result, root) and
@@ -470,6 +477,7 @@ module AccessPath {
* })(foo = foo || {});
* ```
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAnAssignmentTo(string path) {
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
@@ -480,6 +488,7 @@ module AccessPath {
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(string path) {
result = getAReferenceTo(path)
@@ -492,6 +501,7 @@ module AccessPath {
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
overlay[caller]
pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string 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.
*/
overlay[caller?]
pragma[inline]
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
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.
*/
overlay[caller]
pragma[inline]
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
exists(DataFlow::SourceNode root, string accessPath |
@@ -657,7 +669,7 @@ module AccessPath {
*/
cached
predicate hasDominatingWrite(DataFlow::PropRead read) {
Stages::TypeTracking::ref() and
Stages::DataFlowStage::ref() and
// within the same basic block.
exists(ReachableBasicBlock bb, Root root, string path, int ranking |
read.asExpr() = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with locations and program elements that have locations. */
overlay[local]
module;
import javascript
@@ -30,6 +32,7 @@ final class Location extends @location_default {
int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 }
/** Holds if this location starts before location `that`. */
overlay[caller]
pragma[inline]
predicate startsBefore(Location that) {
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`. */
overlay[caller]
pragma[inline]
predicate endsAfter(Location that) {
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
* modules.
*/
overlay[local]
module;
import javascript
private import semmle.javascript.internal.CachedStages
@@ -23,9 +25,11 @@ abstract class Module extends TopLevel {
Import getAnImport() { result.getTopLevel() = this }
/** Gets a module from which this module imports. */
overlay[global]
Module getAnImportedModule() { result = this.getAnImport().getImportedModule() }
/** Gets a symbol exported by this module. */
overlay[global]
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
* this module are only sometimes considered.
*/
overlay[global]
cached
abstract DataFlow::Node getAnExportedValue(string name);
/**
* Gets a value that is exported as the whole exports object of this module.
*/
overlay[global]
cached
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,
* as the interaction between different module types is not standardized.
*/
overlay[global]
DataFlow::Node getDefaultOrBulkExport() {
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
* overriding by subclasses.
*/
overlay[global]
deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this 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
* that file is the result.
*/
overlay[global]
deprecated File resolve(PathExpr path) {
path.getEnclosingModule() = this and
(
@@ -124,6 +133,7 @@ abstract class Import extends AstNode {
abstract Module getEnclosingModule();
/** DEPRECATED. Use `getImportedPathExpr` instead. */
overlay[global]
deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() }
/** 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
* path is assumed to be a possible target of the import.
*/
overlay[global]
Module resolveExternsImport() {
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.
*/
overlay[global]
Module resolveImportedPath() { result.getFile() = this.getImportedFile() }
/**
* Gets the module the path of this import resolves to.
*/
overlay[global]
File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) }
/**
* DEPRECATED. Use `getImportedModule()` instead.
*/
overlay[global]
deprecated Module resolveFromTypeScriptSymbol() {
exists(CanonicalName symbol |
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
* source module of the same name.
*/
overlay[global]
cached
Module getImportedModule() {
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
* of a default export.
*/
overlay[global]
final DataFlow::Node getImportedModuleNodeStrict() {
result = this.getImportedModuleNode() and
not (

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with NPM module definitions and dependencies.
*/
overlay[local?]
module;
import javascript
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'));
* ```
*/
overlay[local]
class NodeModule extends Module {
NodeModule() {
is_module(this) and
@@ -36,11 +37,13 @@ class NodeModule extends Module {
* Gets an abstract value representing one or more values that may flow
* into this module's `module.exports` property.
*/
overlay[global]
pragma[noinline]
DefiniteAbstractValue getAModuleExportsValue() {
result = this.getAModuleExportsProperty().getAValue()
}
overlay[global]
pragma[noinline]
private AbstractProperty getAModuleExportsProperty() {
result.getBase().(AbstractModuleObject).getModule() = this and
@@ -52,12 +55,14 @@ class NodeModule extends Module {
* For performance this predicate only computes relevant expressions (in `getAModuleExportsCandidate`).
* So if using this predicate - consider expanding the list of relevant expressions.
*/
overlay[global]
DataFlow::AnalyzedNode getAModuleExportsNode() {
result = getAModuleExportsCandidate() and
result.getAValue() = this.getAModuleExportsValue()
}
/** Gets a symbol exported by this module. */
overlay[global]
override string getAnExportedSymbol() {
result = super.getAnExportedSymbol()
or
@@ -70,6 +75,7 @@ class NodeModule extends Module {
)
}
overlay[global]
override DataFlow::Node getAnExportedValue(string name) {
// a property write whose base is `exports` or `module.exports`
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
@@ -114,6 +120,7 @@ class NodeModule extends Module {
)
}
overlay[global]
override DataFlow::Node getABulkExportedNode() {
Stages::Imports::ref() and
exists(DataFlow::PropWrite write |
@@ -124,6 +131,7 @@ class NodeModule extends Module {
}
/** Gets a symbol that the module object inherits from its prototypes. */
overlay[global]
private string getAnImplicitlyExportedSymbol() {
exists(ExternalConstructor ec | ec = this.getPrototypeOfExportedExpr() |
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. */
overlay[global]
private ExternalConstructor getPrototypeOfExportedExpr() {
exists(AbstractValue exported | exported = this.getAModuleExportsValue() |
result instanceof ObjectExternal
@@ -146,6 +155,7 @@ class NodeModule extends Module {
)
}
overlay[global]
deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
path.getEnclosingModule() = this and
exists(string pathval | pathval = path.getValue() |
@@ -224,6 +234,7 @@ predicate findNodeModulesFolder(Folder f, Folder nodeModules, int distance) {
/**
* A Node.js `require` variable.
*/
overlay[local]
private class RequireVariable extends Variable {
RequireVariable() {
this = any(ModuleScope m).getVariable("require")
@@ -236,6 +247,7 @@ private class RequireVariable extends Variable {
}
}
overlay[local]
private predicate isModuleModule(EarlyStageNode nd) {
exists(ImportDeclaration imp | imp.getRawImportPath() = "module" |
nd = TDestructuredModuleImportNode(imp)
@@ -249,6 +261,7 @@ private predicate isModuleModule(EarlyStageNode nd) {
)
}
overlay[local]
private predicate isCreateRequire(EarlyStageNode nd) {
exists(PropAccess prop |
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.
*/
overlay[local]
cached
private predicate isRequire(EarlyStageNode nd) {
exists(VarAccess access |
@@ -320,6 +334,7 @@ private predicate isRequire(EarlyStageNode nd) {
* require('fs')
* ```
*/
overlay[local]
class Require extends CallExpr, Import {
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.
*/
overlay[local]
string valueProp() { result = "$PromiseResolveField$" }
/**
* Gets the pseudo-field used to describe rejected values in a promise.
*/
overlay[local]
string errorProp() { result = "$PromiseRejectField$" }
/** 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`).
*/
overlay[caller?]
pragma[inline]
DataFlow::Node promiseStep(DataFlow::Node pred, StepSummary summary) {
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`.
* 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]
DataFlow::SourceNode promiseStep(
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
* terms.
*/
overlay[local]
module;
import javascript
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
* ```
*/
overlay[global]
predicate isUsedAsRegExp() {
exists(RegExpParent parent | parent = this.getRootTerm().getParent() |
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`.
*/
overlay[global]
private predicate isNativeStringMethod(Function func, string name) {
exists(ExternalInstanceMemberDecl decl |
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`,
* not including array indices.
*/
overlay[global]
private predicate isMatchObjectProperty(string name) {
any(ExternalInstanceMemberDecl decl).hasQualifiedName("Array", name)
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. */
overlay[global]
private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
call.getMethodName() = ["match", "matchAll"] 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.
*/
overlay[global]
pragma[inline]
private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
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
value.flowsToExpr(any(ArithmeticExpr e).getAnOperand())
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.
*/
overlay[global]
cached
predicate isInterpretedAsRegExp(DataFlow::Node source) {
Stages::Taint::ref() and
source.analyze().getAType() = TTString() and
canBeString(source) and
(
// The first argument to an invocation of `RegExp` (with or without `new`).
source = DataFlow::globalVarRef("RegExp").getAnInvocation().getArgument(0)
or
// The argument of a call that coerces the argument to a regular expression.
exists(DataFlow::MethodCallNode mce, string methodName |
mce.getReceiver().analyze().getAType() = TTString() and
canBeString(mce.getReceiver()) and
mce.getMethodName() = methodName and
not exists(Function func | func = mce.getACallee() |
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
* as a part of a regular expression.
*/
overlay[global]
private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker t) {
t.start() 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
* as a part of a regular expression.
*/
overlay[global]
private DataFlow::Node regExpSource(DataFlow::Node re) {
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
* as a part of a regular expression.
*/
overlay[global]
abstract class RegExpPatternSource extends DataFlow::Node {
/**
* 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.
*/
overlay[global]
private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::ValueNode {
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
* as a part of a regular expression.
*/
overlay[global]
private class StringRegExpPatternSource extends RegExpPatternSource {
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
* as a part of a regular expression.
*/
overlay[global]
private class StringConcatRegExpPatternSource extends RegExpPatternSource {
DataFlow::Node parse;
@@ -1331,6 +1355,7 @@ module RegExp {
/**
* Gets the AST of a regular expression object that can flow to `node`.
*/
overlay[global]
RegExpTerm getRegExpObjectFromNode(DataFlow::Node node) {
exists(DataFlow::RegExpCreationNode regexp |
regexp.getAReference().flowsTo(node) and
@@ -1342,6 +1367,7 @@ module RegExp {
* Gets the AST of a regular expression that can flow to `node`,
* including `RegExp` objects as well as strings interpreted as regular expressions.
*/
overlay[global]
RegExpTerm getRegExpFromNode(DataFlow::Node node) {
result = getRegExpObjectFromNode(node)
or

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for reasoning about type annotations independently of dialect.
*/
overlay[local]
module;
import javascript
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
* associate it with a named type coming from an dependency.
*/
overlay[global]
TypeNameBindingNode getTypeBinding() { result = this }
/** 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.
*/
overlay[global]
deprecated predicate hasQualifiedName(string 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`.
*/
overlay[global]
deprecated predicate hasQualifiedName(string moduleName, string 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,
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
*/
overlay[global]
final predicate hasUnderlyingType(string 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`,
* 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) {
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.
*/
overlay[global]
deprecated Type getType() { none() }
/**
@@ -142,5 +150,6 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
*
* This unfolds nullability modifiers and generic type applications.
*/
overlay[global]
final DataFlow::ClassNode getClass() { UnderlyingTypes::nodeHasUnderlyingClassType(this, result) }
}

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
import javascript
/**
@@ -31,6 +34,7 @@ class NamespaceDefinition extends Stmt, @namespace_definition, AST::ValueNode {
/**
* Gets the canonical name of the namespace being defined.
*/
overlay[global]
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.
*/
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this }
/**
* Gets the type defined by this declaration.
*/
overlay[global]
deprecated Type getType() { ast_node_type(this.getIdentifier(), result) }
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. */
overlay[global]
deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString {
LiteralExternalModulePath() {
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.
*/
overlay[global]
deprecated TypeName getTypeName() { result.getADefinition() = this }
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.
*/
overlay[global]
deprecated Namespace getNamespace() { result = this.getADeclaration().getNamespace() }
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
* without type information.
*/
overlay[global]
deprecated override Type getType() { ast_node_type(this, result) }
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.
*/
overlay[global]
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
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.
*/
overlay[global]
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.
*/
overlay[global]
deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
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.
*/
overlay[global]
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.
*/
overlay[global]
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
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.
*/
overlay[global]
int getSearchPriority(Folder searchRoot) {
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
* `number` keyword, but there only exists one `number` type.
*/
overlay[global]
deprecated class Type extends @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`.
*/
overlay[global]
deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_type {
/**
* 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`,
* but is still displayed as `boolean` in string representations.
*/
overlay[global]
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}`.
*/
overlay[global]
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,
* and their corresponding types are not considered array types either.
*/
overlay[global]
deprecated class ArrayType extends Type {
ArrayType() {
this instanceof @tuple_type or
@@ -2061,6 +2082,7 @@ deprecated class ArrayType extends Type {
*
* An array type such as `Array<string>`, or equivalently, `string[]`.
*/
overlay[global]
deprecated class PlainArrayType extends ArrayType, TypeReference {
PlainArrayType() { this.hasQualifiedName("Array") }
@@ -2075,6 +2097,7 @@ deprecated class PlainArrayType extends ArrayType, TypeReference {
*
* A read-only array type such as `ReadonlyArray<string>`.
*/
overlay[global]
deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
ReadonlyArrayType() { this.hasQualifiedName("ReadonlyArray") }
}
@@ -2087,6 +2110,7 @@ deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
*
* A tuple type, such as `[number, string]`.
*/
overlay[global]
deprecated class TupleType extends ArrayType, @tuple_type {
/**
* 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.
*/
overlay[global]
deprecated class AnyType extends Type, @any_type { }
/**
@@ -2158,6 +2183,7 @@ deprecated class AnyType extends Type, @any_type { }
*
* The predefined `unknown` type.
*/
overlay[global]
deprecated class UnknownType extends Type, @unknown_type { }
/**
@@ -2168,6 +2194,7 @@ deprecated class UnknownType extends Type, @unknown_type { }
*
* The predefined `string` type.
*/
overlay[global]
deprecated class StringType extends Type, @string_type { }
/**
@@ -2178,6 +2205,7 @@ deprecated class StringType extends Type, @string_type { }
*
* The predefined `number` type.
*/
overlay[global]
deprecated class NumberType extends Type, @number_type { }
/**
@@ -2188,6 +2216,7 @@ deprecated class NumberType extends Type, @number_type { }
*
* The predefined `bigint` type.
*/
overlay[global]
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.
*/
overlay[global]
deprecated class LiteralType extends Type, @literal_type {
/**
* 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`.
*/
overlay[global]
deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_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.
*/
overlay[global]
deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
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.
*/
overlay[global]
deprecated class StringLiteralType extends LiteralType, @string_literal_type {
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.
*/
overlay[global]
deprecated class BigIntLiteralType extends LiteralType {
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`.
*/
overlay[global]
deprecated class BooleanType extends UnionType {
BooleanType() {
this.getAnElementType() instanceof @true_type and
@@ -2299,6 +2334,7 @@ deprecated class BooleanType extends UnionType {
*
* The `string` type or a string literal type.
*/
overlay[global]
deprecated class StringLikeType extends Type {
StringLikeType() {
this instanceof StringType or
@@ -2314,6 +2350,7 @@ deprecated class StringLikeType extends Type {
*
* The `number` type or a number literal type.
*/
overlay[global]
deprecated class NumberLikeType extends Type {
NumberLikeType() {
this instanceof NumberType or
@@ -2329,6 +2366,7 @@ deprecated class NumberLikeType extends Type {
*
* The `boolean`, `true,` or `false` type.
*/
overlay[global]
deprecated class BooleanLikeType extends Type {
BooleanLikeType() {
this instanceof BooleanType or
@@ -2344,6 +2382,7 @@ deprecated class BooleanLikeType extends Type {
*
* The `void` type.
*/
overlay[global]
deprecated class VoidType extends Type, @void_type { }
/**
@@ -2354,6 +2393,7 @@ deprecated class VoidType extends Type, @void_type { }
*
* The `undefined` type.
*/
overlay[global]
deprecated class UndefinedType extends Type, @undefined_type { }
/**
@@ -2364,6 +2404,7 @@ deprecated class UndefinedType extends Type, @undefined_type { }
*
* The `null` type.
*/
overlay[global]
deprecated class NullType extends Type, @null_type { }
/**
@@ -2374,6 +2415,7 @@ deprecated class NullType extends Type, @null_type { }
*
* The `never` type.
*/
overlay[global]
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.
*/
overlay[global]
deprecated class SymbolType extends Type, @symbol_type { }
/**
@@ -2394,6 +2437,7 @@ deprecated class SymbolType extends Type, @symbol_type { }
*
* The `symbol` type.
*/
overlay[global]
deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
/**
@@ -2404,6 +2448,7 @@ deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
*
* A `unique symbol` type.
*/
overlay[global]
deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
/**
* 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.
*/
overlay[global]
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.
*/
overlay[global]
deprecated class TypeReference extends Type, @type_reference {
/**
* 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.
*/
overlay[global]
deprecated class ClassType extends TypeReference {
ClassDefinition declaration;
@@ -2525,6 +2573,7 @@ deprecated class ClassType extends TypeReference {
*
* A type that refers to an interface, possibly with type arguents.
*/
overlay[global]
deprecated class InterfaceType extends TypeReference {
InterfaceDeclaration declaration;
@@ -2544,6 +2593,7 @@ deprecated class InterfaceType extends TypeReference {
*
* A type that refers to an enum.
*/
overlay[global]
deprecated class EnumType extends TypeReference {
EnumDeclaration declaration;
@@ -2563,6 +2613,7 @@ deprecated class EnumType extends TypeReference {
*
* A type that refers to the value of an enum member.
*/
overlay[global]
deprecated class EnumLiteralType extends TypeReference {
EnumMember declaration;
@@ -2582,6 +2633,7 @@ deprecated class EnumLiteralType extends TypeReference {
*
* A type that refers to a type alias.
*/
overlay[global]
deprecated class TypeAliasReference extends TypeReference {
TypeAliasReference() { type_alias(this, _) }
@@ -2601,6 +2653,7 @@ deprecated class TypeAliasReference extends TypeReference {
*
* An anonymous interface type, such as `{ x: number }`.
*/
overlay[global]
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.
*/
overlay[global]
deprecated class TypeVariableType extends Type, @typevariable_type {
/**
* 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.
*/
overlay[global]
deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type {
override TypeName getHostType() { result = this.getCanonicalName().getParent() }
@@ -2681,6 +2736,7 @@ deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_
* - `<T>(x: T) => T`
* - `<S, T>(x: S, y: T) => T`.
*/
overlay[global]
deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type {
override string getName() {
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 {
/**
* 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
* a class constructor, namespace object, enum object, or module object.
*/
overlay[global]
deprecated class TypeofType extends Type, @typeof_type {
/**
* Gets the canonical name of the named value.
@@ -2801,6 +2859,7 @@ module SignatureKind {
*
* A function or constructor signature in a TypeScript type.
*/
overlay[global]
deprecated class CallSignatureType extends @signature_type {
/**
* 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.
*/
overlay[global]
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.
*/
overlay[global]
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 a `then` method whose first argument is a callback that takes a `T` as argument.
*/
overlay[global]
deprecated private class PromiseTypeName extends TypeName {
PromiseTypeName() {
// 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,
* such as `PromiseLike<T>` and `Thenable<T>`.
*/
overlay[global]
deprecated class PromiseType extends TypeReference {
PromiseType() {
this.getNumTypeArgument() = 1 and

View File

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

View File

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

View File

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

View File

@@ -37,6 +37,8 @@
* they represent; additionally, indefinite abstract values record
* the source of imprecision that caused them to arise.
*/
overlay[local]
module;
private import javascript
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
* abstract value coerces to.
*/
pragma[nomagic]
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
* this predicate cannot be relied on to compute all possible prototype objects.
*/
overlay[global]
DefiniteAbstractValue getAPrototype() {
exists(AbstractProtoProperty proto |
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
* of the standard library. Use `isAdditionalFlowStep` for query-specific flow steps.
*/
overlay[local]
class AdditionalFlowStep extends Unit {
/**
* 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() }
/**
* Holds if `pred` &rarr; `succ` should be considered a value-preserving data flow edge that
* crosses calling contexts.
*/
overlay[global]
predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* 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) {
none()
}
@@ -55,6 +59,7 @@ class AdditionalFlowStep extends Unit {
/**
* 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) {
none()
}

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes and predicates for defining flow summaries. */
overlay[local]
module;
private import javascript
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.
*

View File

@@ -3,6 +3,8 @@
* as nodes corresponding to function definitions or nodes corresponding to
* parameters.
*/
overlay[local]
module;
private import javascript
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"))
* ```
*/
overlay[global]
ParameterNode getABoundCallbackParameter(int callback, int param) {
exists(int boundArgs |
result =
@@ -178,6 +181,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
private ObjectLiteralNode getOptionsArgument(int i) { result.flowsTo(this.getArgument(i)) }
/** Gets an abstract value representing possible callees of this call site. */
overlay[global]
final AbstractValue getACalleeValue() {
exists(DataFlow::Node callee, DataFlow::AnalyzedNode analyzed |
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
* the `getACallee(int imprecision)` predicate instead.
*/
overlay[global]
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
* data flow libraries.
*/
overlay[global]
Function getACallee(int imprecision) {
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
* affected by the given analysis incompleteness `cause`.
*/
overlay[global]
predicate isIndefinite(DataFlow::Incompleteness 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
* imprecision.
*/
overlay[global]
predicate isImprecise() {
this.isIndefinite("global") and
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
* likely to be incomplete.
*/
overlay[global]
predicate isIncomplete() {
// the flow analysis identifies a source of incompleteness other than
// 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
* likely to be imprecise or incomplete.
*/
overlay[global]
predicate isUncertain() { this.isImprecise() or this.isIncomplete() }
/**
@@ -763,7 +773,7 @@ module ModuleImportNode {
cached
ModuleImportNode moduleImport(string path) {
// 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
* `"lodash"` as a dependency.
*/
overlay[global]
ModuleImportNode dependencyModuleImport(Dependency dep) {
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`.
*/
overlay[global]
class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
override AST::ValueNode astNode;
AbstractCallable function;
@@ -1329,6 +1341,7 @@ class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
/**
* Helper predicate to get a prototype reference in a file.
*/
overlay[global]
private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) 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.
*/
overlay[global]
private DataFlow::NewNode getAnInstantiationInFile(string name, File f) {
result = AccessPath::getAReferenceTo(name).(DataFlow::LocalSourceNode).getAnInstantiation() and
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.
*/
overlay[global]
pragma[noinline]
private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func) {
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`.
}
overlay[global]
module ClassNode {
/**
* A dataflow node that should be considered a class node.
@@ -1435,6 +1451,7 @@ module ClassNode {
* _.partial(fn, x, y, z)
* ```
*/
overlay[global]
class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Range {
/** Gets a node holding a callback invoked by this partial invocation node. */
DataFlow::Node getACallbackNode() {
@@ -1470,6 +1487,7 @@ class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Ran
}
}
overlay[global]
module PartialInvokeNode {
/**
* 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()) }
/** Gets a data flow node referring to this regular expression. */
overlay[global]
private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) {
t.start() and
result = this
@@ -1725,6 +1744,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
}
/** Gets a data flow node referring to this regular expression. */
overlay[global]
cached
DataFlow::SourceNode getAReference() {
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)`.
* Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks.
*/
overlay[global]
class VarAccessBarrier extends DataFlow::Node {
VarAccessBarrier() {
exists(ConditionGuardNode guard, SsaRefinementNode refinement |

View File

@@ -27,6 +27,8 @@
* so the refinement can evaluate to both `true` and `false` for the same
* candidate value.
*/
overlay[local]
module;
import javascript
private import AbstractValues
@@ -45,6 +47,7 @@ abstract class RefinementCandidate extends Expr {
/**
* Gets a refinement value inferred for this expression in context `ctxt`.
*/
overlay[global]
pragma[nomagic]
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`.
*/
overlay[global]
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 {
override SsaSourceVariable getARefinedVar() { none() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
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.
*/
overlay[global]
RefinementValue eval() { result = TAny() }
}
/** A `null` literal, viewed as a refinement expression. */
private class NullLiteralRefinement extends LiteralRefinement, NullLiteral {
overlay[global]
override RefinementValue eval() { result = TValueWithType(TTNull()) }
}
/** A Boolean literal, viewed as a refinement expression. */
private class BoolRefinement extends LiteralRefinement, BooleanLiteral {
overlay[global]
override RefinementValue eval() {
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. */
private class StringRefinement extends LiteralRefinement, ConstantString {
overlay[global]
override RefinementValue eval() { result = TStringConstant(this.getStringValue()) }
}
/** A numeric literal, viewed as a refinement expression. */
abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral {
overlay[global]
override RefinementValue eval() { result = TValueWithType(TTNumber()) }
}
@@ -112,6 +122,7 @@ abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral
private class IntRefinement extends NumberRefinement, NumberLiteral {
IntRefinement() { this.getValue().toInt() = 0 }
overlay[global]
override RefinementValue eval() { result = TIntConstant(this.getValue().toInt()) }
}
@@ -123,6 +134,7 @@ private class UndefinedInRefinement extends RefinementCandidate,
{
override SsaSourceVariable getARefinedVar() { none() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
ctxt.appliesTo(this) and
result = TValueWithType(TTUndefined())
@@ -135,6 +147,7 @@ private class VariableRefinement extends RefinementCandidate, VarUse {
override SsaSourceVariable getARefinedVar() { result = this.getVariable() }
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
ctxt.appliesTo(this) and
result = ctxt.(VarRefinementContext).getAValue()
@@ -149,6 +162,7 @@ private class ParRefinement extends RefinementCandidate, ParExpr {
result = this.getExpression().(RefinementCandidate).getARefinedVar()
}
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
result = this.getExpression().(RefinementCandidate).eval(ctxt)
}
@@ -162,6 +176,7 @@ private class TypeofRefinement extends RefinementCandidate, TypeofExpr {
result = this.getOperand().(RefinementCandidate).getARefinedVar()
}
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
exists(RefinementValue opVal |
opVal = this.getOperand().(RefinementCandidate).eval(ctxt) and
@@ -182,6 +197,7 @@ private class EqRefinement extends RefinementCandidate, EqualityTest {
result = this.getRightOperand().(RefinementCandidate).getARefinedVar()
}
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
exists(RefinementCandidate l, RefinementValue lv, RefinementCandidate r, RefinementValue rv |
l = this.getLeftOperand() and
@@ -220,6 +236,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
result = this.getIndex().(RefinementCandidate).getARefinedVar()
}
overlay[global]
override RefinementValue eval(RefinementContext ctxt) {
exists(
RefinementCandidate base, RefinementValue baseVal, RefinementCandidate index,
@@ -242,6 +259,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
* if any.
*/
bindingset[s, i]
overlay[global]
private RefinementValue evalIndex(StringConstant s, IntConstant i) {
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.
*/
overlay[global]
newtype TRefinementContext =
/**
* 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.
*/
overlay[global]
class RefinementContext extends TRefinementContext {
/**
* 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
* abstract value.
*/
overlay[global]
class VarRefinementContext extends RefinementContext, TVarRefinementContext {
override predicate appliesTo(RefinementCandidate cand) {
exists(AnalyzedRefinement ref, SsaSourceVariable var |

View File

@@ -5,6 +5,8 @@
* Note that unlike `TypeTracking.qll`, this library only performs
* local tracking within a function.
*/
overlay[local]
module;
private import javascript
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.
*/
overlay[global]
pragma[inline]
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.
*/
overlay[global]
pragma[inline]
DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
t2 = t.step(result, this)

View File

@@ -92,10 +92,15 @@ class AnalyzedNode extends DataFlow::Node {
PrimitiveType getAPrimitiveType() { result = this.getAValue().toPrimitive().getType() }
/** Gets a Boolean value that this node evaluates to. */
bindingset[this]
overlay[caller?]
pragma[inline_late]
boolean getABooleanValue() { result = this.getAValue().getBooleanValue() }
/** 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. */
InferredType getTheType() { result = unique(InferredType t | t = this.getAType()) }

View File

@@ -3,6 +3,8 @@
*
* Provides a representation for abstract values.
*/
overlay[local]
module;
private import javascript
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
* complete as an approximation of expression semantics.
*/
overlay[local]
module;
import javascript
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`.
*/
overlay[local]
DataFlow::Node getSynthesizedNode(AstNode node, string 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.
*/
overlay[local]
class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
/**
* 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`.
*/
overlay[global]
predicate expectsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
/**
* Holds if `node` should not permit flow of values stored in `contents`.
*/
overlay[global]
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) {
barrierGuardIsRelevant(guard) and result = guard.asExpr()
private Expr getALogicalOperatorParent(BarrierGuard guard, boolean possibleOutcome) {
barrierGuardIsRelevant(guard) and result = guard.asExpr() and possibleOutcome = [true, false]
or
result.(LogAndExpr).getAnOperand() = getALogicalAndParent(guard)
result.(LogOrExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
possibleOutcome = false
or
result.getUnderlyingValue() = getALogicalAndParent(guard)
}
/**
* Gets a logical `or` expression, or parenthesized expression, that contains `guard`.
*/
private Expr getALogicalOrParent(BarrierGuard guard) {
barrierGuardIsRelevant(guard) and result = guard.asExpr()
result.(LogAndExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
possibleOutcome = true
or
result.(LogOrExpr).getAnOperand() = getALogicalOrParent(guard)
or
result.getUnderlyingValue() = getALogicalOrParent(guard)
result.getUnderlyingValue() = getALogicalOperatorParent(guard, possibleOutcome)
}
final private class FinalFunction = Function;
@@ -394,15 +387,7 @@ module MakeStateBarrierGuard<
exists(BarrierGuard guard |
barrierGuardIsRelevant(guard) and
exists(Expr e |
exists(Expr returnExpr |
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(Expr returnExpr | returnExpr = getALogicalOperatorParent(guard, guardOutcome) |
exists(SsaExplicitDefinition ssa |
ssa.getDef().getSource() = returnExpr and
ssa.getVariable().getAUse() = this.getAReturnedExpr()

View File

@@ -97,9 +97,14 @@ module CallGraph {
not exists(read.getPropertyName()) and
result = read and
// there exists only local reads of the object, nothing else.
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) |
ref = [obj, any(DataFlow::PropRead r).getBase()]
)
objectOnlyUsedForPropRead(obj)
)
}
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 semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
@@ -194,6 +197,7 @@ module Public {
*/
class ContentSet extends TContentSet {
/** Gets a content that may be stored into when storing into this set. */
overlay[caller?]
pragma[inline]
Content getAStoreContent() {
result = this.asSingleton()
@@ -333,12 +337,14 @@ module Public {
/**
* A content set containing only the given content.
*/
overlay[caller]
pragma[inline]
ContentSet singleton(Content content) { result.asSingleton() = content }
/**
* A content set corresponding to the given property name.
*/
overlay[caller]
pragma[inline]
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.
*/
bindingset[bound]
overlay[caller]
ContentSet arrayElementLowerBoundFromInt(int bound) {
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.
*/
bindingset[n]
overlay[caller]
ContentSet arrayElementFromInt(int n) {
result = arrayElementKnown(n)
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.
*/
bindingset[key]
overlay[caller]
ContentSet mapValueFromKey(string key) {
result = mapValueWithKnownKey(key)
or
@@ -510,6 +519,7 @@ module Public {
* are mapped to their corresponding content sets (which are no longer seen as property names).
*/
bindingset[propertyName]
overlay[caller]
ContentSet fromLegacyProperty(string propertyName) {
result = fromLegacyPseudoProperty(propertyName)
or

View File

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

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import javascript
private import semmle.javascript.dataflow.internal.CallGraphs
private import semmle.javascript.dataflow.internal.DataFlowNode
@@ -310,6 +313,7 @@ private predicate returnNodeImpl(DataFlow::Node node, ReturnKind kind) {
kind = MkExceptionalReturnKind()
}
overlay[global]
private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
kind = MkNormalReturnKind() and result = call.asOrdinaryCall()
or
@@ -336,10 +340,12 @@ class ReturnNode extends DataFlow::Node {
}
/** A node that receives an output from a call. */
overlay[global]
class OutNode extends DataFlow::Node {
OutNode() { this = getAnOutNodeImpl(_, _) }
}
overlay[global]
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { result = getAnOutNodeImpl(call, kind) }
cached
@@ -416,9 +422,11 @@ abstract class LibraryCallable extends string {
LibraryCallable() { any() }
/** Gets a call to this library callable. */
overlay[global]
DataFlow::InvokeNode getACall() { none() }
/** Same as `getACall()` except this does not depend on the call graph or API graph. */
overlay[global]
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()`.
*/
overlay[global]
DataFlow::InvokeNode getACallStage2() { none() }
}
@@ -467,6 +476,7 @@ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition
isParameterNodeImpl(p, c, pos)
}
overlay[global]
private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition pos) {
n = call.asOrdinaryCall().getArgument(pos.asPositional())
or
@@ -523,6 +533,7 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
)
}
overlay[global]
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
isArgumentNodeImpl(n, call, pos)
}
@@ -545,11 +556,13 @@ DataFlowCallable nodeGetEnclosingCallable(Node node) {
node instanceof DataFlow::XmlAttributeNode and result.asFileCallable() = node.getFile()
}
overlay[global]
newtype TDataFlowType =
TFunctionType(Function f) or
TInstanceType(DataFlow::ClassNode cls) or
TAnyType()
overlay[global]
class DataFlowType extends TDataFlowType {
string toDebugString() {
result =
@@ -575,6 +588,7 @@ class DataFlowType extends TDataFlowType {
/**
* Holds if `t1` is strictly stronger than `t2`.
*/
overlay[global]
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
// 't1' is a subclass of 't2'
t1.asInstanceOfClass() = t2.asInstanceOfClass().getADirectSubClass+()
@@ -584,6 +598,7 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
t2 = TAnyType()
}
overlay[global]
private DataFlowType getPreciseType(Node node) {
exists(Function f |
(node = TValueNode(f) or node = TFunctionSelfReferenceNode(f)) and
@@ -598,6 +613,7 @@ private DataFlowType getPreciseType(Node node) {
result = getPreciseType(node.(PostUpdateNode).getPreUpdateNode())
}
overlay[global]
DataFlowType getNodeType(Node node) {
result = getPreciseType(node)
or
@@ -681,19 +697,23 @@ predicate neverSkipInPathGraph(Node node) {
node.asExpr() instanceof VarRef
}
overlay[global]
string ppReprType(DataFlowType t) { none() }
overlay[global]
pragma[inline]
private predicate compatibleTypesWithAny(DataFlowType t1, DataFlowType t2) {
t1 != TAnyType() and
t2 = TAnyType()
}
overlay[global]
pragma[nomagic]
private predicate compatibleTypes1(DataFlowType t1, DataFlowType t2) {
t1.asInstanceOfClass().getADirectSubClass+() = t2.asInstanceOfClass()
}
overlay[global]
pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 = t2
@@ -742,6 +762,7 @@ class ContentApprox extends TContentApprox {
}
}
overlay[global]
pragma[inline]
ContentApprox getContentApprox(Content c) {
c instanceof MkPropertyContent and result = TApproxPropertyContent()
@@ -767,6 +788,7 @@ ContentApprox getContentApprox(Content c) {
c instanceof MkCapturedContent and result = TApproxCapturedContent()
}
overlay[global]
cached
private newtype TDataFlowCall =
MkOrdinaryCall(DataFlow::InvokeNode node) or
@@ -791,6 +813,7 @@ private newtype TDataFlowCall =
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
overlay[global]
class DataFlowCall extends TDataFlowCall {
DataFlowCallable getEnclosingCallable() { none() } // Overridden in subclass
@@ -816,6 +839,7 @@ class DataFlowCall extends TDataFlowCall {
Location getLocation() { none() } // Overridden in subclass
}
overlay[global]
private class OrdinaryCall extends DataFlowCall, MkOrdinaryCall {
private DataFlow::InvokeNode node;
@@ -832,6 +856,7 @@ private class OrdinaryCall extends DataFlowCall, MkOrdinaryCall {
override Location getLocation() { result = node.getLocation() }
}
overlay[global]
private class PartialCall extends DataFlowCall, MkPartialCall {
private DataFlow::PartialInvokeNode node;
private DataFlow::Node callback;
@@ -851,6 +876,7 @@ private class PartialCall extends DataFlowCall, MkPartialCall {
override Location getLocation() { result = node.getLocation() }
}
overlay[global]
private class BoundCall extends DataFlowCall, MkBoundCall {
private DataFlow::InvokeNode node;
private int boundArgs;
@@ -868,6 +894,7 @@ private class BoundCall extends DataFlowCall, MkBoundCall {
override Location getLocation() { result = node.getLocation() }
}
overlay[global]
private class AccessorCall extends DataFlowCall, MkAccessorCall {
private DataFlow::PropRef ref;
@@ -882,6 +909,7 @@ private class AccessorCall extends DataFlowCall, MkAccessorCall {
override Location getLocation() { result = ref.getLocation() }
}
overlay[global]
class SummaryCall extends DataFlowCall, MkSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable enclosingCallable;
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
* we can't find its call sites.
*/
overlay[global]
private class ImpliedLambdaCall extends DataFlowCall, MkImpliedLambdaCall {
private Function function;
@@ -981,6 +1010,7 @@ class DataFlowExpr = Expr;
Node exprNode(DataFlowExpr expr) { result = DataFlow::exprNode(expr) }
overlay[global]
pragma[nomagic]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition 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.
}
overlay[global]
pragma[inline]
DataFlowCallable viableCallable(DataFlowCall node) {
// 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()
}
overlay[global]
private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
result.asOrdinaryCall() = cls.getAReceiverNode().getAPropertyRead().getACall()
or
@@ -1029,6 +1061,7 @@ private DataFlowCall getACallOnThis(DataFlow::ClassNode cls) {
result.asPartialCall().getACallbackNode() = cls.getAReceiverNode().getAPropertyRead()
}
overlay[global]
private predicate downwardCall(DataFlowCall call) {
exists(DataFlow::ClassNode cls |
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`
* might be improved by knowing the call context.
*/
overlay[global]
predicate mayBenefitFromCallContext(DataFlowCall call) { downwardCall(call) }
/** Gets the type of the receiver of `call`. */
overlay[global]
private DataFlowType getThisArgumentType(DataFlowCall call) {
exists(DataFlow::Node node |
isArgumentNodeImpl(node, call, MkThisParameter()) and
@@ -1052,6 +1087,7 @@ private DataFlowType getThisArgumentType(DataFlowCall call) {
}
/** Gets the type of the 'this' parameter of `call`. */
overlay[global]
private DataFlowType getThisParameterType(DataFlowCallable callable) {
exists(DataFlow::Node node |
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
* restricted to those `call`s for which a context might make a difference.
*/
overlay[global]
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
mayBenefitFromCallContext(call) and
result = viableCallable(call) and
@@ -1071,16 +1108,19 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
}
bindingset[node, fun]
overlay[caller]
pragma[inline_late]
private predicate sameContainerAsEnclosingContainer(Node node, Function fun) {
node.getContainer() = fun.getEnclosingContainer()
}
overlay[global]
abstract private class BarrierGuardAdapter extends DataFlow::Node {
// Note: avoid depending on DataFlow::FlowLabel here as it will cause these barriers to be re-evaluated
predicate blocksExpr(boolean outcome, Expr e) { none() }
}
overlay[global]
deprecated private class BarrierGuardAdapterSubclass extends BarrierGuardAdapter instanceof DataFlow::AdditionalBarrierGuardNode
{
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.
*/
overlay[global]
pragma[nomagic]
private predicate legacyBarrier(DataFlow::Node node) {
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.
*/
overlay[global]
pragma[nomagic]
private predicate isBlockedLegacyNode(Node node) {
// 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
* be cross function boundaries.
*/
overlay[global]
private predicate valuePreservingStep(Node node1, Node node2) {
node1.getASuccessor() = node2 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) {
simpleLocalFlowStep(node1, node2) and model = ""
}
overlay[global]
predicate simpleLocalFlowStep(Node node1, Node node2) {
valuePreservingStep(node1, node2) and
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
* variable.
*/
overlay[global]
predicate jumpStep(Node node1, Node node2) {
valuePreservingStep(node1, node2) 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
* value ends up in `node2`.
*/
overlay[global]
predicate readStep(Node node1, ContentSet c, Node node2) {
exists(DataFlow::PropRead read |
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. */
pragma[nomagic]
private Node getPostUpdateForStore(Node base) {
exists(Expr expr |
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.. */
overlay[caller]
pragma[inline]
private Node getStoreTarget(DataFlow::Node base) {
result = getPostUpdateForStore(base)
@@ -1487,6 +1536,7 @@ private int firstSpreadArgumentIndex(InvokeExpr expr) {
* `node2` references an object with a content `c.getAStoreContent()` that
* contains the value of `node1`.
*/
overlay[global]
predicate storeStep(Node node1, ContentSet c, Node node2) {
exists(DataFlow::PropWrite write |
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`
* in `x.f = newValue`.
*/
overlay[global]
predicate clearsContent(Node n, ContentSet c) {
FlowSummaryPrivate::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
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`
* at node `n`.
*/
overlay[global]
predicate expectsContent(Node n, ContentSet c) {
FlowSummaryPrivate::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or
@@ -1602,6 +1654,7 @@ abstract class NodeRegion extends Unit {
/**
* Holds if the node `n` is unreachable when the call context is `call`.
*/
overlay[global]
predicate isUnreachableInCall(NodeRegion n, DataFlowCall call) {
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. */
overlay[global]
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode()) and exists(kind)
or
@@ -1646,6 +1700,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
overlay[global]
class ArgumentNode extends DataFlow::Node {
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
* additional steps from the configuration into account.
*/
overlay[caller?]
pragma[inline]
deprecated predicate localFlowStep(
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.
*/
overlay[local]
cached
predicate identityFunctionStep(DataFlow::Node pred, DataFlow::CallNode succ) {
exists(DataFlow::GlobalVarRefNode global |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Models imports through the NPM `lazy-cache` package.
*/
overlay[local]
module;
import javascript
@@ -43,7 +45,7 @@ module LazyCache {
pragma[noopt]
override DataFlow::Node getImportedModuleNode() {
this instanceof LazyCacheImport and
result = this.flow()
result = DataFlow::valueNode(this)
or
exists(LazyCacheVariable variable, Expr base, PropAccess access, string localName |
// To avoid recursion, this should not depend on `SourceNode`.
@@ -52,12 +54,13 @@ module LazyCache {
access.getBase() = base and
localName = this.getLocalAlias() 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. */
overlay[global]
deprecated private class LazyCachePathExpr extends PathExpr, ConstantString {
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`.
*/
overlay[local]
abstract class Member extends DataFlow::SourceNode {
/** Gets the name of the accessed member. */
abstract string getName();
@@ -17,6 +18,7 @@ module LodashUnderscore {
/**
* An import of `lodash` or `underscore` accessing a given member of that package.
*/
overlay[local]
private class DefaultMember extends Member {
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, the global variable `_` is assumed to refer to `lodash` or `underscore`.
*/
overlay[local]
DataFlow::SourceNode member(string name) { result.(Member).getName() = name }
/**
* Holds if `name` is the name of a member exported from the `lodash` package
* which has a corresponding `lodash.xxx` NPM package.
*/
overlay[local]
private predicate isLodashMember(string name) {
// Can be generated using Object.keys(require('lodash'))
name =
@@ -181,9 +185,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashEach extends DataFlow::SummarizedCallable {
LodashEach() { this = "_.each-like" }
overlay[global]
override DataFlow::CallNode getACall() {
result = member(["each", "eachRight", "forEach", "forEachRight", "every", "some"]).getACall()
}
@@ -195,9 +201,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashMap extends DataFlow::SummarizedCallable {
LodashMap() { this = "_.map" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("map").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -212,9 +220,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashFlatMap extends DataFlow::SummarizedCallable {
LodashFlatMap() { this = "_.flatMap" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("flatMap").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -232,9 +242,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashFlatMapDeep extends DataFlow::SummarizedCallable {
LodashFlatMapDeep() { this = "_.flatMapDeep" }
overlay[global]
override DataFlow::CallNode getACall() {
result = member(["flatMapDeep", "flatMapDepth"]).getACall()
}
@@ -254,9 +266,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashReduce extends DataFlow::SummarizedCallable {
LodashReduce() { this = "_.reduce-like" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["reduce", "reduceRight"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -271,9 +285,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LoashSortBy extends DataFlow::SummarizedCallable {
LoashSortBy() { this = "_.sortBy-like" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["sortBy", "orderBy"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -287,9 +303,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashMinMaxBy extends DataFlow::SummarizedCallable {
LodashMinMaxBy() { this = "_.minBy / _.maxBy" }
overlay[global]
override DataFlow::CallNode getACall() { result = member(["minBy", "maxBy"]).getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -299,9 +317,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashPartition extends DataFlow::SummarizedCallable {
LodashPartition() { this = "_.partition" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("partition").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -311,9 +331,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class UnderscoreMapObject extends DataFlow::SummarizedCallable {
UnderscoreMapObject() { this = "_.mapObject" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("mapObject").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -330,9 +352,11 @@ module LodashUnderscore {
}
}
overlay[local]
private class LodashTap extends DataFlow::SummarizedCallable {
LodashTap() { this = "_.tap" }
overlay[global]
override DataFlow::CallNode getACall() { result = member("tap").getACall() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
@@ -342,6 +366,7 @@ module LodashUnderscore {
}
}
overlay[local?]
private class LodashGroupBy extends DataFlow::SummarizedCallable {
LodashGroupBy() { this = "_.groupBy" }

View File

@@ -8,14 +8,17 @@ import semmle.javascript.security.SensitiveActions
private import semmle.javascript.dataflow.internal.PreCallGraphStep
module NodeJSLib {
overlay[local]
private GlobalVariable processVariable() { variables(result, "process", any(GlobalScope sc)) }
overlay[local]
pragma[nomagic]
private GlobalVarAccess processExprInTopLevel(TopLevel tl) {
result = processVariable().getAnAccess() and
tl = result.getTopLevel()
}
overlay[local]
pragma[nomagic]
private GlobalVarAccess processExprInNodeModule() {
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 import of the `process` module.
*/
overlay[local]
private class ImplicitProcessImport extends DataFlow::ModuleImportNode::Range {
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 `CustomPropertyProjection` to introduce new kinds of property projections.
*/
overlay[local]
module;
import javascript
@@ -137,6 +139,7 @@ private class VarArgsPropertyProjection extends PropertyProjection::Range {
/**
* A taint step for a property projection.
*/
overlay[global]
private class PropertyProjectionTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
// reading from a tainted object yields a tainted result

View File

@@ -1,6 +1,8 @@
/**
* Provides predicates for working with templating libraries.
*/
overlay[local]
module;
import javascript
@@ -45,6 +47,7 @@ module Templating {
Locatable getParent() { template_placeholder_tag_info(this, result, _) }
/** Gets a data flow node representing the value plugged into this placeholder. */
overlay[global]
DataFlow::TemplatePlaceholderTagNode asDataFlowNode() { result.getTag() = this }
/** 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
* in the output without escaping it.
*/
overlay[global]
predicate isRawInterpolation() {
this.getRawText()
.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.
*/
overlay[global]
predicate isEscapingInterpolation() {
this.getRawText()
.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
* is susceptible to code injection.
*/
overlay[global]
predicate isInNestedTemplateContext(string templateType) {
templateType = "AngularJS" 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")`.
*/
overlay[global]
DataFlow::CallNode getAPipeCall(string name) {
result.getCalleeNode().asExpr().(PipeRefExpr).getName() = name
}
@@ -153,16 +160,19 @@ module Templating {
Expr getExpression() { result = this.getChildStmt(0).(ExprStmt).getExpr() }
/** Gets the data flow node representing the initialization of the given variable in this scope. */
overlay[global]
DataFlow::Node getVariableInit(string 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. */
overlay[global]
DataFlow::SourceNode getAVariableUse(string name) {
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. */
overlay[global]
DataFlow::SourceNode getAnAccessPathUse(string accessPath) {
result = this.getAVariableUse(accessPath)
or
@@ -177,6 +187,7 @@ module Templating {
/**
* A place where a template is instantiated or rendered.
*/
overlay[global]
class TemplateInstantiation extends DataFlow::Node instanceof TemplateInstantiation::Range {
/** Gets a data flow node that refers to the instantiated template string, if any. */
DataFlow::SourceNode getOutput() { result = super.getOutput() }
@@ -206,6 +217,7 @@ module Templating {
}
/** Companion module to the `TemplateInstantiation` class. */
overlay[global]
module TemplateInstantiation {
abstract class Range extends DataFlow::Node {
/** 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. */
overlay[global]
private API::Node getTemplateInput(DataFlow::SourceNode succ) {
exists(TemplateInstantiation inst, API::Node base, string name |
base.asSink() = inst.getTemplateParamsNode() and
@@ -258,6 +271,7 @@ module Templating {
)
}
overlay[global]
private class TemplateInputStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
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,
* representing the value plugged into the template.
*/
overlay[global]
private class TemplatePlaceholderStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(TemplatePlaceholderTag tag |
@@ -281,6 +296,7 @@ module Templating {
* A taint step from a `TemplatePlaceholderTag` to the enclosing expression in the
* surrounding JavaScript program.
*/
overlay[global]
private class PlaceholderToGeneratedCodeStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(TemplatePlaceholderTag tag |
@@ -296,6 +312,7 @@ module Templating {
final TemplatePlaceholderTag getAPlaceholder() { result.getFile() = this }
/** Gets a template file referenced by this one via a template inclusion tag, such as `{% include foo %}` */
overlay[global]
TemplateFile getAnImportedFile() {
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
* likely template file being referenced.
*/
overlay[global]
abstract class TemplateFileReference extends DataFlow::Node {
/** Gets the value that identifies the template. */
string getValue() {
@@ -335,6 +353,7 @@ module Templating {
}
/** Get file argument of a template instantiation, seen as a template file reference. */
overlay[global]
private class DefaultTemplateFileReference extends TemplateFileReference {
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
* likely template file being referenced.
*/
overlay[global]
abstract class TemplateFileReferenceString extends string {
bindingset[this]
TemplateFileReferenceString() { this = this }
@@ -382,6 +402,7 @@ module Templating {
}
/** The value of a template reference node, as a template reference string. */
overlay[global]
private class DefaultTemplateReferenceString extends TemplateFileReferenceString {
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. */
overlay[global]
private class UpwardTraversalSuffix extends TemplateFileReferenceString {
TemplateFileReferenceString original;
@@ -412,6 +434,7 @@ module Templating {
* Gets a "fingerprint" for the given template file, which is used to references
* that might refer to it (for pruning purposes only).
*/
overlay[global]
pragma[nomagic]
private string getTemplateFileFingerprint(TemplateFile file) {
result = file.getStem()
@@ -424,6 +447,7 @@ module Templating {
* Gets a "fingerprint" for the given string, which must match one of the fingerprints of
* the referenced file (for pruning purposes only).
*/
overlay[global]
pragma[nomagic]
private string getTemplateRefFingerprint(TemplateFileReferenceString ref) {
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.
*/
overlay[global]
pragma[nomagic]
private TemplateFile getAPotentialTarget(TemplateFileReferenceString 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
* the above rules. For example: `bar` matches `src/bar/index.html`.
*/
overlay[global]
pragma[nomagic]
private TemplateFile getAMatchingTarget(TemplateFileReferenceString ref) {
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`,
* and vice versa in `B/components/foo.js`.
*/
overlay[global]
pragma[nomagic]
private int getRankOfMatchingTarget(
TemplateFile file, Folder baseFolder, TemplateFileReferenceString ref
@@ -508,6 +535,7 @@ module Templating {
/**
* Gets the template file referred to by `ref` when resolved from `baseFolder`.
*/
overlay[global]
private TemplateFile getBestMatchingTarget(Folder baseFolder, TemplateFileReferenceString ref) {
result = max(getAMatchingTarget(ref) as f order by getRankOfMatchingTarget(f, baseFolder, ref))
}
@@ -599,6 +627,7 @@ module Templating {
override string getAPackageName() { result = "dot" }
}
overlay[global]
private TemplateSyntax getOwnTemplateSyntaxInFolder(Folder f) {
exists(PackageDependencies deps |
deps.getADependency(result.getAPackageName(), _) and
@@ -606,6 +635,7 @@ module Templating {
)
}
overlay[global]
private TemplateSyntax getTemplateSyntaxInFolder(Folder f) {
result = getOwnTemplateSyntaxInFolder(f)
or
@@ -613,6 +643,7 @@ module Templating {
result = getTemplateSyntaxInFolder(f.getParentContainer())
}
overlay[global]
private TemplateSyntax getTemplateSyntaxFromInstantiation(TemplateFile file) {
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.
*/
overlay[global]
TemplateSyntax getLikelyTemplateSyntax(TemplateFile file) {
result = getTemplateSyntaxFromInstantiation(file)
or
@@ -632,6 +664,7 @@ module Templating {
}
/** A step through the `safe` pipe, which bypasses HTML escaping. */
overlay[global]
private class SafePipeStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call |
@@ -645,6 +678,7 @@ module Templating {
/**
* An EJS-style `include` call within a template tag, such as `<%- include(file, { params }) %>`.
*/
overlay[global]
private class EjsIncludeCallInTemplate extends TemplateInstantiation::Range, DataFlow::CallNode {
EjsIncludeCallInTemplate() {
exists(TemplatePlaceholderTag tag |
@@ -669,6 +703,7 @@ module Templating {
*
* These API nodes are used in the `getTemplateInput` predicate.
*/
overlay[global]
private class IncludeFunctionAsEntryPoint extends API::EntryPoint {
IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" }
@@ -703,6 +738,7 @@ module Templating {
string getPath() { result = rawPath.trim().replaceAll("\\", "/").regexpReplaceAll("^\\./", "") }
/** Gets the file referenced by this inclusion tag. */
overlay[global]
TemplateFile getImportedFile() {
result =
this.getPath()
@@ -712,6 +748,7 @@ module Templating {
}
/** The imported string from a template inclusion tag. */
overlay[global]
private class TemplateInclusionPathString extends TemplateFileReferenceString {
TemplateInclusionTag tag;
@@ -723,6 +760,7 @@ module Templating {
/**
* A call to a member of the `consolidate` library, seen as a template instantiation.
*/
overlay[global]
private class ConsolidateCall extends TemplateInstantiation::Range, API::CallNode {
string engine;

View File

@@ -422,9 +422,11 @@ private module ClosureLibraryUri {
}
}
overlay[local]
private class QueryStringStringification extends DataFlow::SummarizedCallable {
QueryStringStringification() { this = "query-string stringification" }
overlay[global]
override DataFlow::InvokeNode getACall() {
result =
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 {
string type;
string path;
@@ -57,6 +58,7 @@ private class SummarizedCallableFromModel extends DataFlow::SummarizedCallable {
this = type + ";" + path
}
overlay[global]
override DataFlow::InvokeNode getACall() { ModelOutput::resolvedSummaryBase(type, path, result) }
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
* the type is not intended to match a static type.
*/
overlay[local?]
module;
private import codeql.util.Unit
private import ApiGraphModelsSpecific as Specific

View File

@@ -1,6 +1,8 @@
/**
* 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

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.
*/
bindingset[rawType]
overlay[caller]
predicate parseTypeString(string rawType, string package, string qualifiedName) {
exists(string regexp |
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.
*/
overlay[local]
predicate isPackageUsed(string package) {
package = "global"
or
@@ -68,6 +70,7 @@ predicate isPackageUsed(string package) {
}
bindingset[type]
overlay[local]
predicate isTypeUsed(string type) {
exists(string package |
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
* language semantics modeled by `getExtraNodeFromType`.
*/
overlay[local]
predicate hasImplicitTypeModel(string type, string otherType) { none() }
overlay[local]
pragma[nomagic]
private predicate parseRelevantTypeString(string rawType, string package, string qualifiedName) {
isRelevantFullPath(rawType, _) and
@@ -190,6 +195,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
}
bindingset[node]
overlay[caller?]
pragma[inline_late]
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
@@ -230,6 +236,7 @@ private predicate blockFuzzyCall(DataFlow::CallNode call) {
isCommonBuiltinMethodName(call.getCalleeName())
}
overlay[caller?]
pragma[inline]
API::Node getAFuzzySuccessor(API::Node node) {
result = node.getAMember() and

View File

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

View File

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

View File

@@ -6,10 +6,16 @@ private predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local]
private string getFileFromEntity(@locatable node) {
exists(@location loc, @file file |
hasLocation(node, loc) and
locations_default(loc, file, _, _, _, _) and
files(file, result)
exists(@location loc |
hasLocation(node, loc)
or
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) {
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
* enclosing `StmtContainer`.
*/
overlay[local]
module;
private import javascript
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.
*/
overlay[caller]
pragma[inline]
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)
*/
overlay[local?]
module;
private import javascript
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.
*/
overlay[local?]
module;
private import javascript
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.
*/
overlay[local?]
module;
private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import javascript
private import semmle.javascript.dataflow.FlowSummary
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.
*/
overlay[local?]
module;
private import javascript
private import semmle.javascript.dataflow.internal.DataFlowNode

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Contains flow summaries and steps modeling flow through iterators.
*/
overlay[local?]
module;
private import javascript
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`.
*/
overlay[local?]
module;
private import javascript
private import FlowSummaryUtil

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,12 +14,26 @@
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
where
duplicateProperties(dpid, p, q) and
p = oe.getProperty(i) and
q = oe.getProperty(j) and
dpid = p.getInit() and
dpid.same(q.getInit()) and
i < j and
// only report the next duplicate
not exists(int mid | mid in [i + 1 .. j - 1] | dpid.same(oe.getProperty(mid).getInit()))

View File

@@ -1,5 +1,6 @@
import javascript
overlay[local]
class TestAmdModuleRange extends AmdModuleDefinition::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.CustomAbstractValueDefinitions
overlay[local]
class MyCustomAbstractValueDefinition extends CustomAbstractValueDefinition {
DataFlow::ValueNode node;

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