#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-3.0-or-later """Reject common credential material before it reaches Git history.""" from __future__ import annotations import argparse import os import re from pathlib import Path PATTERNS = { "private key": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"), "GitHub-style token": re.compile(r"\bgh[pousr]_[A-Za-z0-9]{30,}\b"), "OpenAI-style key": re.compile(r"\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b"), "AWS access key": re.compile(r"\bAKIA[0-9A-Z]{16}\b"), "credential in URL": re.compile(r"https?://[^\s/:@]+:[^\s/@]+@"), } SKIP_PARTS = {".git", "work", "build", ".vs", ".idea", ".vscode"} def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--root", type=Path, required=True) args = parser.parse_args() root = args.root.resolve() errors: list[str] = [] scanned = 0 for directory, subdirectories, filenames in os.walk(root): subdirectories[:] = sorted( name for name in subdirectories if name not in SKIP_PARTS and not name.startswith("build-") ) for filename in sorted(filenames): path = Path(directory, filename) relative = path.relative_to(root) if path.stat().st_size > 2_000_000: continue try: text = path.read_text(encoding="utf-8") except UnicodeDecodeError: continue scanned += 1 for label, pattern in PATTERNS.items(): if pattern.search(text): errors.append(f"{relative}: possible {label}") if errors: for error in errors: print(f"secret scan failed: {error}") return 1 print(f"secret scan passed across {scanned} text files") return 0 if __name__ == "__main__": raise SystemExit(main())