@@ -17,15 +17,17 @@ def test_import_subprocess(self, adds_dependency, tmpdir):
1717 input_code = """
1818 import subprocess
1919
20- subprocess.run("echo 'hi'", shell=True)
21- var = "hello"
20+ def foo(cmd):
21+ subprocess.run(cmd, shell=True)
22+ var = "hello"
2223 """
2324 expected = """
2425 import subprocess
2526 from security import safe_command
2627
27- safe_command.run(subprocess.run, "echo 'hi'", shell=True)
28- var = "hello"
28+ def foo(cmd):
29+ safe_command.run(subprocess.run, cmd, shell=True)
30+ var = "hello"
2931 """
3032 self .run_and_assert (tmpdir , input_code , expected )
3133 adds_dependency .assert_called_once_with (Security )
@@ -34,15 +36,17 @@ def test_import_alias(self, adds_dependency, tmpdir):
3436 input_code = """
3537 import subprocess as sub
3638
37- sub.run("echo 'hi'", shell=True)
38- var = "hello"
39+ def foo(cmd):
40+ sub.run(cmd, shell=True)
41+ var = "hello"
3942 """
4043 expected = """
4144 import subprocess as sub
4245 from security import safe_command
4346
44- safe_command.run(sub.run, "echo 'hi'", shell=True)
45- var = "hello"
47+ def foo(cmd):
48+ safe_command.run(sub.run, cmd, shell=True)
49+ var = "hello"
4650 """
4751 self .run_and_assert (tmpdir , input_code , expected )
4852 adds_dependency .assert_called_once_with (Security )
@@ -51,22 +55,25 @@ def test_from_subprocess(self, adds_dependency, tmpdir):
5155 input_code = """
5256 from subprocess import run
5357
54- run("echo 'hi'", shell=True)
55- var = "hello"
58+ def foo(cmd):
59+ run(cmd, shell=True)
60+ var = "hello"
5661 """
5762 expected = """
5863 from subprocess import run
5964 from security import safe_command
6065
61- safe_command.run(run, "echo 'hi'", shell=True)
62- var = "hello"
66+ def foo(cmd):
67+ safe_command.run(run, cmd, shell=True)
68+ var = "hello"
6369 """
6470 self .run_and_assert (tmpdir , input_code , expected )
6571 adds_dependency .assert_called_once_with (Security )
6672
6773 def test_subprocess_nameerror (self , _ , tmpdir ):
6874 input_code = """
69- subprocess.run("echo 'hi'", shell=True)
75+ def foo(cmd):
76+ subprocess.run(cmd, shell=True)
7077
7178 import subprocess
7279 """
@@ -80,32 +87,36 @@ def test_subprocess_nameerror(self, _, tmpdir):
8087 """
8188 import subprocess
8289 import csv
83- subprocess.run("echo 'hi'", shell=True)
84- csv.excel
90+ def foo(cmd):
91+ subprocess.run(cmd, shell=True)
92+ csv.excel
8593 """ ,
8694 """
8795 import subprocess
8896 import csv
8997 from security import safe_command
9098
91- safe_command.run(subprocess.run, "echo 'hi'", shell=True)
92- csv.excel
99+ def foo(cmd):
100+ safe_command.run(subprocess.run, cmd, shell=True)
101+ csv.excel
93102 """ ,
94103 ),
95104 (
96105 """
97106 import subprocess
98107 from csv import excel
99- subprocess.run("echo 'hi'", shell=True)
100- excel
108+ def foo(cmd):
109+ subprocess.run(cmd, shell=True)
110+ excel
101111 """ ,
102112 """
103113 import subprocess
104114 from csv import excel
105115 from security import safe_command
106116
107- safe_command.run(subprocess.run, "echo 'hi'", shell=True)
108- excel
117+ def foo(cmd):
118+ safe_command.run(subprocess.run, cmd, shell=True)
119+ excel
109120 """ ,
110121 ),
111122 ],
@@ -123,38 +134,62 @@ def test_multifunctions(self, adds_dependency, tmpdir):
123134 input_code = """
124135 import subprocess
125136
126- subprocess.run("echo 'hi'", shell=True)
127- subprocess.check_output(["ls", "-l"])"""
137+ def foo(cmd, cmd2):
138+ subprocess.run(cmd, shell=True)
139+ subprocess.check_output([cmd2, "-l"])"""
128140
129141 expected = """
130142 import subprocess
131143 from security import safe_command
132144
133- safe_command.run(subprocess.run, "echo 'hi'", shell=True)
134- subprocess.check_output(["ls", "-l"])"""
145+ def foo(cmd, cmd2):
146+ safe_command.run(subprocess.run, cmd, shell=True)
147+ subprocess.check_output([cmd2, "-l"])"""
135148
136149 self .run_and_assert (tmpdir , input_code , expected )
137150 adds_dependency .assert_called_once_with (Security )
138151
152+ @pytest .mark .parametrize ("command" , ["run" , "Popen" ])
153+ def test_subprocess_imported_cmd (self , adds_dependency , tmpdir , command ):
154+ input_code = f"""
155+ import subprocess
156+ from whatever import x
157+
158+ subprocess.{ command } ([x, "-l"])
159+ """
160+ expected = f"""
161+ import subprocess
162+ from whatever import x
163+ from security import safe_command
164+
165+ safe_command.run(subprocess.{ command } , [x, "-l"])
166+ """
167+ self .run_and_assert (tmpdir , input_code , expected )
168+ adds_dependency .assert_called_once_with (Security )
169+
139170 def test_custom_run (self , _ , tmpdir ):
140171 input_code = """
141172 from app_funcs import run
142173
143- run("echo 'hi'", shell=True)"""
174+ def foo(cmd):
175+ run(cmd, shell=True)
176+ """
144177 expected = input_code
145178 self .run_and_assert (tmpdir , input_code , expected )
146179
147180 def test_subprocess_call (self , adds_dependency , tmpdir ):
148181 input_code = """
149182 import subprocess
150183
151- subprocess.call(["ls", "-l"])
184+ def foo(cmd):
185+ subprocess.call([cmd, "-l"])
152186 """
153187 expected = """
154188 import subprocess
155189 from security import safe_command
156190
157- safe_command.run(subprocess.call, ["ls", "-l"])
191+ def foo(cmd):
192+ safe_command.run(subprocess.call, [cmd, "-l"])
158193 """
159194 self .run_and_assert (tmpdir , input_code , expected )
160195 adds_dependency .assert_called_once_with (Security )
@@ -163,13 +198,58 @@ def test_subprocess_popen(self, adds_dependency, tmpdir):
163198 input_code = """
164199 import subprocess
165200
166- subprocess.Popen(["ls", "-l"])
201+ def foo(cmd):
202+ subprocess.Popen([cmd, "-l"])
167203 """
168204 expected = """
169205 import subprocess
170206 from security import safe_command
171207
172- safe_command.run(subprocess.Popen, ["ls", "-l"])
208+ def foo(cmd):
209+ safe_command.run(subprocess.Popen, [cmd, "-l"])
173210 """
174211 self .run_and_assert (tmpdir , input_code , expected )
175212 adds_dependency .assert_called_once_with (Security )
213+
214+ def test_hardcoded_string (self , _ , tmpdir ):
215+ input_code = (
216+ expected
217+ ) = """
218+ import subprocess
219+
220+ subprocess.Popen("ls -l", shell=True)
221+ """
222+ self .run_and_assert (tmpdir , input_code , expected )
223+
224+ def test_hardcoded_string_propagation (self , _ , tmpdir ):
225+ input_code = (
226+ expected
227+ ) = """
228+ import subprocess
229+
230+ cmd = "ls -l"
231+ subprocess.Popen(cmd, shell=True)
232+ """
233+ self .run_and_assert (tmpdir , input_code , expected )
234+
235+ def test_hardcoded_array (self , _ , tmpdir ):
236+ input_code = (
237+ expected
238+ ) = """
239+ import subprocess
240+
241+ subprocess.Popen(["ls", "-l"])
242+ """
243+ self .run_and_assert (tmpdir , input_code , expected )
244+
245+ @pytest .mark .xfail (reason = "Semgrep doesn't seem to support array propagation" )
246+ def test_hardcoded_array_propagation (self , _ , tmpdir ):
247+ input_code = (
248+ expected
249+ ) = """
250+ import subprocess
251+
252+ cmd = ["ls", "-l"]
253+ subprocess.Popen(cmd)
254+ """
255+ self .run_and_assert (tmpdir , input_code , expected )
0 commit comments