fix: Bugfixes, toten Code entfernt, Cache-Normalisierung & Session

Echte Bugs:
- extractor: JSON-LD-Crash bei Nicht-Dict-Items (isinstance-Guard)
- baseline/__main__: Crawl-Fehler ins Snapshot-Manifest -> erscheinen im Report
- __main__: Whitelist-/Header-/Webshell-Checks nur noch auf status==200
- crawler: Noise-Param-Regex auf Voll-Key (view=/value= nicht mehr verworfen)
- differ/__main__: unerwartetes JSON-LD via eigenem Kanal, auch auf
  unveraenderten Seiten erkannt, kein Re-Alert auf bereits genehmigte Typen

Aufgeraeumt:
- checker: check_internal_paths, find_broken_pages, canonical_is_hijacked,
  check_jsonld_types entfernt (nicht verdrahtet)
- allowed_paths.yaml-Logik und tote Scoring-Keys entfernt
- tote Imports entfernt

Neuer aktiver Schutz:
- Webshell-/Backdoor-Dateinamen-Check verdrahtet (suspicious_filename: 60).
  Regex wortgrenzen-verankert gegen Fehlalarm auf PSEMailerAntispam.js

Effizienz/Struktur:
- crawler nutzt requests.Session (keep-alive)
- Cache-Buster-Normalisierung an einer Stelle (Extraktion) -> stabile
  Snapshots, differ wieder reiner Set-Diff

Tests: 111 gruen (neu: test_checker.py + Regressionstests)
CLAUDE.md aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-06-12 03:20:32 +02:00
commit 68243a97b9
13 changed files with 323 additions and 153 deletions

View file

@ -43,7 +43,11 @@ def diff_text(old: str, new: str, context: int = 2) -> list[str]:
def diff_link_set(old_list: list[dict], new_list: list[dict]) -> dict:
"""Compare two link lists by URL."""
"""Compare two link lists by URL.
Cache-buster query params are stripped at extraction time
(extractor.strip_cache_params), so stored URLs are already stable here.
"""
old_urls = {l["url"] for l in old_list if isinstance(l, dict) and "url" in l}
new_urls = {l["url"] for l in new_list if isinstance(l, dict) and "url" in l}
return {
@ -228,6 +232,20 @@ def compare_snapshots(baseline_pages: dict, current_pages: dict, cfg: dict) -> d
if url in current_pages
]
# Unexpected JSON-LD @types — independent of page_diffs.
# Only flag types that are NOT allowed AND were not already in the baseline
# for that URL (avoids re-alerting on a pre-approved page).
allowed_jsonld = set(cfg.get("allowed_jsonld_types", []))
unexpected_jsonld: list[dict] = []
for url, page in current_pages.items():
base_types = {
j.get("type") for j in baseline_pages.get(url, {}).get("jsonld", [])
}
for j in page.get("jsonld", []):
t = j.get("type")
if t not in allowed_jsonld and t not in base_types:
unexpected_jsonld.append({"url": url, "type": t})
return {
"new_internal_urls": new_urls,
"missing_internal_urls": missing_urls,
@ -235,6 +253,7 @@ def compare_snapshots(baseline_pages: dict, current_pages: dict, cfg: dict) -> d
"removed_external_domains": sorted(old_ext - new_ext),
"page_diffs": page_diffs,
"new_pages_analysis": new_pages_analysis,
"unexpected_jsonld": unexpected_jsonld,
"new_broken_urls": new_broken,
"known_broken_urls": known_broken,
"total_pages_checked": len(common_urls),
@ -305,11 +324,6 @@ def score_diff(diff: dict, cfg: dict) -> dict:
add(sc.get("new_inline_script_suspicious", 30),
f"{u}: Verdächtiges Inline-Script [{', '.join(s.get('patterns', []))}]")
for j in pd.get("new_jsonld", []):
if j.get("unexpected"):
add(sc.get("unexpected_jsonld", 35),
f"{u}: Unerwartetes JSON-LD @type={j.get('type')}")
if pd.get("canonical_changed"):
add(sc.get("unexpected_canonical", 35),
f"{u}: Canonical geändert → {pd.get('new_canonical')}")
@ -326,6 +340,14 @@ def score_diff(diff: dict, cfg: dict) -> dict:
f"Neuer kaputte Link: {entry['url']} (HTTP {entry['status']})")
# known_broken_urls are reported but not scored — they were already in the baseline
for entry in diff.get("unexpected_jsonld", []):
add(sc.get("unexpected_jsonld", 35),
f"{entry['url']}: Unerwartetes JSON-LD @type={entry['type']}")
for entry in diff.get("suspicious_filenames", []):
add(sc.get("suspicious_filename", 60),
f"{entry['page']}: Verdächtiger Dateiname {entry['url']}")
# New pages: immediate suspicious content counts too
for npa in diff.get("new_pages_analysis", []):
u = npa["url"]
@ -337,8 +359,7 @@ def score_diff(diff: dict, cfg: dict) -> dict:
add(sc.get("meta_refresh", 40), f"{u} (neu): Meta-Refresh")
if npa.get("comment_links"):
add(sc.get("comment_links", 25), f"{u} (neu): Kommentar-Link")
if npa.get("unexpected_jsonld"):
add(sc.get("unexpected_jsonld", 35), f"{u} (neu): Unerwartetes JSON-LD")
# Unexpected JSON-LD on new pages is covered by the unexpected_jsonld channel.
yellow = thr.get("yellow", 20)
red = thr.get("red", 60)