mirror of
https://github.com/github/codeql.git
synced 2026-05-25 00:27:09 +02:00
PS: Make API graphs compile again. There is still some TODOs here, but at least it compiles.
This commit is contained in:
@@ -271,7 +271,8 @@ module API {
|
||||
this = Impl::MkMethodAccessNode(result) or
|
||||
this = Impl::MkBackwardNode(result, _) or
|
||||
this = Impl::MkForwardNode(result, _) or
|
||||
this = Impl::MkSinkNode(result)
|
||||
this = Impl::MkSinkNode(result) or
|
||||
this = Impl::MkNamespaceOfTypeNameNode(result)
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
@@ -324,45 +325,6 @@ module API {
|
||||
}
|
||||
}
|
||||
|
||||
/** A node representing a module/class object with epsilon edges to its descendents. */
|
||||
private class ModuleNode extends Node, Impl::MkModule {
|
||||
string qualifiedModule;
|
||||
int n;
|
||||
|
||||
ModuleNode() { this = Impl::MkModule(qualifiedModule, n) }
|
||||
|
||||
ModuleNode getNext() { result = Impl::MkModule(qualifiedModule, n + 1) }
|
||||
|
||||
ModuleNode getPred() { result.getNext() = this }
|
||||
|
||||
string getComponent() { result = qualifiedModule.splitAt(".", n) }
|
||||
|
||||
string getModule() {
|
||||
not exists(this.getPred()) and
|
||||
result = this.getComponent()
|
||||
or
|
||||
result = this.getPred().getModule() + "." + this.getComponent()
|
||||
}
|
||||
|
||||
override string toString() { result = "Module(" + this.getModule() + ")" }
|
||||
}
|
||||
|
||||
/** A node representing instances of a module/class with epsilon edges to its ancestors. */
|
||||
private class InstanceUp extends Node, Impl::MkInstanceUp {
|
||||
/** Gets the module whose instances are represented by this API node. */
|
||||
string getType() { this = Impl::MkInstanceUp(result) }
|
||||
|
||||
override string toString() { result = "ModuleInstanceUp(" + this.getType() + ")" }
|
||||
}
|
||||
|
||||
/** A node representing instances of a module/class with epsilon edges to its descendents. */
|
||||
private class InstanceDownNode extends Node, Impl::MkInstanceDown {
|
||||
/** Gets the module whose instances are represented by this API node. */
|
||||
string getType() { this = Impl::MkInstanceDown(result) }
|
||||
|
||||
override string toString() { result = "ModuleInstanceDown(" + this.getType() + ")" }
|
||||
}
|
||||
|
||||
/** A node corresponding to the method being invoked at a method call. */
|
||||
class MethodAccessNode extends Node, Impl::MkMethodAccessNode {
|
||||
override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" }
|
||||
@@ -378,6 +340,22 @@ module API {
|
||||
override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" }
|
||||
}
|
||||
|
||||
private class UsingNode extends Node, Impl::MkUsingNode {
|
||||
UsingStmt using; // TODO: This should really be the cfg node, I think
|
||||
|
||||
UsingNode() { this = Impl::MkUsingNode(using) }
|
||||
|
||||
override string toString() { result = "UsingNode(" + using + ")" }
|
||||
}
|
||||
|
||||
private class NamespaceOfTypeNameNode extends Node, Impl::MkNamespaceOfTypeNameNode {
|
||||
DataFlow::QualifiedTypeNameNode typeName;
|
||||
|
||||
NamespaceOfTypeNameNode() { this = Impl::MkNamespaceOfTypeNameNode(typeName) }
|
||||
|
||||
override string toString() { result = "NamespaceOfTypeNameNode(" + typeName + ")" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An API entry point.
|
||||
*
|
||||
@@ -415,11 +393,15 @@ module API {
|
||||
/** Gets the root node. */
|
||||
Node root() { result instanceof RootNode }
|
||||
|
||||
/**
|
||||
* Gets the node that represents the module with qualified
|
||||
* name `qualifiedModule`.
|
||||
*/
|
||||
ModuleNode mod(string qualifiedModule, int n) { result = Impl::MkModule(qualifiedModule, n) }
|
||||
bindingset[name]
|
||||
pragma[inline_late]
|
||||
Node namespace(string name) {
|
||||
// This predicate is currently not 'inline_late' because 'n' can be an input or output
|
||||
Impl::namespace(name, result)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) }
|
||||
|
||||
/**
|
||||
* Gets an unqualified call at the top-level with the given method name.
|
||||
@@ -466,44 +448,14 @@ module API {
|
||||
|
||||
cached
|
||||
private module Impl {
|
||||
private predicate isGacModule(string s) {
|
||||
s =
|
||||
[
|
||||
"System.Management.Automation",
|
||||
"Microsoft.Management.Infrastructure",
|
||||
"Microsoft.PowerShell.Security",
|
||||
"Microsoft.PowerShell.Commands.Management",
|
||||
"Microsoft.PowerShell.Commands.Utility"
|
||||
]
|
||||
}
|
||||
|
||||
private predicate isModule(string s, int n) {
|
||||
(
|
||||
any(UsingStmt using).getName() = s
|
||||
or
|
||||
any(Cmd cmd).getNamespaceQualifier() = s
|
||||
or
|
||||
any(TypeNameExpr tn).getName() = s
|
||||
or
|
||||
any(ModuleManifest manifest).getModuleName() = s
|
||||
or
|
||||
isGacModule(s)
|
||||
) and
|
||||
exists(s.splitAt(".", n))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TApiNode =
|
||||
/** The root of the API graph. */
|
||||
MkRoot() or
|
||||
/** The method accessed at `call`, synthetically treated as a separate object. */
|
||||
MkMethodAccessNode(DataFlow::CallNode call) or
|
||||
MkModule(string qualifiedModule, int n) { isModule(qualifiedModule, n) } or
|
||||
/** Instances of `mod` with epsilon edges to its ancestors. */
|
||||
MkInstanceUp(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
|
||||
/** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */
|
||||
MkInstanceDown(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
|
||||
/** Intermediate node for following forward data flow. */
|
||||
MkUsingNode(UsingStmt using) or
|
||||
MkNamespaceOfTypeNameNode(DataFlow::QualifiedTypeNameNode typeName) or
|
||||
MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
|
||||
/** Intermediate node for following backward data flow. */
|
||||
MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
|
||||
@@ -523,10 +475,30 @@ module API {
|
||||
pragma[inline_late]
|
||||
private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e }
|
||||
|
||||
private import frameworks.data.ModelsAsData
|
||||
|
||||
cached
|
||||
predicate namespace(string name, Node node) {
|
||||
exists(DataFlow::QualifiedTypeNameNode typeName |
|
||||
typeName.getNamespace() = name and
|
||||
node = MkNamespaceOfTypeNameNode(typeName)
|
||||
)
|
||||
or
|
||||
exists(UsingStmt using |
|
||||
using.getName().toLowerCase() = name and
|
||||
node = MkUsingNode(using)
|
||||
)
|
||||
or
|
||||
node = ModelOutput::getATypeNode(name)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) }
|
||||
|
||||
cached
|
||||
predicate toplevelCall(string name, Node node) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevel and
|
||||
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevelScriptBlock and
|
||||
call.getName() = name and
|
||||
node = MkMethodAccessNode(call)
|
||||
)
|
||||
@@ -544,26 +516,25 @@ module API {
|
||||
|
||||
cached
|
||||
predicate memberEdge(Node pred, string name, Node succ) {
|
||||
exists(MemberExpr member | succ = getForwardStartNode(getNodeFromExpr(member)) |
|
||||
pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(member.getQualifier()))) and
|
||||
name = member.getMemberName()
|
||||
exists(StringConstExpr read |
|
||||
succ = getForwardStartNode(getNodeFromExpr(read)) and
|
||||
pred = MkRoot() and
|
||||
name = read.getValueString()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::QualifiedTypeNameNode typeName |
|
||||
typeName.getName() = name and
|
||||
pred = MkNamespaceOfTypeNameNode(typeName) and
|
||||
succ = getForwardStartNode(typeName)
|
||||
)
|
||||
// or
|
||||
// TODO: Handle getAMember when the predecessor is a MkUsingNode?
|
||||
}
|
||||
|
||||
cached
|
||||
predicate methodEdge(Node pred, string name, Node succ) {
|
||||
exists(DataFlow::CallNode call | succ = MkMethodAccessNode(call) and name = call.getName() |
|
||||
pred = getForwardEndNode(getALocalSourceStrict(call.getQualifier()))
|
||||
or
|
||||
exists(string qualifiedModule, ModuleManifest manifest, int n |
|
||||
pred = mod(qualifiedModule, n) and
|
||||
not exists(mod(qualifiedModule, n + 1)) and
|
||||
manifest.getModuleName() = qualifiedModule
|
||||
|
|
||||
manifest.getACmdLetToExport() = name
|
||||
or
|
||||
manifest.getAFunctionToExport() = name
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -657,24 +628,10 @@ module API {
|
||||
|
||||
cached
|
||||
predicate instanceEdge(Node pred, Node succ) {
|
||||
exists(string qualifiedType, int n |
|
||||
pred = MkModule(qualifiedType, n) and
|
||||
not exists(MkModule(qualifiedType, n + 1))
|
||||
|
|
||||
exists(DataFlow::TypeNameNode typeName |
|
||||
typeName.getTypeName() = qualifiedType and
|
||||
succ = getForwardStartNode(typeName)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ObjectCreationNode objCreation |
|
||||
objCreation.getConstructedTypeName() = qualifiedType and
|
||||
succ = getForwardStartNode(objCreation)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ParameterNode p |
|
||||
p.getParameter().getStaticType() = qualifiedType and
|
||||
succ = getForwardStartNode(p)
|
||||
)
|
||||
// TODO: Also model parameters with a given type here
|
||||
exists(DataFlow::ObjectCreationNode objCreation |
|
||||
pred = getForwardEndNode(objCreation.getConstructedTypeNode()) and
|
||||
succ = getForwardStartNode(objCreation)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ class SplitPath extends CmdCall {
|
||||
not this.hasNamedArgument("path") and
|
||||
result = this.getPositionalArgument(0)
|
||||
or
|
||||
// TODO: This should not be allowed, but I've seen code doing it
|
||||
// TODO: This should not be allowed, but I've seen code doing it and somehow it works
|
||||
result = this.getNamedArgument("parent")
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import powershell
|
||||
abstract private class AbstractObjectCreation extends CallExpr {
|
||||
/** The name of the type of the object being constructed. */
|
||||
abstract string getConstructedTypeName();
|
||||
|
||||
abstract Expr getConstructedTypeExpr();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11,10 +13,12 @@ abstract private class AbstractObjectCreation extends CallExpr {
|
||||
* [System.IO.FileInfo]::new("C:\\file.txt")
|
||||
* ```
|
||||
*/
|
||||
class NewObjectCreation extends AbstractObjectCreation instanceof ConstructorCall {
|
||||
class NewObjectCreation extends AbstractObjectCreation, ConstructorCall {
|
||||
final override string getConstructedTypeName() {
|
||||
result = ConstructorCall.super.getConstructedTypeName()
|
||||
}
|
||||
|
||||
final override Expr getConstructedTypeExpr() { result = typename }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,19 +27,21 @@ class NewObjectCreation extends AbstractObjectCreation instanceof ConstructorCal
|
||||
* New-Object -TypeName System.IO.FileInfo -ArgumentList "C:\\file.txt"
|
||||
* ```
|
||||
*/
|
||||
class DotNetObjectCreation extends AbstractObjectCreation instanceof CmdCall {
|
||||
class DotNetObjectCreation extends AbstractObjectCreation, CmdCall {
|
||||
DotNetObjectCreation() { this.getName() = "New-Object" }
|
||||
|
||||
final override string getConstructedTypeName() {
|
||||
result = this.getConstructedTypeExpr().(StringConstExpr).getValueString()
|
||||
}
|
||||
|
||||
final override Expr getConstructedTypeExpr() {
|
||||
// Either it's the named argument `TypeName`
|
||||
result = CmdCall.super.getNamedArgument("TypeName").(StringConstExpr).getValueString()
|
||||
result = CmdCall.super.getNamedArgument("TypeName")
|
||||
or
|
||||
// Or it's the first positional argument if that's the named argument
|
||||
not CmdCall.super.hasNamedArgument("TypeName") and
|
||||
exists(StringConstExpr arg | arg = CmdCall.super.getPositionalArgument(0) |
|
||||
result = arg.getValueString() and
|
||||
not arg = CmdCall.super.getNamedArgument(["ArgumentList", "Property"])
|
||||
)
|
||||
result = CmdCall.super.getPositionalArgument(0) and
|
||||
result = CmdCall.super.getNamedArgument(["ArgumentList", "Property"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ module EngineIntrinsics {
|
||||
private class EngineIntrinsicsGlobalEntry extends ModelInput::TypeModel {
|
||||
override DataFlow::Node getASource(string type) {
|
||||
type = "System.Management.Automation.EngineIntrinsics" and
|
||||
result.asExpr().getExpr().(VarReadAccess).getUserPath().toLowerCase() = "executioncontext"
|
||||
result.asExpr().getExpr().(VarReadAccess).getVariable().getName().toLowerCase() = "executioncontext"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable {
|
||||
this = type + ";" + path
|
||||
}
|
||||
|
||||
override Call getACall() {
|
||||
override CallExpr getACall() {
|
||||
exists(API::MethodAccessNode base |
|
||||
ModelOutput::resolvedSummaryBase(type, path, base) and
|
||||
result = base.asCall().asExpr().getExpr()
|
||||
|
||||
@@ -55,27 +55,6 @@ predicate hasImplicitTypeModel(string type, string otherType) {
|
||||
parseType(otherType, type, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
string getConstComponent(string consts, int n) {
|
||||
parseRelevantType(_, consts, _) and
|
||||
result = consts.splitAt(".", n)
|
||||
}
|
||||
|
||||
private int getNumConstComponents(string consts) {
|
||||
result = strictcount(int n | exists(getConstComponent(consts, n)))
|
||||
}
|
||||
|
||||
private DataFlow::TypePathNode getConstantFromConstPath(string consts, int n) {
|
||||
n = 1 and
|
||||
result.getComponent() = getConstComponent(consts, 0)
|
||||
or
|
||||
result = getConstantFromConstPath(consts, n - 1).getConstant(getConstComponent(consts, n - 1))
|
||||
}
|
||||
|
||||
private DataFlow::TypePathNode getConstantFromConstPath(string consts) {
|
||||
result = getConstantFromConstPath(consts, getNumConstComponents(consts))
|
||||
}
|
||||
|
||||
/** Gets a Powershell-specific interpretation of the `(type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[type, path]
|
||||
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) {
|
||||
@@ -91,15 +70,8 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) {
|
||||
|
||||
/** Gets a Powershell-specific interpretation of the given `type`. */
|
||||
API::Node getExtraNodeFromType(string type) {
|
||||
exists(string consts, string suffix, DataFlow::TypePathNode constRef |
|
||||
parseRelevantType(type, consts, suffix) and
|
||||
constRef = getConstantFromConstPath(consts)
|
||||
|
|
||||
suffix = "!" and
|
||||
result = constRef.track()
|
||||
or
|
||||
suffix = "" and
|
||||
result = constRef.track().getInstance()
|
||||
exists(string consts, string suffix | parseRelevantType(type, consts, suffix) |
|
||||
none() // TODO
|
||||
)
|
||||
or
|
||||
type = "" and
|
||||
@@ -187,7 +159,7 @@ predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToke
|
||||
/** An API graph node representing a method call. */
|
||||
class InvokeNode extends API::MethodAccessNode {
|
||||
/** Gets the number of arguments to the call. */
|
||||
int getNumArgument() { result = this.asCall().getNumberOfArguments() }
|
||||
int getNumArgument() { result = count(this.asCall().getAnArgument()) }
|
||||
}
|
||||
|
||||
/** Gets the `InvokeNode` corresponding to a specific invocation of `node`. */
|
||||
|
||||
Reference in New Issue
Block a user