T-HUB/packages/safe-content/src/eslint.ts
glass beadian aa5e262f3a 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/.
2026-10-10 11:56:42 +00:00

31 lines
1.4 KiB
TypeScript

/**
* Flat-config block banning raw-HTML sinks (proposal §4.5 (1)). Spread it into
* the root `eslint.config.mjs`; `packages/safe-content/**` is the only
* sanctioned renderer and is excluded via `ignores`.
*
* import { noRawHtmlSinks } from "@tactical-media-broadcast-hub/safe-content/eslint";
* export default tseslint.config(..., noRawHtmlSinks);
*/
const MESSAGE = "Raw HTML sink — render provider content only through @tactical-media-broadcast-hub/safe-content (proposal §4.5).";
export const RAW_HTML_SINK_SELECTORS: readonly string[] = [
"JSXAttribute[name.name='dangerouslySetInnerHTML']",
"Property[key.name='dangerouslySetInnerHTML']",
"AssignmentExpression > MemberExpression.left[property.name=/^(innerHTML|outerHTML)$/]",
"CallExpression[callee.property.name=/^(insertAdjacentHTML|createContextualFragment|parseFromString|setHTMLUnsafe)$/]",
"CallExpression[callee.object.name='document'][callee.property.name=/^(write|writeln)$/]",
"NewExpression[callee.name='Function']",
"CallExpression[callee.name='eval']",
];
export const noRawHtmlSinks: {
files: string[];
ignores: string[];
rules: { "no-restricted-syntax": ["error", ...{ selector: string; message: string }[]] };
} = {
files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
ignores: ["packages/safe-content/**"],
rules: {
"no-restricted-syntax": ["error", ...RAW_HTML_SINK_SELECTORS.map((selector) => ({ selector, message: MESSAGE }))],
},
};