mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-06 22:58:25 +00:00
Make `test-all.sh` the authoritative standalone frontend runner for npm and CI. Restore stale assertions and reject missing, duplicate, removed or undocumented inventory entries. Fixes #1858. Rebased onto `a2f039d4`. Retains release-routing, map scope-state and Scope Audit stylesheet tests, adds `test-packets-local-channels.js` to the sorted runner, and classifies `test-neighbor-map-btn-clip-e2e.js` under browser. Its separate CI browser step is preserved. Inventory: 280 root suites, 167 standalone, 113 requiring separate setup. The icon repair fixes two suites red on master: `test-issue-1648-m2-emoji-scan.js` and `test-issue-1648-m6-final-sweep.js`. Node/live configured-scope confirmations now use the existing accessible Phosphor check sprite. Values, visibility conditions and scanner assertions are preserved. - Red evidence: `89e45a9` inventory assertions; `4e255df` accessible confirmation assertion. The latest rebase also reproduced both unclassified-file failures before adding their entries. This follow-up only changes runner/classification configuration and counts; no test files modified. - Local validation: all 167 standalone suites; 27 M2 browser checks and 16 neighbor geometry checks in Chromium. Syntax, whitespace, PII, CSS-variable and XSS checks passed. - Browser coverage includes populated/empty/null configured scopes in node and live views. - No new dependencies, requests, application settings or Go changes. Workflow outside the unit step matches master. - Windows validation uses process-local UTF-8 settings. Encoding and node-reach confirmation follow-ups remain separate, as requested. ## Preflight override External `run-all.sh` is unavailable; applicable repository checks were run directly.
62 lines
3.1 KiB
JavaScript
62 lines
3.1 KiB
JavaScript
'use strict';
|
|
// Unit test for #14: the mobile RX coverage leaderboard must HTML-escape the
|
|
// pubkey it interpolates into the row markup (data-rx="..." and the truncated
|
|
// fallback label), not only the name. A no-ACL broker / pre-validation rows
|
|
// could carry a non-hex pubkey, and the rest of the row is built by string
|
|
// concatenation, so an unescaped pubkey is an HTML-injection vector.
|
|
//
|
|
// Like test-coverage-gate.js we slice the real row-building expression out of
|
|
// public/rx-coverage.js and evaluate it in a vm sandbox — no hand-copied
|
|
// duplicate — so the test tracks the actual source.
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const vm = require('vm');
|
|
|
|
const src = fs.readFileSync(path.join(__dirname, 'public', 'rx-coverage.js'), 'utf8');
|
|
|
|
// Slice from `var nm = o.name ...` through the end of the returned row string.
|
|
const startMarker = 'var nm = o.name ? escapeHtml(o.name)';
|
|
const endMarker = "}).join('');";
|
|
const startIdx = src.indexOf(startMarker);
|
|
assert.ok(startIdx >= 0, 'could not locate row-builder start in rx-coverage.js');
|
|
const endIdx = src.indexOf(endMarker, startIdx);
|
|
assert.ok(endIdx >= 0, 'could not locate row-builder end in rx-coverage.js');
|
|
const block = src.slice(startIdx, endIdx);
|
|
|
|
// Canonical escapeHtml (public/app.js).
|
|
function escapeHtml(s) {
|
|
if (s == null) return '';
|
|
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
}
|
|
|
|
function renderRow(o) {
|
|
const sandbox = { o: o, i: 0, selectedRx: '', escapeHtml: escapeHtml };
|
|
vm.createContext(sandbox);
|
|
return vm.runInContext('(function () { ' + block + ' })()', sandbox);
|
|
}
|
|
|
|
// Malicious pubkey that would break out of the data-rx attribute and inject a
|
|
// tag if interpolated raw. With escaping, no raw '<', '>' or attribute-closing
|
|
// '"' survives.
|
|
const evil = '"><img src=x onerror=alert(1)>';
|
|
|
|
// Case 1: no name → pubkey used as the visible label fallback too.
|
|
const row1 = renderRow({ pubkey: evil, name: '', receptions: 1, nodes: 1 });
|
|
assert.ok(row1.indexOf('<img') === -1, 'raw <img must not appear in row (label fallback): ' + row1);
|
|
assert.ok(row1.indexOf('data-rx="' + evil + '"') === -1, 'raw pubkey must not appear unescaped in data-rx');
|
|
assert.ok(row1.indexOf('<img') !== -1 || row1.indexOf('">') !== -1, 'pubkey should be HTML-escaped: ' + row1);
|
|
|
|
// Case 2: name present → label is the (escaped) name, but data-rx still carries
|
|
// the pubkey and must be escaped.
|
|
const row2 = renderRow({ pubkey: evil, name: 'Mob', receptions: 2, nodes: 3 });
|
|
assert.ok(row2.indexOf('<img') === -1, 'raw <img must not appear in row (named): ' + row2);
|
|
|
|
// #a11y: the clickable row must be keyboard-operable (role/tabindex) and expose
|
|
// pressed state, so it isn't a mouse-only <div>.
|
|
assert.ok(/role="button"/.test(row2), 'row must have role="button"');
|
|
assert.ok(/tabindex="0"/.test(row2), 'row must be focusable (tabindex)');
|
|
assert.ok(/aria-pressed="(true|false)"/.test(row2), 'row must expose aria-pressed');
|
|
|
|
console.log('rx-coverage pubkey escaping + row a11y OK');
|