Files
Mark-VIII/docs/superpowers/plans/2026-08-19-encryption-card-redesign.md
T
bzuccaro 52f9f6ecd5 chore: bump version to 1.2; scrub personal details from docs
- Version 1.1 -> 1.2 in the hak5 manifest and MCP serverInfo.
- Removed leaked personal details from process docs: device root password,
  the user's personal iPhone SSID (authorized-test target), the local
  /Users/... checkout path, and the device IP where it appeared alongside
  the password. Replaced with <device-password>, <authorized-test-ssid>,
  <repo>, and <device-ip> placeholders.
2026-08-19 08:21:28 -05:00

8.2 KiB

Encryption Landscape Card — Ring + Key Redesign Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the Encryption Landscape recon card's text headline + clipped canvas legend with a full-size plain-hole ring graph and a wrapping HTML legend (dot + label + count per encryption bucket).

Architecture: Single-file client-side change: the recon view in views.js drops the encValue/encSub text nodes, draws the doughnut with legend:false and a taller height, and populates a new HTML legend container from the existing encCounts bucket map. chart.js's MiniChart.doughnut is unchanged (its canvas legend option is simply no longer used by the enc card). CSS adds flex-wrap legend styles.

Tech Stack: Vanilla JS (no framework), canvas via MiniChart.doughnut in chart.js, plain CSS in app.css. Device deploy via scripts/deploy.sh --password '<device-password>'. Tests: none exist for the frontend; verification is via the deployed device + backend test suite (must stay green).

Global Constraints

  • Do not alter data source, reconEncBucket, per-scan bucketing, re-sync, or the other four recon cards.
  • Legend entries: colored dot + label + count, format ● WPA2 54; buckets with zero APs hidden.
  • Ring center hole stays plain (no text).
  • Keep MiniChart.doughnut's legend option in chart.js (used by no caller after this change, but harmless).
  • Empty state text stays "No encryption data yet — run a scan."
  • No comments added to code unless already present in the surrounding style.
  • Deploy and verify on the device; backend tests/ suite must stay green (291 tests).

Task 1: Ring + HTML legend for Encryption Landscape card

Files:

  • Modify: payload/user/remote_access/pager-webui/www/js/views.js:1497-1507 (card markup)
  • Modify: payload/user/remote_access/pager-webui/www/js/views.js:2256-2322 (drawCharts enc block)
  • Modify: payload/user/remote_access/pager-webui/www/css/app.css (after line 328)
  • Test: none (frontend); verify via device sweep

Interfaces:

  • Consumes: RECON_ENC_BUCKETS (6 names, views.js:852), RECON_ENC_COLORS (6 colors, views.js:851), encCounts object {bucketName: count}, reconEncBucket(a.encryption).

  • Produces: DOM div#recon-enc-legend under the enc card body, populated by drawCharts; canvas#recon-encryption redrawn with { legend: false, height: 120 }. encValue/encSub variables are removed.

  • Step 1: Edit the enc card markup in views.js

Replace lines 1497-1507:

  const encBody = titleCard('Encryption Landscape', null);
  const encValue = h('div', { class: 'recon-card-value', text: '—' });
  const encSub = h('div', { class: 'recon-card-sub', text: '' });
  encBody.appendChild(encValue);
  encBody.appendChild(encSub);
  const encBox = h('div', { class: 'recon-chart-box' });
  encBody.appendChild(encBox);
  const encCanvas = h('canvas', { id: 'recon-encryption' });
  encBox.appendChild(encCanvas);
  const encEmpty = h('div', { class: 'recon-no-data', text: 'No encryption data yet — run a scan.' });
  encBox.appendChild(encEmpty);

with:

  const encBody = titleCard('Encryption Landscape', null);
  const encBox = h('div', { class: 'recon-chart-box' });
  encBody.appendChild(encBox);
  const encCanvas = h('canvas', { id: 'recon-encryption' });
  encBox.appendChild(encCanvas);
  const encEmpty = h('div', { class: 'recon-no-data', text: 'No encryption data yet — run a scan.' });
  encBox.appendChild(encEmpty);
  const encLegend = h('div', { class: 'recon-enc-legend' });
  encBody.appendChild(encLegend);
  • Step 2: Remove the enc text-headline computation in drawCharts

