/** * Miscellaneous utilities * Shared helpers that don't belong in other specific modules. */ /** * Escape a string for literal use inside a `RegExp`. * * Needed wherever a caller-supplied identifier is interpolated into a pattern: an * unescaped `.` or `(` silently changes what the pattern matches, and a crafted * value can produce catastrophic backtracking. Callers are not always trusted — * MCP tool arguments reach some of these paths. */ export declare function escapeRegExp(s: string): string; /** * Quote a value for use inside a double-quoted Graphviz DOT string. * * Three things have to happen, in this order: * * 1. `\` first. Escaping quotes first would leave a trailing `\` escaping the * closing `"` that follows it, swallowing the rest of the document. * 2. `"`, so the value cannot terminate its own string. * 3. Control characters. The emitter is line-oriented (statements are joined with * `\n`), and a file name may legally contain a newline on Linux and macOS — so * an unescaped one splits the statement in two and the tail is parsed as new * DOT. Escaping quotes alone does not prevent that, which is why this is done * here rather than left to the caller. * * Newline/CR/tab become their DOT escape sequences (the backslash is emitted after * step 1, so it stays a real escape). Any other control character has no DOT * representation and is dropped; two names differing only by such a character would * collide, which is accepted as strictly better than emitting a corrupt graph. */ export declare function escapeDotString(s: string): string; /** * Neutralize terminal control sequences in a value that came from an analyzed * repository, before it is printed. * * OpenLore's job is to read repositories it does not trust and report on them, and * SECURITY.md scopes exactly that. A file name may legally contain ESC on Linux and * macOS, so a hostile repository can smuggle cursor-movement, screen-clear or OSC * sequences through any path/symbol OpenLore echoes back. Printed raw, those let the * repository being analyzed overwrite OpenLore's own output — for a tool whose value * is a trustworthy verdict, a forged "[ok] all checks passed" line is the attack, * not a cosmetic glitch. * * Everything in C0, DEL and C1 is removed, which includes ESC and therefore every * sequence introduced by it (CSI, OSC, DCS) without needing to parse them. Newline * and tab go too: these are single-line display values (a path, a symbol name), so a * newline in one is itself a way to forge an extra output line. * * Four classes of NON-C0 Unicode formatting characters go with them, because C0 alone * is not the whole attack surface: * - bidi overrides and isolates (U+202A-U+202E, U+2066-U+2069) and the marks * U+061C / U+200E / U+200F reverse rendered text, so a decision title or symbol * name can display as the opposite of the bytes OpenLore actually verified; * - the line/paragraph separators U+2028 / U+2029 start a new line in many * terminals and editors — the same forged-extra-line attack as `\n`; * - the zero-width characters U+200B-U+200D, the word joiner U+2060 and U+FEFF are * invisible: they let two different symbol names, paths or decision titles render * identically, so a report can name one thing while OpenLore verified another. * These also matter on the MCP/JSON path, which C0 stripping does not need to cover: * `JSON.stringify` escapes control characters to `\uXXXX` but passes every one of * these through verbatim. This is the same class `sanitizeReviewValue` neutralizes * before repository text reaches a PR comment; keep the two in step. * * The WHOLE escape sequence goes, not just its ESC introducer (see * {@link stripTerminalEscapeSequences}). Dropping ESC alone left `[31mIGNORE…`: harmless * to a terminal, but it reads as prose to a model AND welds the leftover `m` onto the * next word, which is exactly how an escape prefix walked past the instruction-shape * screen in `served-content.ts`. A screen that runs after sanitizing must see the text * the model will see, with nothing stuck to it. * * This is for UNTRUSTED values only. OpenLore's own color codes are applied by the * shared color layer AFTER this runs, so they are unaffected. */ export declare function sanitizeForTerminal(value: string, opts?: { keepNewlines?: boolean; }): string; /** * Remove complete terminal escape sequences — introducer AND payload — from an * untrusted value. The companion of {@link sanitizeForTerminal}, which calls it first. * * Why not just "remove ESC": the payload that survives (`[2J`, `[31m`, an OSC string) * is both visual noise and an evasion. Removing the whole sequence restores the word * boundary a downstream lexical screen needs, instead of leaving `m` glued to the next * word. * * Deliberately simple and strictly LINEAR: one alternation of flat character classes, * each with a single unnested quantifier, so hostile input has no path to catastrophic * backtracking (this repository has closed three quadratic ReDoS defects). Only * ESC-introduced sequences match, so a literal `[31m` in a legitimate file name is left * exactly as it is. Anything ESC-introduced that this does not recognize still loses its * ESC to the C0 class in {@link sanitizeForTerminal}, so no escape reaches a terminal. */ export declare function stripTerminalEscapeSequences(value: string): string; /** * JSON for an agent or a wrapper to parse, screened at the VALUES rather than at the * serialized document. * * Screening the finished document is not enough on this face: `JSON.stringify` turns a real * ESC into the six characters `\\u001b`, so there is no ESC byte left for a text pass to * remove — and the consumer that parses the document gets the control character BACK. A * replacer screens each string before it is escaped, so what a consumer parses is inert. * * KNOWN LIMIT, named rather than implied away: object KEYS are not screened here (a replacer * cannot rewrite them). Bidi and zero-width characters in a key are removed by the text pass * in `writeStdout`/`sendJson`; a C0 control in a key survives as its `\\uXXXX` escape. OpenLore * authors its own keys; the only repository-derived keys are paths and symbol names in map-shaped * payloads, and there the value beside the key carries the same text screened. */ export declare function stringifyScreened(value: unknown, indent?: number): string; /** * The same screening applied to a value that is about to be re-serialized by someone else * (the MCP response capper re-serializes with its own truncation), so the screening cannot be * undone downstream. A round trip, because that is exactly the transform the value is headed * for: anything `JSON.stringify` would drop is already absent from the output. */ export declare function screenJsonValue(value: T): T; /** {@link stringifyScreened} over an already-serialized document; non-JSON text falls back to the text pass. */ export declare function screenJsonText(json: string): string; /** True when a path segment would escape the target object into its prototype. */ export declare function isProtoPollutingKey(key: string): boolean; /** * Parse JSON from LLM output, handling markdown code fences. * Strips ``` fences, extracts JSON array or object, returns fallback on failure. */ export declare function parseJSON(text: string, fallback: T): T; //# sourceMappingURL=misc.d.ts.map