injection_shell.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. #
  2. # Copyright 2014 Hewlett-Packard Development Company, L.P.
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. import ast
  6. import re
  7. import bandit
  8. from bandit.core import issue
  9. from bandit.core import test_properties as test
  10. # yuck, regex: starts with a windows drive letter (eg C:)
  11. # or one of our path delimeter characters (/, \, .)
  12. full_path_match = re.compile(r"^(?:[A-Za-z](?=\:)|[\\\/\.])")
  13. def _evaluate_shell_call(context):
  14. no_formatting = isinstance(context.node.args[0], ast.Str)
  15. if no_formatting:
  16. return bandit.LOW
  17. else:
  18. return bandit.HIGH
  19. def gen_config(name):
  20. if name == "shell_injection":
  21. return {
  22. # Start a process using the subprocess module, or one of its
  23. # wrappers.
  24. "subprocess": [
  25. "subprocess.Popen",
  26. "subprocess.call",
  27. "subprocess.check_call",
  28. "subprocess.check_output",
  29. "subprocess.run",
  30. ],
  31. # Start a process with a function vulnerable to shell injection.
  32. "shell": [
  33. "os.system",
  34. "os.popen",
  35. "os.popen2",
  36. "os.popen3",
  37. "os.popen4",
  38. "popen2.popen2",
  39. "popen2.popen3",
  40. "popen2.popen4",
  41. "popen2.Popen3",
  42. "popen2.Popen4",
  43. "commands.getoutput",
  44. "commands.getstatusoutput",
  45. ],
  46. # Start a process with a function that is not vulnerable to shell
  47. # injection.
  48. "no_shell": [
  49. "os.execl",
  50. "os.execle",
  51. "os.execlp",
  52. "os.execlpe",
  53. "os.execv",
  54. "os.execve",
  55. "os.execvp",
  56. "os.execvpe",
  57. "os.spawnl",
  58. "os.spawnle",
  59. "os.spawnlp",
  60. "os.spawnlpe",
  61. "os.spawnv",
  62. "os.spawnve",
  63. "os.spawnvp",
  64. "os.spawnvpe",
  65. "os.startfile",
  66. ],
  67. }
  68. def has_shell(context):
  69. keywords = context.node.keywords
  70. result = False
  71. if "shell" in context.call_keywords:
  72. for key in keywords:
  73. if key.arg == "shell":
  74. val = key.value
  75. if isinstance(val, ast.Num):
  76. result = bool(val.n)
  77. elif isinstance(val, ast.List):
  78. result = bool(val.elts)
  79. elif isinstance(val, ast.Dict):
  80. result = bool(val.keys)
  81. elif isinstance(val, ast.Name) and val.id in ["False", "None"]:
  82. result = False
  83. elif isinstance(val, ast.NameConstant):
  84. result = val.value
  85. else:
  86. result = True
  87. return result
  88. @test.takes_config("shell_injection")
  89. @test.checks("Call")
  90. @test.test_id("B602")
  91. def subprocess_popen_with_shell_equals_true(context, config):
  92. """**B602: Test for use of popen with shell equals true**
  93. Python possesses many mechanisms to invoke an external executable. However,
  94. doing so may present a security issue if appropriate care is not taken to
  95. sanitize any user provided or variable input.
  96. This plugin test is part of a family of tests built to check for process
  97. spawning and warn appropriately. Specifically, this test looks for the
  98. spawning of a subprocess using a command shell. This type of subprocess
  99. invocation is dangerous as it is vulnerable to various shell injection
  100. attacks. Great care should be taken to sanitize all input in order to
  101. mitigate this risk. Calls of this type are identified by a parameter of
  102. 'shell=True' being given.
  103. Additionally, this plugin scans the command string given and adjusts its
  104. reported severity based on how it is presented. If the command string is a
  105. simple static string containing no special shell characters, then the
  106. resulting issue has low severity. If the string is static, but contains
  107. shell formatting characters or wildcards, then the reported issue is
  108. medium. Finally, if the string is computed using Python's string
  109. manipulation or formatting operations, then the reported issue has high
  110. severity. These severity levels reflect the likelihood that the code is
  111. vulnerable to injection.
  112. See also:
  113. - :doc:`../plugins/linux_commands_wildcard_injection`
  114. - :doc:`../plugins/subprocess_without_shell_equals_true`
  115. - :doc:`../plugins/start_process_with_no_shell`
  116. - :doc:`../plugins/start_process_with_a_shell`
  117. - :doc:`../plugins/start_process_with_partial_path`
  118. **Config Options:**
  119. This plugin test shares a configuration with others in the same family,
  120. namely `shell_injection`. This configuration is divided up into three
  121. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  122. that spawn subprocesses, invoke commands within a shell, or invoke commands
  123. without a shell (by replacing the calling process) respectively.
  124. This plugin specifically scans for methods listed in `subprocess` section
  125. that have shell=True specified.
  126. .. code-block:: yaml
  127. shell_injection:
  128. # Start a process using the subprocess module, or one of its
  129. wrappers.
  130. subprocess:
  131. - subprocess.Popen
  132. - subprocess.call
  133. :Example:
  134. .. code-block:: none
  135. >> Issue: subprocess call with shell=True seems safe, but may be
  136. changed in the future, consider rewriting without shell
  137. Severity: Low Confidence: High
  138. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  139. Location: ./examples/subprocess_shell.py:21
  140. 20 subprocess.check_call(['/bin/ls', '-l'], shell=False)
  141. 21 subprocess.check_call('/bin/ls -l', shell=True)
  142. 22
  143. >> Issue: call with shell=True contains special shell characters,
  144. consider moving extra logic into Python code
  145. Severity: Medium Confidence: High
  146. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  147. Location: ./examples/subprocess_shell.py:26
  148. 25
  149. 26 subprocess.Popen('/bin/ls *', shell=True)
  150. 27 subprocess.Popen('/bin/ls %s' % ('something',), shell=True)
  151. >> Issue: subprocess call with shell=True identified, security issue.
  152. Severity: High Confidence: High
  153. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  154. Location: ./examples/subprocess_shell.py:27
  155. 26 subprocess.Popen('/bin/ls *', shell=True)
  156. 27 subprocess.Popen('/bin/ls %s' % ('something',), shell=True)
  157. 28 subprocess.Popen('/bin/ls {}'.format('something'), shell=True)
  158. .. seealso::
  159. - https://security.openstack.org
  160. - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
  161. - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
  162. - https://security.openstack.org/guidelines/dg_avoid-shell-true.html
  163. - https://cwe.mitre.org/data/definitions/78.html
  164. .. versionadded:: 0.9.0
  165. .. versionchanged:: 1.7.3
  166. CWE information added
  167. """ # noqa: E501
  168. if config and context.call_function_name_qual in config["subprocess"]:
  169. if has_shell(context):
  170. if len(context.call_args) > 0:
  171. sev = _evaluate_shell_call(context)
  172. if sev == bandit.LOW:
  173. return bandit.Issue(
  174. severity=bandit.LOW,
  175. confidence=bandit.HIGH,
  176. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  177. text="subprocess call with shell=True seems safe, but "
  178. "may be changed in the future, consider "
  179. "rewriting without shell",
  180. lineno=context.get_lineno_for_call_arg("shell"),
  181. )
  182. else:
  183. return bandit.Issue(
  184. severity=bandit.HIGH,
  185. confidence=bandit.HIGH,
  186. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  187. text="subprocess call with shell=True identified, "
  188. "security issue.",
  189. lineno=context.get_lineno_for_call_arg("shell"),
  190. )
  191. @test.takes_config("shell_injection")
  192. @test.checks("Call")
  193. @test.test_id("B603")
  194. def subprocess_without_shell_equals_true(context, config):
  195. """**B603: Test for use of subprocess without shell equals true**
  196. Python possesses many mechanisms to invoke an external executable. However,
  197. doing so may present a security issue if appropriate care is not taken to
  198. sanitize any user provided or variable input.
  199. This plugin test is part of a family of tests built to check for process
  200. spawning and warn appropriately. Specifically, this test looks for the
  201. spawning of a subprocess without the use of a command shell. This type of
  202. subprocess invocation is not vulnerable to shell injection attacks, but
  203. care should still be taken to ensure validity of input.
  204. Because this is a lesser issue than that described in
  205. `subprocess_popen_with_shell_equals_true` a LOW severity warning is
  206. reported.
  207. See also:
  208. - :doc:`../plugins/linux_commands_wildcard_injection`
  209. - :doc:`../plugins/subprocess_popen_with_shell_equals_true`
  210. - :doc:`../plugins/start_process_with_no_shell`
  211. - :doc:`../plugins/start_process_with_a_shell`
  212. - :doc:`../plugins/start_process_with_partial_path`
  213. **Config Options:**
  214. This plugin test shares a configuration with others in the same family,
  215. namely `shell_injection`. This configuration is divided up into three
  216. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  217. that spawn subprocesses, invoke commands within a shell, or invoke commands
  218. without a shell (by replacing the calling process) respectively.
  219. This plugin specifically scans for methods listed in `subprocess` section
  220. that have shell=False specified.
  221. .. code-block:: yaml
  222. shell_injection:
  223. # Start a process using the subprocess module, or one of its
  224. wrappers.
  225. subprocess:
  226. - subprocess.Popen
  227. - subprocess.call
  228. :Example:
  229. .. code-block:: none
  230. >> Issue: subprocess call - check for execution of untrusted input.
  231. Severity: Low Confidence: High
  232. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  233. Location: ./examples/subprocess_shell.py:23
  234. 22
  235. 23 subprocess.check_output(['/bin/ls', '-l'])
  236. 24
  237. .. seealso::
  238. - https://security.openstack.org
  239. - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
  240. - https://security.openstack.org/guidelines/dg_avoid-shell-true.html
  241. - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
  242. - https://cwe.mitre.org/data/definitions/78.html
  243. .. versionadded:: 0.9.0
  244. .. versionchanged:: 1.7.3
  245. CWE information added
  246. """ # noqa: E501
  247. if config and context.call_function_name_qual in config["subprocess"]:
  248. if not has_shell(context):
  249. return bandit.Issue(
  250. severity=bandit.LOW,
  251. confidence=bandit.HIGH,
  252. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  253. text="subprocess call - check for execution of untrusted "
  254. "input.",
  255. lineno=context.get_lineno_for_call_arg("shell"),
  256. )
  257. @test.takes_config("shell_injection")
  258. @test.checks("Call")
  259. @test.test_id("B604")
  260. def any_other_function_with_shell_equals_true(context, config):
  261. """**B604: Test for any function with shell equals true**
  262. Python possesses many mechanisms to invoke an external executable. However,
  263. doing so may present a security issue if appropriate care is not taken to
  264. sanitize any user provided or variable input.
  265. This plugin test is part of a family of tests built to check for process
  266. spawning and warn appropriately. Specifically, this plugin test
  267. interrogates method calls for the presence of a keyword parameter `shell`
  268. equalling true. It is related to detection of shell injection issues and is
  269. intended to catch custom wrappers to vulnerable methods that may have been
  270. created.
  271. See also:
  272. - :doc:`../plugins/linux_commands_wildcard_injection`
  273. - :doc:`../plugins/subprocess_popen_with_shell_equals_true`
  274. - :doc:`../plugins/subprocess_without_shell_equals_true`
  275. - :doc:`../plugins/start_process_with_no_shell`
  276. - :doc:`../plugins/start_process_with_a_shell`
  277. - :doc:`../plugins/start_process_with_partial_path`
  278. **Config Options:**
  279. This plugin test shares a configuration with others in the same family,
  280. namely `shell_injection`. This configuration is divided up into three
  281. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  282. that spawn subprocesses, invoke commands within a shell, or invoke commands
  283. without a shell (by replacing the calling process) respectively.
  284. Specifically, this plugin excludes those functions listed under the
  285. subprocess section, these methods are tested in a separate specific test
  286. plugin and this exclusion prevents duplicate issue reporting.
  287. .. code-block:: yaml
  288. shell_injection:
  289. # Start a process using the subprocess module, or one of its
  290. wrappers.
  291. subprocess: [subprocess.Popen, subprocess.call,
  292. subprocess.check_call, subprocess.check_output
  293. execute_with_timeout]
  294. :Example:
  295. .. code-block:: none
  296. >> Issue: Function call with shell=True parameter identified, possible
  297. security issue.
  298. Severity: Medium Confidence: High
  299. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  300. Location: ./examples/subprocess_shell.py:9
  301. 8 pop('/bin/gcc --version', shell=True)
  302. 9 Popen('/bin/gcc --version', shell=True)
  303. 10
  304. .. seealso::
  305. - https://security.openstack.org/guidelines/dg_avoid-shell-true.html
  306. - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
  307. - https://cwe.mitre.org/data/definitions/78.html
  308. .. versionadded:: 0.9.0
  309. .. versionchanged:: 1.7.3
  310. CWE information added
  311. """ # noqa: E501
  312. if config and context.call_function_name_qual not in config["subprocess"]:
  313. if has_shell(context):
  314. return bandit.Issue(
  315. severity=bandit.MEDIUM,
  316. confidence=bandit.LOW,
  317. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  318. text="Function call with shell=True parameter identified, "
  319. "possible security issue.",
  320. lineno=context.get_lineno_for_call_arg("shell"),
  321. )
  322. @test.takes_config("shell_injection")
  323. @test.checks("Call")
  324. @test.test_id("B605")
  325. def start_process_with_a_shell(context, config):
  326. """**B605: Test for starting a process with a shell**
  327. Python possesses many mechanisms to invoke an external executable. However,
  328. doing so may present a security issue if appropriate care is not taken to
  329. sanitize any user provided or variable input.
  330. This plugin test is part of a family of tests built to check for process
  331. spawning and warn appropriately. Specifically, this test looks for the
  332. spawning of a subprocess using a command shell. This type of subprocess
  333. invocation is dangerous as it is vulnerable to various shell injection
  334. attacks. Great care should be taken to sanitize all input in order to
  335. mitigate this risk. Calls of this type are identified by the use of certain
  336. commands which are known to use shells. Bandit will report a LOW
  337. severity warning.
  338. See also:
  339. - :doc:`../plugins/linux_commands_wildcard_injection`
  340. - :doc:`../plugins/subprocess_without_shell_equals_true`
  341. - :doc:`../plugins/start_process_with_no_shell`
  342. - :doc:`../plugins/start_process_with_partial_path`
  343. - :doc:`../plugins/subprocess_popen_with_shell_equals_true`
  344. **Config Options:**
  345. This plugin test shares a configuration with others in the same family,
  346. namely `shell_injection`. This configuration is divided up into three
  347. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  348. that spawn subprocesses, invoke commands within a shell, or invoke commands
  349. without a shell (by replacing the calling process) respectively.
  350. This plugin specifically scans for methods listed in `shell` section.
  351. .. code-block:: yaml
  352. shell_injection:
  353. shell:
  354. - os.system
  355. - os.popen
  356. - os.popen2
  357. - os.popen3
  358. - os.popen4
  359. - popen2.popen2
  360. - popen2.popen3
  361. - popen2.popen4
  362. - popen2.Popen3
  363. - popen2.Popen4
  364. - commands.getoutput
  365. - commands.getstatusoutput
  366. :Example:
  367. .. code-block:: none
  368. >> Issue: Starting a process with a shell: check for injection.
  369. Severity: Low Confidence: Medium
  370. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  371. Location: examples/os_system.py:3
  372. 2
  373. 3 os.system('/bin/echo hi')
  374. .. seealso::
  375. - https://security.openstack.org
  376. - https://docs.python.org/3/library/os.html#os.system
  377. - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
  378. - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
  379. - https://cwe.mitre.org/data/definitions/78.html
  380. .. versionadded:: 0.10.0
  381. .. versionchanged:: 1.7.3
  382. CWE information added
  383. """ # noqa: E501
  384. if config and context.call_function_name_qual in config["shell"]:
  385. if len(context.call_args) > 0:
  386. sev = _evaluate_shell_call(context)
  387. if sev == bandit.LOW:
  388. return bandit.Issue(
  389. severity=bandit.LOW,
  390. confidence=bandit.HIGH,
  391. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  392. text="Starting a process with a shell: "
  393. "Seems safe, but may be changed in the future, "
  394. "consider rewriting without shell",
  395. )
  396. else:
  397. return bandit.Issue(
  398. severity=bandit.HIGH,
  399. confidence=bandit.HIGH,
  400. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  401. text="Starting a process with a shell, possible injection"
  402. " detected, security issue.",
  403. )
  404. @test.takes_config("shell_injection")
  405. @test.checks("Call")
  406. @test.test_id("B606")
  407. def start_process_with_no_shell(context, config):
  408. """**B606: Test for starting a process with no shell**
  409. Python possesses many mechanisms to invoke an external executable. However,
  410. doing so may present a security issue if appropriate care is not taken to
  411. sanitize any user provided or variable input.
  412. This plugin test is part of a family of tests built to check for process
  413. spawning and warn appropriately. Specifically, this test looks for the
  414. spawning of a subprocess in a way that doesn't use a shell. Although this
  415. is generally safe, it maybe useful for penetration testing workflows to
  416. track where external system calls are used. As such a LOW severity message
  417. is generated.
  418. See also:
  419. - :doc:`../plugins/linux_commands_wildcard_injection`
  420. - :doc:`../plugins/subprocess_without_shell_equals_true`
  421. - :doc:`../plugins/start_process_with_a_shell`
  422. - :doc:`../plugins/start_process_with_partial_path`
  423. - :doc:`../plugins/subprocess_popen_with_shell_equals_true`
  424. **Config Options:**
  425. This plugin test shares a configuration with others in the same family,
  426. namely `shell_injection`. This configuration is divided up into three
  427. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  428. that spawn subprocesses, invoke commands within a shell, or invoke commands
  429. without a shell (by replacing the calling process) respectively.
  430. This plugin specifically scans for methods listed in `no_shell` section.
  431. .. code-block:: yaml
  432. shell_injection:
  433. no_shell:
  434. - os.execl
  435. - os.execle
  436. - os.execlp
  437. - os.execlpe
  438. - os.execv
  439. - os.execve
  440. - os.execvp
  441. - os.execvpe
  442. - os.spawnl
  443. - os.spawnle
  444. - os.spawnlp
  445. - os.spawnlpe
  446. - os.spawnv
  447. - os.spawnve
  448. - os.spawnvp
  449. - os.spawnvpe
  450. - os.startfile
  451. :Example:
  452. .. code-block:: none
  453. >> Issue: [start_process_with_no_shell] Starting a process without a
  454. shell.
  455. Severity: Low Confidence: Medium
  456. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  457. Location: examples/os-spawn.py:8
  458. 7 os.spawnv(mode, path, args)
  459. 8 os.spawnve(mode, path, args, env)
  460. 9 os.spawnvp(mode, file, args)
  461. .. seealso::
  462. - https://security.openstack.org
  463. - https://docs.python.org/3/library/os.html#os.system
  464. - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
  465. - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html
  466. - https://cwe.mitre.org/data/definitions/78.html
  467. .. versionadded:: 0.10.0
  468. .. versionchanged:: 1.7.3
  469. CWE information added
  470. """ # noqa: E501
  471. if config and context.call_function_name_qual in config["no_shell"]:
  472. return bandit.Issue(
  473. severity=bandit.LOW,
  474. confidence=bandit.MEDIUM,
  475. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  476. text="Starting a process without a shell.",
  477. )
  478. @test.takes_config("shell_injection")
  479. @test.checks("Call")
  480. @test.test_id("B607")
  481. def start_process_with_partial_path(context, config):
  482. """**B607: Test for starting a process with a partial path**
  483. Python possesses many mechanisms to invoke an external executable. If the
  484. desired executable path is not fully qualified relative to the filesystem
  485. root then this may present a potential security risk.
  486. In POSIX environments, the `PATH` environment variable is used to specify a
  487. set of standard locations that will be searched for the first matching
  488. named executable. While convenient, this behavior may allow a malicious
  489. actor to exert control over a system. If they are able to adjust the
  490. contents of the `PATH` variable, or manipulate the file system, then a
  491. bogus executable may be discovered in place of the desired one. This
  492. executable will be invoked with the user privileges of the Python process
  493. that spawned it, potentially a highly privileged user.
  494. This test will scan the parameters of all configured Python methods,
  495. looking for paths that do not start at the filesystem root, that is, do not
  496. have a leading '/' character.
  497. **Config Options:**
  498. This plugin test shares a configuration with others in the same family,
  499. namely `shell_injection`. This configuration is divided up into three
  500. sections, `subprocess`, `shell` and `no_shell`. They each list Python calls
  501. that spawn subprocesses, invoke commands within a shell, or invoke commands
  502. without a shell (by replacing the calling process) respectively.
  503. This test will scan parameters of all methods in all sections. Note that
  504. methods are fully qualified and de-aliased prior to checking.
  505. .. code-block:: yaml
  506. shell_injection:
  507. # Start a process using the subprocess module, or one of its
  508. wrappers.
  509. subprocess:
  510. - subprocess.Popen
  511. - subprocess.call
  512. # Start a process with a function vulnerable to shell injection.
  513. shell:
  514. - os.system
  515. - os.popen
  516. - popen2.Popen3
  517. - popen2.Popen4
  518. - commands.getoutput
  519. - commands.getstatusoutput
  520. # Start a process with a function that is not vulnerable to shell
  521. injection.
  522. no_shell:
  523. - os.execl
  524. - os.execle
  525. :Example:
  526. .. code-block:: none
  527. >> Issue: Starting a process with a partial executable path
  528. Severity: Low Confidence: High
  529. CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
  530. Location: ./examples/partial_path_process.py:3
  531. 2 from subprocess import Popen as pop
  532. 3 pop('gcc --version', shell=False)
  533. .. seealso::
  534. - https://security.openstack.org
  535. - https://docs.python.org/3/library/os.html#process-management
  536. - https://cwe.mitre.org/data/definitions/78.html
  537. .. versionadded:: 0.13.0
  538. .. versionchanged:: 1.7.3
  539. CWE information added
  540. """
  541. if config and len(context.call_args):
  542. if (
  543. context.call_function_name_qual in config["subprocess"]
  544. or context.call_function_name_qual in config["shell"]
  545. or context.call_function_name_qual in config["no_shell"]
  546. ):
  547. node = context.node.args[0]
  548. # some calls take an arg list, check the first part
  549. if isinstance(node, ast.List):
  550. node = node.elts[0]
  551. # make sure the param is a string literal and not a var name
  552. if isinstance(node, ast.Str) and not full_path_match.match(node.s):
  553. return bandit.Issue(
  554. severity=bandit.LOW,
  555. confidence=bandit.HIGH,
  556. cwe=issue.Cwe.OS_COMMAND_INJECTION,
  557. text="Starting a process with a partial executable path",
  558. )