UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

122 lines (121 loc) 5.56 kB
import "./src-vebZIeLe.js"; import { t as expectDefined } from "./expect-CyE8FADM.js"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { _ as resolveRedactOptions, p as redactSensitiveText, t as computeSensitiveRedactionBitmap } from "./redact-BtvPPfTi.js"; //#region src/infra/exec-approval-text-sanitize.ts const EXEC_APPROVAL_INVISIBLE_CHAR_REGEX = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\u115F\u1160\u3164\uFFA0]/gu; const EXEC_APPROVAL_INVISIBLE_CHAR_SINGLE = /^[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000\u115F\u1160\u3164\uFFA0]$/u; const EXEC_APPROVAL_MAX_INPUT = 262144; const EXEC_APPROVAL_MAX_OUTPUT = 16384; const EXEC_APPROVAL_TRUNCATION_MARKER = "…[truncated]"; const EXEC_APPROVAL_OVERSIZED_MARKER = "[exec approval command exceeds display size limit; full text suppressed]"; const EXEC_APPROVAL_WARNING_OVERSIZED_MARKER = "[exec approval warning exceeds display size limit; full text suppressed]"; const BYPASS_MASK = "***"; function formatCodePointEscape(char) { return `\\u{${char.codePointAt(0)?.toString(16).toUpperCase() ?? "FFFD"}}`; } function normalizeDisplayLineBreaks(text) { return text.replace(/\r\n?/g, "\n").replace(/[\u2028\u2029]/g, "\n"); } function escapeInvisibles(text, options) { return text.replace(EXEC_APPROVAL_INVISIBLE_CHAR_REGEX, (char) => options?.preserveLineBreaks && char === "\n" ? "\n" : formatCodePointEscape(char)); } function truncateForDisplay(text) { if (text.length <= EXEC_APPROVAL_MAX_OUTPUT) return { text, truncated: false, oversized: false }; return { text: truncateUtf16Safe(text, EXEC_APPROVAL_MAX_OUTPUT) + EXEC_APPROVAL_TRUNCATION_MARKER, truncated: true, oversized: false }; } function buildStrippedView(original) { const strippedChars = []; const strippedToOrig = []; let offset = 0; for (const cp of original) { if (!EXEC_APPROVAL_INVISIBLE_CHAR_SINGLE.test(cp)) { strippedChars.push(cp); for (let k = 0; k < cp.length; k++) strippedToOrig.push(offset + k); } offset += cp.length; } return { stripped: strippedChars.join(""), strippedToOrig }; } function sanitizeExecApprovalDisplayTextInternal(commandText, options) { if (commandText.length > EXEC_APPROVAL_MAX_INPUT) return { text: options?.oversizedMarker ?? EXEC_APPROVAL_OVERSIZED_MARKER, truncated: false, oversized: true }; const rawRedacted = redactSensitiveText(commandText, { mode: "tools" }); if (commandText.search(EXEC_APPROVAL_INVISIBLE_CHAR_REGEX) === -1) return truncateForDisplay(escapeInvisibles(rawRedacted, options)); const { stripped, strippedToOrig } = buildStrippedView(commandText); if (redactSensitiveText(stripped, { mode: "tools" }) === stripped) return truncateForDisplay(escapeInvisibles(rawRedacted, options)); const redaction = resolveRedactOptions({ mode: "tools" }); const rawMask = computeSensitiveRedactionBitmap(commandText, redaction); const strippedMask = computeSensitiveRedactionBitmap(stripped, redaction); let bypassDetected = false; for (let i = 0; i < strippedMask.length; i++) if (strippedMask[i] && !rawMask[expectDefined(strippedToOrig[i], "stripped to orig entry at i")]) { bypassDetected = true; break; } if (!bypassDetected) return truncateForDisplay(escapeInvisibles(rawRedacted, options)); const unionMask = rawMask.slice(); for (let i = 0; i < strippedMask.length; i++) if (strippedMask[i]) unionMask[expectDefined(strippedToOrig[i], "stripped to orig entry at i")] = true; let out = ""; let i = 0; while (i < commandText.length) { if (unionMask[i]) { let j = i; while (j < commandText.length && unionMask[j]) j++; out += BYPASS_MASK; i = j; continue; } const codePoint = commandText.codePointAt(i) ?? 65533; const cp = String.fromCodePoint(codePoint); out += options?.preserveLineBreaks && cp === "\n" ? cp : EXEC_APPROVAL_INVISIBLE_CHAR_SINGLE.test(cp) ? formatCodePointEscape(cp) : cp; i += cp.length; } return truncateForDisplay(out); } /** Sanitizes exec command text for approval UI without exposing status metadata. */ function sanitizeExecApprovalDisplayText(commandText) { return sanitizeExecApprovalDisplayTextInternal(commandText).text; } /** * Sanitizes exec command text for approval UI and reports whether size caps changed it. */ function sanitizeExecApprovalDisplayTextWithStatus(commandText) { return sanitizeExecApprovalDisplayTextInternal(commandText); } /** * Sanitizes warning prose for approval UI while preserving real line boundaries. */ function sanitizeExecApprovalWarningText(warningText) { return sanitizeExecApprovalWarningTextWithStatus(warningText).text; } /** Sanitizes warning prose and reports whether display bounds suppressed any content. */ function sanitizeExecApprovalWarningTextWithStatus(warningText) { return sanitizeExecApprovalDisplayTextInternal(normalizeDisplayLineBreaks(warningText), { preserveLineBreaks: true, oversizedMarker: EXEC_APPROVAL_WARNING_OVERSIZED_MARKER }); } /** Checks the existing approval code-point cap without materializing every character. */ function exceedsApprovalTextLimit(value, maxLength) { if (value.length <= maxLength) return false; if (value.length > maxLength * 2) return true; let remaining = maxLength; for (const _ of value) if (--remaining < 0) return true; return false; } //#endregion export { sanitizeExecApprovalWarningTextWithStatus as a, sanitizeExecApprovalWarningText as i, sanitizeExecApprovalDisplayText as n, sanitizeExecApprovalDisplayTextWithStatus as r, exceedsApprovalTextLimit as t };