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/.
31 lines
1.4 KiB
TypeScript
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 }))],
|
|
},
|
|
};
|