From 9d36959ac5963e3c67f8516e1f869e355a9ff3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 18 May 2023 18:58:42 +0300 Subject: [PATCH] Implement S601, paramiko_calls --- .../test/fixtures/flake8_bandit/S601.py | 3 ++ crates/ruff/src/checkers/ast/mod.rs | 4 +++ crates/ruff/src/codes.rs | 1 + crates/ruff/src/registry.rs | 1 + crates/ruff/src/rules/flake8_bandit/mod.rs | 1 + .../ruff/src/rules/flake8_bandit/rules/mod.rs | 2 ++ .../flake8_bandit/rules/paramiko_calls.rs | 34 +++++++++++++++++++ ...s__flake8_bandit__tests__S601_S601.py.snap | 12 +++++++ ruff.schema.json | 1 + 9 files changed, 59 insertions(+) create mode 100644 crates/ruff/resources/test/fixtures/flake8_bandit/S601.py create mode 100644 crates/ruff/src/rules/flake8_bandit/rules/paramiko_calls.rs create mode 100644 crates/ruff/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S601_S601.py.snap diff --git a/crates/ruff/resources/test/fixtures/flake8_bandit/S601.py b/crates/ruff/resources/test/fixtures/flake8_bandit/S601.py new file mode 100644 index 00000000000000..1a76018616e35c --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_bandit/S601.py @@ -0,0 +1,3 @@ +import paramiko + +paramiko.exec_command('something; really; unsafe') diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index c6fa2d148b0311..2ddd4dabc665ab 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -2846,6 +2846,10 @@ where if self.settings.rules.enabled(Rule::RequestWithoutTimeout) { flake8_bandit::rules::request_without_timeout(self, func, args, keywords); } + if self.settings.rules.enabled(Rule::ParamikoCalls) + { + flake8_bandit::rules::paramiko_calls(self, func); + } if self .settings .rules diff --git a/crates/ruff/src/codes.rs b/crates/ruff/src/codes.rs index 064fd722f4318e..e4bfc2bc776785 100644 --- a/crates/ruff/src/codes.rs +++ b/crates/ruff/src/codes.rs @@ -507,6 +507,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Bandit, "506") => (RuleGroup::Unspecified, Rule::UnsafeYAMLLoad), (Flake8Bandit, "508") => (RuleGroup::Unspecified, Rule::SnmpInsecureVersion), (Flake8Bandit, "509") => (RuleGroup::Unspecified, Rule::SnmpWeakCryptography), + (Flake8Bandit, "601") => (RuleGroup::Unspecified, Rule::ParamikoCalls), (Flake8Bandit, "602") => (RuleGroup::Unspecified, Rule::SubprocessPopenWithShellEqualsTrue), (Flake8Bandit, "603") => (RuleGroup::Unspecified, Rule::SubprocessWithoutShellEqualsTrue), (Flake8Bandit, "604") => (RuleGroup::Unspecified, Rule::CallWithShellEqualsTrue), diff --git a/crates/ruff/src/registry.rs b/crates/ruff/src/registry.rs index 2de02bde300205..1de0b60915050c 100644 --- a/crates/ruff/src/registry.rs +++ b/crates/ruff/src/registry.rs @@ -422,6 +422,7 @@ ruff_macros::register_rules!( rules::flake8_bandit::rules::HardcodedTempFile, rules::flake8_bandit::rules::HashlibInsecureHashFunction, rules::flake8_bandit::rules::Jinja2AutoescapeFalse, + rules::flake8_bandit::rules::ParamikoCalls, rules::flake8_bandit::rules::LoggingConfigInsecureListen, rules::flake8_bandit::rules::RequestWithNoCertValidation, rules::flake8_bandit::rules::RequestWithoutTimeout, diff --git a/crates/ruff/src/rules/flake8_bandit/mod.rs b/crates/ruff/src/rules/flake8_bandit/mod.rs index 91cd61a150715c..3842b473fd24ca 100644 --- a/crates/ruff/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff/src/rules/flake8_bandit/mod.rs @@ -43,6 +43,7 @@ mod tests { #[test_case(Rule::TryExceptContinue, Path::new("S112.py"); "S112")] #[test_case(Rule::TryExceptPass, Path::new("S110.py"); "S110")] #[test_case(Rule::UnsafeYAMLLoad, Path::new("S506.py"); "S506")] + #[test_case(Rule::ParamikoCalls, Path::new("S601.py"); "S601")] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff/src/rules/flake8_bandit/rules/mod.rs b/crates/ruff/src/rules/flake8_bandit/rules/mod.rs index a7a3f4f92e8b50..e7a85e3f356aeb 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/mod.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/mod.rs @@ -20,6 +20,7 @@ pub(crate) use jinja2_autoescape_false::{jinja2_autoescape_false, Jinja2Autoesca pub(crate) use logging_config_insecure_listen::{ logging_config_insecure_listen, LoggingConfigInsecureListen, }; +pub(crate) use paramiko_calls::{paramiko_calls, ParamikoCalls}; pub(crate) use request_with_no_cert_validation::{ request_with_no_cert_validation, RequestWithNoCertValidation, }; @@ -57,6 +58,7 @@ mod hardcoded_tmp_directory; mod hashlib_insecure_hash_functions; mod jinja2_autoescape_false; mod logging_config_insecure_listen; +mod paramiko_calls; mod request_with_no_cert_validation; mod request_without_timeout; mod shell_injection; diff --git a/crates/ruff/src/rules/flake8_bandit/rules/paramiko_calls.rs b/crates/ruff/src/rules/flake8_bandit/rules/paramiko_calls.rs new file mode 100644 index 00000000000000..55abd5225e6bf6 --- /dev/null +++ b/crates/ruff/src/rules/flake8_bandit/rules/paramiko_calls.rs @@ -0,0 +1,34 @@ +use rustpython_parser::ast::{Expr, Ranged}; + +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; + +use crate::checkers::ast::Checker; + +#[violation] +pub struct ParamikoCalls; + +impl Violation for ParamikoCalls { + #[derive_message_formats] + fn message(&self) -> String { + format!("Possible shell injection via Paramiko call, check inputs are properly sanitized") + } +} + +/// S601 +pub(crate) fn paramiko_calls( + checker: &mut Checker, + func: &Expr, +) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["paramiko", "exec_command"] + }) + { + checker + .diagnostics + .push(Diagnostic::new(ParamikoCalls, func.range())); + } +} diff --git a/crates/ruff/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S601_S601.py.snap b/crates/ruff/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S601_S601.py.snap new file mode 100644 index 00000000000000..8ccf07b52af1d1 --- /dev/null +++ b/crates/ruff/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S601_S601.py.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff/src/rules/flake8_bandit/mod.rs +--- +S601.py:3:1: S601 Possible shell injection via Paramiko call, check inputs are properly sanitized + | +3 | import paramiko +4 | +5 | paramiko.exec_command('something; really; unsafe') + | ^^^^^^^^^^^^^^^^^^^^^ S601 + | + + diff --git a/ruff.schema.json b/ruff.schema.json index 3e0e357729d141..b5bb79883f2562 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2247,6 +2247,7 @@ "S509", "S6", "S60", + "S601", "S602", "S603", "S604",