feat: Binärdateien auf Änderungen prüfen (check-assets / approve-assets)
Neue Befehle: - `python -m scanner check-assets` — SHA-256-Hashes aller JS/CSS/Bild-Assets aus dem letzten Snapshot fetchen und mit Baseline vergleichen - `python -m scanner approve-assets` — aktuelle Hashes als neue Baseline setzen Scoring: geändertes JS 40 Pkt, CSS 30 Pkt, Bild/PDF 20 Pkt. Asset-Baseline liegt in data/baseline/asset_hashes.json. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ecd01abf98
commit
d803f79efe
7 changed files with 506 additions and 1 deletions
|
|
@ -467,6 +467,78 @@ def score_cloak_diff(cloak_diff: dict, cfg: dict) -> dict:
|
|||
return {"score": score, "level": level, "reasons": reasons, "exit_code": exit_code}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asset hash comparison
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def compare_asset_hashes(baseline: dict, current: dict) -> dict:
|
||||
"""
|
||||
Compare asset hash dicts {url: {sha256, size, type, error}}.
|
||||
Returns {changed, new, missing, fetch_errors, total_checked}.
|
||||
"""
|
||||
b_hashed = {url for url, r in baseline.items() if r.get("sha256")}
|
||||
c_hashed = {url for url, r in current.items() if r.get("sha256")}
|
||||
|
||||
changed = [
|
||||
{
|
||||
"url": url,
|
||||
"type": current[url].get("type", "unknown"),
|
||||
"old_sha256": baseline[url]["sha256"][:16] + "…",
|
||||
"new_sha256": current[url]["sha256"][:16] + "…",
|
||||
"size_diff": (current[url].get("size") or 0) - (baseline[url].get("size") or 0),
|
||||
}
|
||||
for url in sorted(b_hashed & c_hashed)
|
||||
if baseline[url]["sha256"] != current[url]["sha256"]
|
||||
]
|
||||
|
||||
return {
|
||||
"changed": changed,
|
||||
"new": sorted(c_hashed - b_hashed),
|
||||
"missing": sorted(b_hashed - set(current)),
|
||||
"fetch_errors": [
|
||||
{"url": url, "error": r["error"]}
|
||||
for url, r in sorted(current.items())
|
||||
if r.get("error")
|
||||
],
|
||||
"total_checked": len(c_hashed),
|
||||
}
|
||||
|
||||
|
||||
def score_asset_diff(asset_diff: dict, cfg: dict) -> dict:
|
||||
"""Score asset hash changes by file type."""
|
||||
sc = cfg.get("scoring", {})
|
||||
thr = cfg.get("thresholds", {"yellow": 20, "red": 60})
|
||||
score = 0
|
||||
reasons: list[str] = []
|
||||
|
||||
def add(pts: int, msg: str) -> None:
|
||||
nonlocal score
|
||||
score += pts
|
||||
reasons.append(f"{msg} (+{pts})")
|
||||
|
||||
for entry in asset_diff.get("changed", []):
|
||||
asset_type = entry.get("type", "unknown")
|
||||
if asset_type == "script":
|
||||
pts = sc.get("changed_script", 40)
|
||||
elif asset_type == "link_rel":
|
||||
pts = sc.get("changed_stylesheet", 30)
|
||||
else:
|
||||
pts = sc.get("changed_asset", 20)
|
||||
size_info = f", {entry['size_diff']:+d} Bytes" if entry.get("size_diff") else ""
|
||||
add(pts, f"Geänderte Datei [{asset_type}]{size_info}: {entry['url']}")
|
||||
|
||||
yellow = thr.get("yellow", 20)
|
||||
red = thr.get("red", 60)
|
||||
if score >= red:
|
||||
level, exit_code = "red", 2
|
||||
elif score >= yellow:
|
||||
level, exit_code = "yellow", 1
|
||||
else:
|
||||
level, exit_code = "green", 0
|
||||
|
||||
return {"score": score, "level": level, "reasons": reasons, "exit_code": exit_code}
|
||||
|
||||
|
||||
def _fingerprint(obj: dict | list | str) -> str:
|
||||
"""Stable hash for deduplicating diff entries."""
|
||||
import json as _json
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue