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
|
||||
private import internal.StmtContainers
|
||||
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
|
||||
@@ -472,5 +474,22 @@ module AST {
|
||||
|
||||
/** Gets the data flow node associated with this program element. */
|
||||
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.NameResolution
|
||||
private import internal.UnderlyingTypes
|
||||
private import internal.BindingInfo
|
||||
|
||||
/**
|
||||
* A type annotation, either in the form of a TypeScript type or a JSDoc comment.
|
||||
*/
|
||||
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. */
|
||||
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
|
||||
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