From 2e290d24eb19046cd5097c2f95895853dd20ed37 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 9 Sep 2025 19:24:10 +0100 Subject: [PATCH 1/3] PS: Add false positive --- .../test/query-tests/security/cwe-089/SqlInjection.expected | 3 +++ powershell/ql/test/query-tests/security/cwe-089/test.ps1 | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected index af5d263afa67..f1b457f05777 100644 --- a/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -4,6 +4,7 @@ edges | test.ps1:1:1:1:10 | userinput | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | provenance | | | test.ps1:1:1:1:10 | userinput | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | provenance | | | test.ps1:1:1:1:10 | userinput | test.ps1:78:13:78:22 | userinput | provenance | | +| test.ps1:1:1:1:10 | userinput | test.ps1:114:76:114:85 | userinput | provenance | | | test.ps1:1:14:1:45 | Call to read-host | test.ps1:1:1:1:10 | userinput | provenance | Src:MaD:0 | | test.ps1:4:1:4:6 | query | test.ps1:5:72:5:77 | query | provenance | | | test.ps1:8:1:8:6 | query | test.ps1:9:72:9:77 | query | provenance | | @@ -23,6 +24,7 @@ nodes | test.ps1:72:15:79:1 | ${...} [element Query] | semmle.label | ${...} [element Query] | | test.ps1:78:13:78:22 | userinput | semmle.label | userinput | | test.ps1:81:15:81:25 | QueryConn2 | semmle.label | QueryConn2 | +| test.ps1:114:76:114:85 | userinput | semmle.label | userinput | subpaths #select | test.ps1:5:72:5:77 | query | test.ps1:1:14:1:45 | Call to read-host | test.ps1:5:72:5:77 | query | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | @@ -30,3 +32,4 @@ subpaths | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | test.ps1:1:14:1:45 | Call to read-host | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | test.ps1:1:14:1:45 | Call to read-host | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | | test.ps1:81:15:81:25 | QueryConn2 | test.ps1:1:14:1:45 | Call to read-host | test.ps1:81:15:81:25 | QueryConn2 | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | +| test.ps1:114:76:114:85 | userinput | test.ps1:1:14:1:45 | Call to read-host | test.ps1:114:76:114:85 | userinput | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | diff --git a/powershell/ql/test/query-tests/security/cwe-089/test.ps1 b/powershell/ql/test/query-tests/security/cwe-089/test.ps1 index de2d64d2ec81..f1cbc1298d48 100644 --- a/powershell/ql/test/query-tests/security/cwe-089/test.ps1 +++ b/powershell/ql/test/query-tests/security/cwe-089/test.ps1 @@ -109,4 +109,6 @@ function TakesTypedParameters([int]$i, [long]$l, [float]$f, [double]$d, [decimal TakesTypedParameters $userinput $userinput $userinput $userinput $userinput $userinput $userinput $userinput $query = "SELECT * FROM MyTable WHERE MyColumn = '$userinput'" -Invoke-Sqlcmd -unknown $userinput -ServerInstance "MyServer" -Database "MyDatabase" -q "SELECT * FROM MyTable" # GOOD \ No newline at end of file +Invoke-Sqlcmd -unknown $userinput -ServerInstance "MyServer" -Database "MyDatabase" -q "SELECT * FROM MyTable" # GOOD + +Invoke-Sqlcmd -ServerInstance "MyServer" -Database "MyDatabase" -InputFile $userinput # GOOD [FALSE POSITIVE] # this is not really what this query is about. \ No newline at end of file From f87f7208ecaec95cd5df9630122fe06d8348f536 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 9 Sep 2025 19:27:09 +0100 Subject: [PATCH 2/3] PS: Remove 'inputfile' as an SQL injection sink. --- .../code/powershell/security/SqlInjectionCustomizations.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powershell/ql/lib/semmle/code/powershell/security/SqlInjectionCustomizations.qll b/powershell/ql/lib/semmle/code/powershell/security/SqlInjectionCustomizations.qll index ea93309874a3..37036d147721 100644 --- a/powershell/ql/lib/semmle/code/powershell/security/SqlInjectionCustomizations.qll +++ b/powershell/ql/lib/semmle/code/powershell/security/SqlInjectionCustomizations.qll @@ -57,8 +57,8 @@ module SqlInjection { exists(DataFlow::CallNode call | call.matchesName("Invoke-Sqlcmd") | this = call.getNamedArgument(query()) or - this = call.getNamedArgument(inputfile()) - or + // If the input is not provided as a query parameter or an input file + // parameter then it's the first argument. not call.hasNamedArgument(query()) and not call.hasNamedArgument(inputfile()) and this = call.getArgument(0) From 84c3df25e20c1f1340b749af0c725b62d0b3ec82 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 9 Sep 2025 19:27:18 +0100 Subject: [PATCH 3/3] PS: Accept test changes. --- .../ql/test/query-tests/security/cwe-089/SqlInjection.expected | 3 --- powershell/ql/test/query-tests/security/cwe-089/test.ps1 | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected index f1b457f05777..af5d263afa67 100644 --- a/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/powershell/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -4,7 +4,6 @@ edges | test.ps1:1:1:1:10 | userinput | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | provenance | | | test.ps1:1:1:1:10 | userinput | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | provenance | | | test.ps1:1:1:1:10 | userinput | test.ps1:78:13:78:22 | userinput | provenance | | -| test.ps1:1:1:1:10 | userinput | test.ps1:114:76:114:85 | userinput | provenance | | | test.ps1:1:14:1:45 | Call to read-host | test.ps1:1:1:1:10 | userinput | provenance | Src:MaD:0 | | test.ps1:4:1:4:6 | query | test.ps1:5:72:5:77 | query | provenance | | | test.ps1:8:1:8:6 | query | test.ps1:9:72:9:77 | query | provenance | | @@ -24,7 +23,6 @@ nodes | test.ps1:72:15:79:1 | ${...} [element Query] | semmle.label | ${...} [element Query] | | test.ps1:78:13:78:22 | userinput | semmle.label | userinput | | test.ps1:81:15:81:25 | QueryConn2 | semmle.label | QueryConn2 | -| test.ps1:114:76:114:85 | userinput | semmle.label | userinput | subpaths #select | test.ps1:5:72:5:77 | query | test.ps1:1:14:1:45 | Call to read-host | test.ps1:5:72:5:77 | query | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | @@ -32,4 +30,3 @@ subpaths | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | test.ps1:1:14:1:45 | Call to read-host | test.ps1:17:24:17:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | test.ps1:1:14:1:45 | Call to read-host | test.ps1:28:24:28:76 | SELECT * FROM MyTable WHERE MyColumn = '$userinput' | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | | test.ps1:81:15:81:25 | QueryConn2 | test.ps1:1:14:1:45 | Call to read-host | test.ps1:81:15:81:25 | QueryConn2 | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | -| test.ps1:114:76:114:85 | userinput | test.ps1:1:14:1:45 | Call to read-host | test.ps1:114:76:114:85 | userinput | This SQL query depends on a $@. | test.ps1:1:14:1:45 | Call to read-host | read from stdin | diff --git a/powershell/ql/test/query-tests/security/cwe-089/test.ps1 b/powershell/ql/test/query-tests/security/cwe-089/test.ps1 index f1cbc1298d48..a7a7a30da5fb 100644 --- a/powershell/ql/test/query-tests/security/cwe-089/test.ps1 +++ b/powershell/ql/test/query-tests/security/cwe-089/test.ps1 @@ -111,4 +111,4 @@ TakesTypedParameters $userinput $userinput $userinput $userinput $userinput $use $query = "SELECT * FROM MyTable WHERE MyColumn = '$userinput'" Invoke-Sqlcmd -unknown $userinput -ServerInstance "MyServer" -Database "MyDatabase" -q "SELECT * FROM MyTable" # GOOD -Invoke-Sqlcmd -ServerInstance "MyServer" -Database "MyDatabase" -InputFile $userinput # GOOD [FALSE POSITIVE] # this is not really what this query is about. \ No newline at end of file +Invoke-Sqlcmd -ServerInstance "MyServer" -Database "MyDatabase" -InputFile $userinput # GOOD # this is not really what this query is about. \ No newline at end of file