Skip to content

Commit

Permalink
Merge pull request #137 from microsoft/powershell-taint-through-strin…
Browse files Browse the repository at this point in the history
…g-interpolation

PS: Taint-flow through string interpolation
  • Loading branch information
MathiasVP authored Nov 8, 2024
2 parents 16aacd8 + f16b2cb commit 0fb75af
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 12 deletions.
16 changes: 16 additions & 0 deletions powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,22 @@ module ExprNodes {

final ExprCfgNode getAnOperand() { e.hasCfgChild(this.getExpr().getAnOperand(), this, result) }
}

class ExpandableStringChildMappinig extends ExprChildMapping, ExpandableStringExpr {
override predicate relevantChild(Ast n) { n = this.getAnExpr() }
}

class ExpandableStringCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "ExpandableStringCfgNode" }

override ExpandableStringChildMappinig e;

override ExpandableStringExpr getExpr() { result = e }

ExprCfgNode getExpr(int i) { e.hasCfgChild(e.getExpr(i), this, result) }

ExprCfgNode getAnExpr() { result = this.getExpr(_) }
}
}

module StmtNodes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,9 @@ class CallNode extends AstNode {

/** A call to operator `&`, viwed as a node in a data flow graph. */
class CallOperatorNode extends CallNode {
CallOperatorNode() { this.getCallNode() instanceof CfgNodes::StmtNodes::CallOperatorCfgNode }
override CfgNodes::StmtNodes::CallOperatorCfgNode call;

Node getCommand() { result.asExpr() = call.getCommand() }
}

/** A use of a type name, viewed as a node in a data flow graph. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@ private module Cached {
cached
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
(
// Flow from an operand to an operation
exists(CfgNodes::ExprNodes::OperationCfgNode op |
op = nodeTo.asExpr() and
op.getAnOperand() = nodeFrom.asExpr()
)
or
// Flow through string interpolation
exists(CfgNodes::ExprNodes::ExpandableStringCfgNode es |
nodeFrom.asExpr() = es.getAnExpr() and
nodeTo.asExpr() = es
)
or
// Although flow through collections is modeled precisely using stores/reads, we still
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
// tracking configurations where the source is a collection.
Expand Down
17 changes: 12 additions & 5 deletions powershell/ql/test/library-tests/dataflow/local/flow.expected
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
| test.ps1:1:7:1:13 | call to Source | test.ps1:1:1:1:13 | ...=... |
| test.ps1:2:1:2:9 | call to Sink | test.ps1:2:1:2:9 | pre-return value for call to Sink |
| test.ps1:2:1:2:9 | call to Sink | test.ps1:2:1:2:9 | pre-return value for call to Sink |
| test.ps1:2:1:2:9 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:2:1:2:9 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:2:1:2:9 | pre-return value for call to Sink | test.ps1:2:1:2:9 | implicit unwrapping of call to Sink |
| test.ps1:4:1:4:3 | b | test.ps1:5:4:5:6 | b |
| test.ps1:4:6:4:13 | call to GetBool | test.ps1:4:1:4:3 | b |
Expand All @@ -14,7 +14,7 @@
| test.ps1:6:11:6:17 | call to Source | test.ps1:6:5:6:17 | ...=... |
| test.ps1:8:1:8:9 | call to Sink | test.ps1:8:1:8:9 | pre-return value for call to Sink |
| test.ps1:8:1:8:9 | call to Sink | test.ps1:8:1:8:9 | pre-return value for call to Sink |
| test.ps1:8:1:8:9 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:8:1:8:9 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:8:1:8:9 | pre-return value for call to Sink | test.ps1:8:1:8:9 | implicit unwrapping of call to Sink |
| test.ps1:10:1:10:3 | c | test.ps1:11:6:11:8 | c |
| test.ps1:10:6:10:16 | [...]... | test.ps1:10:1:10:3 | c |
Expand All @@ -23,7 +23,7 @@
| test.ps1:10:14:10:16 | b | test.ps1:10:6:10:16 | [...]... |
| test.ps1:11:1:11:8 | call to Sink | test.ps1:11:1:11:8 | pre-return value for call to Sink |
| test.ps1:11:1:11:8 | call to Sink | test.ps1:11:1:11:8 | pre-return value for call to Sink |
| test.ps1:11:1:11:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:11:1:11:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:11:1:11:8 | pre-return value for call to Sink | test.ps1:11:1:11:8 | implicit unwrapping of call to Sink |
| test.ps1:11:6:11:8 | [post] c | test.ps1:13:7:13:9 | c |
| test.ps1:11:6:11:8 | c | test.ps1:13:7:13:9 | c |
Expand All @@ -35,7 +35,7 @@
| test.ps1:13:7:13:9 | c | test.ps1:13:7:13:9 | c |
| test.ps1:14:1:14:8 | call to Sink | test.ps1:14:1:14:8 | pre-return value for call to Sink |
| test.ps1:14:1:14:8 | call to Sink | test.ps1:14:1:14:8 | pre-return value for call to Sink |
| test.ps1:14:1:14:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:14:1:14:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:14:1:14:8 | pre-return value for call to Sink | test.ps1:14:1:14:8 | implicit unwrapping of call to Sink |
| test.ps1:14:6:14:8 | [post] d | test.ps1:16:6:16:8 | d |
| test.ps1:14:6:14:8 | d | test.ps1:16:6:16:8 | d |
Expand All @@ -45,5 +45,12 @@
| test.ps1:16:6:16:12 | ...+... | test.ps1:16:6:16:12 | ...+... |
| test.ps1:17:1:17:8 | call to Sink | test.ps1:17:1:17:8 | pre-return value for call to Sink |
| test.ps1:17:1:17:8 | call to Sink | test.ps1:17:1:17:8 | pre-return value for call to Sink |
| test.ps1:17:1:17:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:17:1:17:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:17:1:17:8 | pre-return value for call to Sink | test.ps1:17:1:17:8 | implicit unwrapping of call to Sink |
| test.ps1:19:1:19:3 | f | test.ps1:21:25:21:27 | f |
| test.ps1:19:6:19:12 | call to Source | test.ps1:19:1:19:3 | f |
| test.ps1:19:6:19:12 | call to Source | test.ps1:19:1:19:12 | ...=... |
| test.ps1:21:1:21:28 | call to Sink | test.ps1:21:1:21:28 | pre-return value for call to Sink |
| test.ps1:21:1:21:28 | call to Sink | test.ps1:21:1:21:28 | pre-return value for call to Sink |
| test.ps1:21:1:21:28 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:21:1:21:28 | pre-return value for call to Sink | test.ps1:21:1:21:28 | implicit unwrapping of call to Sink |
19 changes: 14 additions & 5 deletions powershell/ql/test/library-tests/dataflow/local/taint.expected
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
| test.ps1:1:7:1:13 | call to Source | test.ps1:1:1:1:13 | ...=... |
| test.ps1:2:1:2:9 | call to Sink | test.ps1:2:1:2:9 | pre-return value for call to Sink |
| test.ps1:2:1:2:9 | call to Sink | test.ps1:2:1:2:9 | pre-return value for call to Sink |
| test.ps1:2:1:2:9 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:2:1:2:9 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:2:1:2:9 | pre-return value for call to Sink | test.ps1:2:1:2:9 | implicit unwrapping of call to Sink |
| test.ps1:2:1:2:9 | pre-return value for call to Sink | test.ps1:2:1:2:9 | implicit unwrapping of call to Sink |
| test.ps1:4:1:4:3 | b | test.ps1:5:4:5:6 | b |
Expand All @@ -15,7 +15,7 @@
| test.ps1:6:11:6:17 | call to Source | test.ps1:6:5:6:17 | ...=... |
| test.ps1:8:1:8:9 | call to Sink | test.ps1:8:1:8:9 | pre-return value for call to Sink |
| test.ps1:8:1:8:9 | call to Sink | test.ps1:8:1:8:9 | pre-return value for call to Sink |
| test.ps1:8:1:8:9 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:8:1:8:9 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:8:1:8:9 | pre-return value for call to Sink | test.ps1:8:1:8:9 | implicit unwrapping of call to Sink |
| test.ps1:8:1:8:9 | pre-return value for call to Sink | test.ps1:8:1:8:9 | implicit unwrapping of call to Sink |
| test.ps1:10:1:10:3 | c | test.ps1:11:6:11:8 | c |
Expand All @@ -25,7 +25,7 @@
| test.ps1:10:14:10:16 | b | test.ps1:10:6:10:16 | [...]... |
| test.ps1:11:1:11:8 | call to Sink | test.ps1:11:1:11:8 | pre-return value for call to Sink |
| test.ps1:11:1:11:8 | call to Sink | test.ps1:11:1:11:8 | pre-return value for call to Sink |
| test.ps1:11:1:11:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:11:1:11:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:11:1:11:8 | pre-return value for call to Sink | test.ps1:11:1:11:8 | implicit unwrapping of call to Sink |
| test.ps1:11:1:11:8 | pre-return value for call to Sink | test.ps1:11:1:11:8 | implicit unwrapping of call to Sink |
| test.ps1:11:6:11:8 | [post] c | test.ps1:13:7:13:9 | c |
Expand All @@ -38,7 +38,7 @@
| test.ps1:13:7:13:9 | c | test.ps1:13:7:13:9 | c |
| test.ps1:14:1:14:8 | call to Sink | test.ps1:14:1:14:8 | pre-return value for call to Sink |
| test.ps1:14:1:14:8 | call to Sink | test.ps1:14:1:14:8 | pre-return value for call to Sink |
| test.ps1:14:1:14:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:14:1:14:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:14:1:14:8 | pre-return value for call to Sink | test.ps1:14:1:14:8 | implicit unwrapping of call to Sink |
| test.ps1:14:1:14:8 | pre-return value for call to Sink | test.ps1:14:1:14:8 | implicit unwrapping of call to Sink |
| test.ps1:14:6:14:8 | [post] d | test.ps1:16:6:16:8 | d |
Expand All @@ -51,6 +51,15 @@
| test.ps1:16:11:16:12 | 1 | test.ps1:16:6:16:12 | ...+... |
| test.ps1:17:1:17:8 | call to Sink | test.ps1:17:1:17:8 | pre-return value for call to Sink |
| test.ps1:17:1:17:8 | call to Sink | test.ps1:17:1:17:8 | pre-return value for call to Sink |
| test.ps1:17:1:17:8 | implicit unwrapping of call to Sink | test.ps1:1:1:17:8 | return value for test.ps1 |
| test.ps1:17:1:17:8 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:17:1:17:8 | pre-return value for call to Sink | test.ps1:17:1:17:8 | implicit unwrapping of call to Sink |
| test.ps1:17:1:17:8 | pre-return value for call to Sink | test.ps1:17:1:17:8 | implicit unwrapping of call to Sink |
| test.ps1:19:1:19:3 | f | test.ps1:21:25:21:27 | f |
| test.ps1:19:6:19:12 | call to Source | test.ps1:19:1:19:3 | f |
| test.ps1:19:6:19:12 | call to Source | test.ps1:19:1:19:12 | ...=... |
| test.ps1:21:1:21:28 | call to Sink | test.ps1:21:1:21:28 | pre-return value for call to Sink |
| test.ps1:21:1:21:28 | call to Sink | test.ps1:21:1:21:28 | pre-return value for call to Sink |
| test.ps1:21:1:21:28 | implicit unwrapping of call to Sink | test.ps1:1:1:21:28 | return value for test.ps1 |
| test.ps1:21:1:21:28 | pre-return value for call to Sink | test.ps1:21:1:21:28 | implicit unwrapping of call to Sink |
| test.ps1:21:1:21:28 | pre-return value for call to Sink | test.ps1:21:1:21:28 | implicit unwrapping of call to Sink |
| test.ps1:21:25:21:27 | f | test.ps1:21:6:21:28 | here is a string: $f |
6 changes: 5 additions & 1 deletion powershell/ql/test/library-tests/dataflow/local/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ $d = ($c)
Sink $d

$e = $d + 1
Sink $e
Sink $e

$f = Source

Sink "here is a string: $f"

0 comments on commit 0fb75af

Please sign in to comment.