diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S602.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S602.py index ef0626986e..6c40b547e0 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S602.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S602.py @@ -18,3 +18,18 @@ var_string = "true" Popen(var_string, shell=True) Popen([var_string], shell=True) Popen([var_string, ""], shell=True) + +# Check dict display with only double-starred expressions can be falsey. +Popen("true", shell={**{}}) +Popen("true", shell={**{**{}}}) + +# Check pattern with merged defaults/configs +class ShellConfig: + def __init__(self): + self.shell_defaults = {} + + def fetch_shell_config(self, username): + return {} + + def run(self, username): + Popen("true", shell={**self.shell_defaults, **self.fetch_shell_config(username)}) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S604.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S604.py index 3be26d5495..46131a04b0 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S604.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S604.py @@ -3,3 +3,6 @@ def foo(shell): foo(shell=True) + +foo(shell={**{}}) +foo(shell={**{**{}}}) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S609.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S609.py index 848eb4a2fc..6f75dd7451 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S609.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S609.py @@ -6,3 +6,6 @@ subprocess.Popen("/bin/chown root: *", shell=True) subprocess.Popen(["/usr/local/bin/rsync", "*", "some_where:"], shell=True) subprocess.Popen("/usr/local/bin/rsync * no_injection_here:") os.system("tar cf foo.tar bar/*") + +subprocess.Popen(["chmod", "+w", "*.py"], shell={**{}}) +subprocess.Popen(["chmod", "+w", "*.py"], shell={**{**{}}}) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S602_S602.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S602_S602.py.snap index 75f4454ebf..2056011585 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S602_S602.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S602_S602.py.snap @@ -124,4 +124,6 @@ S602 `subprocess` call with `shell=True` identified, security issue 19 | Popen([var_string], shell=True) 20 | Popen([var_string, ""], shell=True) | ^^^^^ +21 | +22 | # Check dict display with only double-starred expressions can be falsey. | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S604_S604.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S604_S604.py.snap index 85cd9d2583..80764d3418 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S604_S604.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S604_S604.py.snap @@ -6,4 +6,6 @@ S604 Function call with `shell=True` parameter identified, security issue | 5 | foo(shell=True) | ^^^ +6 | +7 | foo(shell={**{}}) | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S609_S609.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S609_S609.py.snap index 1891b78f00..96282702f2 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S609_S609.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S609_S609.py.snap @@ -34,10 +34,12 @@ S609 Possible wildcard injection in call due to `*` usage | S609 Possible wildcard injection in call due to `*` usage - --> S609.py:8:11 - | -6 | subprocess.Popen(["/usr/local/bin/rsync", "*", "some_where:"], shell=True) -7 | subprocess.Popen("/usr/local/bin/rsync * no_injection_here:") -8 | os.system("tar cf foo.tar bar/*") - | ^^^^^^^^^^^^^^^^^^^^^^ - | + --> S609.py:8:11 + | + 6 | subprocess.Popen(["/usr/local/bin/rsync", "*", "some_where:"], shell=True) + 7 | subprocess.Popen("/usr/local/bin/rsync * no_injection_here:") + 8 | os.system("tar cf foo.tar bar/*") + | ^^^^^^^^^^^^^^^^^^^^^^ + 9 | +10 | subprocess.Popen(["chmod", "+w", "*.py"], shell={**{}}) + | diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index a26083941a..978a1ae984 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -1294,15 +1294,14 @@ impl Truthiness { return Self::Falsey; } - if dict.items.iter().all(|item| { - matches!( - item, - DictItem { - key: None, - value: Expr::Name(..) - } - ) - }) { + // If the dict consists only of double-starred items (e.g., {**x, **y}), + // consider its truthiness unknown. This matches lists/sets/tuples containing + // only starred elements, which are also Unknown. + if dict + .items + .iter() + .all(|item| matches!(item, DictItem { key: None, .. })) + { // {**foo} / {**foo, **bar} Self::Unknown } else {