Skip to content

Commit 78a1d06

Browse files
authored
Merge pull request #113 from microsoft/powershell-element-content
PS: Add `ElementContent` for tracking flow through arrays
2 parents 421258b + 494ef7a commit 78a1d06

File tree

13 files changed

+742
-39
lines changed

13 files changed

+742
-39
lines changed

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

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,119 @@ class ConstExpr extends @constant_expression, BaseConstExpr {
99

1010
override string toString() { result = this.getValue().toString() }
1111
}
12+
13+
private newtype TConstantValue =
14+
TConstInteger(int value) {
15+
exists(ConstExpr ce | ce.getType() = "Int32" and ce.getValue().getValue().toInt() = value)
16+
} or
17+
TConstDouble(float double) {
18+
exists(ConstExpr ce | ce.getType() = "Double" and ce.getValue().getValue().toFloat() = double)
19+
} or
20+
TConstString(string value) { exists(StringLiteral sl | sl.getValue() = value) } or
21+
TConstBoolean(boolean value) {
22+
exists(VarAccess va |
23+
value = true and
24+
va.getUserPath() = "true"
25+
or
26+
value = false and
27+
va.getUserPath() = "false"
28+
)
29+
} or
30+
TNull()
31+
32+
/** A constant value. */
33+
class ConstantValue extends TConstantValue {
34+
/** Gets a string representation of this value. */
35+
final string toString() { result = this.getValue() }
36+
37+
/** Gets the value of this consant. */
38+
string getValue() { none() }
39+
40+
/** Gets the integer value of this constant, if any. */
41+
int asInt() { none() }
42+
43+
/** Gets the floating point value of this constant, if any. */
44+
float asDouble() { none() }
45+
46+
/** Gets the string value of this constant, if any. */
47+
string asString() { none() }
48+
49+
/** Gets the boolean value of this constant, if any. */
50+
boolean asBoolean() { none() }
51+
52+
/** Holds if this constant represents the null value. */
53+
predicate isNull() { none() }
54+
55+
/** Gets a (unique) serialized version of this value. */
56+
string serialize() { none() }
57+
58+
/** Gets an exprssion that has this value. */
59+
Expr getAnExpr() { none() }
60+
}
61+
62+
/** A constant integer value */
63+
class ConstInteger extends ConstantValue, TConstInteger {
64+
final override int asInt() { this = TConstInteger(result) }
65+
66+
final override string getValue() { result = this.asInt().toString() }
67+
68+
final override string serialize() { result = this.getValue() }
69+
70+
final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() }
71+
}
72+
73+
/** A constant floating point value. */
74+
class ConstDouble extends ConstantValue, TConstDouble {
75+
final override float asDouble() { this = TConstDouble(result) }
76+
77+
final override string getValue() { result = this.asDouble().toString() }
78+
79+
final override string serialize() {
80+
exists(string res | res = this.asDouble().toString() |
81+
if exists(res.indexOf(".")) then result = res else result = res + ".0"
82+
)
83+
}
84+
85+
final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() }
86+
}
87+
88+
/** A constant string value. */
89+
class ConstString extends ConstantValue, TConstString {
90+
final override string asString() { this = TConstString(result) }
91+
92+
final override string getValue() { result = this.asString() }
93+
94+
final override string serialize() {
95+
result = "\"" + this.asString().replaceAll("\"", "\\\"") + "\""
96+
}
97+
98+
final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() }
99+
}
100+
101+
/** A constant boolean value. */
102+
class ConstBoolean extends ConstantValue, TConstBoolean {
103+
final override boolean asBoolean() { this = TConstBoolean(result) }
104+
105+
final override string getValue() { result = this.asBoolean().toString() }
106+
107+
final override string serialize() { result = this.getValue() }
108+
109+
final override VarAccess getAnExpr() {
110+
this.asBoolean() = true and
111+
result.getUserPath() = "true"
112+
or
113+
this.asBoolean() = false and
114+
result.getUserPath() = "false"
115+
}
116+
}
117+
118+
/** The constant null value. */
119+
class NullConst extends ConstantValue, TNull {
120+
final override predicate isNull() { any() }
121+
122+
final override string getValue() { result = "null" }
123+
124+
final override string serialize() { result = this.getValue() }
125+
126+
final override VarAccess getAnExpr() { result.getUserPath() = "null" }
127+
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
import powershell
22

3-
class Expr extends @expression, CmdElement { }
3+
/**
4+
* An expression.
5+
*
6+
* This is the topmost class in the hierachy of all expression in PowerShell.
7+
*/
8+
class Expr extends @expression, CmdElement {
9+
/** Gets the constant value of this expression, if this is known. */
10+
final ConstantValue getValue() { result.getAnExpr() = this }
11+
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import powershell
2+
private import internal.ExplicitWrite
23

34
class IndexExpr extends @index_expression, Expr {
45
override string toString() { result = "...[...]" }
@@ -11,3 +12,19 @@ class IndexExpr extends @index_expression, Expr {
1112

1213
predicate isNullConditional() { index_expression(this, _, _, true) }
1314
}
15+
16+
private predicate isImplicitIndexWrite(Expr e) { none() }
17+
18+
/** An index expression that is being written to. */
19+
class IndexExprWrite extends IndexExpr {
20+
IndexExprWrite() { isExplicitWrite(this, _) or isImplicitIndexWrite(this) }
21+
22+
predicate isExplicit(AssignStmt assign) { isExplicitWrite(this, assign) }
23+
24+
predicate isImplicit() { isImplicitIndexWrite(this) }
25+
}
26+
27+
/** An index expression that is being read from. */
28+
class IndexExprRead extends IndexExpr {
29+
IndexExprRead() { not this instanceof IndexExprWrite }
30+
}

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

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import powershell
2+
private import internal.ExplicitWrite
23

34
private predicate isParameterName(@variable_expression ve) { parameter(_, ve, _, _) }
45

@@ -34,24 +35,16 @@ class VarAccess extends @variable_expression, Expr {
3435
Variable getVariable() { result.getAnAccess() = this }
3536
}
3637

37-
private predicate isExplicitVariableWriteAccess(Expr e, AssignStmt assign) {
38-
e = assign.getLeftHandSide()
39-
or
40-
e = any(ConvertExpr convert | isExplicitVariableWriteAccess(convert, assign)).getExpr()
41-
or
42-
e = any(ArrayLiteral array | isExplicitVariableWriteAccess(array, assign)).getAnElement()
43-
}
44-
4538
private predicate isImplicitVariableWriteAccess(Expr e) { none() }
4639

4740
class VarReadAccess extends VarAccess {
4841
VarReadAccess() { not this instanceof VarWriteAccess }
4942
}
5043

5144
class VarWriteAccess extends VarAccess {
52-
VarWriteAccess() { isExplicitVariableWriteAccess(this, _) or isImplicitVariableWriteAccess(this) }
45+
VarWriteAccess() { isExplicitWrite(this, _) or isImplicitVariableWriteAccess(this) }
5346

54-
predicate isExplicit(AssignStmt assign) { isExplicitVariableWriteAccess(this, assign) }
47+
predicate isExplicit(AssignStmt assign) { isExplicitWrite(this, assign) }
5548

5649
predicate isImplicit() { isImplicitVariableWriteAccess(this) }
5750
}

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class ExprCfgNode extends AstCfgNode {
4646

4747
/** Gets the underlying expression. */
4848
Expr getExpr() { result = e }
49+
50+
final ConstantValue getValue() { result = e.getValue() }
4951
}
5052

5153
/** A control-flow node that wraps an AST statement. */
@@ -323,12 +325,55 @@ module ExprNodes {
323325
/** A control-flow node that wraps a `MemberExpr` expression that is being written to. */
324326
class MemberCfgWriteAccessNode extends MemberCfgNode {
325327
MemberCfgWriteAccessNode() { this.getExpr() instanceof MemberExprWriteAccess }
328+
329+
StmtNodes::AssignStmtCfgNode getAssignStmt() { result.getLeftHandSide() = this }
326330
}
327331

328332
/** A control-flow node that wraps a `MemberExpr` expression that is being read from. */
329333
class MemberCfgReadAccessNode extends MemberCfgNode {
330334
MemberCfgReadAccessNode() { this.getExpr() instanceof MemberExprReadAccess }
331335
}
336+
337+
class ArrayLiteralChildMapping extends ExprChildMapping, ArrayLiteral {
338+
override predicate relevantChild(Ast n) { n = this.getAnElement() }
339+
}
340+
341+
class ArrayLiteralCfgNode extends ExprCfgNode {
342+
override string getAPrimaryQlClass() { result = "ArrayLiteralCfgNode" }
343+
344+
override ArrayLiteralChildMapping e;
345+
346+
ExprCfgNode getElement(int i) { e.hasCfgChild(e.getElement(i), this, result) }
347+
348+
ExprCfgNode getAnElement() { e.hasCfgChild(e.getAnElement(), this, result) }
349+
}
350+
351+
class IndexChildMapping extends ExprChildMapping, IndexExpr {
352+
override predicate relevantChild(Ast n) { n = this.getBase() or n = this.getIndex() }
353+
}
354+
355+
/** A control-flow node that wraps a `MemberExpr` expression. */
356+
class IndexCfgNode extends ExprCfgNode {
357+
override string getAPrimaryQlClass() { result = "IndexCfgNode" }
358+
359+
override IndexChildMapping e;
360+
361+
final ExprCfgNode getBase() { e.hasCfgChild(e.getBase(), this, result) }
362+
363+
final ExprCfgNode getIndex() { e.hasCfgChild(e.getIndex(), this, result) }
364+
}
365+
366+
/** A control-flow node that wraps a `MemberExpr` expression that is being written to. */
367+
class IndexCfgWriteNode extends IndexCfgNode {
368+
IndexCfgWriteNode() { this.getExpr() instanceof IndexExprWrite }
369+
370+
StmtNodes::AssignStmtCfgNode getAssignStmt() { result.getLeftHandSide() = this }
371+
}
372+
373+
/** A control-flow node that wraps a `MemberExpr` expression that is being read from. */
374+
class IndexCfgReadNode extends IndexCfgNode {
375+
IndexCfgReadNode() { this.getExpr() instanceof IndexExprRead }
376+
}
332377
}
333378

334379
module StmtNodes {

0 commit comments

Comments
 (0)