Skip to content

Commit eb0f094

Browse files
authored
Merge pull request #119 from microsoft/powershell-unwrapping
PS: Flow through pipelines
2 parents 8a575c4 + 1b454ed commit eb0f094

24 files changed

+825
-348
lines changed

powershell/ql/lib/powershell.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import semmle.code.powershell.Expression
77
import semmle.code.powershell.CommandBase
88
import semmle.code.powershell.AttributeBase
99
import semmle.code.powershell.PipelineBase
10+
import semmle.code.powershell.PipelineChain
1011
import semmle.code.powershell.BaseConstantExpression
1112
import semmle.code.powershell.ConstantExpression
1213
import semmle.code.powershell.MemberExpressionBase

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class ConstExpr extends @constant_expression, BaseConstExpr {
1313
private newtype TConstantValue =
1414
TConstInteger(int value) {
1515
exists(ConstExpr ce | ce.getType() = "Int32" and ce.getValue().getValue().toInt() = value)
16+
or
17+
value = [0 .. 10] // needed for `trackKnownValue` in `DataFlowPrivate`
1618
} or
1719
TConstDouble(float double) {
1820
exists(ConstExpr ce | ce.getType() = "Double" and ce.getValue().getValue().toFloat() = double)

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,18 @@ abstract private class AbstractFunction extends Ast {
4343
* Gets the i'th parameter of this function, if any.
4444
*
4545
* This does not include the `this` parameter.
46+
*
47+
* The implicit underscore parameter (if any) is included at index `-1`.
4648
*/
4749
final Parameter getParameter(int i) {
50+
result.getFunction() = this and
51+
result.getIndex() = i
52+
}
53+
54+
final Parameter getParameterExcludingPipline(int i) {
4855
result = this.getFunctionParameter(i)
4956
or
50-
result = this.getBody().getParamBlock().getParameter(i)
57+
result = this.getBody().getParamBlock().getParameterExcludingPipline(i)
5158
}
5259

5360
final Parameter getThisParameter() {
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import powershell
22

33
class NamedAttributeArgument extends @named_attribute_argument, Ast {
4-
final override string toString() { result = this.getValue().toString() }
4+
final override string toString() { result = this.getName() }
55

66
final override SourceLocation getLocation() { named_attribute_argument_location(this, result) }
77

88
string getName() { named_attribute_argument(this, result, _) }
99

1010
Expr getValue() { named_attribute_argument(this, _, result) }
1111
}
12+
13+
class ValueFromPipelineAttribute extends NamedAttributeArgument {
14+
ValueFromPipelineAttribute() { this.getName() = "ValueFromPipeline" }
15+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ class ParamBlock extends @param_block, Ast {
1515

1616
Parameter getParameter(int i) { result.hasParameterBlock(this, i) }
1717

18+
Parameter getParameterExcludingPipline(int i) { result.hasParameterBlockExcludingPipeline(this, i) }
19+
1820
Parameter getAParameter() { result = this.getParameter(_) }
1921
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,12 @@ class ScriptBlock extends @script_block, Ast {
5252

5353
final override Scope getEnclosingScope() { result = this }
5454
}
55+
56+
/** A `process` block. */
57+
class ProcessBlock extends NamedBlock {
58+
ScriptBlock scriptBlock;
59+
60+
ProcessBlock() { scriptBlock.getProcessBlock() = this }
61+
62+
ScriptBlock getScriptBlock() { result = scriptBlock }
63+
}

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

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ private predicate hasParameterBlockImpl(Internal::Parameter p, ParamBlock block,
1010
param_block_parameter(block, i, p)
1111
}
1212

13+
private predicate hasParameterBlockExcludingPipelineImpl(
14+
Internal::Parameter p, ParamBlock block, int i
15+
) {
16+
p =
17+
rank[i + 1](Internal::Parameter cand, int j |
18+
hasParameterBlockImpl(cand, block, j) and
19+
not cand.getAnAttribute().(Attribute).getANamedArgument() instanceof
20+
ValueFromPipelineAttribute
21+
|
22+
cand order by j
23+
)
24+
}
25+
1326
/**
1427
* Gets the enclosing scope of `p`.
1528
*
@@ -41,7 +54,7 @@ private predicate isParameterImpl(string name, Scope scope) {
4154
private newtype TParameterImpl =
4255
TInternalParameter(Internal::Parameter p) or
4356
TUnderscore(Scope scope) {
44-
exists(VarAccess va | va.getUserPath() = "_" and scope = va.getEnclosingScope())
57+
exists(VarAccess va | va.getUserPath() = ["_", "PSItem"] and scope = va.getEnclosingScope())
4558
} or
4659
TThisParameter(Scope scope) { exists(scope.getEnclosingFunction().getDeclaringType()) }
4760

@@ -56,15 +69,21 @@ private class ParameterImpl extends TParameterImpl {
5669

5770
predicate hasParameterBlock(ParamBlock block, int i) { none() }
5871

72+
predicate hasParameterBlockExcludingPipeline(ParamBlock block, int i) { none() }
73+
5974
predicate isFunctionParameter(Function f, int i) { none() }
6075

6176
Expr getDefaultValue() { none() }
6277

78+
abstract Attribute getAnAttribute();
79+
6380
VarAccess getAnAccess() {
6481
// TODO: This won't join order nicely.
6582
result.getUserPath() = this.getName() and
6683
result.getEnclosingScope() = this.getEnclosingScope()
6784
}
85+
86+
abstract predicate isPipeline();
6887
}
6988

7089
private class InternalParameter extends ParameterImpl, TInternalParameter {
@@ -82,11 +101,26 @@ private class InternalParameter extends ParameterImpl, TInternalParameter {
82101
hasParameterBlockImpl(p, block, i)
83102
}
84103

104+
override predicate hasParameterBlockExcludingPipeline(ParamBlock block, int i) {
105+
hasParameterBlockExcludingPipelineImpl(p, block, i)
106+
}
107+
85108
override predicate isFunctionParameter(Function f, int i) { isFunctionParameterImpl(p, f, i) }
86109

87110
override Expr getDefaultValue() { result = p.getDefaultValue() }
111+
112+
override Attribute getAnAttribute() { result = p.getAnAttribute() }
113+
114+
override predicate isPipeline() {
115+
this.getAnAttribute().getANamedArgument() instanceof ValueFromPipelineAttribute
116+
}
88117
}
89118

119+
/**
120+
* The variable that represents an element in the pipeline.
121+
*
122+
* This is either the variable `$_` or the variable `$PSItem`.
123+
*/
90124
private class Underscore extends ParameterImpl, TUnderscore {
91125
Scope scope;
92126

@@ -108,6 +142,12 @@ private class Underscore extends ParameterImpl, TUnderscore {
108142
override string getName() { result = "_" }
109143

110144
final override Scope getEnclosingScope() { result = scope }
145+
146+
final override Attribute getAnAttribute() { none() }
147+
148+
final override predicate isPipeline() { any() }
149+
150+
final override predicate isFunctionParameter(Function f, int i) { f.getBody() = scope and i = -1 }
111151
}
112152

113153
private class ThisParameter extends ParameterImpl, TThisParameter {
@@ -120,6 +160,10 @@ private class ThisParameter extends ParameterImpl, TThisParameter {
120160
override string getName() { result = "this" }
121161

122162
final override Scope getEnclosingScope() { result = scope }
163+
164+
final override Attribute getAnAttribute() { none() }
165+
166+
final override predicate isPipeline() { none() }
123167
}
124168

125169
private newtype TVariable =
@@ -128,7 +172,8 @@ private newtype TVariable =
128172
not name = "this" and // This is modeled as a parameter
129173
exists(VarAccess va | va.getUserPath() = name and scope = va.getEnclosingScope())
130174
} or
131-
TParameter(ParameterImpl p)
175+
TParameter(ParameterImpl p) or
176+
TPipelineIteratorVariable(ProcessBlock pb)
132177

133178
private class AbstractVariable extends TVariable {
134179
abstract Location getLocation();
@@ -137,6 +182,8 @@ private class AbstractVariable extends TVariable {
137182

138183
abstract string getName();
139184

185+
final predicate hasName(string s) { this.getName() = s }
186+
140187
abstract Scope getDeclaringScope();
141188

142189
VarAccess getAnAccess() {
@@ -194,6 +241,10 @@ class Parameter extends AbstractLocalScopeVariable, TParameter {
194241

195242
predicate hasParameterBlock(ParamBlock block, int i) { p.hasParameterBlock(block, i) }
196243

244+
predicate hasParameterBlockExcludingPipeline(ParamBlock block, int i) {
245+
p.hasParameterBlockExcludingPipeline(block, i)
246+
}
247+
197248
predicate isFunctionParameter(Function f, int i) { p.isFunctionParameter(f, i) }
198249

199250
Expr getDefaultValue() { result = p.getDefaultValue() }
@@ -210,11 +261,45 @@ class Parameter extends AbstractLocalScopeVariable, TParameter {
210261
*/
211262
int getIndex() { result = this.getFunctionIndex() or result = this.getBlockIndex() }
212263

264+
int getIndexExcludingPipeline() {
265+
result = this.getFunctionIndex() or result = this.getBlockIndexExcludingPipeline()
266+
}
267+
213268
/** Gets the index of this parameter in the parameter block, if any. */
214269
int getBlockIndex() { this.hasParameterBlock(_, result) }
215270

271+
int getBlockIndexExcludingPipeline() { this.hasParameterBlockExcludingPipeline(_, result) }
272+
216273
/** Gets the index of this parameter in the function, if any. */
217274
int getFunctionIndex() { this.isFunctionParameter(_, result) }
218275

219276
Function getFunction() { result.getBody() = this.getDeclaringScope() }
277+
278+
Attribute getAnAttribute() { result = p.getAnAttribute() }
279+
280+
predicate isPipeline() { p.isPipeline() }
281+
}
282+
283+
class PipelineParameter extends Parameter {
284+
PipelineParameter() { this.isPipeline() }
285+
}
286+
287+
/**
288+
* The variable that represents the value of a pipeline during a process block.
289+
*
290+
* That is, it is _not_ the pipeline variable, but the value that is obtained by reading
291+
* from the pipeline.
292+
*/
293+
class PipelineIteratorVariable extends AbstractLocalScopeVariable, TPipelineIteratorVariable {
294+
private ProcessBlock pb;
295+
296+
PipelineIteratorVariable() { this = TPipelineIteratorVariable(pb) }
297+
298+
override Location getLocation() { result = pb.getLocation() }
299+
300+
override string getName() { result = "pipeline iterator for " + pb.toString() }
301+
302+
final override Scope getDeclaringScope() { result = pb.getEnclosingScope() }
303+
304+
ProcessBlock getProcessBlock() { result = pb }
220305
}

0 commit comments

Comments
 (0)