sync: WIN10-I9-64G 2026-10-19
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""release_check.py -- checksum, verify and scan a release directory.
|
||||
|
||||
python release_check.py sums <release_dir> # (re)write 00_SHA256SUMS
|
||||
python release_check.py verify <release_dir> # check every listed file
|
||||
python release_check.py scan <release_dir> [--env ~/skills_env.md] [--mac AA:BB:..]...
|
||||
[--src-dirs 07_ps_app_src,08_imx6_app_src] [--allow KEY]...
|
||||
|
||||
scan reports (never prints a secret value, only the file and the key name):
|
||||
* values of *PASS* / *PASSWORD* / *TOKEN* / *SECRET* / *KEY* keys in --env found
|
||||
in any text file, and "user:password@" URLs; --allow KEY moves that key's hits
|
||||
to "acknowledged" (e.g. a vendor factory-default account that ships in the
|
||||
firmware and is also the brand name) -- still listed, no failure;
|
||||
* tool state files (*.upstate.json) inside --src-dirs;
|
||||
* private keys (-----BEGIN ... PRIVATE KEY-----);
|
||||
* the given board MACs (--mac, e.g. a test board's) and any other MAC literal;
|
||||
* development-machine absolute paths (X:\\..., /home/<user>, /Users/<user>);
|
||||
* ELF / object / archive files inside --src-dirs (build output in a source tree);
|
||||
* Windows reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) that break copies.
|
||||
Exit status 0 = scan finished (read the report), 2 = a secret was found.
|
||||
"""
|
||||
import hashlib, os, re, sys
|
||||
|
||||
SUMS = "00_SHA256SUMS"
|
||||
RESERVED = re.compile(r"^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$", re.I)
|
||||
MAC = re.compile(r"\b[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5}\b")
|
||||
WINPATH = re.compile(r"\b[A-Za-z]:[\\/](?:[^\s\"'<>|]+)")
|
||||
NIXHOME = re.compile(r"/(?:home|Users)/[A-Za-z0-9_.-]+")
|
||||
URLCRED = re.compile(r"[a-z][a-z0-9+.-]*://[^/\s:@]+:[^/\s@]+@", re.I)
|
||||
PRIVKEY = re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----")
|
||||
|
||||
|
||||
def files(root):
|
||||
for d, _, fs in os.walk(root):
|
||||
for f in fs:
|
||||
yield os.path.join(d, f)
|
||||
|
||||
|
||||
def relp(root, p):
|
||||
return "./" + os.path.relpath(p, root).replace(os.sep, "/")
|
||||
|
||||
|
||||
def sha(p):
|
||||
h = hashlib.sha256()
|
||||
with open(p, "rb") as f:
|
||||
for b in iter(lambda: f.read(1 << 20), b""):
|
||||
h.update(b)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def is_text(b):
|
||||
return b"\0" not in b[:8192]
|
||||
|
||||
|
||||
def cmd_sums(root):
|
||||
lines = [f"{sha(p)} {relp(root, p)}" for p in sorted(files(root))
|
||||
if os.path.basename(p) != SUMS]
|
||||
open(os.path.join(root, SUMS), "w", newline="\n").write("\n".join(lines) + "\n")
|
||||
print(f"{len(lines)} files -> {SUMS}")
|
||||
|
||||
|
||||
def cmd_verify(root):
|
||||
bad = n = 0
|
||||
listed = set()
|
||||
for line in open(os.path.join(root, SUMS)):
|
||||
h, p = line.rstrip("\n").split(" ", 1)
|
||||
listed.add(p); n += 1
|
||||
fp = os.path.join(root, p[2:])
|
||||
if not os.path.exists(fp) or sha(fp) != h:
|
||||
bad += 1; print("BAD", p)
|
||||
extra = [relp(root, p) for p in files(root)
|
||||
if os.path.basename(p) != SUMS and relp(root, p) not in listed]
|
||||
for p in extra:
|
||||
print("NOT LISTED", p)
|
||||
print(f"{n} listed, {bad} bad, {len(extra)} not listed")
|
||||
sys.exit(1 if bad or extra else 0)
|
||||
|
||||
|
||||
def env_secrets(path):
|
||||
out = {}
|
||||
if not path or not os.path.exists(path):
|
||||
return out
|
||||
for line in open(path, encoding="utf-8", errors="replace"):
|
||||
m = re.match(r"\s*([A-Z0-9_]*(?:PASS|PASSWORD|TOKEN|SECRET|KEY)[A-Z0-9_]*)\s*=\s*(\S+)", line)
|
||||
if m and len(m.group(2)) >= 4:
|
||||
out[m.group(1)] = m.group(2)
|
||||
return out
|
||||
|
||||
|
||||
def cmd_scan(root, args):
|
||||
env = os.path.expanduser(args[args.index("--env") + 1]) if "--env" in args else None
|
||||
macs = [args[i + 1].lower() for i, a in enumerate(args) if a == "--mac"]
|
||||
srcd = args[args.index("--src-dirs") + 1].split(",") if "--src-dirs" in args else []
|
||||
allow = {args[i + 1] for i, a in enumerate(args) if a == "--allow"}
|
||||
secrets = env_secrets(env)
|
||||
found_secret = False
|
||||
rep = {k: [] for k in ("secret", "acknowledged", "url_cred", "privkey", "board_mac", "mac", "dev_path",
|
||||
"src_binary", "reserved")}
|
||||
for p in files(root):
|
||||
r = relp(root, p)
|
||||
if RESERVED.match(os.path.basename(p)):
|
||||
rep["reserved"].append(r)
|
||||
b = open(p, "rb").read()
|
||||
if any(r.startswith("./" + s.strip("/") + "/") for s in srcd):
|
||||
if p.endswith(".upstate.json"):
|
||||
rep["src_binary"].append(r + " (tool state file, remove)")
|
||||
elif b[:4] == b"\x7fELF" or p.endswith((".o", ".a", ".so", ".ko")):
|
||||
rep["src_binary"].append(r)
|
||||
if not is_text(b):
|
||||
continue
|
||||
t = b.decode("utf-8", "replace")
|
||||
for k, v in secrets.items():
|
||||
if v in t:
|
||||
if k in allow:
|
||||
rep["acknowledged"].append(f"{r} (value of {k})")
|
||||
else:
|
||||
rep["secret"].append(f"{r} (value of {k})"); found_secret = True
|
||||
if URLCRED.search(t):
|
||||
rep["url_cred"].append(r); found_secret = True
|
||||
if PRIVKEY.search(t):
|
||||
rep["privkey"].append(r)
|
||||
ms = {m.lower() for m in MAC.findall(t)}
|
||||
for m in ms:
|
||||
(rep["board_mac"] if m in macs else rep["mac"]).append(f"{r} {m}")
|
||||
dp = sorted(set(WINPATH.findall(t)) | set(NIXHOME.findall(t)))
|
||||
if dp:
|
||||
rep["dev_path"].append(f"{r} ({len(dp)}: {dp[0][:60]}{' ...' if len(dp) > 1 else ''})")
|
||||
titles = {"secret": "SECRET VALUES (must be removed)",
|
||||
"acknowledged": "acknowledged values (--allow; say so in the README)",
|
||||
"url_cred": "user:password@ URLs",
|
||||
"privkey": "private keys (check: product key or leaked?)",
|
||||
"board_mac": "given board MACs (must not ship)", "mac": "other MAC literals (review)",
|
||||
"dev_path": "development-machine paths (review / document)",
|
||||
"src_binary": "binaries inside source dirs", "reserved": "Windows reserved names"}
|
||||
for k, title in titles.items():
|
||||
print(f"== {title}: {len(rep[k])}")
|
||||
for x in rep[k][:40]:
|
||||
print(" ", x)
|
||||
if len(rep[k]) > 40:
|
||||
print(f" ... {len(rep[k]) - 40} more")
|
||||
sys.exit(2 if found_secret else 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit(__doc__)
|
||||
c, root = sys.argv[1], sys.argv[2]
|
||||
{"sums": lambda: cmd_sums(root), "verify": lambda: cmd_verify(root),
|
||||
"scan": lambda: cmd_scan(root, sys.argv[3:])}[c]()
|
||||
Reference in New Issue
Block a user