Skip to content

Commit 7551cce

Browse files
committed
PS: Make API graphs compile again. There is still some TODOs here, but at least it compiles.
1 parent 8092345 commit 7551cce

File tree

6 files changed

+85
-150
lines changed

6 files changed

+85
-150
lines changed

powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll

Lines changed: 66 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ module API {
271271
this = Impl::MkMethodAccessNode(result) or
272272
this = Impl::MkBackwardNode(result, _) or
273273
this = Impl::MkForwardNode(result, _) or
274-
this = Impl::MkSinkNode(result)
274+
this = Impl::MkSinkNode(result) or
275+
this = Impl::MkNamespaceOfTypeNameNode(result)
275276
}
276277

277278
/** Gets the location of this node. */
@@ -324,45 +325,6 @@ module API {
324325
}
325326
}
326327

327-
/** A node representing a module/class object with epsilon edges to its descendents. */
328-
private class ModuleNode extends Node, Impl::MkModule {
329-
string qualifiedModule;
330-
int n;
331-
332-
ModuleNode() { this = Impl::MkModule(qualifiedModule, n) }
333-
334-
ModuleNode getNext() { result = Impl::MkModule(qualifiedModule, n + 1) }
335-
336-
ModuleNode getPred() { result.getNext() = this }
337-
338-
string getComponent() { result = qualifiedModule.splitAt(".", n) }
339-
340-
string getModule() {
341-
not exists(this.getPred()) and
342-
result = this.getComponent()
343-
or
344-
result = this.getPred().getModule() + "." + this.getComponent()
345-
}
346-
347-
override string toString() { result = "Module(" + this.getModule() + ")" }
348-
}
349-
350-
/** A node representing instances of a module/class with epsilon edges to its ancestors. */
351-
private class InstanceUp extends Node, Impl::MkInstanceUp {
352-
/** Gets the module whose instances are represented by this API node. */
353-
string getType() { this = Impl::MkInstanceUp(result) }
354-
355-
override string toString() { result = "ModuleInstanceUp(" + this.getType() + ")" }
356-
}
357-
358-
/** A node representing instances of a module/class with epsilon edges to its descendents. */
359-
private class InstanceDownNode extends Node, Impl::MkInstanceDown {
360-
/** Gets the module whose instances are represented by this API node. */
361-
string getType() { this = Impl::MkInstanceDown(result) }
362-
363-
override string toString() { result = "ModuleInstanceDown(" + this.getType() + ")" }
364-
}
365-
366328
/** A node corresponding to the method being invoked at a method call. */
367329
class MethodAccessNode extends Node, Impl::MkMethodAccessNode {
368330
override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" }
@@ -378,6 +340,22 @@ module API {
378340
override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" }
379341
}
380342

343+
private class UsingNode extends Node, Impl::MkUsingNode {
344+
UsingStmt using; // TODO: This should really be the cfg node, I think
345+
346+
UsingNode() { this = Impl::MkUsingNode(using) }
347+
348+
override string toString() { result = "UsingNode(" + using + ")" }
349+
}
350+
351+
private class NamespaceOfTypeNameNode extends Node, Impl::MkNamespaceOfTypeNameNode {
352+
DataFlow::QualifiedTypeNameNode typeName;
353+
354+
NamespaceOfTypeNameNode() { this = Impl::MkNamespaceOfTypeNameNode(typeName) }
355+
356+
override string toString() { result = "NamespaceOfTypeNameNode(" + typeName + ")" }
357+
}
358+
381359
/**
382360
* An API entry point.
383361
*
@@ -415,11 +393,15 @@ module API {
415393
/** Gets the root node. */
416394
Node root() { result instanceof RootNode }
417395

418-
/**
419-
* Gets the node that represents the module with qualified
420-
* name `qualifiedModule`.
421-
*/
422-
ModuleNode mod(string qualifiedModule, int n) { result = Impl::MkModule(qualifiedModule, n) }
396+
bindingset[name]
397+
pragma[inline_late]
398+
Node namespace(string name) {
399+
// This predicate is currently not 'inline_late' because 'n' can be an input or output
400+
Impl::namespace(name, result)
401+
}
402+
403+
pragma[inline]
404+
Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) }
423405

