use serde_json::json;
use sha2::{Sha256, Digest};
use std::collections::{HashMap, HashSet};
fn main() {
println!("🛠️ Hash Collision Fix Demonstration");
println!("=====================================\n");
let test_resources = vec![
("EC2 Instance 1", json!({
"resource_type": "ec2_instance",
"instance_id": "i-1234567890abcdef0",
"region": "us-east-1",
"account_id": "123456789012"
})),
("EC2 Instance 2", json!({
"resource_type": "ec2_instance",
"instance_id": "i-1234567890abcdef1", "region": "us-east-1",
"account_id": "123456789012"
})),
("S3 Bucket 1", json!({
"resource_type": "s3_bucket",
"bucket_name": "my-test-bucket-001",
"account_id": "123456789012"
})),
("S3 Bucket 2", json!({
"resource_type": "s3_bucket",
"bucket_name": "my-test-bucket-002", "account_id": "123456789012"
})),
("Unknown Resource 1", json!({
"resource_type": "unknown_service",
"name": "resource-001",
"region": "us-east-1",
"account_id": "123456789012"
})),
("Unknown Resource 2", json!({
"resource_type": "unknown_service",
"name": "resource-002", "region": "us-east-1",
"account_id": "123456789012"
})),
];
println!("🔍 Testing canonical ID generation improvements:\n");
let mut canonical_ids = Vec::new();
let mut sha256_hashes = Vec::new();
for (name, metadata) in &test_resources {
let canonical_id = generate_improved_canonical_id(metadata.as_object().unwrap());
let sha256_hash = generate_sha256_hash(&canonical_id);
canonical_ids.push(canonical_id.clone());
sha256_hashes.push(sha256_hash.clone());
println!("📊 {}", name);
println!(" Canonical ID: {}", canonical_id);
println!(" SHA256 Hash: {}", sha256_hash);
println!();
}
println!("🚫 Collision Analysis:");
println!("======================");
let unique_canonical: HashSet<_> = canonical_ids.iter().collect();
if unique_canonical.len() == canonical_ids.len() {
println!("✅ Canonical IDs: No collisions! ({} unique IDs)", unique_canonical.len());
} else {
println!("❌ Canonical IDs: {} collisions detected!", canonical_ids.len() - unique_canonical.len());
}
let unique_hashes: HashSet<_> = sha256_hashes.iter().collect();
if unique_hashes.len() == sha256_hashes.len() {
println!("✅ SHA256 Hashes: No collisions! ({} unique hashes)", unique_hashes.len());
} else {
println!("❌ SHA256 Hashes: {} collisions detected!", sha256_hashes.len() - unique_hashes.len());
}
println!("\n🔄 Old vs New Hash Comparison:");
println!("===============================");
for (name, _) in &test_resources[0..2] { let canonical_id = &canonical_ids[if name.contains("1") { 0 } else { 1 }];
let old_hash = old_style_hash(canonical_id);
let new_hash = &sha256_hashes[if name.contains("1") { 0 } else { 1 }];
println!("📊 {}", name);
println!(" Old Hash (weak): {:x}", old_hash);
println!(" New Hash (strong): {}", new_hash);
println!();
}
println!("🎉 Improvements Summary:");
println!("========================");
println!("✅ Enhanced canonical ID generation with multiple field fallbacks");
println!("✅ Replaced DefaultHasher with SHA-256 for collision resistance");
println!("✅ Better handling of edge cases and unknown resource types");
println!("✅ Deterministic but collision-resistant document IDs");
}
fn generate_improved_canonical_id(metadata: &serde_json::Map<String, serde_json::Value>) -> String {
let resource_type = metadata.get("resource_type")
.and_then(|r| r.as_str())
.unwrap_or("unknown")
.to_lowercase();
if let Some(account_id) = metadata.get("account_id").and_then(|a| a.as_str()) {
match resource_type.as_str() {
"ec2_instance" => {
if let (Some(region), Some(instance_id)) = (
metadata.get("region").and_then(|r| r.as_str()),
metadata.get("instance_id").and_then(|i| i.as_str())
) {
return format!("aws:ec2:{}:{}:{}", account_id, region, instance_id);
}
},
"s3_bucket" => {
if let Some(bucket_name) = metadata.get("bucket_name").and_then(|b| b.as_str()) {
return format!("aws:s3:{}:{}", account_id, bucket_name);
}
},
_ => {}
}
}
let mut id_components = Vec::new();
id_components.push(format!("type:{}", resource_type));
let identifying_fields = ["instance_id", "bucket_name", "name", "id"];
for field in &identifying_fields {
if let Some(value) = metadata.get(*field).and_then(|v| v.as_str()) {
id_components.push(format!("{}:{}", field, value));
break;
}
}
if let Some(region) = metadata.get("region").and_then(|r| r.as_str()) {
id_components.push(format!("region:{}", region));
}
if let Some(account) = metadata.get("account_id").and_then(|a| a.as_str()) {
id_components.push(format!("account:{}", account));
}
format!("fallback:{}", id_components.join(":"))
}
fn generate_sha256_hash(input: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let hash = hasher.finalize();
hex::encode(&hash[0..8]) }
fn old_style_hash(input: &str) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
input.hash(&mut hasher);
hasher.finish()
}