Skip to content

Commit 5fde177

Browse files
authored
Merge pull request #258 from microsoft/fix-call-operator-bug
PS: Fix bug in `CallOperator::getCommand`
2 parents 6d496ee + 72af800 commit 5fde177

File tree

6 files changed

+43
-7
lines changed

6 files changed

+43
-7
lines changed

powershell/ql/lib/semmle/code/powershell/ast/internal/Command.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ class CmdCall extends CallExpr, TCmd {
7676
class CallOperator extends CmdCall {
7777
CallOperator() { getRawAst(this) instanceof Raw::CallOperator }
7878

79-
Expr getCommand() { result = this.getArgument(0) }
79+
Expr getCommand() { result = this.getCallee() }
8080
}
8181

8282
/** A call to the dot-sourcing `.`. */
8383
class DotSourcingOperator extends CmdCall {
8484
DotSourcingOperator() { getRawAst(this) instanceof Raw::DotSourcingOperator }
8585

86-
Expr getPath() { result = this.getArgument(0) }
86+
Expr getCommand() { result = this.getCallee() }
8787
}
8888

8989
class JoinPath extends CmdCall {

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,22 @@ module ExprNodes {
605605

606606
override CallOperator getExpr() { result = e }
607607

608-
ExprCfgNode getCommand() { result = this.getArgument(0) }
608+
ExprCfgNode getCommand() { result = this.getCallee() }
609+
}
610+
611+
private class DotSourcingOperatorChildMapping extends CallExprChildMapping instanceof DotSourcingOperator
612+
{
613+
override predicate relevantChild(Ast child) { super.relevantChild(child) }
614+
}
615+
616+
class DotSourcingOperatorCfgNode extends CallExprCfgNode {
617+
override string getAPrimaryQlClass() { result = "DotSourcingOperatorCfgNode" }
618+
619+
override DotSourcingOperatorChildMapping e;
620+
621+
override DotSourcingOperator getExpr() { result = e }
622+
623+
ExprCfgNode getCommand() { result = this.getCallee() }
609624
}
610625

611626
private class ToStringCallChildmapping extends CallExprChildMapping instanceof ToStringCall {

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,11 +540,18 @@ class CallNode extends ExprNode {
540540
Node getCallee() { result.asExpr() = call.getCallee() }
541541
}
542542

543-
/** A call to operator `&`, viwed as a node in a data flow graph. */
543+
/** A call to operator `&`, viewed as a node in a data flow graph. */
544544
class CallOperatorNode extends CallNode {
545545
override CfgNodes::ExprNodes::CallOperatorCfgNode call;
546546

547-
Node getCommand() { result.asExpr() = call.getCommand() } // TODO: Alternatively, we could remap calls to & as command expressions.
547+
Node getCommand() { result.asExpr() = call.getCommand() }
548+
}
549+
550+
/** A call to operator `.`, viewed as a node in a data flow graph. */
551+
class DotSourcingOperatorNode extends CallNode {
552+
override CfgNodes::ExprNodes::DotSourcingOperatorCfgNode call;
553+
554+
Node getCommand() { result.asExpr() = call.getCommand() }
548555
}
549556

550557
/**

powershell/ql/lib/semmle/code/powershell/security/CommandInjectionCustomizations.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ module CommandInjection {
4646
call.getAnArgument() = this
4747
)
4848
or
49-
// Or the call command itself in case it's a use of operator &.
49+
// Or the call command itself in case it's a use of "operator &" or "operator .".
5050
any(DataFlow::CallOperatorNode call).getCommand() = this
51+
or
52+
any(DataFlow::DotSourcingOperatorNode call).getCommand() = this
5153
}
5254

5355
override string getSinkType() { result = "call to Invoke-Expression" }

powershell/ql/test/query-tests/security/cwe-078/CommandInjection/CommandInjection.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ edges
5353
| test.ps1:170:36:170:41 | input | test.ps1:129:11:129:20 | userinput | provenance | |
5454
| test.ps1:172:42:172:47 | input | test.ps1:136:11:136:20 | userinput | provenance | |
5555
| test.ps1:173:42:173:47 | input | test.ps1:144:11:144:20 | userinput | provenance | |
56+
| test.ps1:214:10:214:32 | Call to read-host | test.ps1:217:7:217:10 | $o | provenance | Src:MaD:0 |
5657
nodes
5758
| test.ps1:3:11:3:20 | userinput | semmle.label | userinput |
5859
| test.ps1:4:23:4:52 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
@@ -109,6 +110,8 @@ nodes
109110
| test.ps1:170:36:170:41 | input | semmle.label | input |
110111
| test.ps1:172:42:172:47 | input | semmle.label | input |
111112
| test.ps1:173:42:173:47 | input | semmle.label | input |
113+
| test.ps1:214:10:214:32 | Call to read-host | semmle.label | Call to read-host |
114+
| test.ps1:217:7:217:10 | $o | semmle.label | $o |
112115
subpaths
113116
#select
114117
| test.ps1:4:23:4:52 | Get-Process -Name $UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:4:23:4:52 | Get-Process -Name $UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
@@ -129,3 +132,4 @@ subpaths
129132
| test.ps1:131:28:131:37 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:131:28:131:37 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
130133
| test.ps1:139:50:139:59 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:139:50:139:59 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
131134
| test.ps1:147:63:147:72 | UserInput | test.ps1:152:10:152:32 | Call to read-host | test.ps1:147:63:147:72 | UserInput | This command depends on a $@. | test.ps1:152:10:152:32 | Call to read-host | user-provided value |
135+
| test.ps1:217:7:217:10 | $o | test.ps1:214:10:214:32 | Call to read-host | test.ps1:217:7:217:10 | $o | This command depends on a $@. | test.ps1:214:10:214:32 | Call to read-host | user-provided value |

powershell/ql/test/query-tests/security/cwe-078/CommandInjection/test.ps1

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,12 @@ function Invoke-InvokeExpressionInjectionSafe4
207207
Invoke-InvokeExpressionInjectionSafe1 -UserInput $input
208208
Invoke-InvokeExpressionInjectionSafe2 -UserInput $input
209209
Invoke-InvokeExpressionInjectionSafe3 -UserInput $input
210-
Invoke-InvokeExpressionInjectionSafe4 -UserInput $input
210+
Invoke-InvokeExpressionInjectionSafe4 -UserInput $input
211+
212+
function false-positive-in-call-operator($d)
213+
{
214+
$o = Read-Host "enter input"
215+
& unzip -o "$o" -d $d # GOOD
216+
217+
. "$o" # BAD
218+
}

0 commit comments

Comments
 (0)