diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs index a02e922310..d5a18e4446 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs @@ -2,6 +2,7 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::helpers::is_const_false; use ruff_python_ast::{self as ast, Arguments}; +use ruff_python_semantic::Modules; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -64,6 +65,13 @@ impl Violation for HashlibInsecureHashFunction { /// S324 pub(crate) fn hashlib_insecure_hash_functions(checker: &mut Checker, call: &ast::ExprCall) { + if !checker + .semantic() + .seen_module(Modules::HASHLIB | Modules::CRYPT) + { + return; + } + if let Some(weak_hash_call) = checker .semantic() .resolve_qualified_name(&call.func) diff --git a/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs b/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs index 7b53364063..aa16786f73 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs @@ -1,6 +1,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::{Expr, ExprAttribute, ExprCall}; +use ruff_python_semantic::Modules; use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; @@ -27,11 +28,6 @@ use crate::checkers::ast::Checker; /// hashed = sha512(b"some data").hexdigest() /// ``` /// -/// ## Fix safety -/// This rule's fix is marked as unsafe, as the target of the `.digest()` call -/// could be a user-defined class that implements a `.hex()` method, rather -/// than a hashlib hash object. -/// /// ## References /// - [Python documentation: `hashlib`](https://docs.python.org/3/library/hashlib.html) #[derive(ViolationMetadata)] @@ -52,6 +48,10 @@ impl Violation for HashlibDigestHex { /// FURB181 pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { + if !checker.semantic().seen_module(Modules::HASHLIB) { + return; + } + if !call.arguments.is_empty() { return; } @@ -105,14 +105,13 @@ pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { | "sha3_512" | "shake_128" | "shake_256" - | "_Hash" ] ) }) { let mut diagnostic = Diagnostic::new(HashlibDigestHex, call.range()); if arguments.is_empty() { - diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( ".hexdigest".to_string(), TextRange::new(value.end(), call.func.end()), ))); diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB181_FURB181.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB181_FURB181.py.snap index a3e4e1d363..f602872c88 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB181_FURB181.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB181_FURB181.py.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/refurb/mod.rs -snapshot_kind: text --- FURB181.py:19:1: FURB181 [*] Use of hashlib's `.digest().hex()` | @@ -13,7 +12,7 @@ FURB181.py:19:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 16 16 | 17 17 | # these will match 18 18 | @@ -33,7 +32,7 @@ FURB181.py:20:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 17 17 | # these will match 18 18 | 19 19 | blake2b().digest().hex() @@ -54,7 +53,7 @@ FURB181.py:21:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 18 18 | 19 19 | blake2b().digest().hex() 20 20 | blake2s().digest().hex() @@ -75,7 +74,7 @@ FURB181.py:22:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 19 19 | blake2b().digest().hex() 20 20 | blake2s().digest().hex() 21 21 | md5().digest().hex() @@ -96,7 +95,7 @@ FURB181.py:23:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 20 20 | blake2s().digest().hex() 21 21 | md5().digest().hex() 22 22 | sha1().digest().hex() @@ -117,7 +116,7 @@ FURB181.py:24:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 21 21 | md5().digest().hex() 22 22 | sha1().digest().hex() 23 23 | sha224().digest().hex() @@ -138,7 +137,7 @@ FURB181.py:25:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 22 22 | sha1().digest().hex() 23 23 | sha224().digest().hex() 24 24 | sha256().digest().hex() @@ -159,7 +158,7 @@ FURB181.py:26:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 23 23 | sha224().digest().hex() 24 24 | sha256().digest().hex() 25 25 | sha384().digest().hex() @@ -180,7 +179,7 @@ FURB181.py:27:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 24 24 | sha256().digest().hex() 25 25 | sha384().digest().hex() 26 26 | sha3_224().digest().hex() @@ -201,7 +200,7 @@ FURB181.py:28:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 25 25 | sha384().digest().hex() 26 26 | sha3_224().digest().hex() 27 27 | sha3_256().digest().hex() @@ -222,7 +221,7 @@ FURB181.py:29:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 26 26 | sha3_224().digest().hex() 27 27 | sha3_256().digest().hex() 28 28 | sha3_384().digest().hex() @@ -243,7 +242,7 @@ FURB181.py:30:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 27 27 | sha3_256().digest().hex() 28 28 | sha3_384().digest().hex() 29 29 | sha3_512().digest().hex() @@ -285,7 +284,7 @@ FURB181.py:34:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 31 31 | shake_128().digest(10).hex() 32 32 | shake_256().digest(10).hex() 33 33 | @@ -306,7 +305,7 @@ FURB181.py:36:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 33 33 | 34 34 | hashlib.sha256().digest().hex() 35 35 | @@ -327,7 +326,7 @@ FURB181.py:38:1: FURB181 [*] Use of hashlib's `.digest().hex()` | = help: Replace with `.hexdigest()` -ℹ Unsafe fix +ℹ Safe fix 35 35 | 36 36 | sha256(b"text").digest().hex() 37 37 | diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 9be76dc208..579c7e290a 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -1411,6 +1411,8 @@ impl<'a> SemanticModel<'a> { "typing_extensions" => self.seen.insert(Modules::TYPING_EXTENSIONS), "attr" | "attrs" => self.seen.insert(Modules::ATTRS), "airflow" => self.seen.insert(Modules::AIRFLOW), + "hashlib" => self.seen.insert(Modules::HASHLIB), + "crypt" => self.seen.insert(Modules::CRYPT), _ => {} } } @@ -2039,6 +2041,8 @@ bitflags! { const ATTRS = 1 << 25; const REGEX = 1 << 26; const AIRFLOW = 1 << 27; + const HASHLIB = 1 << 28; + const CRYPT = 1 << 29; } }