harden(opsec): disguise the honeypot naming on the Threats + settings pages too

Follow-up to bf4366d (audit log): remove the last user-visible mentions of the deception layer
so nothing in the running panel names it "Honeypot" — a shoulder-surf, a shared screen, or a
breached admin session can no longer tell the decoy paths are a trap. Admin-only surfaces, but
closes the gap for full coverage.

- Threats page: subtitle "Honeypot activity" -> "Attack activity"; feed title "Honeypot events"
  -> "Security events"; the Honeytoken KPI -> "Leaked credentials" (DE mirrors).
- Settings: the "Honeypot" status block -> "Threat detection" / "Angriffserkennung"; subtitle and
  note reworded to drop "deception layer"/"decoy paths"/"Täuschung"/"Köder", and the note no longer
  prints the CLUSEV_HONEYPOT env-var name (it named the mechanism).

DB action keys, the CLUSEV_HONEYPOT env var itself, config, and dev code comments are unchanged
(the source is public AGPL, so comments carry no OPSEC value — only the runtime UI matters). A new
regression test asserts the Threats page renders the disguised labels and never shows
"Honeypot"/"Honeytoken"/"Täuschung"/"Köder". 619 tests, de/en parity, Pint; a scan confirms zero
visible "Honeypot"/"Honeytoken" left in any lang value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/v1-foundation
boban 2026-07-05 19:06:46 +02:00
parent bf4366dfc9
commit aa854916ce
5 changed files with 42 additions and 16 deletions

View File

@ -88,10 +88,12 @@ return [
'lp_unban_all_notify' => 'Alle Sperren aufgehoben.',
'lp_lockout_note' => 'Eingeloggte Operatoren werden nie geblockt und können hier ihre eigene IP entsperren. Notfall: clusev unban <ip> auf der Host-Shell.',
// Honeypot-Statusanzeige (per Umgebungsvariable gesteuert, kein Schalter)
'hp_title' => 'Honeypot',
'hp_subtitle' => 'Täuschungsebene gegen Angreifer-Sonden',
'hp_note' => 'Köder-Pfade sperren scannende IP-Adressen sofort. Per CLUSEV_HONEYPOT gesteuert. Treffer erscheinen unter Bedrohungen.',
// Angriffserkennungs-Statusanzeige (per Umgebungsvariable gesteuert, kein Schalter). Bewusst
// neutral benannt (kein "Honeypot"/"Köder"/"Täuschung", Env-Var-Name ausgelassen) — die
// Deception-Ebene darf sich in der UI nicht selbst verraten.
'hp_title' => 'Angriffserkennung',
'hp_subtitle' => 'Automatische Sperre scannender Angreifer',
'hp_note' => 'Sperrt scannende IP-Adressen automatisch und sofort. Über eine Umgebungsvariable steuerbar. Treffer erscheinen unter Bedrohungen.',
'hp_on' => 'Aktiv',
'hp_off' => 'Inaktiv',
];

View File

