2026-06-12 03:28:36 +02:00
|
|
|
|
# Bedienungsanleitung — Website Integrity Scanner
|
|
|
|
|
|
|
2026-06-12 22:17:34 +02:00
|
|
|
|
Überwachungswerkzeug für statische Websites gegen SEO-Spam und unbefugte Manipulationen.
|
|
|
|
|
|
Der Scanner läuft lokal im Verantwortungsbereich des Betreibers.
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
## Für Einsteiger – in 3 Schritten
|
|
|
|
|
|
|
|
|
|
|
|
Sie brauchen kein IT-Wissen. Das Programm zeigt eine **Ampel** und erklärt alles in
|
|
|
|
|
|
einfachen Worten.
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
**Schritt 1 — Ersten Scan durchführen** (Scanner legt das Zielverzeichnis automatisch an):
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
2026-06-12 16:29:43 +02:00
|
|
|
|
python -m scanner https://ihre-website.de init
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
**Schritt 2 — Erststand speichern** (einmalig, wenn die Website in Ordnung ist):
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
2026-06-12 16:29:43 +02:00
|
|
|
|
python -m scanner https://ihre-website.de approve --all --note "Erststand geprüft"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Schritt 3 — täglich prüfen** (am besten automatisch per Cron, siehe Abschnitt 10):
|
|
|
|
|
|
```
|
|
|
|
|
|
python -m scanner https://ihre-website.de scan
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
**Bei 🟡 oder 🔴 nachschauen:**
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
2026-06-12 16:29:43 +02:00
|
|
|
|
python -m scanner https://ihre-website.de report
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Was die Ampel bedeutet:
|
|
|
|
|
|
|
|
|
|
|
|
| Ampel | Bedeutung | Was tun? |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 🟢 | Alles in Ordnung | Nichts. |
|
|
|
|
|
|
| 🟡 | Bitte nachschauen | Bericht lesen; wenn die Änderung gewollt war, mit `approve --all` bestätigen. |
|
|
|
|
|
|
| 🔴 | Achtung | Website ansehen und die Person/Firma informieren, die Ihre Website betreut. |
|
|
|
|
|
|
|
|
|
|
|
|
> **Wichtig:** Die selteneren Prüfungen (Tarnung, externe Links, Dateien) laufen
|
|
|
|
|
|
> **automatisch einmal pro Woche** beim täglichen `scan` mit. Sie müssen dafür **nichts**
|
|
|
|
|
|
> extra einrichten — ein einziger täglicher Aufruf genügt für alles.
|
|
|
|
|
|
>
|
|
|
|
|
|
> Beim ersten `approve --all` wird zusätzlich die **Datei-Überwachung** automatisch
|
|
|
|
|
|
> eingerichtet (Bilder, Skripte, Stylesheets). Auch dafür ist kein weiterer Schritt nötig.
|
|
|
|
|
|
|
|
|
|
|
|
Alles Weitere in dieser Anleitung richtet sich an **Fortgeschrittene** und an
|
|
|
|
|
|
**Ihren Dienstleister**.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
## Inhaltsverzeichnis
|
|
|
|
|
|
|
|
|
|
|
|
1. [Grundprinzip](#1-grundprinzip)
|
|
|
|
|
|
2. [Voraussetzungen und Installation](#2-voraussetzungen-und-installation)
|
|
|
|
|
|
3. [Erstinbetriebnahme](#3-erstinbetriebnahme)
|
|
|
|
|
|
4. [Täglicher Betrieb](#4-täglicher-betrieb)
|
|
|
|
|
|
5. [Befehle im Überblick](#5-befehle-im-überblick)
|
|
|
|
|
|
6. [Ergebnis-Level und Exit-Codes](#6-ergebnis-level-und-exit-codes)
|
|
|
|
|
|
7. [Berichte lesen](#7-berichte-lesen)
|
|
|
|
|
|
8. [Änderungen freigeben (approve)](#8-änderungen-freigeben-approve)
|
|
|
|
|
|
9. [Vorgehen bei einem Alarm](#9-vorgehen-bei-einem-alarm)
|
|
|
|
|
|
10. [Automatisierung per Cron-Job](#10-automatisierung-per-cron-job)
|
|
|
|
|
|
11. [E-Mail-Benachrichtigung einrichten](#11-e-mail-benachrichtigung-einrichten)
|
|
|
|
|
|
12. [Konfigurationsreferenz](#12-konfigurationsreferenz)
|
|
|
|
|
|
13. [Externe Domains verwalten](#13-externe-domains-verwalten)
|
|
|
|
|
|
14. [Rausch-Unterdrückung konfigurieren](#14-rausch-unterdrückung-konfigurieren)
|
|
|
|
|
|
15. [Was der Scanner erkennt](#15-was-der-scanner-erkennt)
|
|
|
|
|
|
16. [Verzeichnisstruktur](#16-verzeichnisstruktur)
|
2026-06-12 09:32:30 +02:00
|
|
|
|
17. [Cloaking-Erkennung](#17-cloaking-erkennung)
|
|
|
|
|
|
18. [Externe Links auf Erreichbarkeit prüfen](#18-externe-links-auf-erreichbarkeit-prüfen)
|
|
|
|
|
|
19. [Binärdateien und Assets prüfen](#19-binärdateien-und-assets-prüfen)
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
20. [KI-gestützte Inhaltsanalyse](#20-ki-gestützte-inhaltsanalyse)
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Grundprinzip
|
|
|
|
|
|
|
|
|
|
|
|
Der Scanner arbeitet nach dem **Baseline-Diff-Prinzip**:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Initiale Sichtprüfung → Baseline festlegen
|
|
|
|
|
|
↓
|
|
|
|
|
|
Täglicher Scan
|
|
|
|
|
|
↓
|
|
|
|
|
|
Aktueller Zustand ──vergleiche── genehmigte Baseline
|
|
|
|
|
|
↓
|
|
|
|
|
|
Abweichungen → Score → GREEN / YELLOW / RED
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- Die **Baseline** ist ein einmal manuell geprüfter und freigegebener Referenzzustand.
|
|
|
|
|
|
Sie wird **niemals automatisch** aktualisiert.
|
|
|
|
|
|
- Jede Abweichung erhält Punkte. Der Gesamt-Score entscheidet über das Alarm-Level.
|
|
|
|
|
|
- Legitime Änderungen (z.B. neuer Artikel) müssen explizit per `approve` freigegeben werden.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Voraussetzungen und Installation
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Python 3.10 oder neuer erforderlich
|
|
|
|
|
|
python3 --version
|
|
|
|
|
|
|
|
|
|
|
|
# Virtuelle Umgebung anlegen und aktivieren
|
|
|
|
|
|
cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten
|
|
|
|
|
|
python3 -m venv venv
|
|
|
|
|
|
source venv/bin/activate
|
|
|
|
|
|
|
|
|
|
|
|
# Abhängigkeiten installieren
|
|
|
|
|
|
pip install -r requirements.txt
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 22:34:25 +02:00
|
|
|
|
Alle Befehle müssen aus dem Projektverzeichnis ausgeführt werden. Alternativ steht das
|
|
|
|
|
|
Wrapper-Script `scanner.sh` zur Verfügung, das automatisch ins Projektverzeichnis wechselt:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./scanner.sh https://ihre-website.de scan
|
|
|
|
|
|
# oder nach Ablage in ~/bin/:
|
|
|
|
|
|
scanner https://ihre-website.de scan
|
|
|
|
|
|
```
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Erstinbetriebnahme
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
Die URL-Syntax legt das Zielverzeichnis beim ersten Aufruf **automatisch** an —
|
|
|
|
|
|
kein manuelles Anlegen von Verzeichnissen oder Konfigurieren von Pfaden nötig.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
### Schritt 1: Ersten Crawl durchführen
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-12 16:29:43 +02:00
|
|
|
|
python -m scanner https://ihre-website.de init
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
Der Scanner legt beim ersten Aufruf automatisch `ihre-website.de/` mit einer
|
|
|
|
|
|
angepassten `config.yaml` an und beginnt sofort mit dem Crawl.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
Die Ausgabe zeigt:
|
|
|
|
|
|
- alle gefundenen internen URLs
|
|
|
|
|
|
- alle externen Domains (mit Hinweis, ob in der Whitelist)
|
|
|
|
|
|
- potenzielle Auffälligkeiten (Hidden Content, Meta-Refresh, verdächtige Scripts)
|
|
|
|
|
|
- Crawl-Fehler (Seiten, die nicht erreichbar waren)
|
|
|
|
|
|
|
2026-06-12 16:29:43 +02:00
|
|
|
|
Außerdem erscheint ein Hinweis, welche Zeilen in `.gitignore` noch einzutragen sind.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
### Schritt 2: Ausgabe manuell prüfen
|
|
|
|
|
|
|
|
|
|
|
|
Wichtig vor dem nächsten Schritt:
|
2026-06-12 16:29:43 +02:00
|
|
|
|
- Alle externen Domains prüfen. Legitime Domains in `ihre-website.de/config/allowed_external.yaml`
|
|
|
|
|
|
eintragen (siehe [Abschnitt 13](#13-externe-domains-verwalten)).
|
2026-06-12 03:28:36 +02:00
|
|
|
|
- Auffälligkeiten im Browser nachschauen.
|
|
|
|
|
|
|
|
|
|
|
|
### Schritt 3: Baseline freigeben
|
|
|
|
|
|
|
|
|
|
|
|
Nur wenn alles sauber ist:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-12 16:29:43 +02:00
|
|
|
|
python -m scanner https://ihre-website.de approve --all --note "Initiale Baseline, geprüft am $(date +%Y-%m-%d)"
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Ab jetzt ist der tägliche Betrieb möglich.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Täglicher Betrieb
|
|
|
|
|
|
|
|
|
|
|
|
### Manueller Scan
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-12 22:34:25 +02:00
|
|
|
|
python -m scanner https://ihre-website.de scan
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 22:34:25 +02:00
|
|
|
|
Ergebnis erscheint direkt im Terminal. Der vollständige Report liegt in `ihre-website.de/reports/`.
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
### Automatischer Scan (Cron)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
crontab -e
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 15:12:25 +02:00
|
|
|
|
Je eine Zeile pro überwachter Website einfügen (mit leichtem Zeitversatz):
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```cron
|
2026-06-12 15:12:25 +02:00
|
|
|
|
# bredelar.info — täglich 03:00 Uhr
|
|
|
|
|
|
0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config bredelar.info/config.yaml scan >> bredelar.info/logs/cron.log 2>&1
|
|
|
|
|
|
|
|
|
|
|
|
# jamulix.de — täglich 03:15 Uhr
|
|
|
|
|
|
15 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config jamulix.de/config.yaml scan >> jamulix.de/logs/cron.log 2>&1
|
2026-06-12 22:17:34 +02:00
|
|
|
|
|
|
|
|
|
|
# www.bergbauspuren-bredelar.de — täglich 03:30 Uhr
|
|
|
|
|
|
30 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config www.bergbauspuren-bredelar.de/config.yaml scan >> www.bergbauspuren-bredelar.de/logs/cron.log 2>&1
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 15:12:25 +02:00
|
|
|
|
Danach prüfen: `crontab -l`
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
Der Exit-Code (0/1/2) ist für Cron-basierte Alarmierung nutzbar (z.B. mit `cronitor` oder
|
|
|
|
|
|
systemd-Timern mit `OnFailure=`).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. Befehle im Überblick
|
|
|
|
|
|
|
|
|
|
|
|
Alle Befehle werden mit `python -m scanner <befehl>` aufgerufen.
|
|
|
|
|
|
Globale Optionen: `--config path/to/config.yaml`, `--verbose` (`-v`).
|
|
|
|
|
|
|
|
|
|
|
|
| Befehl | Zweck |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `init` | Erstcrawl, Ausgabe aller Funde, noch kein Baseline-Update |
|
|
|
|
|
|
| `crawl` | Crawlt die Website, speichert Snapshot (kein Vergleich) |
|
|
|
|
|
|
| `check` | Vergleicht letzten Snapshot mit Baseline, erstellt Report |
|
|
|
|
|
|
| `scan` | `crawl` + `check` in einem Schritt (für den Cron-Betrieb) |
|
|
|
|
|
|
| `approve --all` | Alle neuen Snapshot-URLs in die Baseline übernehmen |
|
|
|
|
|
|
| `approve --url URL` | Einzelne URL freigeben |
|
|
|
|
|
|
| `approve --rebuild` | Gesamte Baseline ersetzen (nach großem Redesign) |
|
|
|
|
|
|
| `report` | Letzten Report im Terminal anzeigen |
|
2026-06-12 22:28:52 +02:00
|
|
|
|
| `report --diff-only` | Nur Textdiffs geänderter Seiten — farbig, kompakt |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| `report --format json` | Letzten Report als JSON anzeigen |
|
|
|
|
|
|
| `status` | Baseline-Datum, letzter Scan, aktueller Risk-Level |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| `cloak-check` | Doppel-Crawl: Browser-UA vs. Googlebot (Cloaking-Erkennung) |
|
|
|
|
|
|
| `check-ext-links` | Externe Links auf Erreichbarkeit prüfen (4xx/5xx) |
|
|
|
|
|
|
| `check-assets` | Binärdateien (JS/CSS/Bilder) auf Inhaltsänderung prüfen |
|
|
|
|
|
|
| `approve-assets` | Aktuelle Asset-Hashes als neue Baseline setzen |
|
2026-06-12 22:17:34 +02:00
|
|
|
|
| `test-alert` | Test-E-Mail senden ohne echten Scan — prüft SMTP-Konfiguration |
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
| `ai-dismiss --all` | KI-Funde als geprüft/akzeptiert quittieren (Fehlalarm) |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
### Beispiele
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Einzelne Seite freigeben (z.B. nach Impressums-Update)
|
|
|
|
|
|
python -m scanner approve --url https://bredelar.info/impressum/ --note "Impressum aktualisiert Juni 2026"
|
|
|
|
|
|
|
|
|
|
|
|
# Alle Änderungen auf einmal freigeben (nur nach vollständiger Prüfung!)
|
|
|
|
|
|
python -m scanner approve --all --note "Redesign vom 2026-06-12 freigegeben"
|
|
|
|
|
|
|
|
|
|
|
|
# Baseline nach komplettem Umbau neu aufbauen
|
|
|
|
|
|
python -m scanner approve --rebuild --note "Komplettes Redesign v2, alle Seiten neu geprüft"
|
|
|
|
|
|
|
|
|
|
|
|
# Bestimmten Snapshot freigeben (statt den neuesten)
|
|
|
|
|
|
python -m scanner approve --all --snapshot data/snapshots/20260612_030000
|
|
|
|
|
|
|
|
|
|
|
|
# Aktuellen Stand prüfen
|
|
|
|
|
|
python -m scanner status
|
|
|
|
|
|
|
|
|
|
|
|
# Letzten Report anzeigen
|
|
|
|
|
|
python -m scanner report
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Ergebnis-Level und Exit-Codes
|
|
|
|
|
|
|
|
|
|
|
|
| Level | Score | Exit-Code | Bedeutung |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| **GREEN** | 0–19 | `0` | Keine Auffälligkeiten |
|
|
|
|
|
|
| **YELLOW** | 20–59 | `1` | Warnung — manuelle Prüfung empfohlen |
|
|
|
|
|
|
| **RED** | ≥ 60 | `2` | Alarm — sofortige Prüfung notwendig |
|
|
|
|
|
|
|
|
|
|
|
|
Die Schwellenwerte sind in `config.yaml` unter `thresholds` anpassbar.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Berichte lesen
|
|
|
|
|
|
|
|
|
|
|
|
Reports liegen in `reports/YYYYMMDD_HHMMSS/` als `report.md` und `report.json`.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Neuesten Report im Terminal lesen
|
|
|
|
|
|
python -m scanner report
|
|
|
|
|
|
|
2026-06-12 22:28:52 +02:00
|
|
|
|
# Nur Diffs geänderter Seiten anzeigen (farbig, kompakt)
|
|
|
|
|
|
python -m scanner report --diff-only
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
# Direkt die Datei öffnen
|
|
|
|
|
|
ls reports/
|
|
|
|
|
|
cat reports/20260612_030000/report.md
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 22:28:52 +02:00
|
|
|
|
`--diff-only` zeigt ausschließlich was sich verändert hat: Textdiffs farbig (`+` grün,
|
|
|
|
|
|
`-` rot, `@@` cyan), neue/fehlende URLs, Link-Änderungen, Hidden Content, Meta-Refresh.
|
|
|
|
|
|
Farben werden automatisch deaktiviert wenn die Ausgabe in eine Pipe oder Datei umgeleitet wird.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
### Aufbau des Reports
|
|
|
|
|
|
|
|
|
|
|
|
| Abschnitt | Inhalt |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| **Zusammenfassung** | Kennzahlen auf einen Blick |
|
|
|
|
|
|
| **Risikobewertung** | Alle Gründe für den Score (was hat wie viele Punkte ausgelöst) |
|
|
|
|
|
|
| **Neue externe Domains** | Domains, die bisher nicht in der Baseline waren |
|
|
|
|
|
|
| **Neue interne URLs** | Seiten, die neu dazugekommen sind |
|
|
|
|
|
|
| **Fehlende URLs** | Seiten, die in der Baseline stehen, jetzt aber nicht mehr gefunden wurden |
|
|
|
|
|
|
| **Geänderte Seiten** | Detailansicht: Textänderungen, Link-Diffs, Meta-Änderungen |
|
|
|
|
|
|
| **Kaputte Links** | `[NEU]` = noch nicht in Baseline, `[bekannt]` = schon bekannt, wird nicht bewertet |
|
|
|
|
|
|
| **Unerwartetes JSON-LD** | Strukturierte Daten mit unbekanntem `@type` |
|
|
|
|
|
|
| **Verdächtige Dateinamen** | Links auf Dateien mit Webshell-typischen Namen |
|
|
|
|
|
|
| **Whitelist-Verstösse** | Externe Domains, die nicht in `allowed_external.yaml` stehen |
|
|
|
|
|
|
| **Fehlende Security-Header** | Informativ (kein Score) — nur auf HTTP-200-Seiten |
|
|
|
|
|
|
| **Crawl-Fehler** | URLs, die der Crawler nicht laden konnte |
|
|
|
|
|
|
| **Nächste Schritte** | Abhängig vom Level: konkrete Handlungsempfehlungen |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. Änderungen freigeben (approve)
|
|
|
|
|
|
|
|
|
|
|
|
Wann `approve` nötig ist:
|
|
|
|
|
|
- Nach einer legitimen Inhaltsänderung (neuer Artikel, aktualisiertes Impressum, ...)
|
|
|
|
|
|
- Nach einem Redesign
|
|
|
|
|
|
- Nach Bereinigung eines echten Angriffs
|
|
|
|
|
|
|
|
|
|
|
|
### Einzelne URL freigeben
|
|
|
|
|
|
|
|
|
|
|
|
Wenn nur eine bestimmte Seite geändert wurde:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner approve --url https://bredelar.info/veranstaltungen/ \
|
|
|
|
|
|
--note "Veranstaltungskalender aktualisiert"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Alle Änderungen freigeben
|
|
|
|
|
|
|
|
|
|
|
|
Nach Prüfung aller markierten URLs:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner approve --all --note "Monatliches Update geprüft und freigegeben"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Achtung**: `--all` übernimmt alles aus dem letzten Snapshot. Nur anwenden, wenn jede
|
|
|
|
|
|
gemeldete Änderung manuell gesichtet wurde.
|
|
|
|
|
|
|
|
|
|
|
|
### Komplette Baseline neu aufbauen
|
|
|
|
|
|
|
|
|
|
|
|
Nach einem großen Redesign oder nach Bereinigung eines Angriffs:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner approve --rebuild --note "Nach Angriff bereinigt, neues Redesign"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`--rebuild` ersetzt die gesamte bestehende Baseline durch den aktuellen Snapshot.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 9. Vorgehen bei einem Alarm
|
|
|
|
|
|
|
|
|
|
|
|
### YELLOW — Warnung
|
|
|
|
|
|
|
2026-06-12 22:34:25 +02:00
|
|
|
|
1. Diffs sichten: `python -m scanner https://ihre-website.de report --diff-only`
|
|
|
|
|
|
2. Vollständigen Report lesen: `python -m scanner https://ihre-website.de report`
|
|
|
|
|
|
3. Markierte URLs im Browser aufrufen und Quelltext prüfen
|
|
|
|
|
|
4. Ist die Änderung legitim?
|
|
|
|
|
|
- Ja: `python -m scanner https://ihre-website.de approve --url <URL> --note "Grund"`
|
2026-06-12 03:28:36 +02:00
|
|
|
|
- Nein: Abschnitt [Angriff](#angriff) lesen
|
|
|
|
|
|
|
|
|
|
|
|
### RED — Alarm
|
|
|
|
|
|
|
|
|
|
|
|
Der Score ist ≥ 60. Typische Auslöser: neue externe Domain (50 Pkt.) + Hidden Content
|
|
|
|
|
|
(40 Pkt.), Webshell-Dateiname (60 Pkt.), Meta-Refresh (40 Pkt.).
|
|
|
|
|
|
|
|
|
|
|
|
#### Angriff
|
|
|
|
|
|
|
|
|
|
|
|
1. **Nicht sofort approve aufrufen** — das würde den kompromittierten Zustand als neue
|
|
|
|
|
|
Baseline festschreiben.
|
2026-06-12 22:34:25 +02:00
|
|
|
|
2. Diffs sichten: `python -m scanner https://ihre-website.de report --diff-only`
|
|
|
|
|
|
3. Report sichern: `cp -r ihre-website.de/reports/$(ls ihre-website.de/reports/ | tail -1) ~/alarm-$(date +%Y%m%d)/`
|
|
|
|
|
|
4. Website im Browser aufrufen und nach sichtbaren Fremdinhalten suchen.
|
|
|
|
|
|
5. Quelltext der betroffenen Seiten prüfen (insbesondere `display:none`-Bereiche).
|
|
|
|
|
|
6. Hoster kontaktieren, SSH-/FTP-Zugriffslogs prüfen.
|
|
|
|
|
|
7. Dateien mit Backup vergleichen (diff oder rsync --dry-run).
|
|
|
|
|
|
8. Nach vollständiger Bereinigung:
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```bash
|
2026-06-12 22:34:25 +02:00
|
|
|
|
python -m scanner https://ihre-website.de scan # muss GREEN ergeben
|
|
|
|
|
|
python -m scanner https://ihre-website.de approve --rebuild --note "Nach Angriff bereinigt $(date +%Y-%m-%d)"
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 10. Automatisierung per Cron-Job
|
|
|
|
|
|
|
2026-06-12 15:12:25 +02:00
|
|
|
|
### Cron-Einträge einrichten
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
crontab -e
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 15:48:30 +02:00
|
|
|
|
Je eine Zeile pro überwachter Website einfügen — mit **Zeitversatz von mindestens 15 Minuten**
|
|
|
|
|
|
zwischen den einzelnen Scans. Ein Scan lädt alle Seiten einer Website herunter; laufen mehrere
|
|
|
|
|
|
Scans gleichzeitig, konkurrieren sie um Netzwerk und CPU und können sich gegenseitig verlangsamen
|
|
|
|
|
|
oder die überwachten Server unnötig belasten.
|
2026-06-12 15:12:25 +02:00
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```cron
|
2026-06-12 15:12:25 +02:00
|
|
|
|
# bredelar.info — täglich 03:00 Uhr
|
|
|
|
|
|
0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config bredelar.info/config.yaml scan >> bredelar.info/logs/cron.log 2>&1
|
|
|
|
|
|
|
|
|
|
|
|
# jamulix.de — täglich 03:15 Uhr
|
|
|
|
|
|
15 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config jamulix.de/config.yaml scan >> jamulix.de/logs/cron.log 2>&1
|
2026-06-12 22:17:34 +02:00
|
|
|
|
|
|
|
|
|
|
# www.bergbauspuren-bredelar.de — täglich 03:30 Uhr
|
|
|
|
|
|
30 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config www.bergbauspuren-bredelar.de/config.yaml scan >> www.bergbauspuren-bredelar.de/logs/cron.log 2>&1
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 15:12:25 +02:00
|
|
|
|
Danach prüfen ob die Einträge gespeichert wurden:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
crontab -l
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Neue Website hinzufügen:**
|
2026-06-12 22:17:34 +02:00
|
|
|
|
1. Ersten Scan starten — Verzeichnis und config.yaml werden automatisch angelegt:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner https://neue-seite.de init
|
|
|
|
|
|
```
|
|
|
|
|
|
2. Externe Domains prüfen und in `neue-seite.de/config/allowed_external.yaml` eintragen
|
|
|
|
|
|
3. Baseline freigeben: `python -m scanner https://neue-seite.de approve --all --note "Erststand geprüft"`
|
|
|
|
|
|
4. Weiteren Cron-Eintrag ergänzen — mit 15 Minuten Abstand zum vorherigen (z.B. 03:45 Uhr)
|
|
|
|
|
|
5. `neue-seite.de/` in `.gitignore` eintragen
|
2026-06-12 15:12:25 +02:00
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
### Log prüfen
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-12 15:12:25 +02:00
|
|
|
|
tail -50 bredelar.info/logs/cron.log
|
|
|
|
|
|
tail -50 jamulix.de/logs/cron.log
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Log-Rotation (optional)
|
|
|
|
|
|
|
|
|
|
|
|
Datei `/etc/logrotate.d/integrity-scanner`:
|
|
|
|
|
|
```
|
2026-06-12 15:12:25 +02:00
|
|
|
|
/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/*/logs/*.log {
|
2026-06-12 03:28:36 +02:00
|
|
|
|
daily
|
|
|
|
|
|
rotate 30
|
|
|
|
|
|
compress
|
|
|
|
|
|
missingok
|
|
|
|
|
|
notifempty
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 11. E-Mail-Benachrichtigung einrichten
|
|
|
|
|
|
|
2026-06-12 22:17:34 +02:00
|
|
|
|
### Konfiguration testen ohne echten Scan
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner https://ihre-website.de test-alert
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Sendet eine Test-Nachricht und prüft dabei SMTP-Host, Zugangsdaten und Empfängeradresse.
|
|
|
|
|
|
Sinnvoll nach jeder Änderung an der SMTP-Konfiguration.
|
|
|
|
|
|
|
|
|
|
|
|
Optional: `test-alert --level red` sendet eine Nachricht mit rotem Alarm-Level.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
### config.yaml anpassen
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
alerting:
|
|
|
|
|
|
min_level: "yellow" # oder "red" — nur bei rotem Alarm mailen
|
|
|
|
|
|
email:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
smtp_host: "mail.linix.de" # SMTP-Server des Providers
|
|
|
|
|
|
smtp_port: 587
|
|
|
|
|
|
smtp_tls: true
|
|
|
|
|
|
smtp_user: "scanner@linix.de"
|
|
|
|
|
|
smtp_password_env: "SCANNER_SMTP_PASSWORD"
|
|
|
|
|
|
from: "scanner@bredelar.info"
|
|
|
|
|
|
to:
|
|
|
|
|
|
- "dieter.schlueter@linix.de"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Passwort als Umgebungsvariable setzen
|
|
|
|
|
|
|
|
|
|
|
|
**Niemals** das Passwort direkt in `config.yaml` eintragen. Stattdessen:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Temporär (bis zum nächsten Login)
|
|
|
|
|
|
export SCANNER_SMTP_PASSWORD="geheimesPasswort"
|
|
|
|
|
|
|
|
|
|
|
|
# Dauerhaft in ~/.bashrc oder ~/.profile
|
|
|
|
|
|
echo 'export SCANNER_SMTP_PASSWORD="geheimesPasswort"' >> ~/.bashrc
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Für den Cron-Job die Variable in der Crontab oder in `/etc/environment` setzen:
|
|
|
|
|
|
```cron
|
|
|
|
|
|
SCANNER_SMTP_PASSWORD=geheimesPasswort
|
|
|
|
|
|
0 3 * * * cd /home/dschlueter/...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 12. Konfigurationsreferenz
|
|
|
|
|
|
|
|
|
|
|
|
Alle Einstellungen in `config.yaml`. Fehlende Werte werden aus den Defaults übernommen.
|
|
|
|
|
|
|
|
|
|
|
|
### Kern-Einstellungen
|
|
|
|
|
|
|
|
|
|
|
|
| Schlüssel | Standard | Bedeutung |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `target` | — | Zu überwachende URL (Pflicht) |
|
|
|
|
|
|
| `user_agent` | `integrity-scanner/1.0` | HTTP User-Agent |
|
|
|
|
|
|
| `request_timeout` | `20` | Timeout pro Seite in Sekunden |
|
|
|
|
|
|
|
|
|
|
|
|
### Crawl-Einstellungen (`crawl:`)
|
|
|
|
|
|
|
|
|
|
|
|
| Schlüssel | Standard | Bedeutung |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `max_pages` | `200` | Maximale Anzahl gecrawlter Seiten |
|
|
|
|
|
|
| `delay_seconds` | `1.0` | Pause zwischen Requests (Server-Schonung) |
|
|
|
|
|
|
| `skip_extensions` | `.jpg`, `.pdf` etc. | Dateitypen, die nicht gecrawlt werden |
|
2026-06-12 22:17:34 +02:00
|
|
|
|
| `sitemap` | `true` | Sitemap.xml auslesen um Seiten ohne eingehende Links zu finden |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
### Score-Gewichte (`scoring:`)
|
|
|
|
|
|
|
|
|
|
|
|
| Ereignis | Standard-Punkte |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| Verdächtiger Dateiname (Webshell) | 60 |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Cloaking: Link nur im Bot-Crawl sichtbar | 60 |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| Neue externe Domain | 50 |
|
|
|
|
|
|
| Hidden Content (CSS-verstecktes Element mit Links/Text) | 40 |
|
|
|
|
|
|
| Meta-Refresh | 40 |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Cloaking: signifikant mehr Text im Bot-Crawl (>100 Zeichen) | 40 |
|
|
|
|
|
|
| Geänderte JavaScript-Datei (`check-assets`) | 40 |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| Unerwartete Canonical-URL | 35 |
|
|
|
|
|
|
| Unerwartetes JSON-LD `@type` | 35 |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Geänderte CSS-Datei (`check-assets`) | 30 |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| Neue interne URL | 30 |
|
|
|
|
|
|
| Neues verdächtiges Inline-Script | 30 |
|
|
|
|
|
|
| Link in HTML-Kommentar | 25 |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Cloaking: `Vary: User-Agent` Header (ohne Inhaltsdiff) | 25 |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| Großer Textblock (>200 Zeichen) | 20 |
|
|
|
|
|
|
| Fehlende interne URL | 20 |
|
|
|
|
|
|
| Neuer kaputte Link (4xx/5xx, nicht in Baseline) | 20 |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Geänderte Bild-/Binärdatei (`check-assets`) | 20 |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
2026-06-13 00:32:31 +02:00
|
|
|
|
KI-Inhaltsanalyse (nur wenn aktiviert; Gesamt-Level aus KI ist auf **Gelb gedeckelt**):
|
|
|
|
|
|
|
|
|
|
|
|
| Ereignis (KI) | Standard-Punkte |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| Pornografie/sexuell expliziter Inhalt (`ai_pornography`) | 50 |
|
|
|
|
|
|
| Diffamierende/strafbare Aussage (`ai_defamation_illegal`) | 50 |
|
|
|
|
|
|
| Politische Propaganda (`ai_propaganda`) | 40 |
|
|
|
|
|
|
| Versteckter Spam (`ai_hidden_spam`) | 40 |
|
|
|
|
|
|
| Problematischer Bildinhalt — Untergrenze (`ai_suspicious_image`) | 40 |
|
|
|
|
|
|
| Themenfremde Werbung (`ai_off_topic_commercial`) | 30 |
|
|
|
|
|
|
| Widersprüchliche Aussage (`ai_contradiction`) | 20 |
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
Alle Werte sind in `config.yaml` unter `scoring:` anpassbar.
|
|
|
|
|
|
|
|
|
|
|
|
### Alarm-Schwellen (`thresholds:`)
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
thresholds:
|
|
|
|
|
|
yellow: 20 # ab hier: YELLOW
|
|
|
|
|
|
red: 60 # ab hier: RED
|
|
|
|
|
|
large_text_block_chars: 200 # ab dieser Zeichenanzahl gilt ein Textblock als "groß"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Erlaubte JSON-LD-Typen (`allowed_jsonld_types:`)
|
|
|
|
|
|
|
|
|
|
|
|
Alle `@type`-Werte, die in den strukturierten Daten der Website vorkommen dürfen.
|
|
|
|
|
|
Unbekannte Typen lösen einen Fund aus (35 Punkte). Aktuell konfiguriert:
|
|
|
|
|
|
`Organization`, `WebSite`, `WebPage`, `Article`, `BreadcrumbList`, `LocalBusiness`,
|
|
|
|
|
|
`Event`, `FAQPage` u.a.
|
|
|
|
|
|
|
|
|
|
|
|
### Security-Header (`security_headers:`)
|
|
|
|
|
|
|
|
|
|
|
|
Liste der Header, die auf jeder Seite erwartet werden. Fehlende Header werden im Report
|
|
|
|
|
|
gemeldet, aber **nicht** bewertet (kein Score-Einfluss).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 13. Externe Domains verwalten
|
|
|
|
|
|
|
|
|
|
|
|
Datei: `config/allowed_external.yaml`
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
# Legitime externe Domains — eine pro Zeile
|
|
|
|
|
|
- fonts.googleapis.com
|
|
|
|
|
|
- www.kloster-bredelar.de
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Neue externe Domain erscheint im Scanner:
|
|
|
|
|
|
1. Domain im Browser prüfen — gehört sie zur Website?
|
|
|
|
|
|
2. Wenn legitim: in `config/allowed_external.yaml` eintragen
|
|
|
|
|
|
3. `python -m scanner approve --url <betroffene-Seite>` oder `--all`
|
|
|
|
|
|
|
|
|
|
|
|
Wenn eine Domain **nicht** in der Liste steht, löst jedes Vorkommen 50 Punkte aus
|
|
|
|
|
|
(YELLOW-Alarm, bei weiteren Funden RED).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 14. Rausch-Unterdrückung konfigurieren
|
|
|
|
|
|
|
|
|
|
|
|
Manche Inhalte ändern sich automatisch (Datumsangaben, Zeitstempel) und würden sonst
|
|
|
|
|
|
bei jedem Scan einen Fund auslösen. Zwei Mechanismen:
|
|
|
|
|
|
|
|
|
|
|
|
### CSS-Selektoren ignorieren (`ignore_selectors:`)
|
|
|
|
|
|
|
|
|
|
|
|
Elemente werden vor dem Diff vollständig aus dem DOM entfernt:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
normalization:
|
|
|
|
|
|
ignore_selectors:
|
|
|
|
|
|
- "#last-modified"
|
|
|
|
|
|
- ".timestamp"
|
|
|
|
|
|
- ".cookie-banner"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Datei: direkt in `config.yaml` oder in `config/ignore_rules.yaml` (kommentierte Beispiele
|
|
|
|
|
|
dort vorhanden).
|
|
|
|
|
|
|
|
|
|
|
|
### Text-Muster ignorieren (`ignore_patterns:`)
|
|
|
|
|
|
|
|
|
|
|
|
Reguläre Ausdrücke, die im normalisierten Text durch `[IGNORED]` ersetzt werden:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
normalization:
|
|
|
|
|
|
ignore_patterns:
|
|
|
|
|
|
- '\b\d{1,2}\.\s*(?:Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)\s+\d{4}\b'
|
|
|
|
|
|
- '\b\d{1,2}\.\d{1,2}\.\d{4}\b'
|
|
|
|
|
|
- 'Stand:\s+\S+'
|
2026-06-12 22:17:34 +02:00
|
|
|
|
# WordPress Contact Form 7: Platzhalter-Text rotiert bei jedem Seitenaufruf
|
|
|
|
|
|
- 'Kommentar oder Nachricht \* \w+'
|
2026-06-12 03:28:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-12 22:17:34 +02:00
|
|
|
|
Das Muster wird auf beiden Seiten des Vergleichs (Baseline und aktueller Snapshot)
|
|
|
|
|
|
angewendet — falsch-positive Alarme durch dynamische CMS-Inhalte werden so unterdrückt.
|
|
|
|
|
|
|
2026-06-12 03:28:36 +02:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 15. Was der Scanner erkennt
|
|
|
|
|
|
|
|
|
|
|
|
### Erkennt der Scanner ...
|
|
|
|
|
|
|
|
|
|
|
|
| Prüfung | Erkannt? | Anmerkung |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| Neuen Text auf einer Seite | Ja | Ab 200 Zeichen bewertet |
|
|
|
|
|
|
| Versteckten Text (CSS display:none etc.) | Ja | 40 Punkte |
|
|
|
|
|
|
| Neue externe Domain | Ja | 50 Punkte |
|
|
|
|
|
|
| Meta-Refresh-Weiterleitung | Ja | 40 Punkte |
|
|
|
|
|
|
| Webshell-Dateien (shell.php, c99 etc.) | Ja | 60 Punkte |
|
|
|
|
|
|
| Links in HTML-Kommentaren | Ja | 25 Punkte |
|
|
|
|
|
|
| Geänderte Canonical-URL | Ja | 35 Punkte |
|
|
|
|
|
|
| Unbekanntes JSON-LD `@type` | Ja | 35 Punkte |
|
|
|
|
|
|
| `eval()`, `atob()`, `XMLHttpRequest` in Scripts | Ja | 30 Punkte |
|
|
|
|
|
|
| Kaputte interne Links (neu) | Ja | 20 Punkte |
|
|
|
|
|
|
| Kaputte interne Links (bekannt) | Ja, informativ | Kein Score |
|
|
|
|
|
|
| Fehlende Security-Header | Ja, informativ | Kein Score |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| Kaputte **externe** Links (4xx/5xx) | Ja, mit `check-ext-links` | Kein Score, nur Report |
|
|
|
|
|
|
| UA-basiertes Cloaking (Googlebot sieht anderen Inhalt) | Ja, mit `cloak-check` | Score 40–60 |
|
|
|
|
|
|
| Veränderungen in JS/CSS/Bildern (Inhalt) | Ja, mit `check-assets` | Score 20–40 |
|
2026-06-13 01:16:23 +02:00
|
|
|
|
| Problematischer Text (Pornografie, Propaganda, Spam, Off-Topic) | Ja, mit KI-Analyse | Gelb; Pornografie/strafbar bei hoher Schwere+Konfidenz → Rot |
|
|
|
|
|
|
| Problematische Bildinhalte + Text im Bild (OCR) | Ja, mit KI-Analyse | Gelb; Pornografie/strafbar bei hoher Schwere+Konfidenz → Rot |
|
2026-06-12 09:32:30 +02:00
|
|
|
|
| IP-basiertes Cloaking | Nein | Nur Google Search Console kann das erkennen |
|
2026-06-12 03:28:36 +02:00
|
|
|
|
| Serverseitige Code-Änderungen | Nein | Nur sichtbare HTML-Ausgabe |
|
|
|
|
|
|
|
|
|
|
|
|
### Grenzen des Scanners
|
|
|
|
|
|
|
2026-06-12 09:32:30 +02:00
|
|
|
|
- **IP-basiertes Cloaking** wird nicht erkannt: Wenn der Server je nach Herkunfts-IP
|
|
|
|
|
|
unterschiedliche Inhalte liefert (nur für echte Googlebot-IPs), sieht der Scanner immer
|
|
|
|
|
|
den normalen Inhalt. Ergänzung: Google Search Console → URL-Prüfung.
|
|
|
|
|
|
- Serverseitig injizierter Code, der nur unter bestimmten Bedingungen aktiv wird, ist
|
|
|
|
|
|
nur per `cloak-check` (UA-Variante) teilweise erkennbar.
|
2026-06-12 03:28:36 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 16. Verzeichnisstruktur
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-06-12 22:34:25 +02:00
|
|
|
|
scanner.sh Wrapper-Script (ausführbar, startet python -m scanner)
|
|
|
|
|
|
meine-seite.de/ Template für neue Targets
|
|
|
|
|
|
ihre-website.de/ Automatisch angelegtes Zielverzeichnis (ein Ordner pro Site)
|
|
|
|
|
|
config.yaml Hauptkonfiguration
|
|
|
|
|
|
config/
|
|
|
|
|
|
allowed_external.yaml Whitelist erlaubter externer Domains
|
|
|
|
|
|
ignore_rules.yaml Kommentierte Beispiele für Rausch-Unterdrückung
|
|
|
|
|
|
data/
|
|
|
|
|
|
baseline/ Freigegebene Referenz (NIEMALS automatisch geändert)
|
|
|
|
|
|
manifest.json Wer/was/wann freigegeben hat
|
|
|
|
|
|
pages/ Pro URL eine JSON-Datei (16-stelliger Hex-Name)
|
|
|
|
|
|
asset_hashes.json SHA-256-Hashes aller freigegebenen Assets
|
|
|
|
|
|
snapshots/ Ein Verzeichnis pro Crawl-Durchlauf
|
|
|
|
|
|
YYYYMMDD_HHMMSS/
|
|
|
|
|
|
manifest.json Zeitstempel, Seitenzahl, Fehler
|
|
|
|
|
|
pages/ Extrahierte Seiten-Daten
|
|
|
|
|
|
logs/
|
|
|
|
|
|
scanner.log Detailliertes Laufzeit-Log
|
|
|
|
|
|
cron.log Ausgabe der Cron-Ausführungen
|
|
|
|
|
|
reports/
|
|
|
|
|
|
YYYYMMDD_HHMMSS/ Normaler Scan-Report
|
|
|
|
|
|
report.md
|
|
|
|
|
|
report.json
|
|
|
|
|
|
YYYYMMDD_HHMMSS_cloak/ Cloaking-Check-Report
|
|
|
|
|
|
YYYYMMDD_HHMMSS_ext-links/ Externer-Links-Report
|
|
|
|
|
|
YYYYMMDD_HHMMSS_assets/ Asset-Check-Report
|
2026-06-12 03:28:36 +02:00
|
|
|
|
scanner/ Python-Paket (Quellcode)
|
|
|
|
|
|
tests/ Automatisierte Tests
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Tests ausführen
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
pytest tests/ -v # alle Tests
|
|
|
|
|
|
pytest tests/test_differ.py -v # einzelnes Testmodul
|
|
|
|
|
|
pytest tests/test_differ.py::TestScoreDiff::test_green_for_no_changes -v # einzelner Test
|
|
|
|
|
|
```
|
2026-06-12 09:32:30 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 17. Cloaking-Erkennung
|
|
|
|
|
|
|
|
|
|
|
|
Cloaking bedeutet: der Server liefert Suchmaschinen-Bots absichtlich anderen Inhalt als
|
|
|
|
|
|
normalen Besuchern — ein klassisches Parasite-SEO-Angriffsmuster.
|
|
|
|
|
|
|
|
|
|
|
|
### Befehl
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner cloak-check
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Der Scanner crawlt die Website zweimal nacheinander: einmal mit dem normalen Browser-User-Agent,
|
|
|
|
|
|
einmal als `Googlebot/2.1`. Unterschiede im Text, in den Links oder im `Vary`-Header werden
|
|
|
|
|
|
gemeldet.
|
|
|
|
|
|
|
|
|
|
|
|
### Scoring
|
|
|
|
|
|
|
|
|
|
|
|
| Fund | Punkte |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| Link nur im Bot-Crawl sichtbar | 60 (RED) |
|
|
|
|
|
|
| Bot sieht >100 Zeichen mehr Text | 40 |
|
|
|
|
|
|
| `Vary: User-Agent` Header ohne Inhaltsdiff | 25 |
|
|
|
|
|
|
|
|
|
|
|
|
### Report
|
|
|
|
|
|
|
|
|
|
|
|
Der Report liegt unter `reports/<ts>_cloak/report.md` und zeigt pro auffälliger Seite:
|
|
|
|
|
|
- ob `Vary: User-Agent` gesetzt ist
|
|
|
|
|
|
- welche Links nur der Bot sieht
|
|
|
|
|
|
- wie viele Zeichen mehr Text der Bot bekommt
|
|
|
|
|
|
- einen Text-Diff (bis zu 20 Zeilen)
|
|
|
|
|
|
|
|
|
|
|
|
### Grenzen
|
|
|
|
|
|
|
|
|
|
|
|
Erkennt nur **UA-basiertes** Cloaking. IP-basiertes Cloaking (Server prüft die
|
|
|
|
|
|
Herkunfts-IP des Crawlers) ist damit nicht erkennbar — dafür ist die Google Search Console
|
|
|
|
|
|
das richtige Werkzeug (URL-Prüfung → Live testen).
|
|
|
|
|
|
|
|
|
|
|
|
### Wann ausführen?
|
|
|
|
|
|
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
**Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über
|
|
|
|
|
|
`periodic_checks` in der `config.yaml`). Der manuelle Aufruf ist nur bei konkretem
|
|
|
|
|
|
Verdacht nötig.
|
2026-06-12 09:32:30 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 18. Externe Links auf Erreichbarkeit prüfen
|
|
|
|
|
|
|
|
|
|
|
|
Der tägliche `scan` erkennt *neue* externe Domains, prüft aber nicht ob bestehende externe
|
|
|
|
|
|
Links noch funktionieren. Dafür gibt es:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner check-ext-links
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Was geprüft wird
|
|
|
|
|
|
|
|
|
|
|
|
Alle externen `<a>`-Links aus dem letzten Snapshot werden per HEAD-Request abgefragt
|
|
|
|
|
|
(Fallback auf GET bei HTTP 405). Gemeldet werden:
|
|
|
|
|
|
|
|
|
|
|
|
- **Kaputte Links** (HTTP 4xx/5xx) — mit Angabe, auf welchen Seiten sie verlinkt sind
|
|
|
|
|
|
- **Verbindungsfehler** (Timeout, DNS-Fehler) — ebenfalls mit Quelladressen
|
|
|
|
|
|
|
|
|
|
|
|
### Scoring
|
|
|
|
|
|
|
|
|
|
|
|
Kaputte externe Links werden **nicht bewertet** (kein Score-Einfluss). Das Ergebnis ist
|
|
|
|
|
|
rein informativ — externe Links brechen regelmäßig ohne Angriffsbezug.
|
|
|
|
|
|
|
|
|
|
|
|
Exit-Code: `0` wenn alle Links erreichbar, `1` wenn mindestens ein kaputtes oder
|
|
|
|
|
|
fehlendes Link gefunden wurde.
|
|
|
|
|
|
|
|
|
|
|
|
### Report
|
|
|
|
|
|
|
|
|
|
|
|
Der Report liegt unter `reports/<ts>_ext-links/report.md`.
|
|
|
|
|
|
|
|
|
|
|
|
### Wann ausführen?
|
|
|
|
|
|
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
**Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über
|
|
|
|
|
|
`periodic_checks` in der `config.yaml`). Ein manueller Aufruf ist nur bei Bedarf nötig.
|
2026-06-12 09:32:30 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 19. Binärdateien und Assets prüfen
|
|
|
|
|
|
|
|
|
|
|
|
Der tägliche `scan` erkennt Änderungen im HTML-Text und an Link-URLs, aber nicht ob der
|
|
|
|
|
|
*Inhalt* einer Bilddatei oder eines JavaScript-Files ausgetauscht wurde. Dafür:
|
|
|
|
|
|
|
2026-06-12 22:34:25 +02:00
|
|
|
|
### Ersteinrichtung
|
|
|
|
|
|
|
|
|
|
|
|
Die Asset-Baseline wird **automatisch** beim ersten `approve --all` angelegt — kein
|
|
|
|
|
|
separater Schritt nötig. Nur wenn die Asset-Baseline manuell neu gesetzt werden soll:
|
2026-06-12 09:32:30 +02:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-12 22:34:25 +02:00
|
|
|
|
python -m scanner https://ihre-website.de approve-assets --note "Manuelle Baseline nach CDN-Wechsel"
|
2026-06-12 09:32:30 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Regelmäßige Prüfung
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner check-assets
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Alle verlinkten JS-, CSS- und Bilddateien aus dem letzten Snapshot werden heruntergeladen
|
|
|
|
|
|
und per SHA-256 gehasht. Abweichungen von der gespeicherten Baseline werden gemeldet.
|
|
|
|
|
|
|
|
|
|
|
|
### Scoring
|
|
|
|
|
|
|
|
|
|
|
|
| Dateityp | Punkte |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| JavaScript (`.js`) | 40 — YELLOW |
|
|
|
|
|
|
| CSS (`.css`, Stylesheets) | 30 — YELLOW |
|
|
|
|
|
|
| Bilder, PDFs, sonstige | 20 — YELLOW |
|
|
|
|
|
|
| 2× JS geändert | 80 — RED |
|
|
|
|
|
|
|
|
|
|
|
|
### Änderungen freigeben
|
|
|
|
|
|
|
|
|
|
|
|
Nach einem legitimen Update (neues CDN-Bundle, neues Logo):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner approve-assets --note "CDN-Update nach Redesign Juli 2026"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`approve-assets` fetcht alle Assets frisch und setzt die neuen Hashes als Baseline.
|
|
|
|
|
|
|
|
|
|
|
|
### Report
|
|
|
|
|
|
|
|
|
|
|
|
Der Report liegt unter `reports/<ts>_assets/report.md` und zeigt pro geänderter Datei:
|
|
|
|
|
|
- den Dateityp
|
|
|
|
|
|
- die ersten 16 Zeichen des alten und neuen SHA-256-Hash
|
|
|
|
|
|
- die Größenänderung in Bytes
|
|
|
|
|
|
|
|
|
|
|
|
### Wann ausführen?
|
|
|
|
|
|
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
**Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über
|
|
|
|
|
|
`periodic_checks` in der `config.yaml`). Die Datei-Vergleichsgrundlage wird zudem beim
|
|
|
|
|
|
ersten `approve --all` automatisch angelegt — `approve-assets` ist nur für den manuellen
|
|
|
|
|
|
Sonderfall gedacht.
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 20. KI-gestützte Inhaltsanalyse
|
|
|
|
|
|
|
|
|
|
|
|
Eine **optionale** Ergänzung zum Integritäts-Kern. Sie prüft Text und Bilder **semantisch**
|
|
|
|
|
|
auf problematische Inhalte, die kein strukturelles Signal hinterlassen:
|
|
|
|
|
|
|
|
|
|
|
|
- sexuell anstößige Inhalte / Pornografie
|
|
|
|
|
|
- politische Propaganda
|
|
|
|
|
|
- diffamierende oder strafbare Aussagen
|
|
|
|
|
|
- versteckter Werbe-/Spam-Text
|
|
|
|
|
|
- Werbung, die thematisch nicht zur Website passt
|
|
|
|
|
|
- in sich widersprüchliche Aussagen
|
|
|
|
|
|
- problematische Bildinhalte **und** Text in Bildern (OCR)
|
|
|
|
|
|
|
|
|
|
|
|
Die KI ist **Ergänzung, nicht Kern**: Ist sie deaktiviert (Standard), verhält sich der
|
|
|
|
|
|
Scanner exakt wie bisher.
|
|
|
|
|
|
|
|
|
|
|
|
### Das Kostengate (warum das günstig ist)
|
|
|
|
|
|
|
|
|
|
|
|
Jeder Text und jedes Bild bekommt einen Fingerabdruck (Hash). Das Ergebnis der KI-Prüfung
|
|
|
|
|
|
wird zu diesem Fingerabdruck in `data/ai_ledger.json` gespeichert. Bei jedem weiteren Scan
|
|
|
|
|
|
gilt:
|
|
|
|
|
|
|
|
|
|
|
|
- **Inhalt unverändert** → gleicher Hash → Ergebnis kommt aus dem Cache → **keine** Abfrage,
|
|
|
|
|
|
keine Kosten.
|
|
|
|
|
|
- **Inhalt neu oder geändert** → eine günstige OpenRouter-Abfrage, Ergebnis wird gespeichert.
|
|
|
|
|
|
|
|
|
|
|
|
Dadurch kann die Analyse bei **jedem** Scan mitlaufen und kostet an den meisten Tagen 0 €.
|
|
|
|
|
|
|
|
|
|
|
|
### Wirkung auf die Ampel
|
|
|
|
|
|
|
2026-06-13 01:16:23 +02:00
|
|
|
|
KI-Funde sind grundsätzlich **auf Gelb gedeckelt** — mit einer bewussten Ausnahme: die
|
|
|
|
|
|
**schwerwiegendsten Kategorien** (Pornografie, strafbar/diffamierend) lösen bei **hoher
|
|
|
|
|
|
Schwere + hoher Konfidenz** ROT aus. Alle anderen Kategorien (Spam, themenfremde Werbung,
|
|
|
|
|
|
Propaganda, Widerspruch) bleiben höchstens Gelb. Nur Funde mit ausreichender Sicherheit
|
|
|
|
|
|
(Konfidenz ≥ `ai_confidence_min`, Standard 0,7) und mindestens mittlerer Schwere werden
|
|
|
|
|
|
überhaupt gewertet — das hält Fehlalarme niedrig.
|
|
|
|
|
|
|
|
|
|
|
|
Die ROT-Eskalation ist konfigurierbar:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
ai_analysis:
|
|
|
|
|
|
red_categories: ["pornography", "defamation_illegal"] # nur diese dürfen Rot auslösen
|
|
|
|
|
|
red_min_severity: "high" # mindestens diese Schwere
|
|
|
|
|
|
red_min_confidence: 0.9 # mindestens diese Konfidenz
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Mit `red_categories: []` bleibt die KI wie früher immer höchstens Gelb. Hintergrund: KI-
|
|
|
|
|
|
Konfidenzen sind nicht perfekt kalibriert, daher dürfen nur die gravierendsten Fälle die
|
|
|
|
|
|
volle ROT-Dringlichkeit („Dienstleister informieren, nichts freigeben") auslösen.
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
|
|
|
|
|
|
Fehlt der API-Key oder schlägt eine Abfrage fehl, wird die Analyse **übersprungen** und der
|
|
|
|
|
|
Scan läuft unverändert weiter. Die KI kann den normalen Betrieb nie blockieren.
|
|
|
|
|
|
|
2026-06-13 00:50:36 +02:00
|
|
|
|
### „Nicht geprüft" ≠ „sauber"
|
|
|
|
|
|
|
|
|
|
|
|
Kann ein Inhalt trotz Modell-Eskalation **und** Wiederholung (`max_retries`) nicht geprüft
|
|
|
|
|
|
werden, weil der Dienst gerade nicht erreichbar ist, wird er **nicht** still als unbedenklich
|
|
|
|
|
|
gewertet. Stattdessen erscheint er als **ungeprüft** im Terminal, im Report und in der E-Mail —
|
|
|
|
|
|
mit URL und dem Hinweis, beim nächsten Lauf erneut zu prüfen. Über `unchecked_level` steuern
|
|
|
|
|
|
Sie die Schärfe:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
ai_analysis:
|
|
|
|
|
|
max_retries: 1 # Wiederholungen der Kette bei Komplettausfall
|
|
|
|
|
|
unchecked_level: "warn" # "warn" = nur Hinweis | "yellow" = Level auf mindestens Gelb anheben
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Für hohe Sicherheitsansprüche empfiehlt sich `"yellow"`: Dann fällt jeder nicht prüfbare
|
|
|
|
|
|
Inhalt sofort als gelbe Warnung auf, statt nur als Notiz zu erscheinen.
|
|
|
|
|
|
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
### Aktivieren
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
ai_analysis:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
api_key_env: "OPENROUTER_API_KEY"
|
|
|
|
|
|
site_context: "Heimat- und Vereinswebsite über die Bergbaugeschichte in Bredelar."
|
|
|
|
|
|
ai_confidence_min: 0.7
|
|
|
|
|
|
attempt_timeout: 30 # Zeitlimit je Modell-Versuch → eskaliert bei Langsamkeit
|
|
|
|
|
|
text:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
models: # Kette: Stufe 1+2 free, Stufe 3 günstig bezahlt
|
|
|
|
|
|
- "qwen/qwen3-next-80b-a3b-instruct:free"
|
|
|
|
|
|
- "meta-llama/llama-3.3-70b-instruct:free"
|
|
|
|
|
|
- "google/gemini-2.5-flash-lite"
|
|
|
|
|
|
min_chars: 200 # nur Seitentexte ab dieser Größe
|
|
|
|
|
|
max_pages_per_scan: 20 # Kostendeckel pro Lauf
|
|
|
|
|
|
image:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
models: # multimodal: Inhalt + OCR
|
|
|
|
|
|
- "google/gemma-4-31b-it:free"
|
|
|
|
|
|
- "nvidia/nemotron-nano-12b-v2-vl:free"
|
|
|
|
|
|
- "google/gemini-2.5-flash-lite"
|
|
|
|
|
|
max_images_per_scan: 15
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Wichtig — `site_context`:** Die KI bewertet die **thematische Passung** zur hier
|
|
|
|
|
|
beschriebenen Website, nicht einzelne Schlüsselwörter. Ein Angreifer kann Heimat- oder
|
|
|
|
|
|
Ortsbegriffe einstreuen, um simple Wortlisten zu täuschen — gegen die semantische
|
|
|
|
|
|
Themenprüfung hilft das nicht. Je präziser der `site_context`, desto besser die Trefferquote.
|
|
|
|
|
|
|
|
|
|
|
|
### API-Key setzen
|
|
|
|
|
|
|
|
|
|
|
|
Das Passwort/Token **niemals** in `config.yaml` eintragen, nur als Umgebungsvariable:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Dauerhaft in ~/.bashrc
|
|
|
|
|
|
echo 'export OPENROUTER_API_KEY="sk-or-..."' >> ~/.bashrc
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Für den Cron-Betrieb die Variable wie bei `SCANNER_SMTP_PASSWORD` in der Crontab oder in
|
|
|
|
|
|
`/etc/environment` hinterlegen.
|
|
|
|
|
|
|
|
|
|
|
|
### Fehlalarm quittieren
|
|
|
|
|
|
|
|
|
|
|
|
Wurde legitimer Inhalt fälschlich als auffällig markiert:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner https://ihre-website.de ai-dismiss --all # alle offenen Funde
|
|
|
|
|
|
python -m scanner https://ihre-website.de ai-dismiss --hash <fp> # gezielt einen Fund
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Der Fingerabdruck `<fp>` steht im Report bei jedem KI-Fund. Ein `approve --all` oder
|
|
|
|
|
|
`approve --url` quittiert die zugehörigen KI-Funde automatisch mit (freigegebener Inhalt
|
|
|
|
|
|
gilt als geprüft).
|
|
|
|
|
|
|
|
|
|
|
|
### Modelle (OpenRouter) und Eskalation
|
|
|
|
|
|
|
|
|
|
|
|
Pro Modalität wird eine **Modell-Kette mit zweifacher Eskalation** abgearbeitet. Schlägt ein
|
|
|
|
|
|
Modell fehl (Fehler, Rate-Limit) oder antwortet es langsamer als `attempt_timeout` Sekunden,
|
|
|
|
|
|
wird automatisch zur nächsten Stufe gewechselt. Stufe 1 und 2 sind kostenlose Modelle, Stufe 3
|
|
|
|
|
|
ist ein günstiges Bezahlmodell — so bleibt die Prüfung in der Regel kostenlos und ist trotzdem
|
|
|
|
|
|
zuverlässig. Welche Stufe das Verdikt geliefert hat, wird im Ledger und Report festgehalten.
|
|
|
|
|
|
|
|
|
|
|
|
| Zweck | Stufe 1 (free) | Stufe 2 (free) | Stufe 3 (bezahlt) |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| Text/Semantik | `qwen3-next-80b-a3b-instruct:free` | `llama-3.3-70b-instruct:free` | `gemini-2.5-flash-lite` |
|
|
|
|
|
|
| Bild + OCR | `gemma-4-31b-it:free` | `nemotron-nano-12b-v2-vl:free` | `gemini-2.5-flash-lite` |
|
|
|
|
|
|
|
|
|
|
|
|
Hinweis: Kostenlose Modelle liefern nicht immer ein gültiges Ergebnis (sie unterstützen das
|
|
|
|
|
|
strikte JSON-Format nicht durchgehend). In dem Fall greift automatisch die nächste Stufe — das
|
|
|
|
|
|
zuverlässige Bezahlmodell auf Stufe 3 fängt solche Fälle ab.
|
|
|
|
|
|
|
feat: adaptiver Modell-Router (Circuit-Breaker) + parallele KI-Analyse
Der reale Erst-Scan dauerte ~10 min, weil pro Kandidat sequenziell erst die
rate-limited Free-Modelle (429) probiert wurden. Zwei Hebel beheben das:
1. ModelRouter: programmatische Telemetrie statt LLM-Orchestrator. Pro Modell
Erfolg/Latenz; ausgefallene/rate-limited Modelle bekommen per Circuit-Breaker
einen Cooldown und werden übersprungen, das schnellste gesunde Modell zuerst.
Failsafe-Pruning toter Slugs über OpenRouter /models (24h-Cache). State
persistent in data/ai_router_state.json.
2. Parallelisierung: ThreadPoolExecutor mit konfigurierbarer max_concurrency
(Default 8, I/O-gebunden → an API-Rate-Limits gebunden, nicht an CPU-Kerne).
Klassifikationen laufen nebenläufig, Merge im Hauptthread (keine Locks),
findings deterministisch sortiert. Fingerprint-Gruppierung: identischer
Inhalt wird nur einmal klassifiziert, Funde aber für alle URLs emittiert.
Realtest bredelar.info: Frisch-Scan von ~10 min auf 1:01 min; Breaker öffnete
14× (Free-Modelle übersprungen), alle Verdikte von gemini-2.5-flash-lite.
Config: max_concurrency, breaker_failure_threshold, breaker_cooldown_seconds,
refresh_models. Tests: 251 grün (+12: Router-Reihung/Breaker/Pruning-failsafe,
parallel==sequenziell, Dedup, deterministische Reihenfolge).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 02:51:30 +02:00
|
|
|
|
### Adaptiver Modell-Router und Parallelität
|
|
|
|
|
|
|
|
|
|
|
|
Statt die Kette stur von vorn abzuarbeiten, **lernt** der Scanner während des Laufs, welche
|
|
|
|
|
|
Modelle gerade funktionieren:
|
|
|
|
|
|
|
|
|
|
|
|
- **Circuit-Breaker:** Antwortet ein Modell wiederholt mit Fehler/Rate-Limit (`429`) oder zu
|
|
|
|
|
|
langsam, wird es für `breaker_cooldown_seconds` übersprungen — keine verschwendeten Versuche
|
|
|
|
|
|
mehr an gerade tote Free-Modelle. Das schnellste gesunde Modell kommt zuerst dran.
|
|
|
|
|
|
- **Live-Abgleich (`refresh_models`):** Einmal täglich gleicht der Router die Modell-Liste mit
|
|
|
|
|
|
OpenRouter ab; nicht mehr existierende Slugs fallen automatisch raus (failsafe — bei Netzfehler
|
|
|
|
|
|
bleibt alles wie gehabt).
|
|
|
|
|
|
- **Parallelität (`max_concurrency`):** Mehrere Inhalte werden gleichzeitig geprüft. Die KI-Calls
|
|
|
|
|
|
warten fast nur auf die API-Antwort (Netzwerk), nicht auf die CPU — deshalb ist die Stellgröße
|
|
|
|
|
|
an die **Rate-Limits der API** gebunden, nicht an die CPU-Kerne. Default 8 passt auf einen
|
|
|
|
|
|
4-Kern-vhost genauso wie auf einen 24-Kern-Rechner.
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
ai_analysis:
|
|
|
|
|
|
max_concurrency: 8 # gleichzeitige API-Anfragen
|
|
|
|
|
|
breaker_failure_threshold: 2 # Fehler, bis ein Modell pausiert wird
|
|
|
|
|
|
breaker_cooldown_seconds: 120 # Pausendauer eines ausgefallenen Modells
|
|
|
|
|
|
refresh_models: true # tote Modell-Slugs automatisch aussortieren
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Wirkung: Der Erst-Scan einer Site (der den ganzen Bestand prüft) wird deutlich schneller, weil
|
|
|
|
|
|
rate-limited Free-Modelle sofort übersprungen werden und die verbleibenden Anfragen nebenläufig
|
|
|
|
|
|
laufen.
|
|
|
|
|
|
|
2026-06-13 04:12:11 +02:00
|
|
|
|
### Vollständige Abdeckung (kein blinder Fleck)
|
|
|
|
|
|
|
|
|
|
|
|
Eine KI-Bildanalyse ist nur sinnvoll, wenn **alle** Bilder geprüft werden — sonst bliebe ein
|
|
|
|
|
|
Bild jenseits eines Limits dauerhaft ungeprüft (z. B. ein eingeschleustes strafbares Logo auf
|
|
|
|
|
|
einer ansonsten stabilen Seite). Deshalb gilt:
|
|
|
|
|
|
|
|
|
|
|
|
- **Geänderte/neue Inhalte werden IMMER sofort geprüft** — ungeachtet jeder Drossel. Fügt jemand
|
|
|
|
|
|
ein Bild auf einer Seite ein, ändert sich diese Seite → das Bild wird im selben Scan analysiert.
|
|
|
|
|
|
- **`max_pages_per_scan` / `max_images_per_scan` drosseln nur den historischen Altbestand**, und
|
|
|
|
|
|
ihr **Default ist `0` = unbegrenzt**: Der erste Scan deckt den kompletten Bestand ab. Dank des
|
|
|
|
|
|
Hash-Caches ist das eine einmalige Ausgabe (wenige Cent); Folge-Scans prüfen nur noch Neues.
|
|
|
|
|
|
- Ein **positiver** Wert ist nur für *sehr große* Sites (z. B. 10.000 Bilder) gedacht, um die
|
|
|
|
|
|
erste Volldurchsicht über mehrere Scans zu strecken. Selbst dann werden geänderte/neue Inhalte
|
|
|
|
|
|
weiter sofort geprüft.
|
|
|
|
|
|
|
|
|
|
|
|
**Initiale Einrichtung:** Beim ersten vollen Durchlauf markiert die KI auch legitime Inhalte
|
|
|
|
|
|
(z. B. Marken-/Partnerlogos auf Firmenseiten) — diese werden einmalig mit `ai-dismiss --hash <fp>`
|
|
|
|
|
|
akzeptiert. Da die Quittung am **Byte-Hash** hängt, wird ein später ausgetauschtes Bild
|
|
|
|
|
|
(andere Bytes → neuer Hash) automatisch neu geprüft und nicht stillschweigend mitakzeptiert.
|
|
|
|
|
|
|
|
|
|
|
|
Hinweis zur Byte-Änderung bekannter Bilder auf stabilen Seiten: Diese deckt zusätzlich die
|
|
|
|
|
|
Datei-Prüfung (`check-assets`) ab — sie schlägt an, wenn sich der Inhalt einer bekannten Datei ändert.
|
2026-06-13 04:02:35 +02:00
|
|
|
|
|
feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)
Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.
Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
übersprungen, Scan läuft unverändert weiter.
Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.
API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).
Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).
Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:04:33 +02:00
|
|
|
|
Audio- und Video-Prüfung sind als abschaltbare Erweiterungspunkte vorbereitet
|
|
|
|
|
|
(`ai_analysis.audio` / `ai_analysis.video`, default deaktiviert) und können aktiviert werden,
|
|
|
|
|
|
sobald eine Website solche Mediendateien direkt einbindet.
|
|
|
|
|
|
|
|
|
|
|
|
### Status prüfen
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m scanner https://ihre-website.de status
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
zeigt bei aktivierter KI, wie viele Inhalte im Cache liegen und wie viele offene Funde es gibt.
|