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
|
|
|
"""
|
|
|
|
|
Klartext-Schicht: übersetzt technische Befunde in Sätze, die auch ein
|
|
|
|
|
Website-Betreiber ohne IT-Wissen versteht.
|
|
|
|
|
|
|
|
|
|
Alle Texte hier sind bewusst laienverständlich. Fachbegriffe (Score, Canonical,
|
|
|
|
|
JSON-LD, Meta-Refresh, Hash) gehören NICHT in dieses Modul — die technischen
|
|
|
|
|
Details stehen weiterhin im Detail-Abschnitt des Reports.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# Ampel-Überschrift je Risiko-Level
|
|
|
|
|
_AMPEL = {
|
|
|
|
|
"green": ("🟢", "Alles in Ordnung."),
|
|
|
|
|
"yellow": ("🟡", "Bitte einmal nachschauen."),
|
|
|
|
|
"red": ("🔴", "Achtung — bitte zeitnah prüfen oder Ihren Dienstleister informieren."),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ampel(level: str) -> tuple[str, str]:
|
|
|
|
|
"""Symbol und Überschrift für ein Risiko-Level (green/yellow/red)."""
|
|
|
|
|
return _AMPEL.get(level, ("⚪", "Status unbekannt."))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ampel_zeile(level: str) -> str:
|
|
|
|
|
"""Einzeilige Ampel-Darstellung, z. B. '🟡 Bitte einmal nachschauen.'"""
|
|
|
|
|
symbol, satz = ampel(level)
|
|
|
|
|
return f"{symbol} {satz}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _zaehle_seiten_befunde(diff: dict) -> dict:
|
|
|
|
|
"""Zählt die Befund-Kategorien über alle geänderten Seiten zusammen."""
|
|
|
|
|
z = {
|
|
|
|
|
"hidden": 0, "meta_refresh": 0, "canonical": 0,
|
|
|
|
|
"comment_links": 0, "scripts": 0, "grosser_text": 0,
|
|
|
|
|
}
|
|
|
|
|
for pd in diff.get("page_diffs", []):
|
|
|
|
|
if pd.get("new_hidden_content"):
|
|
|
|
|
z["hidden"] += 1
|
|
|
|
|
if pd.get("new_meta_refresh"):
|
|
|
|
|
z["meta_refresh"] += 1
|
|
|
|
|
if pd.get("canonical_changed"):
|
|
|
|
|
z["canonical"] += 1
|
|
|
|
|
if pd.get("new_comment_links"):
|
|
|
|
|
z["comment_links"] += 1
|
|
|
|
|
if pd.get("new_inline_scripts"):
|
|
|
|
|
z["scripts"] += 1
|
|
|
|
|
if pd.get("added_chars", 0) > 200:
|
|
|
|
|
z["grosser_text"] += 1
|
|
|
|
|
return z
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
# Laienverständliche Beschreibung je KI-Kategorie.
|
|
|
|
|
_KI_KATEGORIE_TEXT = {
|
|
|
|
|
"pornography": "sexuell anstößige Inhalte",
|
|
|
|
|
"propaganda": "politische Propaganda",
|
|
|
|
|
"defamation_illegal": "möglicherweise strafbare oder verleumderische Aussagen",
|
|
|
|
|
"hidden_spam": "versteckten Werbe-/Spam-Text",
|
|
|
|
|
"off_topic_commercial": "Werbung, die nicht zum Thema Ihrer Website passt",
|
|
|
|
|
"contradiction": "in sich widersprüchliche Aussagen",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
def klartext_befunde(
|
|
|
|
|
diff: dict | None = None,
|
|
|
|
|
cloak_diff: dict | None = None,
|
|
|
|
|
asset_diff: dict | None = None,
|
|
|
|
|
ext_links: dict | None = None,
|
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_result: dict | None = None,
|
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
|
|
|
) -> list[str]:
|
|
|
|
|
"""
|
|
|
|
|
Erzeugt eine Liste laienverständlicher Sätze aus den strukturierten Befunden.
|
|
|
|
|
Leere Befunde → eine beruhigende Meldung.
|
|
|
|
|
"""
|
|
|
|
|
diff = diff or {}
|
|
|
|
|
saetze: list[str] = []
|
|
|
|
|
|
|
|
|
|
# --- Eindeutige Angriffszeichen zuerst ---
|
|
|
|
|
if diff.get("suspicious_filenames"):
|
|
|
|
|
saetze.append(
|
|
|
|
|
"Es wurde auf eine Datei mit verdächtigem Namen verlinkt, wie sie Angreifer "
|
|
|
|
|
"für Schadprogramme verwenden. Das ist ein ernstes Warnzeichen."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if cloak_diff:
|
|
|
|
|
bot_links = sum(len(f.get("extra_links", [])) for f in cloak_diff.get("findings", []))
|
|
|
|
|
bot_text = any(f.get("extra_text_chars", 0) > 100 for f in cloak_diff.get("findings", []))
|
|
|
|
|
if bot_links:
|
|
|
|
|
saetze.append(
|
|
|
|
|
"Ihre Website zeigt der Google-Suchmaschine andere Links als normalen "
|
|
|
|
|
"Besuchern. Das ist eine typische Methode von Spam-Angriffen."
|
|
|
|
|
)
|
|
|
|
|
elif bot_text:
|
|
|
|
|
saetze.append(
|
|
|
|
|
"Ihre Website zeigt der Google-Suchmaschine zusätzlichen Text, den normale "
|
|
|
|
|
"Besucher nicht sehen. Bitte prüfen lassen."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# --- Verdächtige Inhaltsänderungen ---
|
|
|
|
|
z = _zaehle_seiten_befunde(diff)
|
|
|
|
|
if z["hidden"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Auf {z['hidden']} Seite(n) wurde unsichtbarer Text oder unsichtbare Links "
|
|
|
|
|
"gefunden. So versteckt man üblicherweise Spam vor dem Auge des Besuchers."
|
|
|
|
|
)
|
|
|
|
|
if z["meta_refresh"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"{z['meta_refresh']} Seite(n) leiten Besucher jetzt automatisch auf eine "
|
|
|
|
|
"andere Adresse weiter. Wenn Sie das nicht selbst eingerichtet haben, ist es verdächtig."
|
|
|
|
|
)
|
|
|
|
|
if z["scripts"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Auf {z['scripts']} Seite(n) wurde neuer, auffälliger Programmcode eingebaut. "
|
|
|
|
|
"Bitte von Ihrem Dienstleister prüfen lassen."
|
|
|
|
|
)
|
|
|
|
|
if z["comment_links"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Auf {z['comment_links']} Seite(n) wurden versteckte Links im Quelltext gefunden."
|
|
|
|
|
)
|
|
|
|
|
if z["canonical"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Bei {z['canonical']} Seite(n) wurde die hinterlegte 'Original-Adresse' geändert. "
|
|
|
|
|
"Damit versuchen Angreifer manchmal, Ihre Seite bei Google umzuleiten."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# --- Fremde Adressen / neue Seiten ---
|
|
|
|
|
neue_domains = diff.get("new_external_domains", [])
|
|
|
|
|
if neue_domains:
|
|
|
|
|
beispiele = ", ".join(neue_domains[:3])
|
|
|
|
|
mehr = f" (und {len(neue_domains) - 3} weitere)" if len(neue_domains) > 3 else ""
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Es tauchen Verweise auf neue fremde Internet-Adressen auf: {beispiele}{mehr}. "
|
|
|
|
|
"Das kann harmlos sein (z. B. ein neuer Partner-Link) oder ein Hinweis auf einen Eingriff."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
neue_seiten = diff.get("new_internal_urls", [])
|
|
|
|
|
if neue_seiten:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Es sind {len(neue_seiten)} neue Unterseite(n) hinzugekommen. Haben Sie oder Ihr "
|
|
|
|
|
"Dienstleister die angelegt?"
|
|
|
|
|
)
|
|
|
|
|
fehlende = diff.get("missing_internal_urls", [])
|
|
|
|
|
if fehlende:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"{len(fehlende)} Seite(n), die es früher gab, sind nicht mehr auffindbar."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if z["grosser_text"]:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Auf {z['grosser_text']} Seite(n) wurde ein größerer Textblock neu hinzugefügt."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# --- Datei-Veränderungen (Assets) ---
|
|
|
|
|
if asset_diff and asset_diff.get("changed"):
|
|
|
|
|
n = len(asset_diff["changed"])
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"{n} Datei(en) (z. B. Bilder, Skripte oder Stylesheets) haben sich verändert. "
|
|
|
|
|
"Nach einem geplanten Update ist das normal — sonst bitte prüfen lassen."
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-12 12:18:38 +02:00
|
|
|
# --- Kaputte Links (immer melden, bis behoben) ---
|
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
|
|
|
neue_kaputt = diff.get("new_broken_urls", [])
|
2026-06-12 12:18:38 +02:00
|
|
|
bekannte_kaputt = diff.get("known_broken_urls", [])
|
|
|
|
|
alle_kaputt = len(neue_kaputt) + len(bekannte_kaputt)
|
|
|
|
|
if alle_kaputt:
|
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
|
|
|
saetze.append(
|
2026-06-12 12:18:38 +02:00
|
|
|
f"{alle_kaputt} interne Link(s) führen ins Leere (Seite nicht gefunden). "
|
|
|
|
|
"Bitte prüfen und den Verweis auf der betroffenen Seite korrigieren."
|
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
|
|
|
)
|
|
|
|
|
if ext_links and ext_links.get("broken"):
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"{ext_links['broken']} Link(s) zu anderen Websites funktionieren nicht mehr. "
|
|
|
|
|
"Das ist meist harmlos — die fremde Seite wurde gelöscht oder umbenannt."
|
|
|
|
|
)
|
|
|
|
|
|
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
|
|
|
# --- KI-Inhaltsanalyse ---
|
|
|
|
|
if ai_result and ai_result.get("findings"):
|
|
|
|
|
text_funde = [f for f in ai_result["findings"] if f.get("kind") == "text"]
|
|
|
|
|
bild_funde = [f for f in ai_result["findings"] if f.get("kind") == "image"]
|
|
|
|
|
if text_funde:
|
|
|
|
|
kategorien = sorted({
|
|
|
|
|
_KI_KATEGORIE_TEXT[f["category"]]
|
|
|
|
|
for f in text_funde if f.get("category") in _KI_KATEGORIE_TEXT
|
|
|
|
|
})
|
|
|
|
|
beispiele = (" (z. B. " + ", ".join(kategorien[:3]) + ")") if kategorien else ""
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Eine automatische Inhaltsprüfung hat auf {len(text_funde)} Seite(n) Text "
|
|
|
|
|
f"gefunden, der auffällig ist{beispiele}. "
|
|
|
|
|
"Bitte schauen Sie sich die betroffenen Seiten an."
|
|
|
|
|
)
|
|
|
|
|
if bild_funde:
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"Auf {len(bild_funde)} Bild(ern) wurden möglicherweise problematische "
|
|
|
|
|
"Inhalte erkannt. Bitte prüfen Sie die betroffenen Bilder."
|
|
|
|
|
)
|
2026-06-13 00:50:36 +02:00
|
|
|
if ai_result and ai_result.get("unchecked"):
|
|
|
|
|
n = len(ai_result["unchecked"])
|
|
|
|
|
saetze.append(
|
|
|
|
|
f"{n} Inhalt(e) konnten nicht automatisch geprüft werden, weil der "
|
|
|
|
|
"Prüfdienst gerade nicht erreichbar war. „Nicht geprüft“ bedeutet nicht "
|
|
|
|
|
"„unbedenklich“ — bitte beim nächsten Lauf erneut 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
|
|
|
|
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
|
|
|
if not saetze:
|
|
|
|
|
saetze.append("Es hat sich nichts Verdächtiges verändert.")
|
|
|
|
|
|
|
|
|
|
return saetze
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def was_tun(level: str) -> list[str]:
|
|
|
|
|
"""Konkrete, laienverständliche Handlungsempfehlung je Level."""
|
|
|
|
|
if level == "red":
|
|
|
|
|
return [
|
|
|
|
|
"Öffnen Sie Ihre Website im Browser und schauen Sie, ob etwas Fremdes zu sehen ist.",
|
|
|
|
|
"Informieren Sie die Person oder Firma, die Ihre Website betreut.",
|
|
|
|
|
"Geben Sie nichts frei, solange der Verdacht nicht geklärt ist.",
|
|
|
|
|
]
|
|
|
|
|
if level == "yellow":
|
|
|
|
|
return [
|
|
|
|
|
"Schauen Sie sich die unten genannten Punkte einmal an.",
|
|
|
|
|
"Wenn die Änderung von Ihnen oder Ihrem Dienstleister gewollt war, ist alles in Ordnung.",
|
|
|
|
|
"Gewollte Änderungen bestätigen Sie mit: python -m scanner approve --all",
|
|
|
|
|
]
|
|
|
|
|
return ["Sie müssen nichts tun."]
|