Skip to content

Commit 55648ac

Browse files
committed
Add shlex.quote as sanitizer
1 parent c82ab2b commit 55648ac

File tree

3 files changed

+19
-1
lines changed

3 files changed

+19
-1
lines changed

python/ql/lib/semmle/python/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
99
private import semmle.python.dataflow.new.TaintTracking
1010
private import CommandInjectionCustomizations::CommandInjection as CommandInjection
1111
private import semmle.python.Concepts as Concepts
12+
private import semmle.python.ApiGraphs
1213

1314
/**
1415
* Module containing sources, sinks, and sanitizers for shell command constructed from library input.
@@ -17,6 +18,9 @@ module UnsafeShellCommandConstruction {
1718
/** A source for shell command constructed from library input vulnerabilities. */
1819
abstract class Source extends DataFlow::Node { }
1920

21+
/** A sanitizer for shell command constructed from library input vulnerabilities. */
22+
abstract class Sanitizer extends DataFlow::Node { }
23+
2024
private import semmle.python.frameworks.Setuptools
2125

2226
/** An input parameter to a gem seen as a source. */
@@ -156,4 +160,13 @@ module UnsafeShellCommandConstruction {
156160

157161
override DataFlow::Node getStringConstruction() { result = formatCall }
158162
}
163+
164+
/**
165+
* A call to `shlex.quote`, considered as a sanitizer.
166+
*/
167+
class ShlexQuoteAsSanitizer extends Sanitizer, DataFlow::Node {
168+
ShlexQuoteAsSanitizer() {
169+
this = API::moduleImport("shlex").getMember("quote").getACall().getArg(0)
170+
}
171+
}
159172
}

python/ql/lib/semmle/python/security/dataflow/UnsafeShellCommandConstructionQuery.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class Configuration extends TaintTracking::Configuration {
2424
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
2525

2626
override predicate isSanitizer(DataFlow::Node node) {
27-
node instanceof CommandInjection::Sanitizer // using all sanitizers from `rb/command-injection`
27+
node instanceof Sanitizer or
28+
node instanceof CommandInjection::Sanitizer // using all sanitizers from `py/command-injection`
2829
}
2930

3031
// override to require the path doesn't have unmatched return steps

python/ql/test/query-tests/Security/CWE-078-UnsafeShellCommandConstruction/src/unsafe_shell_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import os
22
import subprocess
3+
import shlex
34

45
def unsafe_shell_one(name):
56
os.system("ping " + name) # $result=BAD
67

8+
# shlex.quote sanitizer
9+
os.system("ping " + shlex.quote(name)) # $result=OK
10+
711
# f-strings
812
os.system(f"ping {name}") # $result=BAD
913

0 commit comments

Comments
 (0)