T-HUB/packages/safe-content
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
..
src Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00
package.json Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00
README.md Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00
tsconfig.json Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00

Safe content

The only sanctioned way to render provider content (posts, bios, comments, messages, link previews, display names) in the web UI. Proposal §4.5.

Provider text becomes a closed rich-text AST (text, linebreak, emoji, link, mention, hashtag inside paragraphs) before it reaches React. No node can hold HTML, attributes or styles.

  • parsePlainText handles plain provider text. parseProviderHtml handles the Mastodon/PeerTube HTML subset. Neither renders HTML. script, style, iframe, svg, math, form, template and similar elements are dropped along with their content. Other tags are unwrapped to text.
  • Links must be absolute http(s) URLs with no credentials, checked by parseSafeHttpUrl. Anything else stays as plain text that can't be clicked. Hosts are shown in punycode, and the real host is shown when a link's label doesn't start with it.
  • All media (emoji, preview images) goes through toProxiedMediaUrl (default /api/media-proxy?url=…). The renderer drops any src that isn't a canonical proxy URL.
  • Bidi override/isolate characters are stripped from all text. Display names also lose zero-width characters, and DisplayName flags mixed-script names that use look-alike letters.
  • Limits: 20k input chars, 2,000 nodes, nesting depth 32, 2,048-char URLs. The tokenizer makes one forward pass, so its run time grows linearly with input size.

@tactical-media-broadcast-hub/safe-content/react exports RichText, LinkPreviewCard, DisplayName and SafeLink. They re-validate every prop with coerceRichTextDocument/parseLinkPreview, so an AST loaded from the DB or the network is as safe as a freshly parsed one.

@tactical-media-broadcast-hub/safe-content/eslint exports noRawHtmlSinks, a flat-config block that bans dangerouslySetInnerHTML, innerHTML/outerHTML, insertAdjacentHTML, document.write, DOMParser.parseFromString, eval and new Function outside this package. Spread it into the root eslint.config.mjs.

@tactical-media-broadcast-hub/safe-content/corpus exports the malicious-content corpus for packages/testing and the Playwright suites.