diff --git a/resources/test/fixtures/flake8_bandit/S110.py b/resources/test/fixtures/flake8_bandit/S110.py new file mode 100644 index 00000000000000..7f034956dd88c0 --- /dev/null +++ b/resources/test/fixtures/flake8_bandit/S110.py @@ -0,0 +1,14 @@ +try: + pass +except Exception: + pass + +try: + pass +except: + pass + +try: + pass +except ValueError: + pass diff --git a/src/checkers/ast.rs b/src/checkers/ast.rs index 7d08cdb694f703..e0200115e89db0 100644 --- a/src/checkers/ast.rs +++ b/src/checkers/ast.rs @@ -3420,6 +3420,15 @@ where body, ); } + if self.settings.rules.enabled(&Rule::TryExceptPass) { + flake8_bandit::rules::try_except_pass( + self, + type_.as_deref(), + name.as_deref(), + body, + self.settings.flake8_bandit.check_typed_exception, + ); + } if self.settings.rules.enabled(&Rule::ReraiseNoCause) { tryceratops::rules::reraise_no_cause(self, body); } diff --git a/src/registry.rs b/src/registry.rs index 4f4a73730c7391..62e67a54f6cef5 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -331,6 +331,7 @@ ruff_macros::define_rule_mapping!( S106 => violations::HardcodedPasswordFuncArg, S107 => violations::HardcodedPasswordDefault, S108 => violations::HardcodedTempFile, + S110 => rules::flake8_bandit::rules::TryExceptPass, S113 => violations::RequestWithoutTimeout, S324 => violations::HashlibInsecureHashFunction, S501 => violations::RequestWithNoCertValidation, diff --git a/src/rules/flake8_bandit/mod.rs b/src/rules/flake8_bandit/mod.rs index d3e86f3914f4d1..4cb48a5015dd7f 100644 --- a/src/rules/flake8_bandit/mod.rs +++ b/src/rules/flake8_bandit/mod.rs @@ -31,6 +31,7 @@ mod tests { #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"); "S509")] #[test_case(Rule::LoggingConfigInsecureListen, Path::new("S612.py"); "S612")] #[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"); "S701")] + #[test_case(Rule::TryExceptPass, Path::new("S110.py"); "S110")] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy()); let diagnostics = test_path( @@ -55,6 +56,7 @@ mod tests { "/dev/shm".to_string(), "/foo".to_string(), ], + check_typed_exception: false, }, ..Settings::for_rule(Rule::HardcodedTempFile) }, @@ -62,4 +64,19 @@ mod tests { assert_yaml_snapshot!("S108_extend", diagnostics); Ok(()) } + + #[test] + fn check_typed_exception() -> Result<()> { + let mut settings: super::settings::Settings = Default::default(); + settings.check_typed_exception = true; + let diagnostics = test_path( + Path::new("./resources/test/fixtures/flake8_bandit/S110.py"), + &Settings { + flake8_bandit: settings, + ..Settings::for_rule(Rule::TryExceptPass) + }, + )?; + assert_yaml_snapshot!("S110_typed", diagnostics); + Ok(()) + } } diff --git a/src/rules/flake8_bandit/rules/mod.rs b/src/rules/flake8_bandit/rules/mod.rs index ea6d6544693e1e..6baa4dd9ab5623 100644 --- a/src/rules/flake8_bandit/rules/mod.rs +++ b/src/rules/flake8_bandit/rules/mod.rs @@ -17,6 +17,7 @@ pub use request_with_no_cert_validation::request_with_no_cert_validation; pub use request_without_timeout::request_without_timeout; pub use snmp_insecure_version::snmp_insecure_version; pub use snmp_weak_cryptography::snmp_weak_cryptography; +pub use try_except_pass::{try_except_pass, TryExceptPass}; pub use unsafe_yaml_load::unsafe_yaml_load; mod assert_used; @@ -34,4 +35,5 @@ mod request_with_no_cert_validation; mod request_without_timeout; mod snmp_insecure_version; mod snmp_weak_cryptography; +mod try_except_pass; mod unsafe_yaml_load; diff --git a/src/rules/flake8_bandit/rules/try_except_pass.rs b/src/rules/flake8_bandit/rules/try_except_pass.rs new file mode 100644 index 00000000000000..4ab0048b400663 --- /dev/null +++ b/src/rules/flake8_bandit/rules/try_except_pass.rs @@ -0,0 +1,45 @@ +use ruff_macros::derive_message_formats; +use rustpython_ast::{Expr, ExprKind, Located, Stmt, StmtKind}; + +use crate::ast::types::Range; +use crate::checkers::ast::Checker; +use crate::define_violation; +use crate::registry::Diagnostic; +use crate::violation::Violation; + +define_violation!( + pub struct TryExceptPass; +); +impl Violation for TryExceptPass { + #[derive_message_formats] + fn message(&self) -> String { + format!("Try, Except, Pass detected.") + } +} + +/// S110 +pub fn try_except_pass( + checker: &mut Checker, + type_: Option<&Expr>, + _name: Option<&str>, + body: &[Stmt], + check_typed_exception: bool, +) { + if body.len() == 1 + && body[0].node == StmtKind::Pass + && (check_typed_exception + || match &type_ { + Some(Located { + node: ExprKind::Name { id, .. }, + .. + }) => id == "Exception", + None => true, + _ => false, + }) + { + checker.diagnostics.push(Diagnostic::new( + TryExceptPass, + Range::from_located(&body[0]), + )); + } +} diff --git a/src/rules/flake8_bandit/settings.rs b/src/rules/flake8_bandit/settings.rs index eb0a76d7794fdb..1052b4718f603c 100644 --- a/src/rules/flake8_bandit/settings.rs +++ b/src/rules/flake8_bandit/settings.rs @@ -34,11 +34,19 @@ pub struct Options { /// A list of directories to consider temporary, in addition to those /// specified by `hardcoded-tmp-directory`. pub hardcoded_tmp_directory_extend: Option>, + #[option( + default = "false", + value_type = "bool", + example = "check-typed-exception = true" + )] + /// A list of directories to consider temporary. + pub check_typed_exception: Option, } #[derive(Debug, Hash)] pub struct Settings { pub hardcoded_tmp_directory: Vec, + pub check_typed_exception: bool, } impl From for Settings { @@ -55,6 +63,7 @@ impl From for Settings { .into_iter(), ) .collect(), + check_typed_exception: options.check_typed_exception.unwrap_or(false), } } } @@ -64,6 +73,7 @@ impl From for Options { Self { hardcoded_tmp_directory: Some(settings.hardcoded_tmp_directory), hardcoded_tmp_directory_extend: None, + check_typed_exception: Some(settings.check_typed_exception), } } } @@ -72,6 +82,7 @@ impl Default for Settings { fn default() -> Self { Self { hardcoded_tmp_directory: default_tmp_dirs(), + check_typed_exception: false, } } } diff --git a/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_S110.py.snap b/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_S110.py.snap new file mode 100644 index 00000000000000..4c601ec6c4d38c --- /dev/null +++ b/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_S110.py.snap @@ -0,0 +1,25 @@ +--- +source: src/rules/flake8_bandit/mod.rs +expression: diagnostics +--- +- kind: + TryExceptPass: ~ + location: + row: 4 + column: 4 + end_location: + row: 4 + column: 8 + fix: ~ + parent: ~ +- kind: + TryExceptPass: ~ + location: + row: 9 + column: 4 + end_location: + row: 9 + column: 8 + fix: ~ + parent: ~ + diff --git a/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_typed.snap b/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_typed.snap new file mode 100644 index 00000000000000..2ae7e68a95ed3a --- /dev/null +++ b/src/rules/flake8_bandit/snapshots/ruff__rules__flake8_bandit__tests__S110_typed.snap @@ -0,0 +1,35 @@ +--- +source: src/rules/flake8_bandit/mod.rs +expression: diagnostics +--- +- kind: + TryExceptPass: ~ + location: + row: 4 + column: 4 + end_location: + row: 4 + column: 8 + fix: ~ + parent: ~ +- kind: + TryExceptPass: ~ + location: + row: 9 + column: 4 + end_location: + row: 9 + column: 8 + fix: ~ + parent: ~ +- kind: + TryExceptPass: ~ + location: + row: 14 + column: 4 + end_location: + row: 14 + column: 8 + fix: ~ + parent: ~ +