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
|
|
@ -147,6 +147,49 @@ def check_external_links(
|
|||
return results
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asset hashing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def fetch_asset_hashes(
|
||||
urls: list[str],
|
||||
timeout: int = 15,
|
||||
delay: float = 0.2,
|
||||
) -> dict[str, dict]:
|
||||
"""
|
||||
Fetch binary assets and compute SHA-256 hashes via streaming GET.
|
||||
|
||||
Returns {url: {"sha256": str|None, "size": int|None, "error": str|None}}.
|
||||
"""
|
||||
import hashlib
|
||||
results: dict[str, dict] = {}
|
||||
session = requests.Session()
|
||||
session.headers["User-Agent"] = "integrity-scanner/1.0 (asset-check)"
|
||||
|
||||
try:
|
||||
for i, url in enumerate(urls):
|
||||
if i > 0 and delay > 0:
|
||||
time.sleep(delay)
|
||||
logger.debug("Hashing asset: %s", url)
|
||||
try:
|
||||
resp = session.get(url, timeout=timeout, stream=True)
|
||||
if resp.status_code == 200:
|
||||
h = hashlib.sha256()
|
||||
size = 0
|
||||
for chunk in resp.iter_content(chunk_size=65536):
|
||||
h.update(chunk)
|
||||
size += len(chunk)
|
||||
results[url] = {"sha256": h.hexdigest(), "size": size, "error": None}
|
||||
else:
|
||||
results[url] = {"sha256": None, "size": None, "error": f"HTTP {resp.status_code}"}
|
||||
except requests.exceptions.RequestException as exc:
|
||||
results[url] = {"sha256": None, "size": None, "error": str(exc)}
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def is_suspicious_url(url: str) -> bool:
|
||||
"""Flag URLs with names typical for webshells / backdoors."""
|
||||
filename = urlparse(url).path.rsplit("/", 1)[-1]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue