diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll index 27e43029dee..cd48e0a36ba 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll @@ -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 { diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll index 63e3eaab755..45d3c5ddc39 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll @@ -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. */ diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll index 094379b04cd..8cc0f0a8722 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll @@ -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. diff --git a/powershell/ql/test/library-tests/dataflow/local/flow.expected b/powershell/ql/test/library-tests/dataflow/local/flow.expected index eb7b13d0d01..96670d797bb 100644 --- a/powershell/ql/test/library-tests/dataflow/local/flow.expected +++ b/powershell/ql/test/library-tests/dataflow/local/flow.expected @@ -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 | @@ -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 | @@ -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 | @@ -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 | @@ -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 | diff --git a/powershell/ql/test/library-tests/dataflow/local/taint.expected b/powershell/ql/test/library-tests/dataflow/local/taint.expected index 03e6220eb03..923c723a31f 100644 --- a/powershell/ql/test/library-tests/dataflow/local/taint.expected +++ b/powershell/ql/test/library-tests/dataflow/local/taint.expected @@ -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 | @@ -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 | @@ -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 | @@ -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 | @@ -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 | diff --git a/powershell/ql/test/library-tests/dataflow/local/test.ps1 b/powershell/ql/test/library-tests/dataflow/local/test.ps1 index 4497ad23dd7..4e18ffb1f11 100644 --- a/powershell/ql/test/library-tests/dataflow/local/test.ps1 +++ b/powershell/ql/test/library-tests/dataflow/local/test.ps1 @@ -14,4 +14,8 @@ $d = ($c) Sink $d $e = $d + 1 -Sink $e \ No newline at end of file +Sink $e + +$f = Source + +Sink "here is a string: $f" \ No newline at end of file