integrity_scanner_fuer_stat.../scanner/plain.py

182 lines
7.1 KiB
Python
Raw Normal View History

"""
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
def klartext_befunde(
diff: dict | None = None,
cloak_diff: dict | None = None,
asset_diff: dict | None = None,
ext_links: dict | None = None,
) -> 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."
)
# --- Kaputte Links (immer melden, bis behoben) ---
neue_kaputt = diff.get("new_broken_urls", [])
bekannte_kaputt = diff.get("known_broken_urls", [])
alle_kaputt = len(neue_kaputt) + len(bekannte_kaputt)
if alle_kaputt:
saetze.append(
f"{alle_kaputt} interne Link(s) führen ins Leere (Seite nicht gefunden). "
"Bitte prüfen und den Verweis auf der betroffenen Seite korrigieren."
)
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."
)
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."]