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:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
33
apps/api/src/production/render.test.ts
Normal file
33
apps/api/src/production/render.test.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { renderOverlay, validateRenderInput, overlaySvg } from "./render.js";
|
||||
const png = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
||||
describe("overlay render job injection gates (installed sharp, isolated worker)", () => {
|
||||
it.each(["lower_third", "chyron"])("renders %s hostile text as escaped text into an actual PNG", async templateId => {
|
||||
const input = { templateId, fields: { title: '<script>alert("x")</script> & $(touch /tmp/owned)', subtitle: 'evil; [movie=/etc/passwd]; "', style: "red" } };
|
||||
const svg = overlaySvg(validateRenderInput(input)).svg;
|
||||
expect(svg).toContain("<script>"); expect(svg).not.toContain("<script>");
|
||||
const rendered = await renderOverlay(input); expect(rendered.subarray(0, 8)).toEqual(png);
|
||||
expect(rendered.readUInt32BE(16)).toBe(templateId === "chyron" ? 4096 : 1920);
|
||||
});
|
||||
it("rasterizes a safe tenant geometric logo", async () => {
|
||||
const rendered = await renderOverlay({ templateId: "logo", fields: { svg: '<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><rect width="64" height="64" fill="#FF1610"/></svg>' } });
|
||||
expect(rendered.subarray(0, 8)).toEqual(png); expect(rendered.readUInt32BE(16)).toBe(512);
|
||||
});
|
||||
it.each([
|
||||
'<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><svg>&xxe;</svg>',
|
||||
'<svg><script>alert(1)</script></svg>', '<svg onload="alert(1)"></svg>',
|
||||
'<svg><image href="https://attacker.example/tracker"/></svg>',
|
||||
'<svg><style>@import "https://attacker.example/a.css";</style></svg>',
|
||||
'<svg><foreignObject><div>html</div></foreignObject></svg>',
|
||||
'<svg><path fill="url(https://evil.example/x)"/></svg>',
|
||||
'<svg xmlns:xlink="http://www.w3.org/1999/xlink"><use xlink:href="file:///etc/passwd"/></svg>',
|
||||
'<?xml-stylesheet href="https://evil.example/a"?><svg/>',
|
||||
'<svg width="999999999" height="999999999"/>',
|
||||
'<svg><g></svg></g>', '<svg/><svg/>', '<svg><path d="M0 0" onclick="evil()"/></svg>',
|
||||
])("rejects hostile SVG through the render job: %s", async svg => {
|
||||
await expect(renderOverlay({ templateId: "logo", fields: { svg } })).rejects.toThrow();
|
||||
});
|
||||
it.each(["\u0000", "\n", "\u202e", "a".repeat(81)])("rejects invalid text %s before sharp", async title => {
|
||||
await expect(renderOverlay({ templateId: "lower_third", fields: { title } })).rejects.toThrow("invalid overlay text");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue