From 097e46a05f29238456c987cedabfe9839e70a359 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 10 Nov 2025 23:32:39 -0500 Subject: [PATCH 1/4] fix-21364 --- .../test/fixtures/flake8_bandit/S508.py | 28 ++++++ .../test/fixtures/flake8_bandit/S509.py | 16 ++++ .../rules/snmp_insecure_version.rs | 9 +- .../rules/snmp_weak_cryptography.rs | 9 +- ...s__flake8_bandit__tests__S508_S508.py.snap | 86 +++++++++++++++++++ ...s__flake8_bandit__tests__S509_S509.py.snap | 42 +++++++++ 6 files changed, 182 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S508.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S508.py index cf87d7ad8eeb7..6f9c672ea5082 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S508.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S508.py @@ -4,3 +4,31 @@ CommunityData("public", mpModel=1) # S508 CommunityData("public", mpModel=2) # OK + +# New API paths +import pysnmp.hlapi.asyncio +import pysnmp.hlapi.v1arch +import pysnmp.hlapi.v1arch.asyncio +import pysnmp.hlapi.v1arch.asyncio.auth +import pysnmp.hlapi.v3arch +import pysnmp.hlapi.v3arch.asyncio +import pysnmp.hlapi.v3arch.asyncio.auth +import pysnmp.hlapi.auth + +pysnmp.hlapi.asyncio.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508 +pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508 + +pysnmp.hlapi.asyncio.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v1arch.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.v3arch.CommunityData("public", mpModel=2) # OK +pysnmp.hlapi.auth.CommunityData("public", mpModel=2) # OK diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S509.py b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S509.py index 618f7a1b787d6..9db0d98dda45f 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S509.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bandit/S509.py @@ -5,3 +5,19 @@ auth_no_priv = UsmUserData("securityName", "authName") # S509 less_insecure = UsmUserData("securityName", "authName", "privName") # OK + +# New API paths +import pysnmp.hlapi.asyncio +import pysnmp.hlapi.v3arch.asyncio +import pysnmp.hlapi.v3arch.asyncio.auth +import pysnmp.hlapi.auth + +pysnmp.hlapi.asyncio.UsmUserData("user") # S509 +pysnmp.hlapi.v3arch.asyncio.UsmUserData("user") # S509 +pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509 +pysnmp.hlapi.auth.UsmUserData("user") # S509 + +pysnmp.hlapi.asyncio.UsmUserData("user", "authkey", "privkey") # OK +pysnmp.hlapi.v3arch.asyncio.UsmUserData("user", "authkey", "privkey") # OK +pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user", "authkey", "privkey") # OK +pysnmp.hlapi.auth.UsmUserData("user", "authkey", "privkey") # OK diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs index b490efdfc585d..5f94763bd1b2d 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs @@ -47,10 +47,11 @@ pub(crate) fn snmp_insecure_version(checker: &Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .is_some_and(|qualified_name| { - matches!( - qualified_name.segments(), - ["pysnmp", "hlapi", "CommunityData"] - ) + let segments = qualified_name.segments(); + segments.len() >= 3 + && segments[0] == "pysnmp" + && segments[1] == "hlapi" + && segments[segments.len() - 1] == "CommunityData" }) { if let Some(keyword) = call.arguments.find_keyword("mpModel") { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs index 9f067e2c4e5a0..4eae2086561db 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs @@ -47,10 +47,11 @@ pub(crate) fn snmp_weak_cryptography(checker: &Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .is_some_and(|qualified_name| { - matches!( - qualified_name.segments(), - ["pysnmp", "hlapi", "UsmUserData"] - ) + let segments = qualified_name.segments(); + segments.len() >= 3 + && segments[0] == "pysnmp" + && segments[1] == "hlapi" + && segments[segments.len() - 1] == "UsmUserData" }) { checker.report_diagnostic(SnmpWeakCryptography, call.func.range()); diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap index acbb09666a645..b47cdd7f7787a 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap @@ -20,3 +20,89 @@ S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. 5 | 6 | CommunityData("public", mpModel=2) # OK | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:18:46 + | +16 | import pysnmp.hlapi.auth +17 | +18 | pysnmp.hlapi.asyncio.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +19 | pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:19:58 + | +18 | pysnmp.hlapi.asyncio.CommunityData("public", mpModel=0) # S508 +19 | pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 +21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:20:53 + | +18 | pysnmp.hlapi.asyncio.CommunityData("public", mpModel=0) # S508 +19 | pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 +22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:21:45 + | +19 | pysnmp.hlapi.v1arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 +21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:22:58 + | +20 | pysnmp.hlapi.v1arch.asyncio.CommunityData("public", mpModel=0) # S508 +21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 +22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 +24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:23:53 + | +21 | pysnmp.hlapi.v1arch.CommunityData("public", mpModel=0) # S508 +22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508 +25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:24:45 + | +22 | pysnmp.hlapi.v3arch.asyncio.auth.CommunityData("public", mpModel=0) # S508 +23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 +24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:25:43 + | +23 | pysnmp.hlapi.v3arch.asyncio.CommunityData("public", mpModel=0) # S508 +24 | pysnmp.hlapi.v3arch.CommunityData("public", mpModel=0) # S508 +25 | pysnmp.hlapi.auth.CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +26 | +27 | pysnmp.hlapi.asyncio.CommunityData("public", mpModel=2) # OK + | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap index c52b437891196..da81c2a63004e 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap @@ -18,3 +18,45 @@ S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` 6 | 7 | less_insecure = UsmUserData("securityName", "authName", "privName") # OK | + +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:15:1 + | +13 | import pysnmp.hlapi.auth +14 | +15 | pysnmp.hlapi.asyncio.UsmUserData("user") # S509 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +16 | pysnmp.hlapi.v3arch.asyncio.UsmUserData("user") # S509 +17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509 + | + +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:16:1 + | +15 | pysnmp.hlapi.asyncio.UsmUserData("user") # S509 +16 | pysnmp.hlapi.v3arch.asyncio.UsmUserData("user") # S509 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509 +18 | pysnmp.hlapi.auth.UsmUserData("user") # S509 + | + +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:17:1 + | +15 | pysnmp.hlapi.asyncio.UsmUserData("user") # S509 +16 | pysnmp.hlapi.v3arch.asyncio.UsmUserData("user") # S509 +17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +18 | pysnmp.hlapi.auth.UsmUserData("user") # S509 + | + +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:18:1 + | +16 | pysnmp.hlapi.v3arch.asyncio.UsmUserData("user") # S509 +17 | pysnmp.hlapi.v3arch.asyncio.auth.UsmUserData("user") # S509 +18 | pysnmp.hlapi.auth.UsmUserData("user") # S509 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +19 | +20 | pysnmp.hlapi.asyncio.UsmUserData("user", "authkey", "privkey") # OK + | From 7684134cd190e9e28ea3574676961464ab98af8e Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 13 Nov 2025 18:44:11 -0500 Subject: [PATCH 2/4] Simplify `segments` checks; Apply preview --- crates/ruff_linter/src/preview.rs | 5 +++++ .../ruff_linter/src/rules/flake8_bandit/mod.rs | 17 +++++++++++++++-- .../rules/snmp_insecure_version.rs | 17 ++++++++++++----- .../rules/snmp_weak_cryptography.rs | 17 ++++++++++++----- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 836ba4feea3a6..fd1fca52b048f 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -269,3 +269,8 @@ pub(crate) const fn is_typing_extensions_str_alias_enabled(settings: &LinterSett pub(crate) const fn is_extended_i18n_function_matching_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } + +// https://github.com/astral-sh/ruff/pull/21374 +pub(crate) const fn is_extended_snmp_api_path_detection_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index 46e9a53e4538a..f82f51160e35b 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -35,8 +35,6 @@ mod tests { #[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"))] #[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"))] #[test_case(Rule::SSHNoHostKeyVerification, Path::new("S507.py"))] - #[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"))] - #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))] #[test_case(Rule::SslInsecureVersion, Path::new("S502.py"))] #[test_case(Rule::SslWithBadDefaults, Path::new("S503.py"))] #[test_case(Rule::SslWithNoVersion, Path::new("S504.py"))] @@ -121,6 +119,21 @@ mod tests { Ok(()) } + #[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"))] + #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))] + fn snmp_rules_with_preview(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); + let diagnostics = test_path( + Path::new("flake8_bandit").join(path).as_path(), + &LinterSettings { + preview: PreviewMode::Enabled, + ..LinterSettings::for_rule(rule_code) + }, + )?; + assert_diagnostics!(snapshot, diagnostics); + Ok(()) + } + #[test_case(Rule::UnsafeMarkupUse, Path::new("S704_extend_markup_names.py"))] #[test_case(Rule::UnsafeMarkupUse, Path::new("S704_skip_early_out.py"))] fn extend_allowed_callable(rule_code: Rule, path: &Path) -> Result<()> { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs index 5f94763bd1b2d..8bf5830e31d24 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs @@ -4,6 +4,7 @@ use ruff_text_size::Ranged; use crate::Violation; use crate::checkers::ast::Checker; +use crate::preview::is_extended_snmp_api_path_detection_enabled; /// ## What it does /// Checks for uses of SNMPv1 or SNMPv2. @@ -47,11 +48,17 @@ pub(crate) fn snmp_insecure_version(checker: &Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .is_some_and(|qualified_name| { - let segments = qualified_name.segments(); - segments.len() >= 3 - && segments[0] == "pysnmp" - && segments[1] == "hlapi" - && segments[segments.len() - 1] == "CommunityData" + if is_extended_snmp_api_path_detection_enabled(checker.settings()) { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", .., "CommunityData"] + ) + } else { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", "CommunityData"] + ) + } }) { if let Some(keyword) = call.arguments.find_keyword("mpModel") { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs index 4eae2086561db..390f1bf13a516 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs @@ -4,6 +4,7 @@ use ruff_text_size::Ranged; use crate::Violation; use crate::checkers::ast::Checker; +use crate::preview::is_extended_snmp_api_path_detection_enabled; /// ## What it does /// Checks for uses of the SNMPv3 protocol without encryption. @@ -47,11 +48,17 @@ pub(crate) fn snmp_weak_cryptography(checker: &Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .is_some_and(|qualified_name| { - let segments = qualified_name.segments(); - segments.len() >= 3 - && segments[0] == "pysnmp" - && segments[1] == "hlapi" - && segments[segments.len() - 1] == "UsmUserData" + if is_extended_snmp_api_path_detection_enabled(checker.settings()) { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", .., "UsmUserData"] + ) + } else { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", "UsmUserData"] + ) + } }) { checker.report_diagnostic(SnmpWeakCryptography, call.func.range()); From 86104bcce980f1800787977d0fe69001bd8901d9 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Wed, 19 Nov 2025 14:40:52 -0500 Subject: [PATCH 3/4] combine snmp_rules_with_preview with existing preview_rules test --- .../ruff_linter/src/rules/flake8_bandit/mod.rs | 17 ++--------------- ...8_bandit__tests__preview__S508_S508.py.snap} | 0 ...8_bandit__tests__preview__S509_S509.py.snap} | 0 3 files changed, 2 insertions(+), 15 deletions(-) rename crates/ruff_linter/src/rules/flake8_bandit/snapshots/{ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap => ruff_linter__rules__flake8_bandit__tests__preview__S508_S508.py.snap} (100%) rename crates/ruff_linter/src/rules/flake8_bandit/snapshots/{ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap => ruff_linter__rules__flake8_bandit__tests__preview__S509_S509.py.snap} (100%) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index f82f51160e35b..417c93fd3065d 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -102,6 +102,8 @@ 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::SnmpInsecureVersion, Path::new("S508.py"))] + #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", @@ -119,21 +121,6 @@ mod tests { Ok(()) } - #[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"))] - #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))] - fn snmp_rules_with_preview(rule_code: Rule, path: &Path) -> Result<()> { - let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); - let diagnostics = test_path( - Path::new("flake8_bandit").join(path).as_path(), - &LinterSettings { - preview: PreviewMode::Enabled, - ..LinterSettings::for_rule(rule_code) - }, - )?; - assert_diagnostics!(snapshot, diagnostics); - Ok(()) - } - #[test_case(Rule::UnsafeMarkupUse, Path::new("S704_extend_markup_names.py"))] #[test_case(Rule::UnsafeMarkupUse, Path::new("S704_skip_early_out.py"))] fn extend_allowed_callable(rule_code: Rule, path: &Path) -> Result<()> { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S508_S508.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap rename to crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S508_S508.py.snap diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S509_S509.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap rename to crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__preview__S509_S509.py.snap From 2cc05afee558dd7af517e56bd7e7f9884ce919b0 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Wed, 19 Nov 2025 14:46:09 -0500 Subject: [PATCH 4/4] revert stable test removal --- .../src/rules/flake8_bandit/mod.rs | 2 ++ ...s__flake8_bandit__tests__S508_S508.py.snap | 22 +++++++++++++++++++ ...s__flake8_bandit__tests__S509_S509.py.snap | 20 +++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap diff --git a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs index 417c93fd3065d..7c3bad60d1e73 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/mod.rs @@ -35,6 +35,8 @@ mod tests { #[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"))] #[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"))] #[test_case(Rule::SSHNoHostKeyVerification, Path::new("S507.py"))] + #[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"))] + #[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"))] #[test_case(Rule::SslInsecureVersion, Path::new("S502.py"))] #[test_case(Rule::SslWithBadDefaults, Path::new("S503.py"))] #[test_case(Rule::SslWithNoVersion, Path::new("S504.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap new file mode 100644 index 0000000000000..acbb09666a645 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S508_S508.py.snap @@ -0,0 +1,22 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:3:25 + | +1 | from pysnmp.hlapi import CommunityData +2 | +3 | CommunityData("public", mpModel=0) # S508 + | ^^^^^^^^^ +4 | CommunityData("public", mpModel=1) # S508 + | + +S508 The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. + --> S508.py:4:25 + | +3 | CommunityData("public", mpModel=0) # S508 +4 | CommunityData("public", mpModel=1) # S508 + | ^^^^^^^^^ +5 | +6 | CommunityData("public", mpModel=2) # OK + | diff --git a/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap new file mode 100644 index 0000000000000..c52b437891196 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bandit/snapshots/ruff_linter__rules__flake8_bandit__tests__S509_S509.py.snap @@ -0,0 +1,20 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs +--- +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:4:12 + | +4 | insecure = UsmUserData("securityName") # S509 + | ^^^^^^^^^^^ +5 | auth_no_priv = UsmUserData("securityName", "authName") # S509 + | + +S509 You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. + --> S509.py:5:16 + | +4 | insecure = UsmUserData("securityName") # S509 +5 | auth_no_priv = UsmUserData("securityName", "authName") # S509 + | ^^^^^^^^^^^ +6 | +7 | less_insecure = UsmUserData("securityName", "authName", "privName") # OK + |