Skip to content

Commit 2f835e5

Browse files
authored
Merge pull request #128 from microsoft/powershell-taint-through-operations
PS: Taint through operations
2 parents 0890b22 + d70d409 commit 2f835e5

File tree

10 files changed

+189
-15
lines changed

10 files changed

+189
-15
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ class BinaryExpr extends @binary_expression, Expr {
55

66
int getKind() { binary_expression(this, result, _, _) }
77

8+
/** Gets an operand of this binary expression. */
9+
Expr getAnOperand() {
10+
result = this.getLeft()
11+
or
12+
result = this.getRight()
13+
}
14+
15+
/** Holds if this binary expression has the operands `e1` and `e2`. */
16+
predicate hasOperands(Expr e1, Expr e2) {
17+
e1 = this.getLeft() and
18+
e2 = this.getRight()
19+
or
20+
e1 = this.getRight() and
21+
e2 = this.getLeft()
22+
}
23+
824
Expr getLeft() { binary_expression(this, _, result, _) }
925

1026
Expr getRight() { binary_expression(this, _, _, result) }

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,50 @@ module ExprNodes {
519519

520520
final StmtCfgNode getBase() { e.hasCfgChild(e.getBase(), this, result) }
521521
}
522+
523+
class UnaryExprChildMapping extends ExprChildMapping, UnaryExpr {
524+
override predicate relevantChild(Ast n) { n = this.getOperand() }
525+
}
526+
527+
class UnaryCfgNode extends ExprCfgNode {
528+
override string getAPrimaryQlClass() { result = "UnaryExprCfgNode" }
529+
530+
override UnaryExprChildMapping e;
531+
532+
override UnaryExpr getExpr() { result = e }
533+
534+
final ExprCfgNode getOperand() { e.hasCfgChild(e.getOperand(), this, result) }
535+
}
536+
537+
class BinaryExprChildMapping extends ExprChildMapping, BinaryExpr {
538+
override predicate relevantChild(Ast n) { n = this.getLeft() or n = this.getRight() }
539+
}
540+
541+
class BinaryCfgNode extends ExprCfgNode {
542+
override string getAPrimaryQlClass() { result = "BinaryExprCfgNode" }
543+
544+
override BinaryExprChildMapping e;
545+
546+
override BinaryExpr getExpr() { result = e }
547+
548+
final ExprCfgNode getLeft() { e.hasCfgChild(e.getLeft(), this, result) }
549+
550+
final ExprCfgNode getRight() { e.hasCfgChild(e.getRight(), this, result) }
551+
}
552+
553+
class OperationChildMapping extends ExprChildMapping instanceof Operation {
554+
override predicate relevantChild(Ast n) { n = super.getAnOperand() }
555+
}
556+
557+
class OperationCfgNode extends ExprCfgNode {
558+
override string getAPrimaryQlClass() { result = "OperationCfgNode" }
559+
560+
override OperationChildMapping e;
561+
562+
override Operation getExpr() { result = e }
563+
564+
final ExprCfgNode getAnOperand() { e.hasCfgChild(this.getExpr().getAnOperand(), this, result) }
565+
}
522566
}
523567

