'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 = "o.nodes + '';";
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 + endMarker.length);
// Canonical escapeHtml (public/app.js).
function escapeHtml(s) {
if (s == null) return '';
return String(s).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 = '">
';
// 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('
.
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');