@ -1,22 +1,24 @@
<?php
// Bedrohungen (Honeypot-Dashboard): Sondenverlauf, KPIs, gesperrte IPs (R16).
// Bedrohungen (Angriffserkennung): Sondenverlauf, KPIs, gesperrte IPs (R16). Sichtbare Strings
// bewusst neutral gehalten (kein "Honeypot"/"Köder"/"Täuschung") — die Deception-Ebene darf sich
// dem Betrachter nicht selbst verraten. Die DB-Action-Keys bleiben security.honeypot_*.
return [
// Header
'eyebrow' => 'Sicherheit',
'title' => 'Bedrohungen',
'subtitle' => 'Honeypot-Aktivität und gesperrte IP-Adressen',
'subtitle' => 'Angriffsaktivität und gesperrte IP-Adressen',
'probes_pill' => ':count Sonden (24h)',
// KPI-Kacheln
'kpi_login_attempts' => 'Login-Versuche (24h)',
'kpi_probes' => 'Sonden (24h)',
'kpi_banned' => 'Gesperrte IPs',
'kpi_honeytoken' => 'Honeytoken',
'kpi_honeytoken' => 'Geleakte Zugangsdaten',
'kpi_top_ip' => 'Top-Angreifer',
// Bedrohungs-Feed
'feed_title' => 'Honeypot-Ereignisse',
'feed_title' => 'Sicherheitsereignisse',
'feed_subtitle' => 'neueste zuerst',
'tried_creds' => 'Versuchte Zugangsdaten',
'search_label' => 'Bedrohungen durchsuchen',

View File

@ -88,10 +88,12 @@ return [
'lp_unban_all_notify' => 'All blocks cleared.',
'lp_lockout_note' => 'Logged-in operators are never blocked and can unblock their own IP here. Emergency: clusev unban <ip> on the host shell.',
// Honeypot status indicator (env-driven, no toggle)
'hp_title' => 'Honeypot',
'hp_subtitle' => 'Deception layer against attacker probes',
'hp_note' => 'Decoy paths instantly ban scanning IP addresses. Controlled via CLUSEV_HONEYPOT. Hits appear under Threats.',
// Threat-detection status indicator (env-driven, no toggle). Deliberately neutral naming
// (no "honeypot"/"decoy"/"deception", env-var name omitted) — the deception layer must not
// reveal itself in the UI.
'hp_title' => 'Threat detection',
'hp_subtitle' => 'Automatic ban of scanning attackers',
'hp_note' => 'Automatically bans scanning IP addresses on sight. Controlled via an environment variable. Hits appear under Threats.',
'hp_on' => 'Enabled',
'hp_off' => 'Disabled',
];

View File

@ -1,22 +1,24 @@
<?php
// Threats (honeypot dashboard): probe feed, KPIs, banned IPs (R16).
// Threats (attack-detection dashboard): probe feed, KPIs, banned IPs (R16). Visible strings kept
// deliberately neutral (no "honeypot"/"decoy"/"deception") so the deception layer never reveals
// itself to a viewer. The DB action keys stay security.honeypot_*.
return [
// Header
'eyebrow' => 'Security',
'title' => 'Threats',
'subtitle' => 'Honeypot activity and banned IP addresses',
'subtitle' => 'Attack activity and banned IP addresses',
'probes_pill' => ':count probes (24h)',
// KPI tiles
'kpi_login_attempts' => 'Login attempts (24h)',
'kpi_probes' => 'Probes (24h)',
'kpi_banned' => 'Banned IPs',
'kpi_honeytoken' => 'Honeytokens',
'kpi_honeytoken' => 'Leaked credentials',
'kpi_top_ip' => 'Top attacker',
// Threat feed
'feed_title' => 'Honeypot events',
'feed_title' => 'Security events',
'feed_subtitle' => 'newest first',
'tried_creds' => 'Attempted credentials',
'search_label' => 'Search threats',

View File

@ -129,6 +129,24 @@ class ThreatsPageTest extends TestCase
->assertViewHas('login_attempts_24h', 1);
}
public function test_the_threats_page_never_reveals_the_deception_layer(): void
{
// OPSEC: even the admin-only intel page must not NAME the mechanism — a shoulder-surf, a
// shared screen, or a breached admin session must not learn the decoy paths are a trap.
AuditEvent::create(['actor' => 'system', 'action' => 'security.honeypot_hit', 'target' => '/wp-admin', 'ip' => self::ATTACKER_IP]);
AuditEvent::create(['actor' => 'system', 'action' => 'security.honeytoken_used', 'target' => 'db_password', 'ip' => self::ATTACKER_IP]);
Livewire::actingAs($this->admin())
->test(Index::class)
->assertSee('Angriffsversuch') // disguised event label
->assertSee('Sicherheitsereignisse') // disguised feed title
->assertSee('Geleakte Zugangsdaten') // disguised honeytoken KPI
->assertDontSee('Honeypot')
->assertDontSee('Honeytoken')
->assertDontSee('Täuschung') // "Täuschungsebene"
->assertDontSee('Köder'); // "Köder-Pfade"
}
public function test_tried_credentials_are_html_escaped(): void
{
// Attacker-controlled meta must render escaped, never as live markup.