Ein echter Durchlauf auf bredelar.info deckte zwei Bugs auf, die mit gemockten Tests unsichtbar waren: 1. Hang: attempt_timeout wurde nur als requests-Inaktivitäts-Timeout übergeben. Ein Free-Modell, das die Verbindung offen hält oder Tokens langsam tröpfeln lässt, umging ihn → der ganze Scan hing unbegrenzt (real auf einem Bild >5 min). Fix: _post_with_deadline führt den Call in einem Worker-Thread mit future.result(timeout=...) aus — hartes Wanduhr-Limit, bei Überschreitung Eskalation statt Hang. 2. Verlust bei Abbruch: Das Ledger wurde nur ganz am Ende gespeichert, ein Hang/Crash verwarf alle berechneten Verdikte. Fix: Zwischensicherung nach der Textphase + im finally — Verdikte überleben einen Abbruch. Zusätzlich attempt_timeout 30 → 20 s (flottere Eskalation). Im erneuten Lauf griff der Deadline 4× sauber, der Scan lief vollständig durch (Gelb: ein off_topic_commercial-Fund auf einer Gewerbe-Seite). Tests: 239 grün (+2: Deadline kappt Hang <2s, Textverdikte überleben Bildphasen-Crash). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
220 lines
8.5 KiB
YAML
220 lines
8.5 KiB
YAML
# ============================================================
|
|
# Integrity Scanner — Konfigurationsvorlage
|
|
# ============================================================
|
|
# Verwendung:
|
|
# 1. Dieses Verzeichnis kopieren:
|
|
# cp -r meine-seite.de neue-domain.de
|
|
# 2. In neue-domain.de/config.yaml alle Stellen mit
|
|
# "meine-seite.de" durch die echte Domain ersetzen.
|
|
# 3. Verzeichnis in .gitignore eintragen (data/reports/logs).
|
|
# 4. Erste Einrichtung:
|
|
# python -m scanner --config neue-domain.de/config.yaml init
|
|
# ============================================================
|
|
|
|
# --- Ziel-Website ---
|
|
target: "https://meine-seite.de"
|
|
|
|
# User-Agent, mit dem der Scanner sich bei der Website meldet.
|
|
# Bitte nicht auf einen Browser-Agent ändern — der Scanner soll erkennbar sein.
|
|
user_agent: "integrity-scanner/1.0 (+security-monitoring)"
|
|
|
|
# Timeout in Sekunden pro HTTP-Anfrage
|
|
request_timeout: 20
|
|
|
|
# --- Verzeichnisse (müssen absolut angegeben werden) ---
|
|
# Pfad zum Projektverzeichnis anpassen!
|
|
data_dir: "/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/meine-seite.de/data"
|
|
reports_dir: "/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/meine-seite.de/reports"
|
|
logs_dir: "/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/meine-seite.de/logs"
|
|
config_dir: "/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/meine-seite.de/config"
|
|
|
|
# --- Crawl-Einstellungen ---
|
|
crawl:
|
|
# Maximale Anzahl Seiten pro Crawl (Schutz vor endlosen Websites)
|
|
max_pages: 200
|
|
|
|
# Wartezeit zwischen zwei Seitenanfragen in Sekunden (Server schonen)
|
|
delay_seconds: 1.0
|
|
|
|
respect_robots_txt: false
|
|
|
|
# Sitemap lesen, um nicht verlinkte Seiten (Orphan Pages) zu entdecken.
|
|
# Der Scanner liest sitemap.xml (bzw. robots.txt → Sitemap:) und fügt alle
|
|
# dort aufgeführten URLs in die Crawl-Queue ein.
|
|
sitemap: true
|
|
|
|
# Pfade, die vom Scan ausgeschlossen werden sollen.
|
|
# Nützlich für Bereiche mit gewollt häufig wechselnden Inhalten,
|
|
# z. B. RSS-Feeds, News-Archive, Nutzerprofile.
|
|
# Beispiel: ["/rss/", "/feed/", "/news/archiv/", "/user/"]
|
|
exclude_paths: []
|
|
|
|
# Dateiendungen, die nicht gecrawlt werden (Binärdateien)
|
|
skip_extensions:
|
|
- ".jpg"
|
|
- ".jpeg"
|
|
- ".png"
|
|
- ".gif"
|
|
- ".webp"
|
|
- ".svg"
|
|
- ".ico"
|
|
- ".pdf"
|
|
- ".zip"
|
|
- ".woff"
|
|
- ".woff2"
|
|
- ".ttf"
|
|
- ".otf"
|
|
- ".eot"
|
|
- ".mp4"
|
|
- ".mp3"
|
|
- ".webm"
|
|
- ".avi"
|
|
- ".mov"
|
|
|
|
# --- Bewertungsgewichte ---
|
|
# Jedes Ereignis erhöht den Score. Ab 20 = gelb, ab 60 = rot.
|
|
# Die Werte können hier für diese Website angepasst werden.
|
|
scoring:
|
|
new_external_domain: 50 # Neuer Verweis auf fremde Domain
|
|
new_internal_url: 30 # Neue interne Seite aufgetaucht
|
|
missing_internal_url: 20 # Bekannte Seite verschwunden
|
|
hidden_content: 40 # Unsichtbarer Text/Link (display:none)
|
|
unexpected_canonical: 35 # Canonical-Tag geändert
|
|
meta_refresh: 40 # Automatische Weiterleitung eingebaut
|
|
unexpected_jsonld: 35 # Unbekannter JSON-LD-Typ
|
|
large_text_addition: 20 # Großer neuer Textblock (>200 Zeichen)
|
|
new_inline_script_suspicious: 30 # Verdächtiges inline JavaScript
|
|
comment_links: 25 # Link in HTML-Kommentar versteckt
|
|
new_broken_link: 20 # Neuer kaputt interner Link
|
|
suspicious_filename: 60 # Webshell-typischer Dateiname verlinkt
|
|
cloaking_extra_link: 60 # Nur für Google-Bot sichtbarer Link
|
|
cloaking_extra_text: 40 # Nur für Google-Bot sichtbarer Text
|
|
cloaking_vary_ua: 25 # Server sendet verschiedene Inhalte je UA
|
|
changed_script: 40 # JavaScript-Datei verändert
|
|
changed_stylesheet: 30 # CSS-Datei verändert
|
|
changed_asset: 20 # Bild/PDF/sonstige Datei verändert
|
|
|
|
thresholds:
|
|
yellow: 20 # Ab diesem Score: Warnung (gelb) — Bitte prüfen
|
|
red: 60 # Ab diesem Score: Alarm (rot) — Sofort prüfen
|
|
large_text_block_chars: 200
|
|
|
|
# --- Wöchentliche Zusatzprüfungen ---
|
|
# Laufen automatisch beim täglichen `scan` mit — kein separater Aufruf nötig.
|
|
periodic_checks:
|
|
enabled: true
|
|
interval_days: 7
|
|
cloak_check: true # Prüft ob Google andere Inhalte sieht als Besucher
|
|
ext_links: true # Prüft ob externe Links noch erreichbar sind
|
|
assets: true # Prüft ob CSS/JS/Bilder sich verändert haben
|
|
|
|
# --- E-Mail-Benachrichtigung ---
|
|
# Passwort NIEMALS hier eintragen — nur als Umgebungsvariable:
|
|
# export SCANNER_SMTP_PASSWORD="geheimesPasswort"
|
|
alerting:
|
|
min_level: "yellow" # "yellow" = bei Gelb und Rot mailen; "red" = nur bei Rot
|
|
email:
|
|
enabled: false # Auf true setzen wenn SMTP konfiguriert
|
|
smtp_host: "mail.meine-seite.de"
|
|
smtp_port: 587
|
|
smtp_tls: true
|
|
smtp_user: "scanner@meine-seite.de"
|
|
smtp_password_env: "SCANNER_SMTP_PASSWORD"
|
|
from: "scanner@meine-seite.de"
|
|
to:
|
|
- "admin@meine-seite.de"
|
|
webhook:
|
|
enabled: false
|
|
url: "" # z.B. Slack-Webhook oder Telegram-Bot-URL
|
|
|
|
# --- KI-gestützte Inhaltsanalyse (optional, OpenRouter) ---
|
|
# Prüft Text und Bilder semantisch auf problematische Inhalte (Pornografie,
|
|
# Propaganda, Spam, themenfremde Werbung, strafbare/widersprüchliche Aussagen).
|
|
# Kostengate: nur NEUE/GEÄNDERTE Inhalte lösen einen API-Call aus (Hash-Cache).
|
|
# KI-Funde sind auf GELB gedeckelt. API-Key NUR als Umgebungsvariable setzen:
|
|
# export OPENROUTER_API_KEY="sk-or-..."
|
|
ai_analysis:
|
|
enabled: false
|
|
api_key_env: "OPENROUTER_API_KEY"
|
|
# Themenbeschreibung der Website — die KI bewertet die Passung dazu, nicht
|
|
# einzelne Schlüsselwörter. Bitte ausfüllen, wenn enabled: true.
|
|
site_context: ""
|
|
ai_confidence_min: 0.7
|
|
# KI-Funde sind auf Gelb gedeckelt — AUSSER diese schwerwiegenden Kategorien lösen
|
|
# bei hoher Schwere + hoher Konfidenz ROT aus. Leere Liste [] = immer max. Gelb.
|
|
red_categories: ["pornography", "defamation_illegal"]
|
|
red_min_severity: "high"
|
|
red_min_confidence: 0.9
|
|
attempt_timeout: 20 # HARTES Wanduhr-Limit je Modell-Versuch (Sek.) → Eskalation
|
|
max_retries: 1 # Wiederholungen der ganzen Modell-Kette bei Komplettausfall
|
|
retry_backoff_seconds: 2.0
|
|
# Verhalten, wenn Inhalte trotz Retry NICHT geprüft werden konnten (KI nicht erreichbar):
|
|
# "warn" = sichtbarer Hinweis (Level bleibt unberührt)
|
|
# "yellow" = zusätzlich Gesamt-Level auf mindestens Gelb anheben (für hohe Sicherheit)
|
|
unchecked_level: "warn"
|
|
# Modell-Kette mit zweifacher Eskalation: Stufe 1+2 free, Stufe 3 günstig bezahlt.
|
|
# Schlägt ein Modell fehl oder antwortet zu langsam, wird zur nächsten Stufe eskaliert.
|
|
text:
|
|
enabled: true
|
|
models:
|
|
- "qwen/qwen3-next-80b-a3b-instruct:free" # Stufe 1: free
|
|
- "meta-llama/llama-3.3-70b-instruct:free" # Stufe 2: free
|
|
- "google/gemini-2.5-flash-lite" # Stufe 3: günstig bezahlt, zuverlässig
|
|
min_chars: 200
|
|
max_pages_per_scan: 20
|
|
image:
|
|
enabled: true
|
|
models:
|
|
- "google/gemma-4-31b-it:free" # Stufe 1: free, multimodal + OCR
|
|
- "nvidia/nemotron-nano-12b-v2-vl:free" # Stufe 2: free, Vision
|
|
- "google/gemini-2.5-flash-lite" # Stufe 3: günstig bezahlt, Vision
|
|
max_images_per_scan: 15
|
|
# Audio/Video: vorbereitet, default aus (Medien auf statischen Seiten selten).
|
|
audio:
|
|
enabled: false
|
|
models: ["google/gemini-2.5-flash"]
|
|
video:
|
|
enabled: false
|
|
models: ["google/gemini-2.5-flash"]
|
|
|
|
# --- Normalisierung ---
|
|
# Elemente, die vor dem Vergleich aus dem HTML entfernt werden
|
|
# (z. B. dynamische Datums-/Zeitanzeigen die sich täglich ändern).
|
|
normalization:
|
|
strip_html_comments: true
|
|
collapse_whitespace: true
|
|
ignore_selectors:
|
|
# - "#last-modified" # Element mit dieser ID ignorieren
|
|
# - ".timestamp" # Alle Elemente mit dieser CSS-Klasse ignorieren
|
|
ignore_patterns:
|
|
# - '\d{1,2}\.\d{1,2}\.\d{4}' # Datumsangaben im Text ignorieren
|
|
# - 'Stand:\s+\S+'
|
|
|
|
# --- Erlaubte JSON-LD-Typen ---
|
|
# Alles, was hier nicht steht und auf der Website auftaucht, löst Alarm aus.
|
|
allowed_jsonld_types:
|
|
- "Organization"
|
|
- "WebSite"
|
|
- "WebPage"
|
|
- "Article"
|
|
- "BreadcrumbList"
|
|
- "ItemList"
|
|
- "LocalBusiness"
|
|
- "Place"
|
|
- "Person"
|
|
- "Event"
|
|
- "FAQPage"
|
|
- "Question"
|
|
- "Answer"
|
|
- "ImageObject"
|
|
- "SiteLinksSearchBox"
|
|
- "ContactPage"
|
|
- "AboutPage"
|
|
|
|
# --- Erwartete Security-Header ---
|
|
# Fehlen diese Header, erscheint ein Hinweis im Report (kein Score-Einfluss).
|
|
security_headers:
|
|
- "Content-Security-Policy"
|
|
- "Strict-Transport-Security"
|
|
- "X-Content-Type-Options"
|
|
- "Referrer-Policy"
|