This document explains the hash verification callback system implemented across all compilers to enforce integrity checks at each stage of the compilation pipeline.
The hash verification system provides cryptographic proof that files are not tampered with at any stage of compilation. Client code can subscribe to hash events to:
- Log all hash computations for audit trails
- Enforce custom verification policies (strict vs. permissive)
- Detect tampering in real-time
- Track file integrity across the pipeline
- Prevent MITM attacks on downloaded sources
┌─────────────────────────────────────────────────────────────┐
│ 1. Configuration File Loading │
│ ├─ Hash computed: config_file │
│ └─ Event fired: HashComputed │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Source Files Loading (Local & Remote) │
│ ├─ Hash computed: input_file / downloaded_source │
│ ├─ Event fired: HashComputed │
│ └─ Optional verification against expected hash │
│ ├─ Match → Event: HashVerified │
│ └─ Mismatch → Event: HashMismatch (can abort) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Compilation (via hostlist-compiler) │
│ └─ (No hash events - external tool) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Output File Writing │
│ ├─ Hash computed: output_file │
│ └─ Event fired: HashComputed │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Rules File Copying (if requested) │
│ ├─ Hash computed: copied_rules_file │
│ ├─ Event fired: HashComputed │
│ └─ Can verify against output hash │
│ ├─ Match → Event: HashVerified │
│ └─ Mismatch → Event: HashMismatch │
└─────────────────────────────────────────────────────────────┘
Fired whenever a hash is computed for any file.
Data:
itemIdentifier: File path or identifieritemType: Type (e.g., "config_file", "output_file", "input_file")hash: SHA-384 hash (96 hex characters)sizeBytes: File sizeisVerification: Whether this is for verification purposes
Fired when a hash successfully matches the expected value.
Data:
itemIdentifier: File path or identifieritemType: Type of fileexpectedHash: Expected SHA-384 hashactualHash: Computed SHA-384 hash (should match expected)sizeBytes: File sizecomputationDurationMs: Time taken to compute hash
Fired when a hash does NOT match the expected value.
Data:
itemIdentifier: File path or identifieritemType: Type of fileexpectedHash: Expected SHA-384 hashactualHash: Computed SHA-384 hash (different from expected)sizeBytes: File sizeabort: Whether to abort compilation (default: true)abortReason: Reason for abortingallowContinuation: Handler can set this to continue despite mismatch
Handler Control:
- Set
allowContinuation = trueto continue despite mismatch - Set
abort = falseto prevent compilation failure
Event Handler Trait:
use rules_compiler::events::{
CompilationEventHandler,
HashComputedEventArgs,
HashVerifiedEventArgs,
HashMismatchEventArgs,
};
struct MyHandler;
impl CompilationEventHandler for MyHandler {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
println!("Hash for {}: {}", args.item_type, &args.hash[..16]);
}
fn on_hash_verified(&self, args: &HashVerifiedEventArgs) {
println!("Hash verified for {}", args.item_identifier);
}
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
eprintln!("Hash mismatch for {}", args.item_identifier);
// Optionally allow continuation:
// args.allow_continuation = true;
// args.abort = false;
}
}Usage:
use rules_compiler::{compile_rules_with_events, EventDispatcher};
let mut dispatcher = EventDispatcher::new();
dispatcher.add_handler(Box::new(MyHandler));
let result = compile_rules_with_events("config.yaml", &options, &dispatcher)?;Callback Interface:
import type { HashVerificationCallbacks } from './types.ts';
const callbacks: HashVerificationCallbacks = {
onHashComputed: (event) => {
console.log(`Hash for ${event.itemType}: ${event.hash.slice(0, 16)}...`);
},
onHashVerified: (event) => {
console.log(`Hash verified for ${event.itemIdentifier}`);
},
onHashMismatch: (event) => {
console.error(`Hash mismatch for ${event.itemIdentifier}`);
// Optionally allow continuation:
// event.allowContinuation = true;
},
};Usage:
import { runCompiler } from './compiler.ts';
const result = await runCompiler({
configPath: 'config.yaml',
hashCallbacks: callbacks,
});Event Handler:
using RulesCompiler.Abstractions;
public class MyHashHandler : CompilationEventHandlerBase
{
public override Task OnHashComputedAsync(
HashComputedEventArgs args,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Hash for {args.ItemType}: {args.Hash.Substring(0, 16)}...");
return Task.CompletedTask;
}
public override Task OnHashVerifiedAsync(
HashVerifiedEventArgs args,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Hash verified for {args.ItemIdentifier}");
return Task.CompletedTask;
}
public override Task OnHashMismatchAsync(
HashMismatchEventArgs args,
CancellationToken cancellationToken = default)
{
Console.Error.WriteLine($"Hash mismatch for {args.ItemIdentifier}");
// Optionally allow continuation:
// args.AllowContinuation = true;
// args.Abort = false;
return Task.CompletedTask;
}
}Usage:
// To be implemented in compilation pipelineTo be implemented (similar pattern to Rust/TypeScript)
impl CompilationEventHandler for AuditLogger {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
self.log(format!(
"AUDIT: {} hash={} size={} timestamp={}",
args.item_type,
args.hash,
args.size_bytes,
chrono::Utc::now()
));
}
}impl CompilationEventHandler for StrictVerifier {
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
// Never allow continuation on mismatch
args.abort = true;
args.allow_continuation = false;
self.alert_security_team(args);
}
}impl CompilationEventHandler for DevModeHandler {
fn on_hash_mismatch(&self, args: &mut HashMismatchEventArgs) {
// Log but don't fail in development
eprintln!("WARN: Hash mismatch but allowing continuation in dev mode");
args.allow_continuation = true;
args.abort = false;
}
}impl CompilationEventHandler for DatabaseTracker {
fn on_hash_computed(&self, args: &HashComputedEventArgs) {
self.db.insert_hash_record(
args.item_identifier.clone(),
args.hash.clone(),
chrono::Utc::now(),
);
}
}- SHA-384 Algorithm: All hashes use SHA-384 (96 hex characters) for cryptographic strength
- At-Rest Verification: Local files are hashed to detect tampering
- In-Flight Verification: Downloaded sources can be verified against expected hashes
- MITM Prevention: Hash verification on downloads prevents man-in-the-middle attacks
- Immutable Audit Trail: Hash events create an immutable log of all file states
Example tests are included in examples/hash_audit_handler.rs demonstrating:
- Strict verification (fails on mismatch)
- Permissive verification (logs but continues)
- Custom policy implementation
See examples/hash_audit_handler.rs for a complete implementation of:
- Logging all hash events
- Customizable strictness (strict vs. permissive)
- Comprehensive test coverage
Potential additions:
- Hash database persistence across compilations
- Historical hash tracking and drift detection
- Integration with external validation services
- Support for multiple hash algorithms (SHA-256, BLAKE3)
- Signature verification (GPG, minisign)