mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 21:05:08 +00:00
Implement flake8-bandit
shell injection rules (#3924)
This commit is contained in:
parent
ffac4f6ec3
commit
a6a7584d79
20 changed files with 1320 additions and 0 deletions
|
@ -5,3 +5,4 @@ extend-exclude = ["snapshots", "black"]
|
||||||
trivias = "trivias"
|
trivias = "trivias"
|
||||||
hel = "hel"
|
hel = "hel"
|
||||||
whos = "whos"
|
whos = "whos"
|
||||||
|
spawnve = "spawnve"
|
||||||
|
|
20
crates/ruff/resources/test/fixtures/flake8_bandit/S602.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S602.py
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
from subprocess import Popen, call, check_call, check_output, run
|
||||||
|
|
||||||
|
# Check different Popen wrappers are checked.
|
||||||
|
Popen("true", shell=True)
|
||||||
|
call("true", shell=True)
|
||||||
|
check_call("true", shell=True)
|
||||||
|
check_output("true", shell=True)
|
||||||
|
run("true", shell=True)
|
||||||
|
|
||||||
|
# Check values that truthy values are treated as true.
|
||||||
|
Popen("true", shell=1)
|
||||||
|
Popen("true", shell=[1])
|
||||||
|
Popen("true", shell={1: 1})
|
||||||
|
Popen("true", shell=(1,))
|
||||||
|
|
||||||
|
# Check command argument looks unsafe.
|
||||||
|
var_string = "true"
|
||||||
|
Popen(var_string, shell=True)
|
||||||
|
Popen([var_string], shell=True)
|
||||||
|
Popen([var_string, ""], shell=True)
|
20
crates/ruff/resources/test/fixtures/flake8_bandit/S603.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S603.py
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Values that falsey values are treated as false.
|
||||||
|
Popen("true", shell=0)
|
||||||
|
Popen("true", shell=[])
|
||||||
|
Popen("true", shell={})
|
||||||
|
Popen("true", shell=None)
|
||||||
|
|
||||||
|
# Unknown values are treated as falsey.
|
||||||
|
Popen("true", shell=True if True else False)
|
||||||
|
|
||||||
|
# No value is also caught.
|
||||||
|
Popen("true")
|
5
crates/ruff/resources/test/fixtures/flake8_bandit/S604.py
vendored
Normal file
5
crates/ruff/resources/test/fixtures/flake8_bandit/S604.py
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def foo(shell):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
foo(shell=True)
|
25
crates/ruff/resources/test/fixtures/flake8_bandit/S605.py
vendored
Normal file
25
crates/ruff/resources/test/fixtures/flake8_bandit/S605.py
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import commands
|
||||||
|
import popen2
|
||||||
|
|
||||||
|
# Check all shell functions.
|
||||||
|
os.system("true")
|
||||||
|
os.popen("true")
|
||||||
|
os.popen2("true")
|
||||||
|
os.popen3("true")
|
||||||
|
os.popen4("true")
|
||||||
|
popen2.popen2("true")
|
||||||
|
popen2.popen3("true")
|
||||||
|
popen2.popen4("true")
|
||||||
|
popen2.Popen3("true")
|
||||||
|
popen2.Popen4("true")
|
||||||
|
commands.getoutput("true")
|
||||||
|
commands.getstatusoutput("true")
|
||||||
|
|
||||||
|
|
||||||
|
# Check command argument looks unsafe.
|
||||||
|
var_string = "true"
|
||||||
|
os.system(var_string)
|
||||||
|
os.system([var_string])
|
||||||
|
os.system([var_string, ""])
|
20
crates/ruff/resources/test/fixtures/flake8_bandit/S606.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/flake8_bandit/S606.py
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Check all shell functions.
|
||||||
|
os.execl("true")
|
||||||
|
os.execle("true")
|
||||||
|
os.execlp("true")
|
||||||
|
os.execlpe("true")
|
||||||
|
os.execv("true")
|
||||||
|
os.execve("true")
|
||||||
|
os.execvp("true")
|
||||||
|
os.execvpe("true")
|
||||||
|
os.spawnl("true")
|
||||||
|
os.spawnle("true")
|
||||||
|
os.spawnlp("true")
|
||||||
|
os.spawnlpe("true")
|
||||||
|
os.spawnv("true")
|
||||||
|
os.spawnve("true")
|
||||||
|
os.spawnvp("true")
|
||||||
|
os.spawnvpe("true")
|
||||||
|
os.startfile("true")
|
44
crates/ruff/resources/test/fixtures/flake8_bandit/S607.py
vendored
Normal file
44
crates/ruff/resources/test/fixtures/flake8_bandit/S607.py
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Check all functions.
|
||||||
|
subprocess.Popen("true")
|
||||||
|
subprocess.call("true")
|
||||||
|
subprocess.check_call("true")
|
||||||
|
subprocess.check_output("true")
|
||||||
|
subprocess.run("true")
|
||||||
|
os.system("true")
|
||||||
|
os.popen("true")
|
||||||
|
os.popen2("true")
|
||||||
|
os.popen3("true")
|
||||||
|
os.popen4("true")
|
||||||
|
popen2.popen2("true")
|
||||||
|
popen2.popen3("true")
|
||||||
|
popen2.popen4("true")
|
||||||
|
popen2.Popen3("true")
|
||||||
|
popen2.Popen4("true")
|
||||||
|
commands.getoutput("true")
|
||||||
|
commands.getstatusoutput("true")
|
||||||
|
os.execl("true")
|
||||||
|
os.execle("true")
|
||||||
|
os.execlp("true")
|
||||||
|
os.execlpe("true")
|
||||||
|
os.execv("true")
|
||||||
|
os.execve("true")
|
||||||
|
os.execvp("true")
|
||||||
|
os.execvpe("true")
|
||||||
|
os.spawnl("true")
|
||||||
|
os.spawnle("true")
|
||||||
|
os.spawnlp("true")
|
||||||
|
os.spawnlpe("true")
|
||||||
|
os.spawnv("true")
|
||||||
|
os.spawnve("true")
|
||||||
|
os.spawnvp("true")
|
||||||
|
os.spawnvpe("true")
|
||||||
|
os.startfile("true")
|
||||||
|
|
||||||
|
# Check it does not fail for full paths.
|
||||||
|
os.system("/bin/ls")
|
||||||
|
os.system("./bin/ls")
|
||||||
|
os.system(["/bin/ls"])
|
||||||
|
os.system(["/bin/ls", "/tmp"])
|
||||||
|
os.system(r"C:\\bin\ls")
|
|
@ -2651,6 +2651,16 @@ where
|
||||||
self, func, args, keywords,
|
self, func, args, keywords,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if self.settings.rules.any_enabled(&[
|
||||||
|
Rule::SubprocessWithoutShellEqualsTrue,
|
||||||
|
Rule::SubprocessPopenWithShellEqualsTrue,
|
||||||
|
Rule::CallWithShellEqualsTrue,
|
||||||
|
Rule::StartProcessWithAShell,
|
||||||
|
Rule::StartProcessWithNoShell,
|
||||||
|
Rule::StartProcessWithPartialPath,
|
||||||
|
]) {
|
||||||
|
flake8_bandit::rules::shell_injection(self, func, args, keywords);
|
||||||
|
}
|
||||||
|
|
||||||
// flake8-comprehensions
|
// flake8-comprehensions
|
||||||
if self.settings.rules.enabled(Rule::UnnecessaryGeneratorList) {
|
if self.settings.rules.enabled(Rule::UnnecessaryGeneratorList) {
|
||||||
|
|
|
@ -507,6 +507,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||||
(Flake8Bandit, "506") => Rule::UnsafeYAMLLoad,
|
(Flake8Bandit, "506") => Rule::UnsafeYAMLLoad,
|
||||||
(Flake8Bandit, "508") => Rule::SnmpInsecureVersion,
|
(Flake8Bandit, "508") => Rule::SnmpInsecureVersion,
|
||||||
(Flake8Bandit, "509") => Rule::SnmpWeakCryptography,
|
(Flake8Bandit, "509") => Rule::SnmpWeakCryptography,
|
||||||
|
(Flake8Bandit, "602") => Rule::SubprocessPopenWithShellEqualsTrue,
|
||||||
|
(Flake8Bandit, "603") => Rule::SubprocessWithoutShellEqualsTrue,
|
||||||
|
(Flake8Bandit, "604") => Rule::CallWithShellEqualsTrue,
|
||||||
|
(Flake8Bandit, "605") => Rule::StartProcessWithAShell,
|
||||||
|
(Flake8Bandit, "606") => Rule::StartProcessWithNoShell,
|
||||||
|
(Flake8Bandit, "607") => Rule::StartProcessWithPartialPath,
|
||||||
(Flake8Bandit, "608") => Rule::HardcodedSQLExpression,
|
(Flake8Bandit, "608") => Rule::HardcodedSQLExpression,
|
||||||
(Flake8Bandit, "612") => Rule::LoggingConfigInsecureListen,
|
(Flake8Bandit, "612") => Rule::LoggingConfigInsecureListen,
|
||||||
(Flake8Bandit, "701") => Rule::Jinja2AutoescapeFalse,
|
(Flake8Bandit, "701") => Rule::Jinja2AutoescapeFalse,
|
||||||
|
|
|
@ -446,6 +446,12 @@ ruff_macros::register_rules!(
|
||||||
rules::flake8_bandit::rules::RequestWithoutTimeout,
|
rules::flake8_bandit::rules::RequestWithoutTimeout,
|
||||||
rules::flake8_bandit::rules::SnmpInsecureVersion,
|
rules::flake8_bandit::rules::SnmpInsecureVersion,
|
||||||
rules::flake8_bandit::rules::SnmpWeakCryptography,
|
rules::flake8_bandit::rules::SnmpWeakCryptography,
|
||||||
|
rules::flake8_bandit::rules::SubprocessPopenWithShellEqualsTrue,
|
||||||
|
rules::flake8_bandit::rules::SubprocessWithoutShellEqualsTrue,
|
||||||
|
rules::flake8_bandit::rules::CallWithShellEqualsTrue,
|
||||||
|
rules::flake8_bandit::rules::StartProcessWithAShell,
|
||||||
|
rules::flake8_bandit::rules::StartProcessWithNoShell,
|
||||||
|
rules::flake8_bandit::rules::StartProcessWithPartialPath,
|
||||||
rules::flake8_bandit::rules::SuspiciousEvalUsage,
|
rules::flake8_bandit::rules::SuspiciousEvalUsage,
|
||||||
rules::flake8_bandit::rules::SuspiciousFTPLibUsage,
|
rules::flake8_bandit::rules::SuspiciousFTPLibUsage,
|
||||||
rules::flake8_bandit::rules::SuspiciousInsecureCipherUsage,
|
rules::flake8_bandit::rules::SuspiciousInsecureCipherUsage,
|
||||||
|
|
|
@ -18,6 +18,7 @@ mod tests {
|
||||||
|
|
||||||
#[test_case(Rule::Assert, Path::new("S101.py"); "S101")]
|
#[test_case(Rule::Assert, Path::new("S101.py"); "S101")]
|
||||||
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"); "S103")]
|
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"); "S103")]
|
||||||
|
#[test_case(Rule::CallWithShellEqualsTrue, Path::new("S604.py"); "S604")]
|
||||||
#[test_case(Rule::ExecBuiltin, Path::new("S102.py"); "S102")]
|
#[test_case(Rule::ExecBuiltin, Path::new("S102.py"); "S102")]
|
||||||
#[test_case(Rule::HardcodedBindAllInterfaces, Path::new("S104.py"); "S104")]
|
#[test_case(Rule::HardcodedBindAllInterfaces, Path::new("S104.py"); "S104")]
|
||||||
#[test_case(Rule::HardcodedPasswordDefault, Path::new("S107.py"); "S107")]
|
#[test_case(Rule::HardcodedPasswordDefault, Path::new("S107.py"); "S107")]
|
||||||
|
@ -32,6 +33,11 @@ mod tests {
|
||||||
#[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"); "S113")]
|
#[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"); "S113")]
|
||||||
#[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"); "S508")]
|
#[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"); "S508")]
|
||||||
#[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"); "S509")]
|
#[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"); "S509")]
|
||||||
|
#[test_case(Rule::StartProcessWithAShell, Path::new("S605.py"); "S605")]
|
||||||
|
#[test_case(Rule::StartProcessWithNoShell, Path::new("S606.py"); "S606")]
|
||||||
|
#[test_case(Rule::StartProcessWithPartialPath, Path::new("S607.py"); "S607")]
|
||||||
|
#[test_case(Rule::SubprocessPopenWithShellEqualsTrue, Path::new("S602.py"); "S602")]
|
||||||
|
#[test_case(Rule::SubprocessWithoutShellEqualsTrue, Path::new("S603.py"); "S603")]
|
||||||
#[test_case(Rule::SuspiciousPickleUsage, Path::new("S301.py"); "S301")]
|
#[test_case(Rule::SuspiciousPickleUsage, Path::new("S301.py"); "S301")]
|
||||||
#[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"); "S312")]
|
#[test_case(Rule::SuspiciousTelnetUsage, Path::new("S312.py"); "S312")]
|
||||||
#[test_case(Rule::TryExceptContinue, Path::new("S112.py"); "S112")]
|
#[test_case(Rule::TryExceptContinue, Path::new("S112.py"); "S112")]
|
||||||
|
|
|
@ -22,6 +22,11 @@ pub use request_with_no_cert_validation::{
|
||||||
request_with_no_cert_validation, RequestWithNoCertValidation,
|
request_with_no_cert_validation, RequestWithNoCertValidation,
|
||||||
};
|
};
|
||||||
pub use request_without_timeout::{request_without_timeout, RequestWithoutTimeout};
|
pub use request_without_timeout::{request_without_timeout, RequestWithoutTimeout};
|
||||||
|
pub use shell_injection::{
|
||||||
|
shell_injection, CallWithShellEqualsTrue, StartProcessWithAShell, StartProcessWithNoShell,
|
||||||
|
StartProcessWithPartialPath, SubprocessPopenWithShellEqualsTrue,
|
||||||
|
SubprocessWithoutShellEqualsTrue,
|
||||||
|
};
|
||||||
pub use snmp_insecure_version::{snmp_insecure_version, SnmpInsecureVersion};
|
pub use snmp_insecure_version::{snmp_insecure_version, SnmpInsecureVersion};
|
||||||
pub use snmp_weak_cryptography::{snmp_weak_cryptography, SnmpWeakCryptography};
|
pub use snmp_weak_cryptography::{snmp_weak_cryptography, SnmpWeakCryptography};
|
||||||
pub use suspicious_function_call::{
|
pub use suspicious_function_call::{
|
||||||
|
@ -52,6 +57,7 @@ mod jinja2_autoescape_false;
|
||||||
mod logging_config_insecure_listen;
|
mod logging_config_insecure_listen;
|
||||||
mod request_with_no_cert_validation;
|
mod request_with_no_cert_validation;
|
||||||
mod request_without_timeout;
|
mod request_without_timeout;
|
||||||
|
mod shell_injection;
|
||||||
mod snmp_insecure_version;
|
mod snmp_insecure_version;
|
||||||
mod snmp_weak_cryptography;
|
mod snmp_weak_cryptography;
|
||||||
mod suspicious_function_call;
|
mod suspicious_function_call;
|
||||||
|
|
372
crates/ruff/src/rules/flake8_bandit/rules/shell_injection.rs
Normal file
372
crates/ruff/src/rules/flake8_bandit/rules/shell_injection.rs
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
//! Checks relating to shell injection.
|
||||||
|
|
||||||
|
use num_bigint::BigInt;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword};
|
||||||
|
|
||||||
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::types::Range;
|
||||||
|
use ruff_python_semantic::context::Context;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
checkers::ast::Checker, registry::Rule, rules::flake8_bandit::helpers::string_literal,
|
||||||
|
};
|
||||||
|
|
||||||
|
static FULL_PATH_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^([A-Za-z]:|[\\/.])").unwrap());
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct SubprocessPopenWithShellEqualsTrue {
|
||||||
|
seems_safe: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for SubprocessPopenWithShellEqualsTrue {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
if self.seems_safe {
|
||||||
|
format!(
|
||||||
|
"`subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("`subprocess` call with `shell=True` identified, security issue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct SubprocessWithoutShellEqualsTrue;
|
||||||
|
|
||||||
|
impl Violation for SubprocessWithoutShellEqualsTrue {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("`subprocess` call: check for execution of untrusted input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct CallWithShellEqualsTrue;
|
||||||
|
|
||||||
|
impl Violation for CallWithShellEqualsTrue {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Function call with `shell=True` parameter identified, security issue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct StartProcessWithAShell {
|
||||||
|
seems_safe: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for StartProcessWithAShell {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
if self.seems_safe {
|
||||||
|
format!("Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`")
|
||||||
|
} else {
|
||||||
|
format!("Starting a process with a shell, possible injection detected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct StartProcessWithNoShell;
|
||||||
|
|
||||||
|
impl Violation for StartProcessWithNoShell {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Starting a process without a shell")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct StartProcessWithPartialPath;
|
||||||
|
|
||||||
|
impl Violation for StartProcessWithPartialPath {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Starting a process with a partial executable path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum CallKind {
|
||||||
|
Subprocess,
|
||||||
|
Shell,
|
||||||
|
NoShell,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the [`CallKind`] of the given function call.
|
||||||
|
fn get_call_kind(func: &Expr, context: &Context) -> Option<CallKind> {
|
||||||
|
context
|
||||||
|
.resolve_call_path(func)
|
||||||
|
.and_then(|call_path| match call_path.as_slice() {
|
||||||
|
&[module, submodule] => match module {
|
||||||
|
"os" => match submodule {
|
||||||
|
"execl" | "execle" | "execlp" | "execlpe" | "execv" | "execve" | "execvp"
|
||||||
|
| "execvpe" | "spawnl" | "spawnle" | "spawnlp" | "spawnlpe" | "spawnv"
|
||||||
|
| "spawnve" | "spawnvp" | "spawnvpe" | "startfile" => Some(CallKind::NoShell),
|
||||||
|
"system" | "popen" | "popen2" | "popen3" | "popen4" => Some(CallKind::Shell),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
"subprocess" => match submodule {
|
||||||
|
"Popen" | "call" | "check_call" | "check_output" | "run" => {
|
||||||
|
Some(CallKind::Subprocess)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
"popen2" => match submodule {
|
||||||
|
"popen2" | "popen3" | "popen4" | "Popen3" | "Popen4" => Some(CallKind::Shell),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
"commands" => match submodule {
|
||||||
|
"getoutput" | "getstatusoutput" => Some(CallKind::Shell),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum Truthiness {
|
||||||
|
// The `shell` keyword argument is set and evaluates to `False`.
|
||||||
|
Falsey,
|
||||||
|
// The `shell` keyword argument is set and evaluates to `True`.
|
||||||
|
Truthy,
|
||||||
|
// The `shell` keyword argument is set, but its value is unknown.
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Keyword> for Truthiness {
|
||||||
|
fn from(value: &Keyword) -> Self {
|
||||||
|
match &value.node.value.node {
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::Bool(b),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if *b {
|
||||||
|
Truthiness::Truthy
|
||||||
|
} else {
|
||||||
|
Truthiness::Falsey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::Int(int),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if int == &BigInt::from(0u8) {
|
||||||
|
Truthiness::Falsey
|
||||||
|
} else {
|
||||||
|
Truthiness::Truthy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::Float(float),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if (float - 0.0).abs() < f64::EPSILON {
|
||||||
|
Truthiness::Falsey
|
||||||
|
} else {
|
||||||
|
Truthiness::Truthy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::None,
|
||||||
|
..
|
||||||
|
} => Truthiness::Falsey,
|
||||||
|
ExprKind::List { elts, .. }
|
||||||
|
| ExprKind::Set { elts, .. }
|
||||||
|
| ExprKind::Tuple { elts, .. } => {
|
||||||
|
if elts.is_empty() {
|
||||||
|
Truthiness::Falsey
|
||||||
|
} else {
|
||||||
|
Truthiness::Truthy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Dict { keys, .. } => {
|
||||||
|
if keys.is_empty() {
|
||||||
|
Truthiness::Falsey
|
||||||
|
} else {
|
||||||
|
Truthiness::Truthy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Truthiness::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct ShellKeyword<'a> {
|
||||||
|
/// Whether the `shell` keyword argument is set and evaluates to `True`.
|
||||||
|
truthiness: Truthiness,
|
||||||
|
/// The `shell` keyword argument.
|
||||||
|
keyword: &'a Keyword,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the `shell` keyword argument to the given function call, if any.
|
||||||
|
fn find_shell_keyword(keywords: &[Keyword]) -> Option<ShellKeyword> {
|
||||||
|
keywords
|
||||||
|
.iter()
|
||||||
|
.find(|keyword| {
|
||||||
|
keyword
|
||||||
|
.node
|
||||||
|
.arg
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |arg| arg == "shell")
|
||||||
|
})
|
||||||
|
.map(|keyword| ShellKeyword {
|
||||||
|
truthiness: keyword.into(),
|
||||||
|
keyword,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the value provided to the `shell` call seems safe. This is based on Bandit's
|
||||||
|
/// definition: string literals are considered okay, but dynamically-computed values are not.
|
||||||
|
fn shell_call_seems_safe(arg: &Expr) -> bool {
|
||||||
|
matches!(
|
||||||
|
arg.node,
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::Str(_),
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the [`Expr`] as a string literal, if it's a string or a list of strings.
|
||||||
|
fn try_string_literal(expr: &Expr) -> Option<&str> {
|
||||||
|
match &expr.node {
|
||||||
|
ExprKind::List { elts, .. } => {
|
||||||
|
if elts.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
string_literal(&elts[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => string_literal(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// S602, S603, S604, S605, S606, S607
|
||||||
|
pub fn shell_injection(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) {
|
||||||
|
let call_kind = get_call_kind(func, &checker.ctx);
|
||||||
|
|
||||||
|
if matches!(call_kind, Some(CallKind::Subprocess)) {
|
||||||
|
if let Some(arg) = args.first() {
|
||||||
|
match find_shell_keyword(keywords) {
|
||||||
|
// S602
|
||||||
|
Some(ShellKeyword {
|
||||||
|
truthiness: Truthiness::Truthy,
|
||||||
|
keyword,
|
||||||
|
}) => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::SubprocessPopenWithShellEqualsTrue)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
SubprocessPopenWithShellEqualsTrue {
|
||||||
|
seems_safe: shell_call_seems_safe(arg),
|
||||||
|
},
|
||||||
|
Range::from(keyword),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// S603
|
||||||
|
Some(ShellKeyword {
|
||||||
|
truthiness: Truthiness::Falsey | Truthiness::Unknown,
|
||||||
|
keyword,
|
||||||
|
}) => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::SubprocessWithoutShellEqualsTrue)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
SubprocessWithoutShellEqualsTrue,
|
||||||
|
Range::from(keyword),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// S603
|
||||||
|
None => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::SubprocessWithoutShellEqualsTrue)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
SubprocessWithoutShellEqualsTrue,
|
||||||
|
Range::from(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(ShellKeyword {
|
||||||
|
truthiness: Truthiness::Truthy,
|
||||||
|
keyword,
|
||||||
|
}) = find_shell_keyword(keywords)
|
||||||
|
{
|
||||||
|
// S604
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::CallWithShellEqualsTrue)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
CallWithShellEqualsTrue,
|
||||||
|
Range::from(keyword),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// S605
|
||||||
|
if matches!(call_kind, Some(CallKind::Shell)) {
|
||||||
|
if let Some(arg) = args.first() {
|
||||||
|
if checker.settings.rules.enabled(Rule::StartProcessWithAShell) {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
StartProcessWithAShell {
|
||||||
|
seems_safe: shell_call_seems_safe(arg),
|
||||||
|
},
|
||||||
|
Range::from(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// S606
|
||||||
|
if matches!(call_kind, Some(CallKind::NoShell)) {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::StartProcessWithNoShell)
|
||||||
|
{
|
||||||
|
checker
|
||||||
|
.diagnostics
|
||||||
|
.push(Diagnostic::new(StartProcessWithNoShell, Range::from(func)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// S607
|
||||||
|
if call_kind.is_some() {
|
||||||
|
if let Some(arg) = args.first() {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(Rule::StartProcessWithPartialPath)
|
||||||
|
{
|
||||||
|
if let Some(value) = try_string_literal(arg) {
|
||||||
|
if FULL_PATH_REGEX.find(value).is_none() {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
StartProcessWithPartialPath,
|
||||||
|
Range::from(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S602.py:4:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
4 | # Check different Popen wrappers are checked.
|
||||||
|
5 | Popen("true", shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
6 | call("true", shell=True)
|
||||||
|
7 | check_call("true", shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:5:14: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
5 | # Check different Popen wrappers are checked.
|
||||||
|
6 | Popen("true", shell=True)
|
||||||
|
7 | call("true", shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
8 | check_call("true", shell=True)
|
||||||
|
9 | check_output("true", shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:6:20: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
6 | Popen("true", shell=True)
|
||||||
|
7 | call("true", shell=True)
|
||||||
|
8 | check_call("true", shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
9 | check_output("true", shell=True)
|
||||||
|
10 | run("true", shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:7:22: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
7 | call("true", shell=True)
|
||||||
|
8 | check_call("true", shell=True)
|
||||||
|
9 | check_output("true", shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
10 | run("true", shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:8:13: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
8 | check_call("true", shell=True)
|
||||||
|
9 | check_output("true", shell=True)
|
||||||
|
10 | run("true", shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
11 |
|
||||||
|
12 | # Check values that truthy values are treated as true.
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:11:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
11 | # Check values that truthy values are treated as true.
|
||||||
|
12 | Popen("true", shell=1)
|
||||||
|
| ^^^^^^^ S602
|
||||||
|
13 | Popen("true", shell=[1])
|
||||||
|
14 | Popen("true", shell={1: 1})
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:12:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
12 | # Check values that truthy values are treated as true.
|
||||||
|
13 | Popen("true", shell=1)
|
||||||
|
14 | Popen("true", shell=[1])
|
||||||
|
| ^^^^^^^^^ S602
|
||||||
|
15 | Popen("true", shell={1: 1})
|
||||||
|
16 | Popen("true", shell=(1,))
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:13:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
13 | Popen("true", shell=1)
|
||||||
|
14 | Popen("true", shell=[1])
|
||||||
|
15 | Popen("true", shell={1: 1})
|
||||||
|
| ^^^^^^^^^^^^ S602
|
||||||
|
16 | Popen("true", shell=(1,))
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:14:15: S602 `subprocess` call with `shell=True` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
14 | Popen("true", shell=[1])
|
||||||
|
15 | Popen("true", shell={1: 1})
|
||||||
|
16 | Popen("true", shell=(1,))
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
17 |
|
||||||
|
18 | # Check command argument looks unsafe.
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:18:19: S602 `subprocess` call with `shell=True` identified, security issue
|
||||||
|
|
|
||||||
|
18 | # Check command argument looks unsafe.
|
||||||
|
19 | var_string = "true"
|
||||||
|
20 | Popen(var_string, shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
21 | Popen([var_string], shell=True)
|
||||||
|
22 | Popen([var_string, ""], shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:19:21: S602 `subprocess` call with `shell=True` identified, security issue
|
||||||
|
|
|
||||||
|
19 | var_string = "true"
|
||||||
|
20 | Popen(var_string, shell=True)
|
||||||
|
21 | Popen([var_string], shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
22 | Popen([var_string, ""], shell=True)
|
||||||
|
|
|
||||||
|
|
||||||
|
S602.py:20:25: S602 `subprocess` call with `shell=True` identified, security issue
|
||||||
|
|
|
||||||
|
20 | Popen(var_string, shell=True)
|
||||||
|
21 | Popen([var_string], shell=True)
|
||||||
|
22 | Popen([var_string, ""], shell=True)
|
||||||
|
| ^^^^^^^^^^ S602
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S603.py:4:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
4 | # Different Popen wrappers are checked.
|
||||||
|
5 | Popen("true", shell=False)
|
||||||
|
| ^^^^^^^^^^^ S603
|
||||||
|
6 | call("true", shell=False)
|
||||||
|
7 | check_call("true", shell=False)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:5:14: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
5 | # Different Popen wrappers are checked.
|
||||||
|
6 | Popen("true", shell=False)
|
||||||
|
7 | call("true", shell=False)
|
||||||
|
| ^^^^^^^^^^^ S603
|
||||||
|
8 | check_call("true", shell=False)
|
||||||
|
9 | check_output("true", shell=False)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:6:20: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
6 | Popen("true", shell=False)
|
||||||
|
7 | call("true", shell=False)
|
||||||
|
8 | check_call("true", shell=False)
|
||||||
|
| ^^^^^^^^^^^ S603
|
||||||
|
9 | check_output("true", shell=False)
|
||||||
|
10 | run("true", shell=False)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:7:22: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
7 | call("true", shell=False)
|
||||||
|
8 | check_call("true", shell=False)
|
||||||
|
9 | check_output("true", shell=False)
|
||||||
|
| ^^^^^^^^^^^ S603
|
||||||
|
10 | run("true", shell=False)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:8:13: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
8 | check_call("true", shell=False)
|
||||||
|
9 | check_output("true", shell=False)
|
||||||
|
10 | run("true", shell=False)
|
||||||
|
| ^^^^^^^^^^^ S603
|
||||||
|
11 |
|
||||||
|
12 | # Values that falsey values are treated as false.
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:11:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
11 | # Values that falsey values are treated as false.
|
||||||
|
12 | Popen("true", shell=0)
|
||||||
|
| ^^^^^^^ S603
|
||||||
|
13 | Popen("true", shell=[])
|
||||||
|
14 | Popen("true", shell={})
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:12:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
12 | # Values that falsey values are treated as false.
|
||||||
|
13 | Popen("true", shell=0)
|
||||||
|
14 | Popen("true", shell=[])
|
||||||
|
| ^^^^^^^^ S603
|
||||||
|
15 | Popen("true", shell={})
|
||||||
|
16 | Popen("true", shell=None)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:13:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
13 | Popen("true", shell=0)
|
||||||
|
14 | Popen("true", shell=[])
|
||||||
|
15 | Popen("true", shell={})
|
||||||
|
| ^^^^^^^^ S603
|
||||||
|
16 | Popen("true", shell=None)
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:14:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
14 | Popen("true", shell=[])
|
||||||
|
15 | Popen("true", shell={})
|
||||||
|
16 | Popen("true", shell=None)
|
||||||
|
| ^^^^^^^^^^ S603
|
||||||
|
17 |
|
||||||
|
18 | # Unknown values are treated as falsey.
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:17:15: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
17 | # Unknown values are treated as falsey.
|
||||||
|
18 | Popen("true", shell=True if True else False)
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S603
|
||||||
|
19 |
|
||||||
|
20 | # No value is also caught.
|
||||||
|
|
|
||||||
|
|
||||||
|
S603.py:20:7: S603 `subprocess` call: check for execution of untrusted input
|
||||||
|
|
|
||||||
|
20 | # No value is also caught.
|
||||||
|
21 | Popen("true")
|
||||||
|
| ^^^^^^ S603
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S604.py:5:5: S604 Function call with `shell=True` parameter identified, security issue
|
||||||
|
|
|
||||||
|
5 | foo(shell=True)
|
||||||
|
| ^^^^^^^^^^ S604
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S605.py:7:11: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
7 | # Check all shell functions.
|
||||||
|
8 | os.system("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
9 | os.popen("true")
|
||||||
|
10 | os.popen2("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:8:10: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
8 | # Check all shell functions.
|
||||||
|
9 | os.system("true")
|
||||||
|
10 | os.popen("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
11 | os.popen2("true")
|
||||||
|
12 | os.popen3("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:9:11: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
9 | os.system("true")
|
||||||
|
10 | os.popen("true")
|
||||||
|
11 | os.popen2("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
12 | os.popen3("true")
|
||||||
|
13 | os.popen4("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:10:11: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
10 | os.popen("true")
|
||||||
|
11 | os.popen2("true")
|
||||||
|
12 | os.popen3("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
13 | os.popen4("true")
|
||||||
|
14 | popen2.popen2("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:11:11: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
11 | os.popen2("true")
|
||||||
|
12 | os.popen3("true")
|
||||||
|
13 | os.popen4("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
14 | popen2.popen2("true")
|
||||||
|
15 | popen2.popen3("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:12:15: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
12 | os.popen3("true")
|
||||||
|
13 | os.popen4("true")
|
||||||
|
14 | popen2.popen2("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
15 | popen2.popen3("true")
|
||||||
|
16 | popen2.popen4("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:13:15: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
13 | os.popen4("true")
|
||||||
|
14 | popen2.popen2("true")
|
||||||
|
15 | popen2.popen3("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
16 | popen2.popen4("true")
|
||||||
|
17 | popen2.Popen3("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:14:15: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
14 | popen2.popen2("true")
|
||||||
|
15 | popen2.popen3("true")
|
||||||
|
16 | popen2.popen4("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
17 | popen2.Popen3("true")
|
||||||
|
18 | popen2.Popen4("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:15:15: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
15 | popen2.popen3("true")
|
||||||
|
16 | popen2.popen4("true")
|
||||||
|
17 | popen2.Popen3("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
18 | popen2.Popen4("true")
|
||||||
|
19 | commands.getoutput("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:16:15: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
16 | popen2.popen4("true")
|
||||||
|
17 | popen2.Popen3("true")
|
||||||
|
18 | popen2.Popen4("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
19 | commands.getoutput("true")
|
||||||
|
20 | commands.getstatusoutput("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:17:20: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
17 | popen2.Popen3("true")
|
||||||
|
18 | popen2.Popen4("true")
|
||||||
|
19 | commands.getoutput("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
20 | commands.getstatusoutput("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:18:26: S605 Starting a process with a shell: seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||||
|
|
|
||||||
|
18 | popen2.Popen4("true")
|
||||||
|
19 | commands.getoutput("true")
|
||||||
|
20 | commands.getstatusoutput("true")
|
||||||
|
| ^^^^^^ S605
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:23:11: S605 Starting a process with a shell, possible injection detected
|
||||||
|
|
|
||||||
|
23 | # Check command argument looks unsafe.
|
||||||
|
24 | var_string = "true"
|
||||||
|
25 | os.system(var_string)
|
||||||
|
| ^^^^^^^^^^ S605
|
||||||
|
26 | os.system([var_string])
|
||||||
|
27 | os.system([var_string, ""])
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:24:11: S605 Starting a process with a shell, possible injection detected
|
||||||
|
|
|
||||||
|
24 | var_string = "true"
|
||||||
|
25 | os.system(var_string)
|
||||||
|
26 | os.system([var_string])
|
||||||
|
| ^^^^^^^^^^^^ S605
|
||||||
|
27 | os.system([var_string, ""])
|
||||||
|
|
|
||||||
|
|
||||||
|
S605.py:25:11: S605 Starting a process with a shell, possible injection detected
|
||||||
|
|
|
||||||
|
25 | os.system(var_string)
|
||||||
|
26 | os.system([var_string])
|
||||||
|
27 | os.system([var_string, ""])
|
||||||
|
| ^^^^^^^^^^^^^^^^ S605
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S606.py:4:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
4 | # Check all shell functions.
|
||||||
|
5 | os.execl("true")
|
||||||
|
| ^^^^^^^^ S606
|
||||||
|
6 | os.execle("true")
|
||||||
|
7 | os.execlp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:5:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
5 | # Check all shell functions.
|
||||||
|
6 | os.execl("true")
|
||||||
|
7 | os.execle("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
8 | os.execlp("true")
|
||||||
|
9 | os.execlpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:6:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
6 | os.execl("true")
|
||||||
|
7 | os.execle("true")
|
||||||
|
8 | os.execlp("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
9 | os.execlpe("true")
|
||||||
|
10 | os.execv("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:7:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
7 | os.execle("true")
|
||||||
|
8 | os.execlp("true")
|
||||||
|
9 | os.execlpe("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
10 | os.execv("true")
|
||||||
|
11 | os.execve("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:8:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
8 | os.execlp("true")
|
||||||
|
9 | os.execlpe("true")
|
||||||
|
10 | os.execv("true")
|
||||||
|
| ^^^^^^^^ S606
|
||||||
|
11 | os.execve("true")
|
||||||
|
12 | os.execvp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:9:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
9 | os.execlpe("true")
|
||||||
|
10 | os.execv("true")
|
||||||
|
11 | os.execve("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
12 | os.execvp("true")
|
||||||
|
13 | os.execvpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:10:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
10 | os.execv("true")
|
||||||
|
11 | os.execve("true")
|
||||||
|
12 | os.execvp("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
13 | os.execvpe("true")
|
||||||
|
14 | os.spawnl("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:11:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
11 | os.execve("true")
|
||||||
|
12 | os.execvp("true")
|
||||||
|
13 | os.execvpe("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
14 | os.spawnl("true")
|
||||||
|
15 | os.spawnle("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:12:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
12 | os.execvp("true")
|
||||||
|
13 | os.execvpe("true")
|
||||||
|
14 | os.spawnl("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
15 | os.spawnle("true")
|
||||||
|
16 | os.spawnlp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:13:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
13 | os.execvpe("true")
|
||||||
|
14 | os.spawnl("true")
|
||||||
|
15 | os.spawnle("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
16 | os.spawnlp("true")
|
||||||
|
17 | os.spawnlpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:14:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
14 | os.spawnl("true")
|
||||||
|
15 | os.spawnle("true")
|
||||||
|
16 | os.spawnlp("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
17 | os.spawnlpe("true")
|
||||||
|
18 | os.spawnv("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:15:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
15 | os.spawnle("true")
|
||||||
|
16 | os.spawnlp("true")
|
||||||
|
17 | os.spawnlpe("true")
|
||||||
|
| ^^^^^^^^^^^ S606
|
||||||
|
18 | os.spawnv("true")
|
||||||
|
19 | os.spawnve("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:16:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
16 | os.spawnlp("true")
|
||||||
|
17 | os.spawnlpe("true")
|
||||||
|
18 | os.spawnv("true")
|
||||||
|
| ^^^^^^^^^ S606
|
||||||
|
19 | os.spawnve("true")
|
||||||
|
20 | os.spawnvp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:17:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
17 | os.spawnlpe("true")
|
||||||
|
18 | os.spawnv("true")
|
||||||
|
19 | os.spawnve("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
20 | os.spawnvp("true")
|
||||||
|
21 | os.spawnvpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:18:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
18 | os.spawnv("true")
|
||||||
|
19 | os.spawnve("true")
|
||||||
|
20 | os.spawnvp("true")
|
||||||
|
| ^^^^^^^^^^ S606
|
||||||
|
21 | os.spawnvpe("true")
|
||||||
|
22 | os.startfile("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:19:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
19 | os.spawnve("true")
|
||||||
|
20 | os.spawnvp("true")
|
||||||
|
21 | os.spawnvpe("true")
|
||||||
|
| ^^^^^^^^^^^ S606
|
||||||
|
22 | os.startfile("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S606.py:20:1: S606 Starting a process without a shell
|
||||||
|
|
|
||||||
|
20 | os.spawnvp("true")
|
||||||
|
21 | os.spawnvpe("true")
|
||||||
|
22 | os.startfile("true")
|
||||||
|
| ^^^^^^^^^^^^ S606
|
||||||
|
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bandit/mod.rs
|
||||||
|
---
|
||||||
|
S607.py:9:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
9 | subprocess.check_output("true")
|
||||||
|
10 | subprocess.run("true")
|
||||||
|
11 | os.system("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
12 | os.popen("true")
|
||||||
|
13 | os.popen2("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:10:10: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
10 | subprocess.run("true")
|
||||||
|
11 | os.system("true")
|
||||||
|
12 | os.popen("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
13 | os.popen2("true")
|
||||||
|
14 | os.popen3("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:11:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
11 | os.system("true")
|
||||||
|
12 | os.popen("true")
|
||||||
|
13 | os.popen2("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
14 | os.popen3("true")
|
||||||
|
15 | os.popen4("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:12:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
12 | os.popen("true")
|
||||||
|
13 | os.popen2("true")
|
||||||
|
14 | os.popen3("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
15 | os.popen4("true")
|
||||||
|
16 | popen2.popen2("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:13:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
13 | os.popen2("true")
|
||||||
|
14 | os.popen3("true")
|
||||||
|
15 | os.popen4("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
16 | popen2.popen2("true")
|
||||||
|
17 | popen2.popen3("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:21:10: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
21 | commands.getoutput("true")
|
||||||
|
22 | commands.getstatusoutput("true")
|
||||||
|
23 | os.execl("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
24 | os.execle("true")
|
||||||
|
25 | os.execlp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:22:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
22 | commands.getstatusoutput("true")
|
||||||
|
23 | os.execl("true")
|
||||||
|
24 | os.execle("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
25 | os.execlp("true")
|
||||||
|
26 | os.execlpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:23:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
23 | os.execl("true")
|
||||||
|
24 | os.execle("true")
|
||||||
|
25 | os.execlp("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
26 | os.execlpe("true")
|
||||||
|
27 | os.execv("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:24:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
24 | os.execle("true")
|
||||||
|
25 | os.execlp("true")
|
||||||
|
26 | os.execlpe("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
27 | os.execv("true")
|
||||||
|
28 | os.execve("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:25:10: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
25 | os.execlp("true")
|
||||||
|
26 | os.execlpe("true")
|
||||||
|
27 | os.execv("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
28 | os.execve("true")
|
||||||
|
29 | os.execvp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:26:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
26 | os.execlpe("true")
|
||||||
|
27 | os.execv("true")
|
||||||
|
28 | os.execve("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
29 | os.execvp("true")
|
||||||
|
30 | os.execvpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:27:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
27 | os.execv("true")
|
||||||
|
28 | os.execve("true")
|
||||||
|
29 | os.execvp("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
30 | os.execvpe("true")
|
||||||
|
31 | os.spawnl("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:28:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
28 | os.execve("true")
|
||||||
|
29 | os.execvp("true")
|
||||||
|
30 | os.execvpe("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
31 | os.spawnl("true")
|
||||||
|
32 | os.spawnle("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:29:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
29 | os.execvp("true")
|
||||||
|
30 | os.execvpe("true")
|
||||||
|
31 | os.spawnl("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
32 | os.spawnle("true")
|
||||||
|
33 | os.spawnlp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:30:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
30 | os.execvpe("true")
|
||||||
|
31 | os.spawnl("true")
|
||||||
|
32 | os.spawnle("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
33 | os.spawnlp("true")
|
||||||
|
34 | os.spawnlpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:31:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
31 | os.spawnl("true")
|
||||||
|
32 | os.spawnle("true")
|
||||||
|
33 | os.spawnlp("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
34 | os.spawnlpe("true")
|
||||||
|
35 | os.spawnv("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:32:13: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
32 | os.spawnle("true")
|
||||||
|
33 | os.spawnlp("true")
|
||||||
|
34 | os.spawnlpe("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
35 | os.spawnv("true")
|
||||||
|
36 | os.spawnve("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:33:11: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
33 | os.spawnlp("true")
|
||||||
|
34 | os.spawnlpe("true")
|
||||||
|
35 | os.spawnv("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
36 | os.spawnve("true")
|
||||||
|
37 | os.spawnvp("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:34:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
34 | os.spawnlpe("true")
|
||||||
|
35 | os.spawnv("true")
|
||||||
|
36 | os.spawnve("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
37 | os.spawnvp("true")
|
||||||
|
38 | os.spawnvpe("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:35:12: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
35 | os.spawnv("true")
|
||||||
|
36 | os.spawnve("true")
|
||||||
|
37 | os.spawnvp("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
38 | os.spawnvpe("true")
|
||||||
|
39 | os.startfile("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:36:13: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
36 | os.spawnve("true")
|
||||||
|
37 | os.spawnvp("true")
|
||||||
|
38 | os.spawnvpe("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
39 | os.startfile("true")
|
||||||
|
|
|
||||||
|
|
||||||
|
S607.py:37:14: S607 Starting a process with a partial executable path
|
||||||
|
|
|
||||||
|
37 | os.spawnvp("true")
|
||||||
|
38 | os.spawnvpe("true")
|
||||||
|
39 | os.startfile("true")
|
||||||
|
| ^^^^^^ S607
|
||||||
|
40 |
|
||||||
|
41 | # Check it does not fail for full paths.
|
||||||
|
|
|
||||||
|
|
||||||
|
|
6
ruff.schema.json
generated
6
ruff.schema.json
generated
|
@ -2163,6 +2163,12 @@
|
||||||
"S509",
|
"S509",
|
||||||
"S6",
|
"S6",
|
||||||
"S60",
|
"S60",
|
||||||
|
"S602",
|
||||||
|
"S603",
|
||||||
|
"S604",
|
||||||
|
"S605",
|
||||||
|
"S606",
|
||||||
|
"S607",
|
||||||
"S608",
|
"S608",
|
||||||
"S61",
|
"S61",
|
||||||
"S612",
|
"S612",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue