From 3f389acf3cde1f633c4cbc2ed9361a4e2114fe02 Mon Sep 17 00:00:00 2001 From: spark Date: Wed, 21 Oct 2026 15:43:57 +0000 Subject: [PATCH] spark: add file hash validator for SHA256 verification --- wizard-work/spark/file_hash_validator.py | 84 ++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 wizard-work/spark/file_hash_validator.py diff --git a/wizard-work/spark/file_hash_validator.py b/wizard-work/spark/file_hash_validator.py new file mode 100644 index 0000000..84d8b87 --- /dev/null +++ b/wizard-work/spark/file_hash_validator.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""File Hash Validator - verify SHA256 checksums for file integrity. + +Usage: + python file_hash_validator.py + python file_hash_validator.py --verify + +A checksum file format: " " (same as sha256sum output) +""" + +import hashlib +import sys +from pathlib import Path + + +def compute_sha256(filepath: str) -> str: + """Compute SHA256 hash of a file.""" + sha256_hash = hashlib.sha256() + with open(filepath, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha256_hash.update(chunk) + return sha256_hash.hexdigest() + + +def verify_file(filepath: str, expected_hash: str) -> bool: + """Verify a file's hash matches expected value.""" + if not Path(filepath).exists(): + print(f"ERROR: File not found: {filepath}") + return False + actual = compute_sha256(filepath) + if actual.lower() == expected_hash.lower(): + print(f"OK: {filepath}") + print(f" SHA256: {actual}") + return True + else: + print(f"FAIL: {filepath}") + print(f" Expected: {expected_hash}") + print(f" Actual: {actual}") + return False + + +def verify_checksum_file(checksum_file: str) -> int: + """Verify all files in a checksum file. Returns count of failed verifications.""" + if not Path(checksum_file).exists(): + print(f"ERROR: Checksum file not found: {checksum_file}") + return 1 + + failed = 0 + with open(checksum_file, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + parts = line.split() + if len(parts) >= 2: + expected_hash = parts[0] + filepath = parts[1] + if not verify_file(filepath, expected_hash): + failed += 1 + else: + print(f"SKIP: Malformed line: {line}") + + if failed == 0: + print(f"\nAll files verified successfully.") + else: + print(f"\n{failed} file(s) failed verification.") + return failed + + +def main(): + if len(sys.argv) < 3: + print(__doc__) + sys.exit(1) + + if sys.argv[1] == "--verify": + exit_code = verify_checksum_file(sys.argv[2]) + else: + exit_code = 0 if verify_file(sys.argv[1], sys.argv[2]) else 1 + + sys.exit(exit_code) + + +if __name__ == "__main__": + main() \ No newline at end of file