Files
openclaw/packages/normalization-core/src/balanced-json.ts
T
Peter Steinberger 58a538767c fix(security): centralize provider diagnostic redaction (#121599)
* fix(security): centralize provider diagnostic redaction

* fix(workers): redact finishing diagnostics

* fix(security): fail closed on malformed diagnostics

* fix(security): redact media wrapper diagnostics

* fix(security): normalize diagnostic field redaction

* fix(security): redact diagnostic media URIs

* fix(ai): preserve diagnostic compatibility in error projection

* refactor(ai): simplify diagnostic error handling

* fix(security): redact credential query diagnostics

* fix(security): redact diagnostic media arrays

* fix(security): redact diagnostic credential headers

* test: align shared expectations with current runtime contracts

* test(ui): align managed media and picker expectations

* fix(security): redact plural media diagnostics

* fix(ai): preserve bracketed provider errors

* fix(security): cover generic diagnostic credentials

* fix(security): redact prefixed diagnostic JSON

* fix(security): redact embedded diagnostic JSON

* test(agents): align cache trace media redaction

* fix(security): redact cookie diagnostics

* refactor(ai): isolate diagnostic host policy

* refactor(ai): narrow diagnostic helper exports

* fix(security): bound diagnostic descriptor reads

* fix(security): bound diagnostic graph traversal
2026-08-10 18:53:45 -07:00

73 lines
2.1 KiB
TypeScript

type JsonOpeningDelimiter = "{" | "[";
type BalancedJsonFragment = {
json: string;
startIndex: number;
endIndex: number;
};
function isJsonOpeningDelimiter(
char: string | undefined,
openers: readonly JsonOpeningDelimiter[],
): char is JsonOpeningDelimiter {
return (char === "{" || char === "[") && openers.includes(char);
}
/** Extracts the first balanced JSON object/array from text. */
export function extractBalancedJsonPrefix(
raw: string,
opts: { openers?: readonly JsonOpeningDelimiter[] } = {},
): BalancedJsonFragment | null {
const openers = opts.openers ?? (["{", "["] as const);
let start = 0;
while (start < raw.length && !isJsonOpeningDelimiter(raw[start], openers)) {
start += 1;
}
const stack: JsonOpeningDelimiter[] = [];
let inString = false;
let escaped = false;
for (let index = start; index < raw.length; index += 1) {
const char = raw[index];
if (inString) {
if (escaped) {
escaped = false;
} else if (char === "\\") {
escaped = true;
} else if (char === '"') {
inString = false;
}
} else if (char === '"') {
inString = true;
} else if (isJsonOpeningDelimiter(char, openers)) {
stack.push(char);
} else if (stack.length > 0 && char === (stack.at(-1) === "{" ? "}" : "]")) {
stack.pop();
if (stack.length === 0) {
return { json: raw.slice(start, index + 1), startIndex: start, endIndex: index };
}
}
}
return null;
}
/** Extracts every balanced JSON object/array fragment from arbitrary text. */
export function extractBalancedJsonFragments(
raw: string,
opts: { openers?: readonly JsonOpeningDelimiter[] } = {},
): BalancedJsonFragment[] {
const fragments: BalancedJsonFragment[] = [];
for (let offset = 0; offset < raw.length;) {
const fragment = extractBalancedJsonPrefix(raw.slice(offset), opts);
if (!fragment) {
break;
}
fragments.push({
json: fragment.json,
startIndex: offset + fragment.startIndex,
endIndex: offset + fragment.endIndex,
});
offset += fragment.endIndex + 1;
}
return fragments;
}