Filesystem integrity and provenance tracker using cryptographic hashes. Creates, verifies, and compares file manifests for compliance, security monitoring, and deployment verification.
Detecting unauthorized filesystem changes is critical for:
- Security monitoring: Was the web root modified?
- Compliance audits: Can you prove files haven't changed?
- Deployment verification: Does production match the release?
- Backup validation: Is the backup complete and uncorrupted?
hash-audit creates SHA-256 manifests and verifies them against current filesystem state.
Create Manifest Verify Later
───────────────── ───────────────────
/var/www/ /var/www/
├── index.html ──┐ ├── index.html ✓ hash matches
├── app.js ──┼──► manifest ├── app.js ✗ MODIFIED
├── config.json ──┘ (JSON) ├── config.json ✓ hash matches
├── backdoor.php ✗ EXTRA (not in manifest)
└── (styles.css) ✗ MISSING
cd hash-audit
perl -Ilib bin/hash-audit --help# Create manifest for directory
hash-audit /var/www > www.manifest.json
hash-audit -o www.json /var/www
# Include permissions and mtimes
hash-audit --perms --mtime -o full.json /app
# Ignore patterns
hash-audit -i '.git' -i '*.log' -i 'node_modules' /project# Verify current state against baseline
hash-audit --verify www.json /var/www
# Quick check in scripts
if hash-audit -q --verify manifest.json /app; then
echo "Integrity OK"
else
echo "ALERT: Files modified!"
fi# See what changed between two points in time
hash-audit --compare v1.json --compare v2.json
# Track deployment changes
hash-audit -o before.json /app
deploy_new_version
hash-audit -o after.json /app
hash-audit --compare before.json --compare after.json{
"version": 1,
"generated": 1704067200,
"root": "/var/www",
"algorithm": "sha256",
"files": {
"index.html": {
"type": "file",
"size": 1234,
"hash": "abc123..."
}
}
}abc123... index.html
def456... app.js
use HashAudit;
my $auditor = HashAudit->new(
include_perms => 1,
include_mtime => 1,
ignore_patterns => ['.git', '*.tmp'],
);
# Create manifest
my $result = $auditor->create_manifest('/var/www');
die $result->{error} unless $result->{success};
# Save for later
$auditor->save_manifest($result->{manifest}, 'baseline.json');
# Later: verify
my $loaded = $auditor->load_manifest('baseline.json');
my $verify = $auditor->verify_manifest($loaded->{manifest}, '/var/www');
if ($verify->{valid}) {
print "All files verified\n";
} else {
for my $v (@{$verify->{violations}}) {
print "$v->{type}: $v->{path}\n";
}
}| Option | Description |
|---|---|
-c, --create |
Create manifest (default mode) |
-v, --verify=FILE |
Verify against manifest |
-C, --compare=FILE |
Compare manifests (use twice) |
-o, --output=FILE |
Write to file |
-f, --format=FMT |
Output format: json, text |
-p, --perms |
Include permissions |
-m, --mtime |
Include modification times |
-H, --hidden |
Include hidden files |
-L, --follow-symlinks |
Follow symbolic links |
-i, --ignore=PAT |
Ignore pattern (repeatable) |
--verbose |
Show progress |
-q, --quiet |
Minimal output |
| Code | Meaning |
|---|---|
| 0 | Success / verification passed / manifests identical |
| 1 | Verification failed / manifests differ |
| 2 | Error |
| Type | Meaning |
|---|---|
MISSING |
File in manifest not found on disk |
EXTRA |
File on disk not in manifest |
MODIFIED |
File hash doesn't match |
SIZE |
File size changed |
TYPE |
Entry type changed (file → directory) |
ERROR |
Could not read file |
Generate test directories for validation:
# Create baseline directory with 50 files
bin/generate-test-data --files 50 --output baseline
# Create baseline + modified version
bin/generate-test-data --files 30 -m 5 -a 3 -D 2
# Create manifest and verify
hash-audit -o baseline.json baseline
hash-audit --verify baseline.json baseline-modifiedprove -l t/# After incident: check for unauthorized changes
hash-audit --verify baseline.json /var/www | grep -E "MODIFIED|EXTRA"
# Cron job for continuous monitoring
0 * * * * hash-audit -q --verify /etc/baseline.json /etc || alert "Config changed!"# Verify deployment matches release
hash-audit --verify release-v2.3.json /app
echo "Deployment verified: exit code $?"# Create auditable baseline
hash-audit --perms --mtime -o audit-$(date +%Y%m%d).json /data
# Prove files unchanged since baseline
hash-audit --verify audit-20240101.json /data# Before backup
hash-audit -o pre-backup.json /important
# After restore
hash-audit --verify pre-backup.json /restored- SHA-256: Industry standard, collision-resistant
- JSON manifests: Human-readable, easily parsed
- Streaming hashes: Files hashed without loading into memory
- No external deps: Uses only core Perl modules
- Deterministic output: Same input always produces same manifest
- sha256sum - Hash individual files
- AIDE - Advanced intrusion detection
- Tripwire - File integrity checker
Ed Bates — TECHBLIP LLC
Licensed under the Apache License, Version 2.0.