Replace lines 2256-2266 (the encCounts loop stays — only the topEnc block goes):

    let topEnc = null, topEncN = 0;
    Object.keys(encCounts).forEach((k) => {
      if (encCounts[k] > topEncN) { topEnc = k; topEncN = encCounts[k]; }
    });
    encValue.textContent = topEnc || '—';
    encSub.textContent = topEnc ? topEncN + ' of ' + n + ' APs' : '';

with nothing (delete those lines). The encCounts computation immediately above must remain.

  • Step 3: Rewrite the enc chart block in drawCharts

Replace lines 2308-2322:

    const enc = document.getElementById('recon-encryption');
    if (enc && typeof MiniChart !== 'undefined' && MiniChart.doughnut) {
      try {
        if (aps.length) {
          MiniChart.doughnut(enc, RECON_ENC_BUCKETS.map((k, i) => ({
            label: k, value: encCounts[k] || 0, color: RECON_ENC_COLORS[i]
          })), { legend: true, height: 66 });
          enc.classList.remove('hidden');
          encEmpty.classList.add('hidden');
        } else {
          enc.classList.add('hidden');
          encEmpty.classList.remove('hidden');
        }
      } catch (e) {}
    }

with:

    const enc = document.getElementById('recon-encryption');
    if (enc && typeof MiniChart !== 'undefined' && MiniChart.doughnut) {
      try {
        if (aps.length) {
          MiniChart.doughnut(enc, RECON_ENC_BUCKETS.map((k, i) => ({
            label: k, value: encCounts[k] || 0, color: RECON_ENC_COLORS[i]
          })), { legend: false, height: 120 });
          enc.classList.remove('hidden');
          encEmpty.classList.add('hidden');
        } else {
          enc.classList.add('hidden');
          encEmpty.classList.remove('hidden');
        }
      } catch (e) {}
    }
    const encLegend = document.getElementById('recon-enc-legend');
    if (encLegend) {
      encLegend.innerHTML = '';
      RECON_ENC_BUCKETS.forEach((k, i) => {
        const c = encCounts[k] || 0;
        if (!c) return;
        const entry = h('div', { class: 'recon-enc-entry' });
        const dot = h('span', { class: 'recon-enc-dot' });
        dot.style.background = RECON_ENC_COLORS[i];
        entry.appendChild(dot);
        entry.appendChild(h('span', { class: 'recon-enc-label', text: k }));
        entry.appendChild(h('span', { class: 'recon-enc-count', text: String(c) }));
        encLegend.appendChild(entry);
      });
    }
  • Step 4: Add the legend CSS to app.css

Insert after line 328 (the .recon-no-data rule):

.recon-enc-legend { display: flex; flex-wrap: wrap; gap: 2px 10px; margin-top: 4px; align-items: baseline; }
.recon-enc-entry { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--text); }
.recon-enc-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.recon-enc-label { color: var(--muted); }
.recon-enc-count { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
  • Step 5: Syntax check both JS files

Run: node --check payload/user/remote_access/pager-webui/www/js/views.js Run: node --check payload/user/remote_access/pager-webui/www/js/chart.js Expected: exit 0, no output.

  • Step 6: Run the backend test suite

Run: cd <repo> && python3 -m pytest tests/ -q 2>&1 | tail -3 Expected: 291 passed (or the current passing count) — no regressions from unrelated files.

  • Step 7: Deploy to the device

Run: cd <repo> && ./scripts/deploy.sh --password '<device-password>' Expected: deploy completes with EXTRACT_OK / success output.

  • Step 8: Verify the enc card on the device

Recreate the CDP venv if absent (python3 -m venv /tmp/cdpenv2 && /tmp/cdpenv2/bin/pip install -q websocket-client), then drive headless Chrome against http://:8080 (login <device-password>, go to #/recon, wait ~20s) and assert:

  1. document.getElementById('recon-encryption') canvas has non-zero width attribute and the card is visible (not .hidden).
  2. document.getElementById('recon-enc-legend') contains entries whose text matches /WPA2/ and /\d+/, and no recon-card-value element exists inside the enc card.
  3. Zero Runtime.exceptionThrown events. Expected: all three pass; screenshots unavailable, text-state assertions only.
  • Step 9: Commit
cd <repo> && git add payload/user/remote_access/pager-webui/www/js/views.js payload/user/remote_access/pager-webui/www/css/app.css && git commit -m "ui: encryption landscape card — ring + HTML legend with counts"