Skip to content

feat(alerts): SSID impostor detection from existing neighbor scans - #1393

Merged
gnacho merged 1 commit into
mainfrom
feat/1358-ssid-impostor
Oct 13, 2026
Merged

gnacho merged 1 commit into
mainfrom
feat/1358-ssid-impostor

Conversation

@gnacho

@gnacho gnacho commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Request (#1358)

NetPulse already collects neighbor scans per radio (SSID + BSSID + channel + signal) and already knows its own fleet BSSIDs. That is enough for a high-value security alert with zero new collection: if a scanned BSSID advertises an SSID that belongs to the fleet but is not one of our radios, something is impersonating the network (classic evil-twin signature).

Changes

  • New AgentRegistry.LocalSsids(): fleet SSIDs from agent OwnBssids plus dawn/usteer local APs. The approved list derives from the fleet itself - zero-config, nothing to type by hand.
  • New Live.EvalSsidImpostors(): one bounded query over the last 6h of wifi_scans. A candidate BSSID confirms only with >=2 real scans (distinct timestamps, so a frozen re-pushed payload does not count) separated by >=20 min dwell, and the alert auto-resolves after 2h unseen. Stable alert ID per BSSID; in-memory tracking on purpose: a restarted server re-alerts if the impostor is still around (security beats re-notice).
  • Own BSSIDs come from the agent registry AND SSH-polled radios (iwinfo), so an SSH-only router broadcasting a fleet SSID is not a false positive.
  • Category clients, not signal: signal defaults to LevelNone and a security alert must not be invisible on stock config.
  • Wired after SaveScan in the agent ingest handler (the exact moment the evidence changes); i18n es/en (title, description with vars, hint).

Verification

  • 7 new tests: single sighting does not alert, dwell confirms exactly once, own/foreign BSSID ignored, frozen payload does not confirm, auto-resolve, latest-sighting attribution (row-order regression caught during E2E), LocalSsids composition. Full server suite green.
  • Live E2E on the home fleet: planted an old sighting in wifi_scans + a signed synthetic agent push (ephemeral agent, removed afterwards via the fix(agents): no upgrade flag on stale agents + remove-from-fleet action #1385 DELETE) - alert fired with correct router/signal vars, rendered translated in the alert feed (critical counter, URGENT badge, hint). Test data fully cleaned.

Closes #1358

…1358)

An unknown BSSID advertising a fleet SSID is the classic evil-twin
signature. No new collection needed: passive neighbor scans already land
in wifi_scans, and fleet BSSIDs/SSIDs already come from the agent
registry (#1087) plus SSH-polled radios.

- New AgentRegistry.LocalSsids() (OwnBssids + dawn/usteer local APs).
- Live.EvalSsidImpostors(): one bounded query over the last 6h of
  wifi_scans. A candidate confirms only with >=2 real scans (distinct
  timestamps, so a frozen re-pushed payload does not count) separated by
  >=20 min dwell, and auto-resolves after 2h unseen. Alert id is stable
  per BSSID; tracking lives in memory on purpose - a restarted server
  re-alerts if the impostor is still around (security over re-notice).
- Category clients, not signal: signal defaults to LevelNone and a
  security alert must not be invisible on stock config.
- Wired after SaveScan in the agent ingest handler (the exact moment the
  evidence changes); i18n es/en (title, description with vars, hint).

Tests: single sighting does not alert, dwell confirms once, own/foreign
BSSID ignored, frozen payload does not confirm, auto-resolve, LocalSsids
composition.

Closes #1358
@gnacho
gnacho merged commit 46037ca into main Oct 13, 2026
2 checks passed
@gnacho
gnacho deleted the feat/1358-ssid-impostor branch October 13, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSID impostor alert (evil twin lite) from existing neighbor scans

1 participant