Replaced Match::finding_id’s SHA1-based hashing with a fast xxh3_64 digest that keeps IDs deterministic while eliminating a hot-path SHA1 dependency

This commit is contained in:
Mick Grove 2025-09-24 12:22:56 -07:00
commit ae5c8eecbe
4 changed files with 11 additions and 34 deletions

View file

@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
## [v1.54.0]
- Added first-class Gitea support, including CLI commands, environment-based authentication, documentation, and integration with scans and repository enumeration.
- Populate the finding path from git blob metadata so history-derived secrets display their file location instead of an empty path
- Replaced Match::finding_ids SHA1-based hashing with a fast xxh3_64 digest that keeps IDs deterministic while eliminating a hot-path SHA1 dependency
## [v1.53.0]
- Added first-class Bitbucket support, including CLI commands, authentication helpers, documentation, and integration testing.

View file

@ -1,7 +1,6 @@
use std::{
borrow::Cow,
hash::{Hash, Hasher},
io::Write,
str,
sync::{Arc, Mutex},
};
@ -18,7 +17,6 @@ use schemars::{
JsonSchema,
};
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use smallvec::SmallVec;
use tracing::debug;
use xxhash_rust::xxh3::xxh3_64;
@ -863,34 +861,12 @@ impl Match {
}
pub fn finding_id(&self) -> String {
let mut h = Sha1::new();
write!(&mut h, "{}\0", self.rule.finding_sha1_fingerprint())
.expect("should be able to write to memory");
serde_json::to_writer(&mut h, &self.groups)
let mut buffer = Vec::with_capacity(128);
buffer.extend_from_slice(self.rule.finding_sha1_fingerprint().as_bytes());
buffer.push(0);
serde_json::to_writer(&mut buffer, &self.groups)
.expect("should be able to serialize groups as JSON");
let hash: sha2::digest::generic_array::GenericArray<
u8,
sha2::digest::typenum::UInt<
sha2::digest::typenum::UInt<
sha2::digest::typenum::UInt<
sha2::digest::typenum::UInt<
sha2::digest::typenum::UInt<
sha2::digest::typenum::UTerm,
sha2::digest::consts::B1,
>,
sha2::digest::consts::B0,
>,
sha2::digest::consts::B1,
>,
sha2::digest::consts::B0,
>,
sha2::digest::consts::B0,
>,
> = h.finalize();
// Take the first 8 bytes of the hash
let mut num = u64::from_be_bytes([
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
]);
let mut num = xxh3_64(&buffer);
// Ensure the number is positive and within i64 range
num &= 0x7FFF_FFFF_FFFF_FFFF; // Clear the sign bit to make it positive
// Convert to string

View file

@ -17,7 +17,8 @@ use schemars::{
JsonSchema,
};
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
// use sha1::{Digest, Sha1};
use xxhash_rust::xxh3::xxh3_64;
/// Returns false as the default value.
fn default_false() -> bool {
@ -341,9 +342,8 @@ impl RuleSyntax {
/// Computes a content-based fingerprint of the rule's pattern.
pub fn finding_sha1_fingerprint(&self) -> String {
let mut hasher = Sha1::new();
hasher.update(self.pattern.as_bytes());
format!("{:x}", hasher.finalize())
let hash = xxh3_64(self.pattern.as_bytes());
format!("{:x}", hash)
}
/// Serializes the rule syntax to JSON.

View file

@ -102,7 +102,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
// ───────────── Case 1: running == latest ─────────────
if release.version == running_v {
let plain = format!("Kingfisher {running_v} is up to date");
info!("{}", plain.as_str());
info!("{}", plain);
return Some(plain);
}