Skip to content

Commit 2ffbf17

Browse files
committed
PS: Dataflow additions to support api graphs.
1 parent 9a03d10 commit 2ffbf17

File tree

2 files changed

+109
-2
lines changed

2 files changed

+109
-2
lines changed

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import DataFlowPublic
77
private import DataFlowDispatch
88
private import SsaImpl as SsaImpl
99
private import FlowSummaryImpl as FlowSummaryImpl
10+
private import semmle.code.powershell.frameworks.data.ModelsAsData
1011

1112
/** Gets the callable in which this node occurs. */
1213
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
@@ -199,6 +200,13 @@ private module Cached {
199200
TProcessNode(ProcessBlock process) or
200201
TProcessPropertyByNameNode(PipelineByPropertyNameIteratorVariable iter) {
201202
isProcessPropertyByNameNode(iter, _)
203+
} or
204+
TScriptBlockNode(ScriptBlock scriptBlock) or
205+
TForbiddenRecursionGuard() {
206+
none() and
207+
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
208+
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
209+
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
202210
}
203211

204212
cached
@@ -1115,6 +1123,22 @@ private class ProcessPropertyByNameNode extends TProcessPropertyByNameNode, Node
11151123
}
11161124
}
11171125

1126+
class ScriptBlockNode extends TScriptBlockNode, NodeImpl {
1127+
private ScriptBlock scriptBlock;
1128+
1129+
ScriptBlockNode() { this = TScriptBlockNode(scriptBlock) }
1130+
1131+
ScriptBlock getScriptBlock() { result = scriptBlock }
1132+
1133+
override CfgScope getCfgScope() { result = scriptBlock }
1134+
1135+
override Location getLocationImpl() { result = scriptBlock.getLocation() }
1136+
1137+
override string toStringImpl() { result = scriptBlock.toString() }
1138+
1139+
override predicate nodeIsHidden() { any() }
1140+
}
1141+
11181142
/** A node that performs a type cast. */
11191143
class CastNode extends Node {
11201144
CastNode() { none() }
@@ -1167,9 +1191,13 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
11671191
/** Extra data-flow steps needed for lambda flow analysis. */
11681192
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
11691193

1170-
predicate knownSourceModel(Node source, string model) { none() }
1194+
predicate knownSourceModel(Node source, string model) {
1195+
source = ModelOutput::getASourceNode(_, model).asSource()
1196+
}
11711197

1172-
predicate knownSinkModel(Node sink, string model) { none() }
1198+
predicate knownSinkModel(Node sink, string model) {
1199+
sink = ModelOutput::getASinkNode(_, model).asSink()
1200+
}
11731201

11741202
class DataFlowSecondLevelScope = Unit;
11751203

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ private import powershell
22
private import DataFlowDispatch
33
private import DataFlowPrivate
44
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
5+
private import semmle.code.powershell.ApiGraphs
56
private import semmle.code.powershell.Cfg
67

78
/**
@@ -95,11 +96,81 @@ class ParameterNode extends Node {
9596
final Parameter getParameter() { result = getParameter(this) }
9697
}
9798

99+
/**
100+
* A data flow node corresponding to a method, block, or lambda expression.
101+
*/
102+
class CallableNode extends Node instanceof ScriptBlockNode {
103+
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
104+
exists(DataFlowCallable c |
105+
c.asCfgScope() = this.asCallableAstNode() and
106+
result = getParameterPosition(node, c)
107+
)
108+
}
109+
110+
/** Gets the underlying AST node as a `Callable`. */
111+
ScriptBlock asCallableAstNode() { result = super.getScriptBlock() }
112+
113+
/** Gets the `n`th positional parameter. */
114+
ParameterNode getParameter(int n) {
115+
this.getParameterPosition(result).isPositional(n, emptyNamedSet())
116+
}
117+
118+
/** Gets the number of positional parameters of this callable. */
119+
final int getNumberOfParameters() { result = count(this.getParameter(_)) }
120+
121+
/** Gets the keyword parameter of the given name. */
122+
ParameterNode getKeywordParameter(string name) {
123+
this.getParameterPosition(result).isKeyword(name)
124+
}
125+
126+
/**
127+
* Gets a data flow node whose value is about to be returned by this callable.
128+
*/
129+
Node getAReturnNode() { result = getAReturnNode(this.asCallableAstNode()) }
130+
}
131+
98132
/**
99133
* A data-flow node that is a source of local flow.
100134
*/
101135
class LocalSourceNode extends Node {
102136
LocalSourceNode() { isLocalSourceNode(this) }
137+
138+
/** Starts tracking this node forward using API graphs. */
139+
pragma[inline]
140+
API::Node track() { result = API::Internal::getNodeForForwardTracking(this) }
141+
142+
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
143+
pragma[inline]
144+
predicate flowsTo(Node nodeTo) { flowsTo(this, nodeTo) }
145+
146+
/**
147+
* Gets a node that this node may flow to using one heap and/or interprocedural step.
148+
*
149+
* See `TypeTracker` for more details about how to use this.
150+
*/
151+
pragma[inline]
152+
LocalSourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
153+
154+
/**
155+
* Gets a node that may flow into this one using one heap and/or interprocedural step.
156+
*
157+
* See `TypeBackTracker` for more details about how to use this.
158+
*/
159+
pragma[inline]
160+
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t = t2.step(result, this) }
161+
162+
/**
163+
* Gets a node to which data may flow from this node in zero or
164+
* more local data-flow steps.
165+
*/
166+
pragma[inline]
167+
Node getALocalUse() { flowsTo(this, result) }
168+
169+
/** Gets a method call where this node flows to the receiver. */
170+
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
171+
172+
/** Gets a call to a method named `name`, where this node flows to the receiver. */
173+
CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
103174
}
104175

105176
/**
@@ -361,6 +432,8 @@ class ObjectCreationNode extends Node {
361432
}
362433

363434
final CfgNodes::ObjectCreationCfgNode getObjectCreationNode() { result = objectCreation }
435+
436+
string getConstructedTypeName() { result = this.getObjectCreationNode().getConstructedTypeName() }
364437
}
365438

366439
/** A call, viewed as a node in a data flow graph. */
@@ -370,4 +443,10 @@ class CallNode extends AstNode {
370443
CallNode() { call = this.getCfgNode() }
371444

372445
CfgNodes::CallCfgNode getCallNode() { result = call }
446+
447+
string getName() { result = call.getName() }
448+
449+
Node getQualifier() { result.asExpr() = call.getQualifier() }
450+
451+
int getNumberOfArguments() { result = call.getNumberOfArguments() }
373452
}

0 commit comments

Comments
 (0)