File size: 2,652 Bytes
412a862 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /* XSS regression tests for the rendering helpers in app.js.
Run: node static/app.test.js
Covers the audit's confirmed vectors: custom repo id, lookup/parser/narrator
errors, and model-generated narrator output. esc() and safeUrl() are pure and
run under Node; sanitizeHtml() needs a DOM, so it is exercised only when one
is present (browser / jsdom) and otherwise reported as skipped. */
const { esc, safeUrl, sanitizeHtml } = require("./app.js");
let pass = 0, fail = 0;
function ok(cond, msg) { if (cond) { pass++; } else { fail++; console.error("FAIL:", msg); } }
// The exact payload from the audit, entered as a custom Hugging Face model id.
const PAYLOAD = `<img src=x onerror="document.documentElement.dataset.fitcheckXss='yes'">`;
// 1. esc() neutralises the proven exploit (repo id path, error path, narrator).
const e = esc(PAYLOAD);
ok(!e.includes("<"), "esc removes '<'");
ok(!e.includes(">"), "esc removes '>'");
ok(!/onerror=/.test(e) || !e.includes('"'), "esc breaks the onerror attribute (quotes escaped)");
ok(e.includes("<img"), "esc encodes the tag as text");
// 2. esc handles the field types from the audit (all plain-text sinks).
ok(esc(`"><script>alert(1)</script>`).indexOf("<script>") === -1, "esc neutralises script tag in errors/output");
ok(esc(null) === "" && esc(undefined) === "", "esc tolerates null/undefined");
ok(esc("plain text 12 GB") === "plain text 12 GB", "esc leaves safe text intact");
// 3. safeUrl blocks javascript: and other non-http(s) schemes (href sinks).
ok(safeUrl("javascript:alert(1)") === "#", "safeUrl blocks javascript:");
ok(safeUrl("data:text/html,<script>") === "#", "safeUrl blocks data:");
ok(safeUrl(" JavaScript:alert(1)") === "#", "safeUrl blocks scheme with whitespace/case");
ok(safeUrl("https://huggingface.co/x") === "https://huggingface.co/x", "safeUrl allows https");
ok(safeUrl("http://example.com") === "http://example.com", "safeUrl allows http");
// 4. sanitizeHtml (engine rich-text fields) — only if a DOM exists.
if (typeof document !== "undefined") {
ok(!/<script/i.test(sanitizeHtml("<b>ok</b><script>alert(1)</script>")), "sanitizeHtml drops <script>");
ok(!/onerror/i.test(sanitizeHtml(`<img src=x onerror="x()">`)), "sanitizeHtml drops onerror / img");
ok(sanitizeHtml("<b>bold</b>").includes("<b>bold</b>"), "sanitizeHtml keeps allowed <b>");
ok(sanitizeHtml(`<a href="javascript:x">y</a>`).indexOf("javascript:") === -1, "sanitizeHtml strips javascript: href");
} else {
console.log("(sanitizeHtml DOM tests skipped: no document in this runtime)");
}
console.log(`\n${pass} passed, ${fail} failed.`);
process.exit(fail ? 1 : 0);
|