- Top 5 recon cards restyled as stat cards: value + sub-line + mini chart, consistent 190px anatomy; Handshakes links to the handshakes page; Previous Scans shows count/latest plus a compact picker + action row - Channel map records lobe geometry (canvas.__reconLobes / __reconLobesHit); hovering lists the networks under the cursor, click pins the tooltip, mouseleave hides it; empty states hide the tooltip too - Recon focus sidebar gains 'Send to PineAP — Twin this network': routes to Evil WPA (psk2/sae/owe from recon encryption) or OpenAP (with BSSID) and prefills the form; one-shot PineAPPrefill is consumed by the attack launcher and never auto-deploys or leaks into manual forms - Existing Actions verified end-to-end and hardened: capture/stop handshake sync the auto-collect toggle, examine buttons disable while pending - Recon resilience: loadDetail retries after failed render or 503 (no more permanently blank page), scan-list errors surface in the scan bar, poll guard prevents overlapping loads, chart draws are individually isolated - Auto-collect toggle re-syncs from get_config on the slow poll so the UI tracks the pager's own settings
124 lines
6.0 KiB
Markdown
124 lines
6.0 KiB
Markdown
# Mark VIII UI Hardening + Recon Polish — Design
|
|
|
|
Date: 2026-08-18
|
|
Status: Approved in advance by user (auto-approve; user unavailable for review)
|
|
|
|
## Context
|
|
|
|
Mark VIII (web UI for the WiFi Pineapple Pager) has been through major dev
|
|
changes. The user runs a live test tomorrow and needs the UI rock solid.
|
|
Verified on device:
|
|
|
|
- MCP server works: `tools/list` returns 16 tools; `device.state`,
|
|
`recon.aps` respond correctly.
|
|
- API endpoints used by the Recon "Actions" buttons work
|
|
(`/api/recon/examine`, `/api/pineap/set_config`).
|
|
- Recon detail endpoint is slow (~1.5 s) and can 503 when the sqlite DB is
|
|
busy; the UI swallows errors silently and can stay blank.
|
|
|
|
## Findings
|
|
|
|
1. **Recon top cards (5)**: equal 200 px cards with 20 px titles, centered
|
|
content, charts sized for wide cards. "Previous Scans" crams 5 icon
|
|
buttons + a 50+-option select into a ~220 px card → buttons stack into
|
|
three rows. "Channel Distribution" bar chart is unreadable at card width.
|
|
Handshakes toggle text wraps awkwardly.
|
|
2. **Channel map**: canvas lobes render but have zero interactivity — no way
|
|
to see which networks are under the cursor.
|
|
3. **Focus sidebar Actions**: "Capture WPA Handshakes" / "Stop Handshake
|
|
Capture" / "Examine BSSID" / "Examine Channel" work at the API level but
|
|
give weak feedback; there is no path from a recon target to the PineAP
|
|
evil-twin form.
|
|
4. **Robustness**: `loadDetail()` in the recon view swallows render/network
|
|
errors with `.catch(() => {})`; if rendering throws after `detailId` is
|
|
set, the page stays blank forever (guard short-circuits). `load()` has no
|
|
pending guard → overlapping polls. `hsAuto` (loghandshake) checkbox is
|
|
only synced at view creation — it drifts from the pager's own settings
|
|
when changed elsewhere (pager UI / another browser).
|
|
|
|
## Changes
|
|
|
|
### 1. Recon title cards — redesign (`views.js` + `app.css`)
|
|
|
|
New card anatomy (all five consistent):
|
|
- Compact header: 12 px uppercase muted title (clickable where it links).
|
|
- Primary value: 28 px bold.
|
|
- Sub-line: 12 px muted context text.
|
|
- Optional mini chart: fixed 96 px tall.
|
|
|
|
Cards (`flex: 1 1 0; min-width: 170px; height: 190px` — five fit one row
|
|
even in a ~1000 px pane):
|
|
1. **Wireless Landscape** — value: total networks (APs); sub: "N clients ·
|
|
M unassociated"; mini doughnut (APs/Clients/Unassociated).
|
|
2. **Channel Distribution** — value: busiest channel "CH 6"; sub: "N of K
|
|
APs on CH 6 · channels seen"; mini bar chart of channel counts.
|
|
3. **Encryption Landscape** — value: top encryption bucket; sub: "N of K
|
|
APs"; mini doughnut of buckets.
|
|
4. **Handshakes** — value: handshake count (links to
|
|
`#/recon/handshakes`); sub: "captured"; auto-collect toggle (restyled).
|
|
5. **Previous Scans** — value: scan count; sub: latest scan start time;
|
|
compact select row + single row of icon buttons.
|
|
|
|
Empty states get copy that matches the card ("No landscape data yet —
|
|
run a scan").
|
|
|
|
### 2. Channel map hover (`chart.js` + `views.js` + `app.css`)
|
|
|
|
- `MiniChart.channelMap` records per-lobe geometry on the canvas:
|
|
`canvas.__reconLobes = [{ ap, cx, half, topPx, basePx }]` (CSS px).
|
|
- `views.js` attaches `mousemove`/`mouseleave`/`click` to the map canvas:
|
|
- hit test: `t=(mx-cx)/half`, lift = `0.5+0.5·cos(πt)`, hovered if
|
|
`my >= basePx - lift·peakPx - 4` and `my <= basePx + 8`;
|
|
- tooltip div (absolute, inside `.recon-map-box`) lists every network
|
|
under the cursor: SSID/MAC/channel/freq/signal/encryption/vendor;
|
|
- click pins the tooltip until the next move or click;
|
|
- `mouseleave` hides it.
|
|
- Lobe geometry regenerates on every `renderChannelMap()` (redraw), so
|
|
stale geometry is impossible.
|
|
|
|
### 3. Focus sidebar: Actions validation + Send to PineAP (`views.js`)
|
|
|
|
- Existing buttons stay; toast feedback improved (already verified working
|
|
at API level; re-verified end-to-end in browser).
|
|
- New primary button **"Send to PineAP — Twin this network"**:
|
|
- encryption bucket `Open` → navigate `#/pineap/open` with prefill
|
|
`{ssid, hidden, channel, bssid}`;
|
|
- anything else → navigate `#/pineap/evilwpa` with prefill
|
|
`{ssid, hidden, channel, enctype}` where enctype maps from recon
|
|
encryption: SAE→`sae`, OWE→`owe`, WPA3-only→`sae`, else `psk2`;
|
|
- Enterprise networks still go to Evil WPA (psk2) — noted in the prefill
|
|
banner.
|
|
- Prefill mechanism: `window.PineAPPrefill = { set, consume }` (module
|
|
singleton in `views.js`); `consume()` clears after use so a stale prefill
|
|
never leaks into a manually opened form.
|
|
- `attackLauncher()` consumes the prefill when building the form (SSID,
|
|
hidden, channel via `chanSelect`, enctype, BSSID) and renders a muted
|
|
banner: "Prefilled from Recon — verify, set the passphrase, then Deploy."
|
|
Deploy is never triggered automatically (no attacks without an explicit
|
|
user action).
|
|
|
|
### 4. Robustness / sync hardening (`views.js`)
|
|
|
|
- `loadDetail()`: on fetch failure keep `detailId` unset so the poll
|
|
retries; surface "Scan data unavailable — retrying…" in the scan status
|
|
line; wrap the render body so one chart's exception cannot blank the
|
|
table (each chart draw also wrapped individually).
|
|
- `drawCharts()`: wrap each chart section in try/catch.
|
|
- `load()`: `loadPending` guard against overlapping polls; surface scan-list
|
|
errors in the status line.
|
|
- `hsAuto` re-syncs from `/api/pineap/get_config` on the 30 s slow poll
|
|
(stays in sync with the pager's own UI/settings changes).
|
|
- Version bumps in `index.html` for `app.css`, `chart.js`, `views.js`.
|
|
|
|
### 5. Verification
|
|
|
|
- Backend unchanged → existing `tests/` still pass (run the suite).
|
|
- Deploy via `scripts/deploy.sh --password` (device: 172.16.52.1).
|
|
- End-to-end in browser (device UI):
|
|
- login; recon page: cards populated with scan data; no blank-page state;
|
|
- click an AP row → focus sidebar → each Action button verified by
|
|
reading back state (`get_config`) and API responses;
|
|
- Send to PineAP → form pre-filled on the right tab (open vs WPA target);
|
|
- channel map click → tooltip shows networks under cursor;
|
|
- reboot-resilience spot check via service restart (pagerwebui restart).
|