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 2aa94c573118a..b8c7adf7cc833 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 @@ -6,10 +6,12 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; /// ## What it does -/// Checks for the use of `.digest().hex()`. +/// Checks for the use of `.digest().hex()` on a hashlib hash, like `sha512`. /// /// ## Why is this bad? -/// Use `.hexdigest()` to get a hex digest from a hash. +/// When generating a hex digest from a hash, it's preferable to use the +/// `.hexdigest()` method, rather than calling `.digest()` and then `.hex()`, +/// as the former is more concise and readable. /// /// ## Example /// ```python @@ -25,8 +27,13 @@ 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` — Secure hashes and message digests](https://docs.python.org/3/library/hashlib.html) +/// - [Python documentation: `hashlib`](https://docs.python.org/3/library/hashlib.html) #[violation] pub struct HashlibDigestHex; @@ -35,11 +42,11 @@ impl Violation for HashlibDigestHex { #[derive_message_formats] fn message(&self) -> String { - format!("Use of hashlib `.digest().hex()`") + format!("Use of hashlib's `.digest().hex()`") } fn fix_title(&self) -> Option { - Some("Replace hashlib `.digest().hex()` with `.hexdigest()`".to_string()) + Some("Replace with `.hexdigest()`".to_string()) } } @@ -72,14 +79,11 @@ pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { return; } - let Expr::Call(ExprCall { - func, range: r3, .. - }) = value.as_ref() - else { + let Expr::Call(ExprCall { func, .. }) = value.as_ref() else { return; }; - if !checker.semantic().resolve_call_path(func).is_some_and( + if checker.semantic().resolve_call_path(func).is_some_and( |call_path: smallvec::SmallVec<[&str; 8]>| { matches!( call_path.as_slice(), @@ -104,15 +108,13 @@ pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { ) }, ) { - return; - } - let mut diagnostic = Diagnostic::new(HashlibDigestHex, call.range()); - if arguments.is_empty() { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - ".hexdigest".to_string(), - TextRange::new(r3.end(), call.func.end()), - ))); + let mut diagnostic = Diagnostic::new(HashlibDigestHex, call.range()); + if arguments.is_empty() { + diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( + ".hexdigest".to_string(), + TextRange::new(value.end(), call.func.end()), + ))); + } + checker.diagnostics.push(diagnostic); } - - checker.diagnostics.push(diagnostic); } 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 bd91543e7fc78..cca7645c90adf 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,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/refurb/mod.rs --- -FURB181.py:19:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:19:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 17 | # these will match 18 | @@ -10,9 +10,9 @@ FURB181.py:19:1: FURB181 [*] Use of hashlib `.digest().hex()` 20 | blake2s().digest().hex() 21 | md5().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 16 16 | 17 17 | # these will match 18 18 | @@ -22,7 +22,7 @@ FURB181.py:19:1: FURB181 [*] Use of hashlib `.digest().hex()` 21 21 | md5().digest().hex() 22 22 | sha1().digest().hex() -FURB181.py:20:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:20:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 19 | blake2b().digest().hex() 20 | blake2s().digest().hex() @@ -30,9 +30,9 @@ FURB181.py:20:1: FURB181 [*] Use of hashlib `.digest().hex()` 21 | md5().digest().hex() 22 | sha1().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 17 17 | # these will match 18 18 | 19 19 | blake2b().digest().hex() @@ -42,7 +42,7 @@ FURB181.py:20:1: FURB181 [*] Use of hashlib `.digest().hex()` 22 22 | sha1().digest().hex() 23 23 | sha224().digest().hex() -FURB181.py:21:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:21:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 19 | blake2b().digest().hex() 20 | blake2s().digest().hex() @@ -51,9 +51,9 @@ FURB181.py:21:1: FURB181 [*] Use of hashlib `.digest().hex()` 22 | sha1().digest().hex() 23 | sha224().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 18 18 | 19 19 | blake2b().digest().hex() 20 20 | blake2s().digest().hex() @@ -63,7 +63,7 @@ FURB181.py:21:1: FURB181 [*] Use of hashlib `.digest().hex()` 23 23 | sha224().digest().hex() 24 24 | sha256().digest().hex() -FURB181.py:22:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:22:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 20 | blake2s().digest().hex() 21 | md5().digest().hex() @@ -72,9 +72,9 @@ FURB181.py:22:1: FURB181 [*] Use of hashlib `.digest().hex()` 23 | sha224().digest().hex() 24 | sha256().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 19 19 | blake2b().digest().hex() 20 20 | blake2s().digest().hex() 21 21 | md5().digest().hex() @@ -84,7 +84,7 @@ FURB181.py:22:1: FURB181 [*] Use of hashlib `.digest().hex()` 24 24 | sha256().digest().hex() 25 25 | sha384().digest().hex() -FURB181.py:23:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:23:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 21 | md5().digest().hex() 22 | sha1().digest().hex() @@ -93,9 +93,9 @@ FURB181.py:23:1: FURB181 [*] Use of hashlib `.digest().hex()` 24 | sha256().digest().hex() 25 | sha384().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 20 20 | blake2s().digest().hex() 21 21 | md5().digest().hex() 22 22 | sha1().digest().hex() @@ -105,7 +105,7 @@ FURB181.py:23:1: FURB181 [*] Use of hashlib `.digest().hex()` 25 25 | sha384().digest().hex() 26 26 | sha3_224().digest().hex() -FURB181.py:24:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:24:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 22 | sha1().digest().hex() 23 | sha224().digest().hex() @@ -114,9 +114,9 @@ FURB181.py:24:1: FURB181 [*] Use of hashlib `.digest().hex()` 25 | sha384().digest().hex() 26 | sha3_224().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 21 21 | md5().digest().hex() 22 22 | sha1().digest().hex() 23 23 | sha224().digest().hex() @@ -126,7 +126,7 @@ FURB181.py:24:1: FURB181 [*] Use of hashlib `.digest().hex()` 26 26 | sha3_224().digest().hex() 27 27 | sha3_256().digest().hex() -FURB181.py:25:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:25:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 23 | sha224().digest().hex() 24 | sha256().digest().hex() @@ -135,9 +135,9 @@ FURB181.py:25:1: FURB181 [*] Use of hashlib `.digest().hex()` 26 | sha3_224().digest().hex() 27 | sha3_256().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 22 22 | sha1().digest().hex() 23 23 | sha224().digest().hex() 24 24 | sha256().digest().hex() @@ -147,7 +147,7 @@ FURB181.py:25:1: FURB181 [*] Use of hashlib `.digest().hex()` 27 27 | sha3_256().digest().hex() 28 28 | sha3_384().digest().hex() -FURB181.py:26:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:26:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 24 | sha256().digest().hex() 25 | sha384().digest().hex() @@ -156,9 +156,9 @@ FURB181.py:26:1: FURB181 [*] Use of hashlib `.digest().hex()` 27 | sha3_256().digest().hex() 28 | sha3_384().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 23 23 | sha224().digest().hex() 24 24 | sha256().digest().hex() 25 25 | sha384().digest().hex() @@ -168,7 +168,7 @@ FURB181.py:26:1: FURB181 [*] Use of hashlib `.digest().hex()` 28 28 | sha3_384().digest().hex() 29 29 | sha3_512().digest().hex() -FURB181.py:27:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:27:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 25 | sha384().digest().hex() 26 | sha3_224().digest().hex() @@ -177,9 +177,9 @@ FURB181.py:27:1: FURB181 [*] Use of hashlib `.digest().hex()` 28 | sha3_384().digest().hex() 29 | sha3_512().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 24 24 | sha256().digest().hex() 25 25 | sha384().digest().hex() 26 26 | sha3_224().digest().hex() @@ -189,7 +189,7 @@ FURB181.py:27:1: FURB181 [*] Use of hashlib `.digest().hex()` 29 29 | sha3_512().digest().hex() 30 30 | sha512().digest().hex() -FURB181.py:28:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:28:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 26 | sha3_224().digest().hex() 27 | sha3_256().digest().hex() @@ -198,9 +198,9 @@ FURB181.py:28:1: FURB181 [*] Use of hashlib `.digest().hex()` 29 | sha3_512().digest().hex() 30 | sha512().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 25 25 | sha384().digest().hex() 26 26 | sha3_224().digest().hex() 27 27 | sha3_256().digest().hex() @@ -210,7 +210,7 @@ FURB181.py:28:1: FURB181 [*] Use of hashlib `.digest().hex()` 30 30 | sha512().digest().hex() 31 31 | shake_128().digest(10).hex() -FURB181.py:29:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:29:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 27 | sha3_256().digest().hex() 28 | sha3_384().digest().hex() @@ -219,9 +219,9 @@ FURB181.py:29:1: FURB181 [*] Use of hashlib `.digest().hex()` 30 | sha512().digest().hex() 31 | shake_128().digest(10).hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 26 26 | sha3_224().digest().hex() 27 27 | sha3_256().digest().hex() 28 28 | sha3_384().digest().hex() @@ -231,7 +231,7 @@ FURB181.py:29:1: FURB181 [*] Use of hashlib `.digest().hex()` 31 31 | shake_128().digest(10).hex() 32 32 | shake_256().digest(10).hex() -FURB181.py:30:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:30:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 28 | sha3_384().digest().hex() 29 | sha3_512().digest().hex() @@ -240,9 +240,9 @@ FURB181.py:30:1: FURB181 [*] Use of hashlib `.digest().hex()` 31 | shake_128().digest(10).hex() 32 | shake_256().digest(10).hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 27 27 | sha3_256().digest().hex() 28 28 | sha3_384().digest().hex() 29 29 | sha3_512().digest().hex() @@ -252,7 +252,7 @@ FURB181.py:30:1: FURB181 [*] Use of hashlib `.digest().hex()` 32 32 | shake_256().digest(10).hex() 33 33 | -FURB181.py:31:1: FURB181 Use of hashlib `.digest().hex()` +FURB181.py:31:1: FURB181 Use of hashlib's `.digest().hex()` | 29 | sha3_512().digest().hex() 30 | sha512().digest().hex() @@ -260,9 +260,9 @@ FURB181.py:31:1: FURB181 Use of hashlib `.digest().hex()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181 32 | shake_256().digest(10).hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -FURB181.py:32:1: FURB181 Use of hashlib `.digest().hex()` +FURB181.py:32:1: FURB181 Use of hashlib's `.digest().hex()` | 30 | sha512().digest().hex() 31 | shake_128().digest(10).hex() @@ -271,9 +271,9 @@ FURB181.py:32:1: FURB181 Use of hashlib `.digest().hex()` 33 | 34 | hashlib.sha256().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -FURB181.py:34:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:34:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 32 | shake_256().digest(10).hex() 33 | @@ -282,9 +282,9 @@ FURB181.py:34:1: FURB181 [*] Use of hashlib `.digest().hex()` 35 | 36 | sha256(b"text").digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 31 31 | shake_128().digest(10).hex() 32 32 | shake_256().digest(10).hex() 33 33 | @@ -294,7 +294,7 @@ FURB181.py:34:1: FURB181 [*] Use of hashlib `.digest().hex()` 36 36 | sha256(b"text").digest().hex() 37 37 | -FURB181.py:36:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:36:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 34 | hashlib.sha256().digest().hex() 35 | @@ -303,9 +303,9 @@ FURB181.py:36:1: FURB181 [*] Use of hashlib `.digest().hex()` 37 | 38 | hash_algo().digest().hex() | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 33 33 | 34 34 | hashlib.sha256().digest().hex() 35 35 | @@ -315,7 +315,7 @@ FURB181.py:36:1: FURB181 [*] Use of hashlib `.digest().hex()` 38 38 | hash_algo().digest().hex() 39 39 | -FURB181.py:38:1: FURB181 [*] Use of hashlib `.digest().hex()` +FURB181.py:38:1: FURB181 [*] Use of hashlib's `.digest().hex()` | 36 | sha256(b"text").digest().hex() 37 | @@ -324,9 +324,9 @@ FURB181.py:38:1: FURB181 [*] Use of hashlib `.digest().hex()` 39 | 40 | # not yet supported | - = help: Replace hashlib `.digest().hex()` with `.hexdigest()` + = help: Replace with `.hexdigest()` -ℹ Safe fix +ℹ Unsafe fix 35 35 | 36 36 | sha256(b"text").digest().hex() 37 37 |