524568
module StmtNodes {

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,22 @@ private module Cached {
3333
*/
3434
cached
3535
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
36-
// Although flow through collections is modeled precisely using stores/reads, we still
37-
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
38-
// tracking configurations where the source is a collection.
39-
exists(DataFlow::ContentSet c | readStep(nodeFrom, c, nodeTo) |
40-
c.isSingleton(any(DataFlow::Content::ElementContent ec))
36+
(
37+
exists(CfgNodes::ExprNodes::OperationCfgNode op |
38+
op = nodeTo.asExpr() and
39+
op.getAnOperand() = nodeFrom.asExpr()
40+
)
4141
or
42-
c.isKnownOrUnknownElement(_)
43-
// or
44-
// TODO: We do't generate this one from readSteps yet, but we will as
45-
// soon as we start on models-as-data.
46-
// c.isAnyElement()
42+
// Although flow through collections is modeled precisely using stores/reads, we still
43+
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
44+
// tracking configurations where the source is a collection.
45+
exists(DataFlow::ContentSet c | readStep(nodeFrom, c, nodeTo) |
46+
c.isSingleton(any(DataFlow::Content::ElementContent ec))
47+
or
48+
c.isKnownOrUnknownElement(_)
49+
or
50+
c.isAnyElement()
51+
)
4752
) and
4853
model = ""
4954
}

powershell/ql/lib/semmle/code/powershell/internal/Internal.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ module Private {
22
import Parameter::Private
33
import ExplicitWrite::Private
44
import Argument::Private
5+
import Operation::Private
56
}
67

78
module Public {
89
import Parameter::Public
910
import ExplicitWrite::Public
1011
import Argument::Public
12+
import Operation::Public
1113
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import powershell
2+
3+
module Private {
4+
abstract private class AbstractOperation extends Expr {
5+
abstract Expr getAnOperand();
6+
7+
abstract int getKind();
8+
}
9+
10+
class BinaryOperation extends BinaryExpr, AbstractOperation {
11+
final override Expr getAnOperand() { result = BinaryExpr.super.getAnOperand() }
12+
13+
final override int getKind() { result = BinaryExpr.super.getKind() }
14+
}
15+
16+
class UnaryOperation extends UnaryExpr, AbstractOperation {
17+
final override Expr getAnOperand() { result = UnaryExpr.super.getOperand() }
18+
19+
final override int getKind() { result = UnaryExpr.super.getKind() }
20+
}
21+
22+
final class Operation = AbstractOperation;
23+
}
24+
25+
module Public {
26+
class Operation = Private::Operation;
27+
28+
class BinaryOperation = Private::BinaryOperation;
29+
30+
class UnaryOperation = Private::UnaryOperation;
31+
}

powershell/ql/test/library-tests/dataflow/local/test.expected renamed to powershell/ql/test/library-tests/dataflow/local/flow.expected

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
| test.ps1:1:7:1:13 | Source | test.ps1:1:1:1:13 | ...=... |
44
| test.ps1:2:1:2:9 | Sink | test.ps1:2:1:2:9 | pre-return value for Sink |
55
| test.ps1:2:1:2:9 | Sink | test.ps1:2:1:2:9 | pre-return value for Sink |
6-
| test.ps1:2:1:2:9 | implicit unwrapping of Sink | test.ps1:1:1:14:8 | return value for test.ps1 |
6+
| test.ps1:2:1:2:9 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
77
| test.ps1:2:1:2:9 | pre-return value for Sink | test.ps1:2:1:2:9 | implicit unwrapping of Sink |
88
| test.ps1:4:1:4:3 | b | test.ps1:5:4:5:6 | b |
99
| test.ps1:4:6:4:13 | GetBool | test.ps1:4:1:4:3 | b |
@@ -14,7 +14,7 @@
1414
| test.ps1:6:11:6:17 | Source | test.ps1:6:5:6:17 | ...=... |
1515
| test.ps1:8:1:8:9 | Sink | test.ps1:8:1:8:9 | pre-return value for Sink |
1616
| test.ps1:8:1:8:9 | Sink | test.ps1:8:1:8:9 | pre-return value for Sink |
17-
| test.ps1:8:1:8:9 | implicit unwrapping of Sink | test.ps1:1:1:14:8 | return value for test.ps1 |
17+
| test.ps1:8:1:8:9 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
1818
| test.ps1:8:1:8:9 | pre-return value for Sink | test.ps1:8:1:8:9 | implicit unwrapping of Sink |
1919
| test.ps1:10:1:10:3 | c | test.ps1:11:6:11:8 | c |
2020
| test.ps1:10:6:10:16 | [...]... | test.ps1:10:1:10:3 | c |
@@ -23,7 +23,7 @@
2323
| test.ps1:10:14:10:16 | b | test.ps1:10:6:10:16 | [...]... |
2424
| test.ps1:11:1:11:8 | Sink | test.ps1:11:1:11:8 | pre-return value for Sink |
2525
| test.ps1:11:1:11:8 | Sink | test.ps1:11:1:11:8 | pre-return value for Sink |
26-
| test.ps1:11:1:11:8 | implicit unwrapping of Sink | test.ps1:1:1:14:8 | return value for test.ps1 |
26+
| test.ps1:11:1:11:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
2727
| test.ps1:11:1:11:8 | pre-return value for Sink | test.ps1:11:1:11:8 | implicit unwrapping of Sink |
2828
| test.ps1:11:6:11:8 | [post] c | test.ps1:13:7:13:9 | c |
2929
| test.ps1:11:6:11:8 | c | test.ps1:13:7:13:9 | c |
@@ -35,5 +35,15 @@
3535
| test.ps1:13:7:13:9 | c | test.ps1:13:7:13:9 | c |
3636
| test.ps1:14:1:14:8 | Sink | test.ps1:14:1:14:8 | pre-return value for Sink |
3737
| test.ps1:14:1:14:8 | Sink | test.ps1:14:1:14:8 | pre-return value for Sink |
38-
| test.ps1:14:1:14:8 | implicit unwrapping of Sink | test.ps1:1:1:14:8 | return value for test.ps1 |
38+
| test.ps1:14:1:14:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
3939
| test.ps1:14:1:14:8 | pre-return value for Sink | test.ps1:14:1:14:8 | implicit unwrapping of Sink |
40+
| test.ps1:14:6:14:8 | [post] d | test.ps1:16:6:16:8 | d |
41+
| test.ps1:14:6:14:8 | d | test.ps1:16:6:16:8 | d |
42+
| test.ps1:16:1:16:3 | e | test.ps1:17:6:17:8 | e |
43+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:1:16:3 | e |
44+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:1:16:12 | ...=... |
45+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:6:16:12 | ...+... |
46+
| test.ps1:17:1:17:8 | Sink | test.ps1:17:1:17:8 | pre-return value for Sink |
47+
| test.ps1:17:1:17:8 | Sink | test.ps1:17:1:17:8 | pre-return value for Sink |
48+
| test.ps1:17:1:17:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
49+
| test.ps1:17:1:17:8 | pre-return value for Sink | test.ps1:17:1:17:8 | implicit unwrapping of Sink |
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
| test.ps1:1:1:1:4 | a1 | test.ps1:2:6:2:9 | a1 |
2+
| test.ps1:1:7:1:13 | Source | test.ps1:1:1:1:4 | a1 |
3+
| test.ps1:1:7:1:13 | Source | test.ps1:1:1:1:13 | ...=... |
4+
| test.ps1:2:1:2:9 | Sink | test.ps1:2:1:2:9 | pre-return value for Sink |
5+
| test.ps1:2:1:2:9 | Sink | test.ps1:2:1:2:9 | pre-return value for Sink |
6+
| test.ps1:2:1:2:9 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
7+
| test.ps1:2:1:2:9 | pre-return value for Sink | test.ps1:2:1:2:9 | implicit unwrapping of Sink |
8+
| test.ps1:2:1:2:9 | pre-return value for Sink | test.ps1:2:1:2:9 | implicit unwrapping of Sink |
9+
| test.ps1:4:1:4:3 | b | test.ps1:5:4:5:6 | b |
10+
| test.ps1:4:6:4:13 | GetBool | test.ps1:4:1:4:3 | b |
11+
| test.ps1:4:6:4:13 | GetBool | test.ps1:4:1:4:13 | ...=... |
12+
| test.ps1:5:4:5:6 | b | test.ps1:10:14:10:16 | b |
13+
| test.ps1:6:5:6:8 | a2 | test.ps1:8:6:8:9 | a2 |
14+
| test.ps1:6:11:6:17 | Source | test.ps1:6:5:6:8 | a2 |
15+
| test.ps1:6:11:6:17 | Source | test.ps1:6:5:6:17 | ...=... |
16+
| test.ps1:8:1:8:9 | Sink | test.ps1:8:1:8:9 | pre-return value for Sink |
17+
| test.ps1:8:1:8:9 | Sink | test.ps1:8:1:8:9 | pre-return value for Sink |
18+
| test.ps1:8:1:8:9 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
19+
| test.ps1:8:1:8:9 | pre-return value for Sink | test.ps1:8:1:8:9 | implicit unwrapping of Sink |
20+
| test.ps1:8:1:8:9 | pre-return value for Sink | test.ps1:8:1:8:9 | implicit unwrapping of Sink |
21+
| test.ps1:10:1:10:3 | c | test.ps1:11:6:11:8 | c |
22+
| test.ps1:10:6:10:16 | [...]... | test.ps1:10:1:10:3 | c |
23+
| test.ps1:10:6:10:16 | [...]... | test.ps1:10:1:10:16 | ...=... |
24+
| test.ps1:10:6:10:16 | [...]... | test.ps1:10:6:10:16 | [...]... |
25+
| test.ps1:10:14:10:16 | b | test.ps1:10:6:10:16 | [...]... |
26+
| test.ps1:11:1:11:8 | Sink | test.ps1:11:1:11:8 | pre-return value for Sink |
27+
| test.ps1:11:1:11:8 | Sink | test.ps1:11:1:11:8 | pre-return value for Sink |
28+
| test.ps1:11:1:11:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
29+
| test.ps1:11:1:11:8 | pre-return value for Sink | test.ps1:11:1:11:8 | implicit unwrapping of Sink |
30+
| test.ps1:11:1:11:8 | pre-return value for Sink | test.ps1:11:1:11:8 | implicit unwrapping of Sink |
31+
| test.ps1:11:6:11:8 | [post] c | test.ps1:13:7:13:9 | c |
32+
| test.ps1:11:6:11:8 | c | test.ps1:13:7:13:9 | c |
33+
| test.ps1:13:1:13:3 | d | test.ps1:14:6:14:8 | d |
34+
| test.ps1:13:6:13:10 | (...) | test.ps1:13:1:13:3 | d |
35+
| test.ps1:13:6:13:10 | (...) | test.ps1:13:1:13:10 | ...=... |
36+
| test.ps1:13:6:13:10 | (...) | test.ps1:13:6:13:10 | (...) |
37+
| test.ps1:13:7:13:9 | c | test.ps1:13:6:13:10 | (...) |
38+
| test.ps1:13:7:13:9 | c | test.ps1:13:7:13:9 | c |
39+
| test.ps1:14:1:14:8 | Sink | test.ps1:14:1:14:8 | pre-return value for Sink |
40+
| test.ps1:14:1:14:8 | Sink | test.ps1:14:1:14:8 | pre-return value for Sink |
41+
| test.ps1:14:1:14:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
42+
| test.ps1:14:1:14:8 | pre-return value for Sink | test.ps1:14:1:14:8 | implicit unwrapping of Sink |
43+
| test.ps1:14:1:14:8 | pre-return value for Sink | test.ps1:14:1:14:8 | implicit unwrapping of Sink |
44+
| test.ps1:14:6:14:8 | [post] d | test.ps1:16:6:16:8 | d |
45+
| test.ps1:14:6:14:8 | d | test.ps1:16:6:16:8 | d |
46+
| test.ps1:16:1:16:3 | e | test.ps1:17:6:17:8 | e |
47+
| test.ps1:16:6:16:8 | d | test.ps1:16:6:16:12 | ...+... |
48+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:1:16:3 | e |
49+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:1:16:12 | ...=... |
50+
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:6:16:12 | ...+... |
51+
| test.ps1:16:11:16:12 | 1 | test.ps1:16:6:16:12 | ...+... |
52+
| test.ps1:17:1:17:8 | Sink | test.ps1:17:1:17:8 | pre-return value for Sink |
53+
| test.ps1:17:1:17:8 | Sink | test.ps1:17:1:17:8 | pre-return value for Sink |
54+
| test.ps1:17:1:17:8 | implicit unwrapping of Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
55+
| test.ps1:17:1:17:8 | pre-return value for Sink | test.ps1:17:1:17:8 | implicit unwrapping of Sink |
56+
| test.ps1:17:1:17:8 | pre-return value for Sink | test.ps1:17:1:17:8 | implicit unwrapping of Sink |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import powershell
2+
import semmle.code.powershell.dataflow.TaintTracking
3+
import semmle.code.powershell.dataflow.DataFlow
4+
5+
from DataFlow::Node pred, DataFlow::Node succ
6+
where TaintTracking::localTaintStep(pred, succ)
7+
select pred, succ

powershell/ql/test/library-tests/dataflow/local/test.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ $c = [string]$b
1111
Sink $c
1212

1313
$d = ($c)
14-
Sink $d
14+
Sink $d
15+
16+
$e = $d + 1
17+
Sink $e

0 commit comments

Comments
 (0)