Files
digital-drosophila/tools/repo_guard.py
T

47 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Check staged Git blobs, not working-tree copies. Does not alter the index."""
import json
import re
import subprocess
import sys
def git(*args):
return subprocess.check_output(["git", *args])
def main():
errors, total, count = [], 0, 0
for row in git("ls-files", "--stage", "-z").split(b"\0"):
if not row:
continue
meta, rawpath = row.split(b"\t", 1)
mode, oid, stage = meta.decode().split()
path = rawpath.decode("utf-8")
count += 1
if mode == "160000" or path.startswith(("datasets/", "experiments/")) or any(p in path.split("/") for p in (".venv", ".git", "__pycache__")):
errors.append({"path":path, "rule":"forbidden data/environment/submodule"})
continue
size = int(git("cat-file", "-s", oid))
total += size
if size > 25 * 1024 * 1024:
errors.append({"path":path, "rule":"blob exceeds 25 MiB policy"})
continue
if re.search(r"(^|/)(?:\.env(?:\..*)?|id_rsa|id_ed25519)$|\.(?:pem|key|p12)$", path, re.I):
errors.append({"path":path, "rule":"credential filename"})
if path.endswith(".gz"):
continue # generated hash-only inventory, validated separately
data = git("cat-file", "blob", oid).decode("utf-8", errors="replace")
rules = {
"private key": r"-----BEGIN (?:OPENSSH |RSA |EC )?PRIVATE KEY-----",
"access token": r"(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{30,}|AKIA[A-Z0-9]{16})",
"credential URL": r"https?://[^\s/]+:[^\s/@]+@",
"literal password": r"(?i)(?:password|passwd|api_key|secret_key)\s*[:=]\s*['\"][^'\"\n]{6,}['\"]",
}
for rule, pattern in rules.items():
if re.search(pattern, data):
errors.append({"path":path, "rule":rule})
print(json.dumps({"pass":not errors, "files":count, "logical_bytes":total, "issues":errors}, indent=2))
return int(bool(errors))
if __name__ == "__main__":
sys.exit(main())