PS: Use ! to mark static members/methods.

This commit is contained in:
Mathias Vorreiter Pedersen
2024-11-12 20:09:20 +00:00
parent 50c05517d2
commit f103fed6de
11 changed files with 202 additions and 100 deletions

View File

@@ -326,19 +326,25 @@ module API {
/** A node representing a module/class object with epsilon edges to its descendents. */
private class ModuleNode extends Node, Impl::MkModule {
/** Gets the module represented by this API node. */
string getModule() { this = Impl::MkModule(result) }
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() + ")" }
TypeNode getType(string name) { result.getType() = this.getModule() + "." + name } // TODO: Check that name exists in module
}
private class TypeNode extends Node, Impl::MkType {
/** Gets the type represented by this API node. */
string getType() { this = Impl::MkType(result) }
override string toString() { result = "Type(" + this.getType() + ")" }
}
/** A node representing instances of a module/class with epsilon edges to its ancestors. */
@@ -413,13 +419,7 @@ module API {
* Gets the node that represents the module with qualified
* name `qualifiedModule`.
*/
ModuleNode mod(string qualifiedModule) { result = Impl::MkModule(qualifiedModule) }
/**
* Gets the node that represents the type with qualified
* name `qualifiedType`.
*/
TypeNode type(string qualifiedType) { result = Impl::MkType(qualifiedType) }
ModuleNode mod(string qualifiedModule, int n) { result = Impl::MkModule(qualifiedModule, n) }
/**
* Gets an unqualified call at the top-level with the given method name.
@@ -466,26 +466,31 @@ module API {
cached
private module Impl {
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
) 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) {
any(UsingStmt using).getName() = qualifiedModule
or
any(Cmd cmd).getNamespaceQualifier() = qualifiedModule
or
any(TypeNameExpr tn).getName() = qualifiedModule
or
any(ModuleManifest manifest).getModuleName() = qualifiedModule
} or
MkType(string qualifiedType) { any(ConstantValue cv).asString() = qualifiedType } or // TODO
MkModule(string qualifiedModule, int n) { isModule(qualifiedModule, n) } or
/** Instances of `mod` with epsilon edges to its ancestors. */
MkInstanceUp(string qualifiedType) { exists(MkType(qualifiedType)) } or
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(MkType(qualifiedType)) } or
MkInstanceDown(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
/** Intermediate node for following forward data flow. */
MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
/** Intermediate node for following backward data flow. */
@@ -525,14 +530,6 @@ module API {
)
}
cached
predicate typeEdge(Node pred, string name, Node succ) {
exists(ModuleNode mod |
pred = mod and
succ = mod.getType(name)
)
}
cached
predicate memberEdge(Node pred, string name, Node succ) {
exists(MemberExpr member | succ = getForwardStartNode(getNodeFromExpr(member)) |
@@ -546,8 +543,9 @@ module API {
exists(DataFlow::CallNode call | succ = MkMethodAccessNode(call) and name = call.getName() |
pred = getForwardEndNode(getALocalSourceStrict(call.getQualifier()))
or
exists(string qualifiedModule, ModuleManifest manifest |
pred = mod(qualifiedModule) and
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
@@ -647,8 +645,15 @@ module API {
cached
predicate instanceEdge(Node pred, Node succ) {
// An instance of a type
exists(string qualifiedType | pred = MkType(qualifiedType) |
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)
@@ -659,15 +664,6 @@ module API {
succ = getForwardStartNode(p)
)
)
or
// A use of a module (or static type?)
// TODO: Consider implicit module qualiifers and use instance on all of them
exists(string qualifiedType, DataFlow::TypeNameNode typeName |
pred = MkModule(qualifiedType) and
typeName.getTypeName() = qualifiedType
|
succ = getForwardStartNode(typeName)
)
}
cached

View File

@@ -610,7 +610,13 @@ module StmtNodes {
final override ExprCfgNode getCommand() { s.hasCfgChild(s.getCommand(), this, result) }
final override string getName() { result = s.getCmdName().getValue().getValue() }
final override string getName() { result = s.getCommandName() }
/** Holds if the command is qualified. */
predicate isQualified() { s.isQualified() }
/** Gets the namespace qualifier of this command, if any. */
string getNamespaceQualifier() { result = s.getNamespaceQualifier() }
}
/** A control-flow node that wraps a call to operator `&` */

View File

@@ -202,6 +202,7 @@ private module Cached {
isProcessPropertyByNameNode(iter, _)
} or
TScriptBlockNode(ScriptBlock scriptBlock) or
TTypePathNode(int n, CfgNode cfg) { isTypePathNode(_, n, cfg) } or
TForbiddenRecursionGuard() {
none() and
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
@@ -1148,6 +1149,64 @@ class ScriptBlockNode extends TScriptBlockNode, NodeImpl {
override predicate nodeIsHidden() { any() }
}
private predicate isTypePathNode(string type, int n, CfgNode cfg) {
exists(CfgNodes::ExprNodes::TypeNameCfgNode typeName, string s |
cfg = typeName and
type = typeName.getTypeName() and
s = type.splitAt(".", n)
)
or
exists(CfgNodes::StmtNodes::CmdCfgNode cmd, string s |
cfg = cmd.getCommand() and
type = cmd.getNamespaceQualifier() and
s = type.splitAt(".", n)
)
}
/**
* A dataflow node that represents a component of a type or module path.
*
* For example, `System`, `System.Management`, `System.Management.Automation`,
* and `System.Management.Automation.PowerShell` in the type
* name `[System.Management.Automation.PowerShell]`.
*/
class TypePathNodeImpl extends TTypePathNode, NodeImpl {
int n;
CfgNode cfg;
TypePathNodeImpl() { this = TTypePathNode(n, cfg) }
string getType() { isTypePathNode(result, n, cfg) }
predicate isComplete() { not exists(this.getNext()) }
int getIndex() { result = n }
string getComponent() { result = this.getType().splitAt(".", n) }
override CfgScope getCfgScope() { result = cfg.getScope() }
override Location getLocationImpl() { result = cfg.getLocation() }
override string toStringImpl() {
not exists(this.getPrev()) and
result = this.getComponent()
or
result = this.getPrev() + "." + this.getComponent()
}
override predicate nodeIsHidden() { any() }
TypePathNodeImpl getNext() { result = TTypePathNode(n + 1, cfg) }
TypePathNodeImpl getPrev() { result.getNext() = this }
TypePathNodeImpl getConstant(string s) {
s = result.getComponent() and
result = this.getNext()
}
}
/** A node that performs a type cast. */
class CastNode extends Node {
CastNode() { none() }

View File

@@ -193,6 +193,21 @@ class PostUpdateNode extends Node {
Node getPreUpdateNode() { result = pre }
}
/**
* A dataflow node that represents a component of a type or module path.
*
* For example, `System`, `System.Management`, `System.Management.Automation`,
* and `System.Management.Automation.PowerShell` in the type
* name `[System.Management.Automation.PowerShell]`.
*/
class TypePathNode extends Node instanceof TypePathNodeImpl {
string getComponent() { result = super.getComponent() }
TypePathNode getConstant(string s) { result = super.getConstant(s) }
API::Node track() { result = API::mod(super.getType(), super.getIndex()) }
}
cached
private module Cached {
cached

View File

@@ -3,4 +3,4 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["Microsoft.PowerShell.Utility", "Method[Read-Host].ReturnValue", "stdin"]
- ["Microsoft.PowerShell.Utility!", "Method[Read-Host].ReturnValue", "stdin"]

View File

@@ -3,4 +3,4 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["Microsoft.Win32.Registry", "Method[GetValue]", "windows-registry"]
- ["Microsoft.Win32.Registry!", "Method[GetValue]", "windows-registry"]

View File

@@ -3,6 +3,6 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["Microsoft.Win32.RegistryKey", "Instance.Method[GetValue].ReturnValue", "windows-registry"]
- ["Microsoft.Win32.RegistryKey", "Instance.Method[GetValueNames].ReturnValue", "windows-registry"]
- ["Microsoft.Win32.RegistryKey", "Instance.Method[GetSubKeyNames].ReturnValue", "windows-registry"]
- ["Microsoft.Win32.RegistryKey", "Method[GetValue].ReturnValue", "windows-registry"]
- ["Microsoft.Win32.RegistryKey", "Method[GetValueNames].ReturnValue", "windows-registry"]
- ["Microsoft.Win32.RegistryKey", "Method[GetSubKeyNames].ReturnValue", "windows-registry"]

View File

@@ -3,7 +3,7 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["System.Environment", "Method[ExpandEnvironmentVariables].ReturnValue", "environment"]
- ["System.Environment", "Method[GetCommandLineArgs].ReturnValue", "commandargs"]
- ["System.Environment", "Method[GetEnvironmentVariable].ReturnValue", "environment"]
- ["System.Environment", "Method[GetEnvironmentVariables].ReturnValue", "environment"]
- ["System.Environment!", "Method[ExpandEnvironmentVariables].ReturnValue", "environment"]
- ["System.Environment!", "Method[GetCommandLineArgs].ReturnValue", "commandargs"]
- ["System.Environment!", "Method[GetEnvironmentVariable].ReturnValue", "environment"]
- ["System.Environment!", "Method[GetEnvironmentVariables].ReturnValue", "environment"]

View File

@@ -3,19 +3,19 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["System.IO.File", "Method[AppendText].ReturnValue", "file-write"]
- ["System.IO.File", "Method[Create].ReturnValue", "file-write"]
- ["System.IO.File", "Method[CreateText].ReturnValue", "file-write"]
- ["System.IO.File", "Method[Open].ReturnValue", "file-write"]
- ["System.IO.File", "Method[Open].ReturnValue", "file"]
- ["System.IO.File", "Method[OpenRead].ReturnValue", "file"]
- ["System.IO.File", "Method[OpenText].ReturnValue", "file"]
- ["System.IO.File", "Method[OpenWrite].ReturnValue", "file-write"]
- ["System.IO.File", "Method[ReadAllBytes].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadAllBytesAsync].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadAllLines].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadAllLinesAsync].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadAllText].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadAllTextAsync].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadLines].ReturnValue", "file"]
- ["System.IO.File", "Method[ReadLinesAsync].ReturnValue", "file"]
- ["System.IO.File!", "Method[AppendText].ReturnValue", "file-write"]
- ["System.IO.File!", "Method[Create].ReturnValue", "file-write"]
- ["System.IO.File!", "Method[CreateText].ReturnValue", "file-write"]
- ["System.IO.File!", "Method[Open].ReturnValue", "file-write"]
- ["System.IO.File!", "Method[Open].ReturnValue", "file"]
- ["System.IO.File!", "Method[OpenRead].ReturnValue", "file"]
- ["System.IO.File!", "Method[OpenText].ReturnValue", "file"]
- ["System.IO.File!", "Method[OpenWrite].ReturnValue", "file-write"]
- ["System.IO.File!", "Method[ReadAllBytes].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadAllBytesAsync].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadAllLines].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadAllLinesAsync].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadAllText].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadAllTextAsync].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadLines].ReturnValue", "file"]
- ["System.IO.File!", "Method[ReadLinesAsync].ReturnValue", "file"]

View File

@@ -3,11 +3,11 @@ extensions:
pack: microsoft-sdl/powershell-all
extensible: sourceModel
data:
- ["System.IO.FileInfo", "Method[AppendText].ReturnValue", "file-write"]
- ["System.IO.FileInfo", "Method[Create].ReturnValue", "file-write"]
- ["System.IO.FileInfo", "Method[CreateText].ReturnValue", "file-write"]
- ["System.IO.FileInfo", "Method[Open].ReturnValue", "file-write"]
- ["System.IO.FileInfo", "Method[Open].ReturnValue", "file"]
- ["System.IO.FileInfo", "Method[OpenRead].ReturnValue", "file"]
- ["System.IO.FileInfo", "Method[OpenText].ReturnValue", "file"]
- ["System.IO.FileInfo", "Method[OpenWrite].ReturnValue", "file-write"]
- ["System.IO.FileInfo!", "Method[AppendText].ReturnValue", "file-write"]
- ["System.IO.FileInfo!", "Method[Create].ReturnValue", "file-write"]
- ["System.IO.FileInfo!", "Method[CreateText].ReturnValue", "file-write"]
- ["System.IO.FileInfo!", "Method[Open].ReturnValue", "file-write"]
- ["System.IO.FileInfo!", "Method[Open].ReturnValue", "file"]
- ["System.IO.FileInfo!", "Method[OpenRead].ReturnValue", "file"]
- ["System.IO.FileInfo!", "Method[OpenText].ReturnValue", "file"]
- ["System.IO.FileInfo!", "Method[OpenWrite].ReturnValue", "file-write"]

View File

@@ -32,11 +32,11 @@ bindingset[rawType]
predicate isTypeUsed(string rawType) { any() }
bindingset[rawType]
private predicate parseType(string rawType, string mod, string type) {
private predicate parseType(string rawType, string consts, string suffix) {
exists(string regexp |
regexp = "(.+)\\.([^\\.]+)" and
mod = rawType.regexpCapture(regexp, 1) and
type = rawType.regexpCapture(regexp, 2)
regexp = "([^!]+)(!|)" and
consts = rawType.regexpCapture(regexp, 1) and
suffix = rawType.regexpCapture(regexp, 2)
)
}
@@ -50,7 +50,31 @@ private predicate parseRelevantType(string rawType, string consts, string suffix
* language semantics modeled by `getExtraNodeFromType`.
*/
bindingset[otherType]
predicate hasImplicitTypeModel(string type, string otherType) { none() }
predicate hasImplicitTypeModel(string type, string otherType) {
// A::B! can be used to obtain A::B
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]
@@ -66,18 +90,20 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) {
}
/** Gets a Powershell-specific interpretation of the given `type`. */
API::Node getExtraNodeFromType(string qualifiedType) {
qualifiedType = "" and
result = API::root()
or
// TODO: How to distinguish between these cases? And do we need to?
exists(string mod, string type | parseRelevantType(qualifiedType, mod, type) |
result = API::mod(qualifiedType)
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
result = API::mod(qualifiedType).getInstance()
or
result = API::mod(mod).getType(type)
suffix = "" and
result = constRef.track().getInstance()
)
or
type = "" and
result = API::root()
}
/**