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,57 @@
/**
* Bidi embedding/override/isolate controls (U+202A–U+202E, U+2066–U+2069) can
* visually reorder text ("evil" + U+202E + "txt.exe"); they are always removed.
*/
const BIDI_CONTROLS = /[\u202A-\u202E\u2066-\u2069]/g;
/** Marks and zero-width characters used for spoofing look-alike names. */
const INVISIBLES = /[\u200B-\u200F\u061C\u180E\u2060-\u2064\uFEFF\u034F\u115F\u1160\u3164\uFFA0]/g;
/** C0/C1 controls except tab and newline; lone surrogates. */
const CONTROLS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
/** Normalises body text: strips controls and bidi overrides, keeps ZWJ (emoji sequences). */
export function neutralizeText(input: string): string {
return input.replace(CONTROLS, "").replace(BIDI_CONTROLS, "").replace(/\r\n?/g, "\n");
}
/**
* Display names, usernames, channel names: single line, no bidi or zero-width
* characters at all, whitespace collapsed, length-capped.
*/
export function neutralizeDisplayName(input: string, maxLength = 100): string {
const cleaned = input
.normalize("NFC")
.replace(CONTROLS, "")
.replace(BIDI_CONTROLS, "")
.replace(INVISIBLES, "")
.replace(/\s+/g, " ")
.trim();
return truncateCodePoints(cleaned, maxLength);
}
export function truncateCodePoints(input: string, max: number): string {
if (input.length <= max) return input;
const chars = Array.from(input);
return chars.length <= max ? input : chars.slice(0, max).join("") + "…";
}
const SCRIPT_RUNS: readonly [string, RegExp][] = [
["Latin", /\p{Script=Latin}/u],
["Cyrillic", /\p{Script=Cyrillic}/u],
["Greek", /\p{Script=Greek}/u],
["Armenian", /\p{Script=Armenian}/u],
["Cherokee", /\p{Script=Cherokee}/u],
];
/**
* Homoglyph heuristic: true when a single word mixes Latin with a script that
* has Latin look-alikes ("pаypal" with Cyrillic "а"). The UI should flag such
* names rather than trust them; it is a warning, not a sanitiser.
*/
export function hasMixedScriptConfusables(input: string): boolean {
for (const word of input.normalize("NFKC").split(/[\s\p{P}]+/u)) {
let seen = 0;
for (const [, re] of SCRIPT_RUNS) if (re.test(word)) seen++;
if (seen > 1) return true;
}
return false;
}