424406
/**
425407
* Gets an unqualified call at the top-level with the given method name.
@@ -466,44 +448,14 @@ module API {
466448

467449
cached
468450
private module Impl {
469-
private predicate isGacModule(string s) {
470-
s =
471-
[
472-
"System.Management.Automation",
473-
"Microsoft.Management.Infrastructure",
474-
"Microsoft.PowerShell.Security",
475-
"Microsoft.PowerShell.Commands.Management",
476-
"Microsoft.PowerShell.Commands.Utility"
477-
]
478-
}
479-
480-
private predicate isModule(string s, int n) {
481-
(
482-
any(UsingStmt using).getName() = s
483-
or
484-
any(Cmd cmd).getNamespaceQualifier() = s
485-
or
486-
any(TypeNameExpr tn).getName() = s
487-
or
488-
any(ModuleManifest manifest).getModuleName() = s
489-
or
490-
isGacModule(s)
491-
) and
492-
exists(s.splitAt(".", n))
493-
}
494-
495451
cached
496452
newtype TApiNode =
497453
/** The root of the API graph. */
498454
MkRoot() or
499455
/** The method accessed at `call`, synthetically treated as a separate object. */
500456
MkMethodAccessNode(DataFlow::CallNode call) or
501-
MkModule(string qualifiedModule, int n) { isModule(qualifiedModule, n) } or
502-
/** Instances of `mod` with epsilon edges to its ancestors. */
503-
MkInstanceUp(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
504-
/** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */
505-
MkInstanceDown(string qualifiedType) { exists(MkModule(qualifiedType, _)) } or
506-
/** Intermediate node for following forward data flow. */
457+
MkUsingNode(UsingStmt using) or
458+
MkNamespaceOfTypeNameNode(DataFlow::QualifiedTypeNameNode typeName) or
507459
MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
508460
/** Intermediate node for following backward data flow. */
509461
MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or
@@ -523,10 +475,30 @@ module API {
523475
pragma[inline_late]
524476
private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e }
525477

478+
private import frameworks.data.ModelsAsData
479+
480+
cached
481+
predicate namespace(string name, Node node) {
482+
exists(DataFlow::QualifiedTypeNameNode typeName |
483+
typeName.getNamespace() = name and
484+
node = MkNamespaceOfTypeNameNode(typeName)
485+
)
486+
or
487+
exists(UsingStmt using |
488+
using.getName().toLowerCase() = name and
489+
node = MkUsingNode(using)
490+
)
491+
or
492+
node = ModelOutput::getATypeNode(name)
493+
}
494+
495+
cached
496+
predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) }
497+
526498
cached
527499
predicate toplevelCall(string name, Node node) {
528500
exists(DataFlow::CallNode call |
529-
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevel and
501+
call.asExpr().getExpr().getEnclosingScope() instanceof TopLevelScriptBlock and
530502
call.getName() = name and
531503
node = MkMethodAccessNode(call)
532504
)
@@ -544,26 +516,25 @@ module API {
544516

545517
cached
546518
predicate memberEdge(Node pred, string name, Node succ) {
547-
exists(MemberExpr member | succ = getForwardStartNode(getNodeFromExpr(member)) |
548-
pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(member.getQualifier()))) and
549-
name = member.getMemberName()
519+
exists(StringConstExpr read |
520+
succ = getForwardStartNode(getNodeFromExpr(read)) and
521+
pred = MkRoot() and
522+
name = read.getValueString()
523+
)
524+
or
525+
exists(DataFlow::QualifiedTypeNameNode typeName |
526+
typeName.getName() = name and
527+
pred = MkNamespaceOfTypeNameNode(typeName) and
528+
succ = getForwardStartNode(typeName)
550529
)
530+
// or
531+
// TODO: Handle getAMember when the predecessor is a MkUsingNode?
551532
}
552533

553534
cached
554535
predicate methodEdge(Node pred, string name, Node succ) {
555536
exists(DataFlow::CallNode call | succ = MkMethodAccessNode(call) and name = call.getName() |
556537
pred = getForwardEndNode(getALocalSourceStrict(call.getQualifier()))
557-
or
558-
exists(string qualifiedModule, ModuleManifest manifest, int n |
559-
pred = mod(qualifiedModule, n) and
560-
not exists(mod(qualifiedModule, n + 1)) and
561-
manifest.getModuleName() = qualifiedModule
562-
|
563-
manifest.getACmdLetToExport() = name
564-
or
565-
manifest.getAFunctionToExport() = name
566-
)
567538
)
568539
}
569540

@@ -657,24 +628,10 @@ module API {
657628

658629
cached
659630
predicate instanceEdge(Node pred, Node succ) {
660-
exists(string qualifiedType, int n |
661-
pred = MkModule(qualifiedType, n) and
662-
not exists(MkModule(qualifiedType, n + 1))
663-
|
664-
exists(DataFlow::TypeNameNode typeName |
665-
typeName.getTypeName() = qualifiedType and
666-
succ = getForwardStartNode(typeName)
667-
)
668-
or
669-
exists(DataFlow::ObjectCreationNode objCreation |
670-
objCreation.getConstructedTypeName() = qualifiedType and
671-
succ = getForwardStartNode(objCreation)
672-
)
673-
or
674-
exists(DataFlow::ParameterNode p |
675-
p.getParameter().getStaticType() = qualifiedType and
676-
succ = getForwardStartNode(p)
677-
)
631+
// TODO: Also model parameters with a given type here
632+
exists(DataFlow::ObjectCreationNode objCreation |
633+
pred = getForwardEndNode(objCreation.getConstructedTypeNode()) and
634+
succ = getForwardStartNode(objCreation)
678635
)
679636
}
680637

powershell/ql/lib/semmle/code/powershell/ast/internal/Command.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class SplitPath extends CmdCall {
111111
not this.hasNamedArgument("path") and
112112
result = this.getPositionalArgument(0)
113113
or
114-
// TODO: This should not be allowed, but I've seen code doing it
114+
// TODO: This should not be allowed, but I've seen code doing it and somehow it works
115115
result = this.getNamedArgument("parent")
116116
}
117117

powershell/ql/lib/semmle/code/powershell/ast/internal/ObjectCreation.qll

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import powershell
33
abstract private class AbstractObjectCreation extends CallExpr {
44
/** The name of the type of the object being constructed. */
55
abstract string getConstructedTypeName();
6+
7+
abstract Expr getConstructedTypeExpr();
68
}
79

810
/**
@@ -11,10 +13,12 @@ abstract private class AbstractObjectCreation extends CallExpr {
1113
* [System.IO.FileInfo]::new("C:\\file.txt")
1214
* ```
1315
*/
14-
class NewObjectCreation extends AbstractObjectCreation instanceof ConstructorCall {
16+
class NewObjectCreation extends AbstractObjectCreation, ConstructorCall {
1517
final override string getConstructedTypeName() {
1618
result = ConstructorCall.super.getConstructedTypeName()
1719
}
20+
21+
final override Expr getConstructedTypeExpr() { result = typename }
1822
}
1923

2024
/**
@@ -23,19 +27,21 @@ class NewObjectCreation extends AbstractObjectCreation instanceof ConstructorCal
2327
* New-Object -TypeName System.IO.FileInfo -ArgumentList "C:\\file.txt"
2428
* ```
2529
*/
26-
class DotNetObjectCreation extends AbstractObjectCreation instanceof CmdCall {
30+
class DotNetObjectCreation extends AbstractObjectCreation, CmdCall {
2731
DotNetObjectCreation() { this.getName() = "New-Object" }
2832

2933
final override string getConstructedTypeName() {
34+
result = this.getConstructedTypeExpr().(StringConstExpr).getValueString()
35+
}
36+
37+
final override Expr getConstructedTypeExpr() {
3038
// Either it's the named argument `TypeName`
31-
result = CmdCall.super.getNamedArgument("TypeName").(StringConstExpr).getValueString()
39+
result = CmdCall.super.getNamedArgument("TypeName")
3240
or
3341
// Or it's the first positional argument if that's the named argument
3442
not CmdCall.super.hasNamedArgument("TypeName") and
35-
exists(StringConstExpr arg | arg = CmdCall.super.getPositionalArgument(0) |
36-
result = arg.getValueString() and
37-
not arg = CmdCall.super.getNamedArgument(["ArgumentList", "Property"])
38-
)
43+
result = CmdCall.super.getPositionalArgument(0) and
44+
result = CmdCall.super.getNamedArgument(["ArgumentList", "Property"])
3945
}
4046
}
4147

powershell/ql/lib/semmle/code/powershell/frameworks/SystemManagementAutomationEngineIntrinsics/EngineIntrinsics.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module EngineIntrinsics {
66
private class EngineIntrinsicsGlobalEntry extends ModelInput::TypeModel {
77
override DataFlow::Node getASource(string type) {
88
type = "System.Management.Automation.EngineIntrinsics" and
9-
result.asExpr().getExpr().(VarReadAccess).getUserPath().toLowerCase() = "executioncontext"
9+
result.asExpr().getExpr().(VarReadAccess).getVariable().getName().toLowerCase() = "executioncontext"
1010
}
1111
}
1212
}

powershell/ql/lib/semmle/code/powershell/frameworks/data/ModelsAsData.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable {
3030
this = type + ";" + path
3131
}
3232

33-
override Call getACall() {
33+
override CallExpr getACall() {
3434
exists(API::MethodAccessNode base |
3535
ModelOutput::resolvedSummaryBase(type, path, base) and
3636
result = base.asCall().asExpr().getExpr()

powershell/ql/lib/semmle/code/powershell/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,6 @@ predicate hasImplicitTypeModel(string type, string otherType) {
5555
parseType(otherType, type, _)
5656
}
5757

58-
pragma[nomagic]
59-
string getConstComponent(string consts, int n) {
60-
parseRelevantType(_, consts, _) and
61-
result = consts.splitAt(".", n)
62-
}
63-
64-
private int getNumConstComponents(string consts) {
65-
result = strictcount(int n | exists(getConstComponent(consts, n)))
66-
}
67-
68-
private DataFlow::TypePathNode getConstantFromConstPath(string consts, int n) {
69-
n = 1 and
70-
result.getComponent() = getConstComponent(consts, 0)
71-
or
72-
result = getConstantFromConstPath(consts, n - 1).getConstant(getConstComponent(consts, n - 1))
73-
}
74-
75-
private DataFlow::TypePathNode getConstantFromConstPath(string consts) {
76-
result = getConstantFromConstPath(consts, getNumConstComponents(consts))
77-
}
78-
7958
/** Gets a Powershell-specific interpretation of the `(type, path)` tuple after resolving the first `n` access path tokens. */
8059
bindingset[type, path]
8160
API::Node getExtraNodeFromPath(string type, AccessPath path, int n) {
@@ -91,15 +70,8 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) {
9170

9271
/** Gets a Powershell-specific interpretation of the given `type`. */
9372
API::Node getExtraNodeFromType(string type) {
94-
exists(string consts, string suffix, DataFlow::TypePathNode constRef |
95-
parseRelevantType(type, consts, suffix) and
96-
constRef = getConstantFromConstPath(consts)
97-
|
98-
suffix = "!" and
99-
result = constRef.track()
100-
or
101-
suffix = "" and
102-
result = constRef.track().getInstance()
73+
exists(string consts, string suffix | parseRelevantType(type, consts, suffix) |
74+
none() // TODO
10375
)
10476
or
10577
type = "" and
@@ -187,7 +159,7 @@ predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToke
187159
/** An API graph node representing a method call. */
188160
class InvokeNode extends API::MethodAccessNode {
189161
/** Gets the number of arguments to the call. */
190-
int getNumArgument() { result = this.asCall().getNumberOfArguments() }
162+
int getNumArgument() { result = count(this.asCall().getAnArgument()) }
191163
}
192164

193165
/** Gets the `InvokeNode` corresponding to a specific invocation of `node`. */

0 commit comments

Comments
 (0)