mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
JS: Add public API
This commit is contained in:
@@ -5,6 +5,8 @@
|
|||||||
import javascript
|
import javascript
|
||||||
private import internal.StmtContainers
|
private import internal.StmtContainers
|
||||||
private import semmle.javascript.internal.CachedStages
|
private import semmle.javascript.internal.CachedStages
|
||||||
|
private import semmle.javascript.internal.TypeResolution
|
||||||
|
private import semmle.javascript.internal.BindingInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A program element corresponding to JavaScript code, such as an expression
|
* A program element corresponding to JavaScript code, such as an expression
|
||||||
@@ -472,5 +474,22 @@ module AST {
|
|||||||
|
|
||||||
/** Gets the data flow node associated with this program element. */
|
/** Gets the data flow node associated with this program element. */
|
||||||
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
|
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the results of name-resolution for this expression.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
ExprNameBindingNode getNameBinding() { result = this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets information about the type of this expression.
|
||||||
|
*
|
||||||
|
* This can be used to map an expression to the classes it may be an instance of
|
||||||
|
* (according to the type system), or to associate it with a named type coming
|
||||||
|
* from a dependency.
|
||||||
|
*/
|
||||||
|
TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,20 @@ import javascript
|
|||||||
private import internal.StmtContainers
|
private import internal.StmtContainers
|
||||||
private import internal.NameResolution
|
private import internal.NameResolution
|
||||||
private import internal.UnderlyingTypes
|
private import internal.UnderlyingTypes
|
||||||
|
private import internal.BindingInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type annotation, either in the form of a TypeScript type or a JSDoc comment.
|
* A type annotation, either in the form of a TypeScript type or a JSDoc comment.
|
||||||
*/
|
*/
|
||||||
class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||||
|
/**
|
||||||
|
* Gets information about the results of name-resolution for this type.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
TypeNameBindingNode getTypeBinding() { result = this }
|
||||||
|
|
||||||
/** Holds if this is the `any` type. */
|
/** Holds if this is the `any` type. */
|
||||||
predicate isAny() { none() }
|
predicate isAny() { none() }
|
||||||
|
|
||||||
|
|||||||
159
javascript/ql/lib/semmle/javascript/internal/BindingInfo.qll
Normal file
159
javascript/ql/lib/semmle/javascript/internal/BindingInfo.qll
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* Provides a limited public interface to name/type resolution information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private import javascript
|
||||||
|
private import semmle.javascript.internal.NameResolution
|
||||||
|
private import semmle.javascript.internal.TypeResolution
|
||||||
|
private import semmle.javascript.internal.UnderlyingTypes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for accessing name-resolution info about type names.
|
||||||
|
*/
|
||||||
|
class TypeNameBindingNode extends NameResolution::Node {
|
||||||
|
/**
|
||||||
|
* Holds if type refers to, or is an alias for, the given type name relative to the global scope.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* ```ts
|
||||||
|
* var x: Document; // hasQualifiedName("Document")
|
||||||
|
* var x: Electron; // hasQualifiedName("Electron")
|
||||||
|
* var x: Electron.BrowserWindow; // hasQualifiedName("Electron.BrowserWindow")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
predicate hasQualifiedName(string qualifiedName) {
|
||||||
|
NameResolution::nodeRefersToModule(this, "global", qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this refers a value exported by the given module, with the given
|
||||||
|
* qualified name. If the `qualifiedName` is empty, this refers to the module itself.
|
||||||
|
*
|
||||||
|
* For example, the type annotations below have the following name bindings:
|
||||||
|
* ```ts
|
||||||
|
* import { Request } from "express";
|
||||||
|
*
|
||||||
|
* var x: Request; // hasUnderlyingType("express", "Request")
|
||||||
|
* var x: Request | null; // no result (see hasUnderlyingType)
|
||||||
|
* var x: Request & { prop: string }; // no result (see hasUnderlyingType)
|
||||||
|
*
|
||||||
|
* interface CustomSubtype extends Request {}
|
||||||
|
*
|
||||||
|
* var x: CustomSubtype; // no result (see hasUnderlyingType)
|
||||||
|
*
|
||||||
|
* var x: typeof import("express"); // hasUnderlyingType("express", "")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
predicate hasQualifiedName(string moduleName, string qualifiedName) {
|
||||||
|
NameResolution::nodeRefersToModule(this, moduleName, qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this type refers to the given type exported from the given module, after
|
||||||
|
* unfolding unions and intersections, and following subtype relations.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* ```ts
|
||||||
|
* import { Request } from "express";
|
||||||
|
*
|
||||||
|
* var x: Request; // hasUnderlyingType("express", "Request")
|
||||||
|
* var x: Request | null; // hasUnderlyingType("express", "Request")
|
||||||
|
* var x: Request & { prop: string }; // hasUnderlyingType("express", "Request")
|
||||||
|
*
|
||||||
|
* interface CustomSubtype extends Request {}
|
||||||
|
*
|
||||||
|
* var x: CustomSubtype; // hasUnderlyingType("express", "Request")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
predicate hasUnderlyingType(string moduleName, string qualifiedName) {
|
||||||
|
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this type refers to the given type from the global scope, after
|
||||||
|
* unfolding unions and intersections, and following subtype relations.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* ```ts
|
||||||
|
* var x: Document; // hasUnderlyingType("Document")
|
||||||
|
* var x: Document | null; // hasUnderlyingType("Document")
|
||||||
|
* var x: Document & { prop: string }; // hasUnderlyingType("Document")
|
||||||
|
*
|
||||||
|
* interface CustomSubtype extends Document {}
|
||||||
|
*
|
||||||
|
* var x: CustomSubtype; // hasUnderlyingType("Document")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
predicate hasUnderlyingType(string qualifiedName) {
|
||||||
|
UnderlyingTypes::nodeHasUnderlyingType(this, qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the declaration of the type being referenced by this name.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* ```ts
|
||||||
|
* class Foo {}
|
||||||
|
*
|
||||||
|
* type T = Foo;
|
||||||
|
* var x: T; // getTypeDefinition() maps T to the class Foo above
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Note that this has no result for function-style classes referenced from
|
||||||
|
* a JSDoc comment.
|
||||||
|
*/
|
||||||
|
TypeDefinition getTypeDefinition() { TypeResolution::trackType(result) = this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a class that this type refers to, after unfolding unions and intersections (but not subtyping).
|
||||||
|
*
|
||||||
|
* For example, the type of `x` maps to the class `C` in each example below:
|
||||||
|
* ```ts
|
||||||
|
* class C {}
|
||||||
|
*
|
||||||
|
* var x: C;
|
||||||
|
* var x: C | null;
|
||||||
|
* var x: C & { prop: string };
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
DataFlow::ClassNode getAnUnderlyingClass() {
|
||||||
|
UnderlyingTypes::nodeHasUnderlyingClassType(this, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for accessing name-resolution info about expressions.
|
||||||
|
*/
|
||||||
|
class ExprNameBindingNode extends NameResolution::Node {
|
||||||
|
/**
|
||||||
|
* Holds if this refers a value exported by the given module, with the given
|
||||||
|
* qualified name. If the `qualifiedName` is empty, this refers to the module itself.
|
||||||
|
*
|
||||||
|
* For example, the type annotations below have the following name bindings:
|
||||||
|
* ```ts
|
||||||
|
* import * as f from "foo";
|
||||||
|
*
|
||||||
|
* var x = f; // hasQualifiedName(f, "")
|
||||||
|
* var x = f.x.y; // hasQualifiedName(f, "x.y")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
predicate hasQualifiedName(string moduleName, string qualifiedName) {
|
||||||
|
NameResolution::nodeRefersToModule(this, moduleName, qualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class, or function acting as a class, referenced by this name.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* class Foo {}
|
||||||
|
* const T = Foo;
|
||||||
|
* var x = T; // getClassNode() maps T to the class Foo above
|
||||||
|
*
|
||||||
|
* function Bar() {}
|
||||||
|
* Bar.prototype.blah = function() {};
|
||||||
|
* const S = Bar;
|
||||||
|
* var x = S; // getClassNode() maps S to the function Bar above
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
DataFlow::ClassNode getClassNode() { NameResolution::nodeRefersToClass(this, result) }
|
||||||
|
}
|
||||||
@@ -519,4 +519,19 @@ module NameResolution {
|
|||||||
cls.flowsTo(AccessPath::getAnAssignmentTo(name)) and
|
cls.flowsTo(AccessPath::getAnAssignmentTo(name)) and
|
||||||
not cls.getTopLevel().isExterns() // don't propagate externs classes
|
not cls.getTopLevel().isExterns() // don't propagate externs classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `node` refers to the given class.
|
||||||
|
*/
|
||||||
|
pragma[nomagic]
|
||||||
|
predicate nodeRefersToClass(Node node, DataFlow::ClassNode cls) {
|
||||||
|
exists(string name |
|
||||||
|
classHasGlobalName(cls, name) and
|
||||||
|
nodeRefersToModule(node, "global", name)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
trackClassValue(cls.getAstNode()) = node
|
||||||
|
or
|
||||||
|
trackFunctionValue(cls.getAstNode()) = node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user