Skip to content

Commit 90999b3

Browse files
authored
Merge pull request #283 from microsoft/fix-sqlcmd-sink-ps
PS: Fix `&sqlcmd` sinks
2 parents 9681711 + c8eb734 commit 90999b3

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

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

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,27 @@ module SqlInjection {
5252

5353
private string inputfile() { result = ["inputfile", "i"] }
5454

55+
bindingset[call]
56+
pragma[inline_late]
57+
private predicate sqlCmdSinkCommon(DataFlow::CallNode call, DataFlow::Node s) {
58+
s = call.getNamedArgument(query())
59+
or
60+
// If the input is not provided as a query parameter or an input file
61+
// parameter then it's the first argument.
62+
not call.hasNamedArgument(query()) and
63+
not call.hasNamedArgument(inputfile()) and
64+
s = call.getArgument(0)
65+
or
66+
// TODO: Here we really should pick a splat argument, but we don't yet extract whether an
67+
// argument is a splat argument.
68+
s = unique( | | call.getAnArgument())
69+
}
70+
5571
class InvokeSqlCmdSink extends Sink {
5672
InvokeSqlCmdSink() {
57-
exists(DataFlow::CallNode call | call.matchesName("Invoke-Sqlcmd") |
58-
this = call.getNamedArgument(query())
59-
or
60-
// If the input is not provided as a query parameter or an input file
61-
// parameter then it's the first argument.
62-
not call.hasNamedArgument(query()) and
63-
not call.hasNamedArgument(inputfile()) and
64-
this = call.getArgument(0)
65-
or
66-
// TODO: Here we really should pick a splat argument, but we don't yet extract whether an
67-
// argument is a splat argument.
68-
this = unique( | | call.getAnArgument())
73+
exists(DataFlow::CallNode call |
74+
call.matchesName("Invoke-Sqlcmd") and
75+
sqlCmdSinkCommon(call, this)
6976
)
7077
}
7178

@@ -94,11 +101,17 @@ module SqlInjection {
94101
override string getSinkType() { result = "write to " + memberName }
95102
}
96103

104+
/**
105+
* A call of the form `&sqlcmd`.
106+
*
107+
* We don't know if this is actually a call to an SQL execution procedure, but it is
108+
* very common to define a `sqlcmd` variable and point it to an SQL execution program.
109+
*/
97110
class SqlCmdSink extends Sink {
98111
SqlCmdSink() {
99112
exists(DataFlow::CallOperatorNode call |
100113
call.getCommand().asExpr().getValue().stringMatches("sqlcmd") and
101-
call.getAnArgument() = this
114+
sqlCmdSinkCommon(call, this)
102115
)
103116
}
104117

powershell/ql/test/query-tests/security/cwe-089/test.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,6 @@ $QueryConn3 = @{
136136
inputfile = $userinput
137137
}
138138

139-
Invoke-Sqlcmd @QueryConn3 # GOOD
139+
Invoke-Sqlcmd @QueryConn3 # GOOD
140+
141+
&sqlcmd -e -S $userinput -U "Login" -P "MyPassword" -d "MyDBName" -i "input_file.sql" # GOOD

0 commit comments

Comments
 (0)