mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
332 lines
9.8 KiB
Plaintext
332 lines
9.8 KiB
Plaintext
/**
|
|
* Provides classes for working with the Closure-Library module system.
|
|
*/
|
|
overlay[local?]
|
|
module;
|
|
|
|
import javascript
|
|
|
|
module Closure {
|
|
/** A call to `goog.require` */
|
|
class RequireCallExpr extends CallExpr {
|
|
RequireCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.require" }
|
|
|
|
/** Gets the imported namespace name. */
|
|
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
|
|
}
|
|
|
|
/** A call to `goog.provide` */
|
|
class ProvideCallExpr extends CallExpr {
|
|
ProvideCallExpr() { this.getCallee().(PropAccess).getQualifiedName() = "goog.provide" }
|
|
|
|
/** Gets the imported namespace name. */
|
|
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
|
|
}
|
|
|
|
/** A call to `goog.module` or `goog.declareModuleId`. */
|
|
private class ModuleDeclarationCall extends CallExpr {
|
|
private string kind;
|
|
|
|
ModuleDeclarationCall() {
|
|
this.getCallee().(PropAccess).getQualifiedName() = kind and
|
|
kind = ["goog.module", "goog.declareModuleId"]
|
|
}
|
|
|
|
/** Gets the declared namespace. */
|
|
string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
|
|
|
|
/** Gets the string `goog.module` or `goog.declareModuleId` depending on which method is being called. */
|
|
string getModuleKind() { result = kind }
|
|
}
|
|
|
|
/**
|
|
* A reference to a Closure namespace.
|
|
*/
|
|
overlay[global]
|
|
deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {
|
|
/**
|
|
* Gets the namespace being referenced.
|
|
*/
|
|
string getClosureNamespace() { result = super.getClosureNamespace() }
|
|
}
|
|
|
|
overlay[global]
|
|
deprecated module ClosureNamespaceRef {
|
|
/**
|
|
* A reference to a Closure namespace.
|
|
*
|
|
* Can be subclassed to classify additional nodes as namespace references.
|
|
*/
|
|
abstract class Range extends DataFlow::Node {
|
|
/**
|
|
* Gets the namespace being referenced.
|
|
*/
|
|
abstract string getClosureNamespace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Can be subclassed to classify additional nodes as namespace accesses.
|
|
*/
|
|
abstract class Range extends ClosureNamespaceRef::Range { }
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
DefaultNamespaceRef() { this = DataFlow::globalVarRef("goog").getAMethodCall() }
|
|
|
|
override string getClosureNamespace() { result = this.getArgument(0).getStringValue() }
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
|
|
/**
|
|
* A top-level call to `goog.provide`.
|
|
*/
|
|
overlay[global]
|
|
deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef {
|
|
DefaultClosureProvideCall() {
|
|
this.getMethodName() = "provide" and
|
|
isTopLevelExpr(this)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
DefaultClosureRequireCall() { this.getMethodName() = "require" }
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
isTopLevelExpr(this)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A top-level call to `goog.module` or `goog.declareModuleId`.
|
|
*/
|
|
overlay[global]
|
|
deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration
|
|
{ }
|
|
|
|
pragma[noinline]
|
|
private RequireCallExpr getARequireInTopLevel(ClosureModule m) { result.getTopLevel() = m }
|
|
|
|
/**
|
|
* A module using the Closure module system, declared using `goog.module()` or `goog.declareModuleId()`.
|
|
*/
|
|
class ClosureModule extends Module {
|
|
private ModuleDeclarationCall decl;
|
|
|
|
ClosureModule() { decl.getTopLevel() = this }
|
|
|
|
/**
|
|
* Gets the call to `goog.module` or `goog.declareModuleId` in this module.
|
|
*/
|
|
overlay[global]
|
|
deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this }
|
|
|
|
/**
|
|
* Gets the namespace of this module.
|
|
*/
|
|
string getClosureNamespace() { result = decl.getClosureNamespace() }
|
|
|
|
override Module getAnImportedModule() {
|
|
result.(ClosureModule).getClosureNamespace() =
|
|
getARequireInTopLevel(this).getClosureNamespace()
|
|
}
|
|
|
|
/**
|
|
* Gets the top-level `exports` variable in this module, if this module is defined by
|
|
* a `good.module` call.
|
|
*
|
|
* This variable denotes the object exported from this module.
|
|
*
|
|
* Has no result for ES6 modules using `goog.declareModuleId`.
|
|
*/
|
|
Variable getExportsVariable() {
|
|
decl.getModuleKind() = "goog.module" and
|
|
result = this.getScope().getVariable("exports")
|
|
}
|
|
|
|
overlay[global]
|
|
override DataFlow::Node getAnExportedValue(string name) {
|
|
exists(DataFlow::PropWrite write, Expr base |
|
|
result = write.getRhs() and
|
|
write.writes(base.flow(), name, _) and
|
|
(
|
|
base = this.getExportsVariable().getAReference()
|
|
or
|
|
base = this.getExportsVariable().getAnAssignedExpr()
|
|
)
|
|
)
|
|
}
|
|
|
|
overlay[global]
|
|
override DataFlow::Node getABulkExportedNode() {
|
|
result = this.getExportsVariable().getAnAssignedExpr().flow()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A global Closure script, that is, a toplevel that is executed in the global scope and
|
|
* contains a toplevel call to `goog.provide` or `goog.require`.
|
|
*/
|
|
class ClosureScript extends TopLevel {
|
|
ClosureScript() {
|
|
not this instanceof ClosureModule and
|
|
(
|
|
any(ProvideCallExpr provide).getTopLevel() = this
|
|
or
|
|
any(RequireCallExpr require).getTopLevel() = this
|
|
)
|
|
}
|
|
|
|
/** Gets the identifier of a namespace required by this module. */
|
|
string getARequiredNamespace() {
|
|
exists(RequireCallExpr require |
|
|
require.getTopLevel() = this and
|
|
result = require.getClosureNamespace()
|
|
)
|
|
}
|
|
|
|
/** Gets the identifer of a namespace provided by this module. */
|
|
string getAProvidedNamespace() {
|
|
exists(ProvideCallExpr require |
|
|
require.getTopLevel() = this and
|
|
result = require.getClosureNamespace()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds if `name` is a closure namespace, including proper namespace prefixes.
|
|
*/
|
|
overlay[global]
|
|
pragma[noinline]
|
|
predicate isClosureNamespace(string name) {
|
|
exists(string namespace |
|
|
namespace =
|
|
[
|
|
any(RequireCallExpr ref).getClosureNamespace(),
|
|
any(ModuleDeclarationCall c).getClosureNamespace()
|
|
]
|
|
|
|
|
name = namespace.substring(0, namespace.indexOf("."))
|
|
or
|
|
name = namespace
|
|
)
|
|
or
|
|
name = "goog" // The closure libraries themselves use the "goog" namespace
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
isClosureNamespace(name)
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
|
|
/**
|
|
* 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() }
|
|
|
|
override predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) {
|
|
index >= 0 and
|
|
callback = this.getArgument(0) and
|
|
argument = this.getArgument(index + 2)
|
|
}
|
|
|
|
override DataFlow::SourceNode getBoundFunction(DataFlow::Node callback, int boundArgs) {
|
|
boundArgs = this.getNumArgument() - 2 and
|
|
callback = this.getArgument(0) and
|
|
result = this
|
|
}
|
|
|
|
override DataFlow::Node getBoundReceiver(DataFlow::Node callback) {
|
|
callback = this.getArgument(0) and
|
|
result = this.getArgument(1)
|
|
}
|
|
}
|
|
}
|