Tactical Media Broadcast Hub — initial release

Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
This commit is contained in:
glass beadian 2026-10-10 11:56:42 +00:00
commit aa5e262f3a
880 changed files with 111042 additions and 0 deletions

View file

@ -0,0 +1,207 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { ATTACKER_ORIGIN, FORGED_DOCUMENTS, MALICIOUS_HTML, MALICIOUS_LINK_PREVIEWS, MALICIOUS_PLAIN } from "./corpus.js";
import { parseProviderHtml } from "./html.js";
import { parsePlainText } from "./plain.js";
import { DisplayName, LinkPreviewCard, RichText } from "./react.js";
import { parseSafeHttpUrl } from "./url.js";
const PROXY_PREFIX = "/api/media-proxy?url=";
const ALLOWED_TAGS = new Set(["div", "p", "a", "span", "br", "img", "strong", "bdi"]);
const ALLOWED_ATTRS = new Set([
"class", "dir", "href", "rel", "target", "referrerpolicy", "src", "alt", "title",
"loading", "decoding", "draggable", "data-truncated", "data-confusable",
]);
function unescapeAttr(value: string): string {
return value.replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
}
/**
* Asserts rendered markup is inert: only allowlisted tags/attributes, every
* href an absolute credential-free http(s) URL with the safe rel, every src
* routed through the media proxy (so nothing is fetched from an attacker
* origin), and no text that the browser could re-parse as markup.
*/
function assertInert(html: string): void {
const tagRe = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:\s+[a-zA-Z-]+(?:="[^"]*")?)*)\s*\/?>/g;
const stripped = html.replace(tagRe, "");
// Anything left that looks like a tag would mean unescaped markup leaked out.
expect(stripped).not.toMatch(/<[a-zA-Z!/?]/);
for (const match of html.matchAll(tagRe)) {
const [, closing, tag, attrs = ""] = match;
if (closing) {
expect(attrs.trim()).toBe("");
continue;
}
expect(ALLOWED_TAGS, `tag <${tag}> in ${html}`).toContain(tag!.toLowerCase());
const attrMap = new Map<string, string>();
for (const attr of attrs.matchAll(/([a-zA-Z-]+)(?:="([^"]*)")?/g)) {
const name = attr[1]!.toLowerCase();
const value = unescapeAttr(attr[2] ?? "");
expect(ALLOWED_ATTRS, `attribute ${name} in ${html}`).toContain(name);
expect(name).not.toMatch(/^on/);
attrMap.set(name, value);
if (name === "href") {
expect(value).toMatch(/^https?:\/\/[^/]/);
expect(parseSafeHttpUrl(value)?.href).toBe(value);
}
if (name === "src") {
expect(value.startsWith(PROXY_PREFIX), `src ${value}`).toBe(true);
expect(value).not.toMatch(/^\/\//);
}
if (name !== "href" && name !== "src") expect(value).not.toMatch(/(javascript|vbscript|data):/i);
}
if (tag === "a") {
expect(attrMap.get("rel")).toBe("noopener noreferrer nofollow ugc");
expect(attrMap.get("referrerpolicy")).toBe("no-referrer");
}
if (tag === "img") expect(attrMap.get("referrerpolicy")).toBe("no-referrer");
}
expect(html).not.toMatch(/dangerouslySetInnerHTML|srcset|srcdoc|style=/i);
}
/** Every request-causing attribute (src) targets our own origin only. */
function assertNoAttackerRequests(html: string): void {
for (const m of html.matchAll(/\ssrc="([^"]*)"/g)) {
expect(m[1]!.startsWith(PROXY_PREFIX)).toBe(true);
}
expect(html).not.toMatch(new RegExp(`src="https?://[^"]*${ATTACKER_ORIGIN.replace(".", "\\.")}`));
}
const emojis = [
{ shortcode: "blobcat", url: "https://cdn.ok.example/blobcat.png" },
{ shortcode: "evil", url: "javascript:alert(1)" },
{ shortcode: "x\" onerror=\"alert(1)", url: "https://cdn.ok.example/x.png" },
];
describe("malicious HTML corpus", () => {
it.each(MALICIOUS_HTML.map((p) => [p]))("renders inert: %s", (payload) => {
const doc = parseProviderHtml(payload, { emojis });
const html = renderToStaticMarkup(<RichText doc={doc} />);
assertInert(html);
assertNoAttackerRequests(html);
expect(JSON.stringify(doc)).not.toMatch(/javascript:|vbscript:|"data:/i);
});
it("drops script/style/iframe content rather than showing it", () => {
const html = renderToStaticMarkup(
<RichText doc={parseProviderHtml("<p>a<script>alert(1)</script>b<style>x{}</style>c<iframe>d</iframe>e<svg><text>f</text></svg>g</p>")} />,
);
expect(html).toBe('<div dir="auto"><p dir="auto">abceg</p></div>');
});
it("renders unsafe-scheme links as non-clickable text", () => {
for (const href of ["javascript:alert(1)", "data:text/html,x", "vbscript:x", "//evil.example/", "https://u:p@evil.example/", "file:///etc/passwd"]) {
const html = renderToStaticMarkup(<RichText doc={parseProviderHtml(`<a href="${href}">label</a>`)} />);
expect(html).toBe('<div dir="auto"><p dir="auto">label</p></div>');
}
});
it("shows the real destination host when the label names another host", () => {
const html = renderToStaticMarkup(<RichText doc={parseProviderHtml('<a href="https://evil.example/login">https://bank.example/login</a>')} />);
expect(html).toContain('<span class="sc-link-host"> (evil.example)</span>');
});
it("exposes homoglyph IDN hosts as punycode", () => {
const doc = parseProviderHtml('<a href="https://pаypal.com/">paypal.com</a>');
const link = doc.children[0]!.children[0]!;
expect(link).toMatchObject({ type: "link", host: "xn--pypal-4ve.com" });
expect(renderToStaticMarkup(<RichText doc={doc} />)).toContain("(xn--pypal-4ve.com)");
});
it("strips bidi overrides and isolates from body text", () => {
const doc = parseProviderHtml("<p>file‮gnp.exe ⁦x⁩</p>");
expect(JSON.stringify(doc)).not.toMatch(/[‪-‮⁦-⁩]/);
});
it("emits entity-encoded markup only as escaped text", () => {
const html = renderToStaticMarkup(<RichText doc={parseProviderHtml("&lt;script&gt;alert(1)&lt;/script&gt;")} />);
expect(html).toContain("&lt;script&gt;alert(1)&lt;/script&gt;");
});
});
describe("malicious plain-text corpus", () => {
it.each(MALICIOUS_PLAIN.map((p) => [p]))("renders inert: %s", (payload) => {
const html = renderToStaticMarkup(<RichText doc={parsePlainText(payload, { emojis })} />);
assertInert(html);
assertNoAttackerRequests(html);
});
it("never links javascript:/data: or protocol-relative text", () => {
const html = renderToStaticMarkup(<RichText doc={parsePlainText("javascript:alert(1) data:text/html,x //evil.example/x")} />);
expect(html).not.toContain("<a");
});
});
describe("link-preview payloads", () => {
it.each(MALICIOUS_LINK_PREVIEWS.map((p, i) => [i, p]))("renders inert: #%s", (_i, preview) => {
const html = renderToStaticMarkup(<LinkPreviewCard preview={preview} />);
assertInert(html);
assertNoAttackerRequests(html);
});
it("rejects unsafe targets entirely", () => {
expect(renderToStaticMarkup(<LinkPreviewCard preview={{ url: "javascript:alert(1)", title: "x" }} />)).toBe("");
});
it("routes preview images through the media proxy", () => {
const html = renderToStaticMarkup(<LinkPreviewCard preview={{ url: "https://ok.example/", image: "https://evil.example/og.png" }} />);
expect(html).toContain(`src="${PROXY_PREFIX}https%3A%2F%2Fevil.example%2Fog.png"`);
});
it("caps preview text length", () => {
const html = renderToStaticMarkup(<LinkPreviewCard preview={{ url: "https://ok.example/", title: "x".repeat(100_000) }} />);
expect(html.length).toBeLessThan(2_000);
});
});
describe("forged ASTs", () => {
it.each(FORGED_DOCUMENTS.map((d, i) => [i, d]))("are re-validated at render: #%s", (_i, doc) => {
const html = renderToStaticMarkup(<RichText doc={doc} />);
assertInert(html);
assertNoAttackerRequests(html);
expect(html).not.toMatch(/<[^>]*onerror/i);
});
});
describe("display names", () => {
it("removes bidi and zero-width spoofing characters", () => {
const html = renderToStaticMarkup(<DisplayName name={"ad​min‮nimda <script>"} />);
expect(html).toBe("<bdi>adminnimda &lt;script&gt;</bdi>");
});
it("flags mixed-script homoglyph names", () => {
expect(renderToStaticMarkup(<DisplayName name={"pаypal"} />)).toContain('data-confusable="true"');
expect(renderToStaticMarkup(<DisplayName name="paypal" />)).not.toContain("data-confusable");
});
});
describe("huge and deeply nested input", () => {
const cases: [string, string][] = [
["1MB of '<'", "<".repeat(1_000_000)],
["unterminated attribute", "<a href=\"" + "x".repeat(1_000_000)],
["100k nested divs", "<div>".repeat(100_000) + "deep" + "</div>".repeat(100_000)],
["100k nested svg", "<svg>".repeat(100_000) + "<img src=x onerror=alert(1)>"],
["50k links", '<a href="https://ok.example/">x</a>'.repeat(50_000)],
["comment bomb", "<!--".repeat(200_000)],
["attribute flood", "<a " + "x=1 ".repeat(200_000) + 'href="https://ok.example/">x</a>'],
["unclosed raw text", "<script>" + "a".repeat(1_000_000)],
];
it.each(cases)("%s: bounded time and output, inert", (_name, payload) => {
const start = performance.now();
const doc = parseProviderHtml(payload);
const html = renderToStaticMarkup(<RichText doc={doc} />);
expect(performance.now() - start).toBeLessThan(2_000);
expect(html.length).toBeLessThan(200_000);
assertInert(html);
expect(doc.truncated).toBe(true);
});
it("plain text: huge input is clipped", () => {
const doc = parsePlainText("https://ok.example/ @a #b\n".repeat(200_000));
expect(doc.truncated).toBe(true);
assertInert(renderToStaticMarkup(<RichText doc={doc} />));
});
});