From 3e32eae18f2cffdec53ef4a1a95d44de1e93f6c6 Mon Sep 17 00:00:00 2001 From: trag1c Date: Tue, 1 Apr 2025 20:14:03 +0200 Subject: [PATCH 1/4] Merge S603 arms --- .../rules/flake8_bandit/rules/shell_injection.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 790ade37d3a70..539792d3c556d 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -310,19 +310,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { } } // S603 - Some(ShellKeyword { - truthiness: - Truthiness::False | Truthiness::Falsey | Truthiness::None | Truthiness::Unknown, - }) => { - if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { - checker.report_diagnostic(Diagnostic::new( - SubprocessWithoutShellEqualsTrue, - call.func.range(), - )); - } - } - // S603 - None => { + _ => { if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { checker.report_diagnostic(Diagnostic::new( SubprocessWithoutShellEqualsTrue, From 8fae415af2b2dcb110c2c206df9c8b0dae1c2de7 Mon Sep 17 00:00:00 2001 From: trag1c Date: Wed, 2 Apr 2025 00:07:08 +0200 Subject: [PATCH 2/4] Allow str and list[str] literals in subprocess calls --- .../test/fixtures/flake8_bandit/S603.py | 36 +++-- .../flake8_bandit/rules/shell_injection.rs | 37 +++++- ...s__flake8_bandit__tests__S603_S603.py.snap | 125 ++++++++++-------- 3 files changed, 125 insertions(+), 73 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py index da9b2dae3f0cf..64ce5aaddf9fc 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py @@ -1,20 +1,34 @@ from subprocess import Popen, call, check_call, check_output, run # Different Popen wrappers are checked. -Popen("true", shell=False) -call("true", shell=False) -check_call("true", shell=False) -check_output("true", shell=False) -run("true", shell=False) +a = input() +Popen(a, shell=False) +call(a, shell=False) +check_call(a, shell=False) +check_output(a, shell=False) +run(a, shell=False) # Values that falsey values are treated as false. -Popen("true", shell=0) -Popen("true", shell=[]) -Popen("true", shell={}) -Popen("true", shell=None) +Popen(a, shell=0) +Popen(a, shell=[]) +Popen(a, shell={}) +Popen(a, shell=None) # Unknown values are treated as falsey. -Popen("true", shell=True if True else False) +Popen(a, shell=True if True else False) # No value is also caught. -Popen("true") +Popen(a) + +# Literals are fine, they're trusted. +run("true") +Popen(["true"]) + +# Fine through assignments as well. +run(c := "true") +cmd = ["true"] +run(cmd) + +# Imperfect literals cannot be trusted. +cmd = ["echo", input()] +run(cmd) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 539792d3c556d..3d5e2f8a7e3f9 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -4,6 +4,7 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::helpers::Truthiness; use ruff_python_ast::{self as ast, Arguments, Expr}; +use ruff_python_semantic::analyze::typing::find_binding_value; use ruff_python_semantic::SemanticModel; use ruff_text_size::Ranged; @@ -287,6 +288,30 @@ impl Violation for UnixCommandWildcardInjection { } } +/// Check if an expression is a trusted input for subprocess.run. +/// We assume that any str or list[str] literal +/// (or variables directly assigned such literals) can be trusted. +fn is_trusted_input(checker: &Checker, arg: &Expr) -> bool { + match arg { + Expr::StringLiteral(_) => true, + Expr::List(ruff_python_ast::ExprList { elts, .. }) => { + elts.iter().all(|elt| matches!(elt, Expr::StringLiteral(_))) + } + Expr::Named(ruff_python_ast::ExprNamed { value, .. }) => is_trusted_input(checker, value), + Expr::Name(name) => { + let semantic = checker.semantic(); + let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else { + return false; + }; + let Some(value) = find_binding_value(binding, semantic) else { + return false; + }; + is_trusted_input(checker, value) + } + _ => false, + } +} + /// S602, S603, S604, S605, S606, S607, S609 pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { let call_kind = get_call_kind(&call.func, checker.semantic()); @@ -311,11 +336,13 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { } // S603 _ => { - if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { - checker.report_diagnostic(Diagnostic::new( - SubprocessWithoutShellEqualsTrue, - call.func.range(), - )); + if !is_trusted_input(checker, arg) { + if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { + checker.report_diagnostic(Diagnostic::new( + SubprocessWithoutShellEqualsTrue, + call.func.range(), + )); + } } } } diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap index dba0c9317442a..f32fc594df7ea 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap @@ -1,104 +1,115 @@ --- source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs --- -S603.py:4:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:5:1: S603 `subprocess` call: check for execution of untrusted input | 3 | # Different Popen wrappers are checked. -4 | Popen("true", shell=False) +4 | a = input() +5 | Popen(a, shell=False) | ^^^^^ S603 -5 | call("true", shell=False) -6 | check_call("true", shell=False) +6 | call(a, shell=False) +7 | check_call(a, shell=False) | -S603.py:5:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:6:1: S603 `subprocess` call: check for execution of untrusted input | -3 | # Different Popen wrappers are checked. -4 | Popen("true", shell=False) -5 | call("true", shell=False) +4 | a = input() +5 | Popen(a, shell=False) +6 | call(a, shell=False) | ^^^^ S603 -6 | check_call("true", shell=False) -7 | check_output("true", shell=False) +7 | check_call(a, shell=False) +8 | check_output(a, shell=False) | -S603.py:6:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:7:1: S603 `subprocess` call: check for execution of untrusted input | -4 | Popen("true", shell=False) -5 | call("true", shell=False) -6 | check_call("true", shell=False) +5 | Popen(a, shell=False) +6 | call(a, shell=False) +7 | check_call(a, shell=False) | ^^^^^^^^^^ S603 -7 | check_output("true", shell=False) -8 | run("true", shell=False) +8 | check_output(a, shell=False) +9 | run(a, shell=False) | -S603.py:7:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:8:1: S603 `subprocess` call: check for execution of untrusted input | -5 | call("true", shell=False) -6 | check_call("true", shell=False) -7 | check_output("true", shell=False) +6 | call(a, shell=False) +7 | check_call(a, shell=False) +8 | check_output(a, shell=False) | ^^^^^^^^^^^^ S603 -8 | run("true", shell=False) +9 | run(a, shell=False) | -S603.py:8:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:9:1: S603 `subprocess` call: check for execution of untrusted input | - 6 | check_call("true", shell=False) - 7 | check_output("true", shell=False) - 8 | run("true", shell=False) + 7 | check_call(a, shell=False) + 8 | check_output(a, shell=False) + 9 | run(a, shell=False) | ^^^ S603 - 9 | -10 | # Values that falsey values are treated as false. +10 | +11 | # Values that falsey values are treated as false. | -S603.py:11:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input | -10 | # Values that falsey values are treated as false. -11 | Popen("true", shell=0) +11 | # Values that falsey values are treated as false. +12 | Popen(a, shell=0) | ^^^^^ S603 -12 | Popen("true", shell=[]) -13 | Popen("true", shell={}) +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) | -S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input | -10 | # Values that falsey values are treated as false. -11 | Popen("true", shell=0) -12 | Popen("true", shell=[]) +11 | # Values that falsey values are treated as false. +12 | Popen(a, shell=0) +13 | Popen(a, shell=[]) | ^^^^^ S603 -13 | Popen("true", shell={}) -14 | Popen("true", shell=None) +14 | Popen(a, shell={}) +15 | Popen(a, shell=None) | -S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:14:1: S603 `subprocess` call: check for execution of untrusted input | -11 | Popen("true", shell=0) -12 | Popen("true", shell=[]) -13 | Popen("true", shell={}) +12 | Popen(a, shell=0) +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) | ^^^^^ S603 -14 | Popen("true", shell=None) +15 | Popen(a, shell=None) | -S603.py:14:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:15:1: S603 `subprocess` call: check for execution of untrusted input | -12 | Popen("true", shell=[]) -13 | Popen("true", shell={}) -14 | Popen("true", shell=None) +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) +15 | Popen(a, shell=None) | ^^^^^ S603 -15 | -16 | # Unknown values are treated as falsey. +16 | +17 | # Unknown values are treated as falsey. | -S603.py:17:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:18:1: S603 `subprocess` call: check for execution of untrusted input | -16 | # Unknown values are treated as falsey. -17 | Popen("true", shell=True if True else False) +17 | # Unknown values are treated as falsey. +18 | Popen(a, shell=True if True else False) | ^^^^^ S603 -18 | -19 | # No value is also caught. +19 | +20 | # No value is also caught. | -S603.py:20:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input | -19 | # No value is also caught. -20 | Popen("true") +20 | # No value is also caught. +21 | Popen(a) | ^^^^^ S603 +22 | +23 | # Literals are fine, they're trusted. + | + +S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input + | +32 | # Imperfect literals cannot be trusted. +33 | cmd = ["echo", input()] +34 | run(cmd) + | ^^^ S603 | From e6f515c4533b76502b85f806ca98a7f57e423d38 Mon Sep 17 00:00:00 2001 From: trag1c Date: Wed, 2 Apr 2025 13:32:17 +0200 Subject: [PATCH 3/4] Don't trust literal assignments (bar instant walrus) --- .../test/fixtures/flake8_bandit/S603.py | 12 ++++++----- .../flake8_bandit/rules/shell_injection.rs | 20 ++++--------------- ...s__flake8_bandit__tests__S603_S603.py.snap | 18 +++++++++++++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py index 64ce5aaddf9fc..7b7a73e3f7ed1 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py @@ -24,11 +24,13 @@ run("true") Popen(["true"]) -# Fine through assignments as well. -run(c := "true") +# Not through assignments though. cmd = ["true"] run(cmd) -# Imperfect literals cannot be trusted. -cmd = ["echo", input()] -run(cmd) +# Instant named expressions are fine. +run(c := "true") + +# But non-instant are not. +(e := "echo") +run(e) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 3d5e2f8a7e3f9..21a83ed9d7938 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -4,7 +4,6 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::helpers::Truthiness; use ruff_python_ast::{self as ast, Arguments, Expr}; -use ruff_python_semantic::analyze::typing::find_binding_value; use ruff_python_semantic::SemanticModel; use ruff_text_size::Ranged; @@ -289,25 +288,14 @@ impl Violation for UnixCommandWildcardInjection { } /// Check if an expression is a trusted input for subprocess.run. -/// We assume that any str or list[str] literal -/// (or variables directly assigned such literals) can be trusted. -fn is_trusted_input(checker: &Checker, arg: &Expr) -> bool { +/// We assume that any str or list[str] literal can be trusted. +fn is_trusted_input(arg: &Expr) -> bool { match arg { Expr::StringLiteral(_) => true, Expr::List(ruff_python_ast::ExprList { elts, .. }) => { elts.iter().all(|elt| matches!(elt, Expr::StringLiteral(_))) } - Expr::Named(ruff_python_ast::ExprNamed { value, .. }) => is_trusted_input(checker, value), - Expr::Name(name) => { - let semantic = checker.semantic(); - let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else { - return false; - }; - let Some(value) = find_binding_value(binding, semantic) else { - return false; - }; - is_trusted_input(checker, value) - } + Expr::Named(named) => is_trusted_input(&named.value), _ => false, } } @@ -336,7 +324,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { } // S603 _ => { - if !is_trusted_input(checker, arg) { + if !is_trusted_input(arg) { if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { checker.report_diagnostic(Diagnostic::new( SubprocessWithoutShellEqualsTrue, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap index f32fc594df7ea..bde3991a746ec 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap @@ -106,10 +106,20 @@ S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input 23 | # Literals are fine, they're trusted. | -S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:29:1: S603 `subprocess` call: check for execution of untrusted input | -32 | # Imperfect literals cannot be trusted. -33 | cmd = ["echo", input()] -34 | run(cmd) +27 | # Not through assignments though. +28 | cmd = ["true"] +29 | run(cmd) + | ^^^ S603 +30 | +31 | # Instant named expressions are fine. + | + +S603.py:36:1: S603 `subprocess` call: check for execution of untrusted input + | +34 | # But non-instant are not. +35 | (e := "echo") +36 | run(e) | ^^^ S603 | From 18f0291c0f904e5c35281bd8b0696c421438eaac Mon Sep 17 00:00:00 2001 From: trag1c Date: Wed, 2 Apr 2025 15:51:35 +0200 Subject: [PATCH 4/4] Gate change behind preview; apply review comments --- .../test/fixtures/flake8_bandit/S603.py | 7 +- .../src/rules/flake8_bandit/mod.rs | 1 + .../flake8_bandit/rules/shell_injection.rs | 4 +- ...s__flake8_bandit__tests__S603_S603.py.snap | 101 ++++++++++++-- ..._bandit__tests__preview__S603_S603.py.snap | 125 ++++++++++++++++++ 5 files changed, 223 insertions(+), 15 deletions(-) create mode 100644 crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py index 7b7a73e3f7ed1..09f336fd9535c 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S603.py @@ -8,7 +8,7 @@ check_output(a, shell=False) run(a, shell=False) -# Values that falsey values are treated as false. +# Falsey values are treated as false. Popen(a, shell=0) Popen(a, shell=[]) Popen(a, shell={}) @@ -23,6 +23,11 @@ # Literals are fine, they're trusted. run("true") Popen(["true"]) +Popen("true", shell=False) +call("true", shell=False) +check_call("true", shell=False) +check_output("true", shell=False) +run("true", shell=False) # Not through assignments though. cmd = ["true"] diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index 3eaab135b601b..5f5d7e4bfd311 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -104,6 +104,7 @@ mod tests { #[test_case(Rule::SuspiciousURLOpenUsage, Path::new("S310.py"))] #[test_case(Rule::SuspiciousNonCryptographicRandomUsage, Path::new("S311.py"))] #[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"))] + #[test_case(Rule::SubprocessWithoutShellEqualsTrue, Path::new("S603.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 21a83ed9d7938..539b03ad5a39f 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -292,7 +292,7 @@ impl Violation for UnixCommandWildcardInjection { fn is_trusted_input(arg: &Expr) -> bool { match arg { Expr::StringLiteral(_) => true, - Expr::List(ruff_python_ast::ExprList { elts, .. }) => { + Expr::List(ast::ExprList { elts, .. }) => { elts.iter().all(|elt| matches!(elt, Expr::StringLiteral(_))) } Expr::Named(named) => is_trusted_input(&named.value), @@ -324,7 +324,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) { } // S603 _ => { - if !is_trusted_input(arg) { + if !is_trusted_input(arg) || checker.settings.preview.is_disabled() { if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { checker.report_diagnostic(Diagnostic::new( SubprocessWithoutShellEqualsTrue, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap index bde3991a746ec..e0576d382b0df 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S603_S603.py.snap @@ -47,12 +47,12 @@ S603.py:9:1: S603 `subprocess` call: check for execution of untrusted input 9 | run(a, shell=False) | ^^^ S603 10 | -11 | # Values that falsey values are treated as false. +11 | # Falsey values are treated as false. | S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input | -11 | # Values that falsey values are treated as false. +11 | # Falsey values are treated as false. 12 | Popen(a, shell=0) | ^^^^^ S603 13 | Popen(a, shell=[]) @@ -61,7 +61,7 @@ S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input | -11 | # Values that falsey values are treated as false. +11 | # Falsey values are treated as false. 12 | Popen(a, shell=0) 13 | Popen(a, shell=[]) | ^^^^^ S603 @@ -106,20 +106,97 @@ S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input 23 | # Literals are fine, they're trusted. | +S603.py:24:1: S603 `subprocess` call: check for execution of untrusted input + | +23 | # Literals are fine, they're trusted. +24 | run("true") + | ^^^ S603 +25 | Popen(["true"]) +26 | Popen("true", shell=False) + | + +S603.py:25:1: S603 `subprocess` call: check for execution of untrusted input + | +23 | # Literals are fine, they're trusted. +24 | run("true") +25 | Popen(["true"]) + | ^^^^^ S603 +26 | Popen("true", shell=False) +27 | call("true", shell=False) + | + +S603.py:26:1: S603 `subprocess` call: check for execution of untrusted input + | +24 | run("true") +25 | Popen(["true"]) +26 | Popen("true", shell=False) + | ^^^^^ S603 +27 | call("true", shell=False) +28 | check_call("true", shell=False) + | + +S603.py:27:1: S603 `subprocess` call: check for execution of untrusted input + | +25 | Popen(["true"]) +26 | Popen("true", shell=False) +27 | call("true", shell=False) + | ^^^^ S603 +28 | check_call("true", shell=False) +29 | check_output("true", shell=False) + | + +S603.py:28:1: S603 `subprocess` call: check for execution of untrusted input + | +26 | Popen("true", shell=False) +27 | call("true", shell=False) +28 | check_call("true", shell=False) + | ^^^^^^^^^^ S603 +29 | check_output("true", shell=False) +30 | run("true", shell=False) + | + S603.py:29:1: S603 `subprocess` call: check for execution of untrusted input | -27 | # Not through assignments though. -28 | cmd = ["true"] -29 | run(cmd) +27 | call("true", shell=False) +28 | check_call("true", shell=False) +29 | check_output("true", shell=False) + | ^^^^^^^^^^^^ S603 +30 | run("true", shell=False) + | + +S603.py:30:1: S603 `subprocess` call: check for execution of untrusted input + | +28 | check_call("true", shell=False) +29 | check_output("true", shell=False) +30 | run("true", shell=False) + | ^^^ S603 +31 | +32 | # Not through assignments though. + | + +S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input + | +32 | # Not through assignments though. +33 | cmd = ["true"] +34 | run(cmd) + | ^^^ S603 +35 | +36 | # Instant named expressions are fine. + | + +S603.py:37:1: S603 `subprocess` call: check for execution of untrusted input + | +36 | # Instant named expressions are fine. +37 | run(c := "true") | ^^^ S603 -30 | -31 | # Instant named expressions are fine. +38 | +39 | # But non-instant are not. | -S603.py:36:1: S603 `subprocess` call: check for execution of untrusted input +S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input | -34 | # But non-instant are not. -35 | (e := "echo") -36 | run(e) +39 | # But non-instant are not. +40 | (e := "echo") +41 | run(e) | ^^^ S603 | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap new file mode 100644 index 0000000000000..2b8d7c974f855 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S603_S603.py.snap @@ -0,0 +1,125 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S603.py:5:1: S603 `subprocess` call: check for execution of untrusted input + | +3 | # Different Popen wrappers are checked. +4 | a = input() +5 | Popen(a, shell=False) + | ^^^^^ S603 +6 | call(a, shell=False) +7 | check_call(a, shell=False) + | + +S603.py:6:1: S603 `subprocess` call: check for execution of untrusted input + | +4 | a = input() +5 | Popen(a, shell=False) +6 | call(a, shell=False) + | ^^^^ S603 +7 | check_call(a, shell=False) +8 | check_output(a, shell=False) + | + +S603.py:7:1: S603 `subprocess` call: check for execution of untrusted input + | +5 | Popen(a, shell=False) +6 | call(a, shell=False) +7 | check_call(a, shell=False) + | ^^^^^^^^^^ S603 +8 | check_output(a, shell=False) +9 | run(a, shell=False) + | + +S603.py:8:1: S603 `subprocess` call: check for execution of untrusted input + | +6 | call(a, shell=False) +7 | check_call(a, shell=False) +8 | check_output(a, shell=False) + | ^^^^^^^^^^^^ S603 +9 | run(a, shell=False) + | + +S603.py:9:1: S603 `subprocess` call: check for execution of untrusted input + | + 7 | check_call(a, shell=False) + 8 | check_output(a, shell=False) + 9 | run(a, shell=False) + | ^^^ S603 +10 | +11 | # Falsey values are treated as false. + | + +S603.py:12:1: S603 `subprocess` call: check for execution of untrusted input + | +11 | # Falsey values are treated as false. +12 | Popen(a, shell=0) + | ^^^^^ S603 +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) + | + +S603.py:13:1: S603 `subprocess` call: check for execution of untrusted input + | +11 | # Falsey values are treated as false. +12 | Popen(a, shell=0) +13 | Popen(a, shell=[]) + | ^^^^^ S603 +14 | Popen(a, shell={}) +15 | Popen(a, shell=None) + | + +S603.py:14:1: S603 `subprocess` call: check for execution of untrusted input + | +12 | Popen(a, shell=0) +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) + | ^^^^^ S603 +15 | Popen(a, shell=None) + | + +S603.py:15:1: S603 `subprocess` call: check for execution of untrusted input + | +13 | Popen(a, shell=[]) +14 | Popen(a, shell={}) +15 | Popen(a, shell=None) + | ^^^^^ S603 +16 | +17 | # Unknown values are treated as falsey. + | + +S603.py:18:1: S603 `subprocess` call: check for execution of untrusted input + | +17 | # Unknown values are treated as falsey. +18 | Popen(a, shell=True if True else False) + | ^^^^^ S603 +19 | +20 | # No value is also caught. + | + +S603.py:21:1: S603 `subprocess` call: check for execution of untrusted input + | +20 | # No value is also caught. +21 | Popen(a) + | ^^^^^ S603 +22 | +23 | # Literals are fine, they're trusted. + | + +S603.py:34:1: S603 `subprocess` call: check for execution of untrusted input + | +32 | # Not through assignments though. +33 | cmd = ["true"] +34 | run(cmd) + | ^^^ S603 +35 | +36 | # Instant named expressions are fine. + | + +S603.py:41:1: S603 `subprocess` call: check for execution of untrusted input + | +39 | # But non-instant are not. +40 | (e := "echo") +41 | run(e) + | ^^^ S603 + |