import { test } from "node:test"; import assert from "node:assert/strict"; import { escapeMarkdown, htmlToText } from "../src/adapters/discord/html-to-text.ts"; // Everything an email carries is attacker-controlled: anyone who learns an // address can send to it, and Discord renders markdown in bot messages. The // headline risk is a masked link, `[label](url)`, arriving as a clickable // link whose visible text lies about where it goes. test("markdown escaping", async (t) => { await t.test("defuses a masked link in plain text", () => { const out = escapeMarkdown("Verify at [www.yourbank.com](https://evil.example/steal)"); assert.match(out, /\\\[www\.yourbank\.com\\\]/); }); await t.test("defuses a masked link written into an anchor's own label", () => { const out = htmlToText('[www.yourbank.com](https://evil.example)'); assert.match(out, /\\\[/); assert.ok(out.includes("()"), out); }); await t.test("defuses brackets smuggled in as HTML entities", () => { // Entity decoding has to happen before escaping, or [ slips through // as a live bracket. assert.ok(htmlToText("

[label](https://evil.example)

").includes("\\[label\\]")); }); await t.test("escapes formatting characters", () => { assert.doesNotMatch(escapeMarkdown("**b** _i_ `c` ~s~ ||x||"), /(? { assert.ok(escapeMarkdown("\\[x](y)").startsWith("\\\\\\[")); }); await t.test("leaves ordinary punctuation readable", () => { // Escaping these too would be correct but makes normal prose unreadable, // and they're cosmetic at worst: no link can be forged with them. const prose = "Order #123 (shipped) - 50% off > see details"; assert.equal(escapeMarkdown(prose), prose); }); }); test("link rendering", async (t) => { await t.test("keeps the label and shows the real destination", () => { assert.equal( htmlToText('Click here'), "Click here ()" ); }); await t.test("never escapes the URL itself", () => { // The URL is held out of the text while escaping runs; a mangled one // would be worse than useless. assert.doesNotMatch(htmlToText('x'), /\\/); }); await t.test("keeps each URL with its own label", () => { const out = htmlToText('A
B'); assert.ok(out.includes("A ()"), out); assert.ok(out.includes("B ()"), out); }); await t.test("shows a repeated destination only once", () => { const out = htmlToText('One Two'); assert.equal((out.match(/x\.example/g) ?? []).length, 1); }); await t.test("drops javascript: URLs but keeps the text", () => { assert.equal(htmlToText('bad'), "bad"); }); await t.test("drops image-only links entirely", () => { assert.equal(htmlToText(''), ""); }); }); test("content extraction", async (t) => { await t.test("strips scripts, styles and comments", () => { assert.equal(htmlToText("

Hi

"), "Hi"); }); await t.test("strips invisible preheader padding", () => { assert.equal(htmlToText("

Hi​​­

"), "Hi"); }); await t.test("strips short screen-reader-only labels", () => { assert.equal(htmlToText('Company logo

Real

'), "Real"); }); await t.test("keeps long hidden blocks, which are usually real content", () => { const long = "word ".repeat(100); assert.ok(htmlToText(`
${long}
`).includes("word")); }); });