openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
88 lines (87 loc) • 3.33 kB
JavaScript
import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import { t as pruneMapToMaxSize } from "./map-size-CNcWiFKu.js";
//#region src/logging/secret-redaction-registry.ts
const MIN_SECRET_VALUE_LENGTH = 6;
const MAX_SECRET_VALUES = 512;
const registeredValues = /* @__PURE__ */ new Map();
let compiledMatcher;
let firstChars;
function invalidateMatcher() {
firstChars = void 0;
compiledMatcher = void 0;
}
function registerOneSecretValue(value) {
if (registeredValues.delete(value)) {
registeredValues.set(value, true);
return;
}
registeredValues.set(value, true);
pruneMapToMaxSize(registeredValues, MAX_SECRET_VALUES);
invalidateMatcher();
}
/** Registers one resolved secret for exact-value log redaction. */
function registerSecretValueForRedaction(value) {
if (value.length < MIN_SECRET_VALUE_LENGTH) return;
const encoded = encodeURIComponent(value);
if (encoded !== value) registerOneSecretValue(encoded);
const jsonEscaped = JSON.stringify(value).slice(1, -1);
if (jsonEscaped !== value) registerOneSecretValue(jsonEscaped);
registerOneSecretValue(value);
}
/** Returns whether a value has SecretRef provenance in the process registry. */
function isSecretValueRegisteredForRedaction(value) {
return registeredValues.has(value);
}
function hasRegisteredSecretValuesForRedaction() {
return registeredValues.size > 0;
}
/** Replaces registered exact values while preserving the caller's mask convention. */
function redactRegisteredSecretValues(text, mask) {
if (!text || registeredValues.size === 0) return text;
let couldMatch = false;
firstChars ??= new Set([...registeredValues.keys()].map((value) => value.charAt(0)));
for (const firstChar of firstChars) if (text.includes(firstChar)) {
couldMatch = true;
break;
}
if (!couldMatch) return text;
if (!compiledMatcher) {
const buckets = /* @__PURE__ */ new Map();
for (const value of [...registeredValues.keys()].toSorted((left, right) => right.length - left.length)) {
const prefix = value.slice(0, MIN_SECRET_VALUE_LENGTH);
const bucket = buckets.get(prefix);
if (bucket) bucket.push(value);
else buckets.set(prefix, [value]);
}
compiledMatcher = {
prefixes: new RegExp([...buckets.keys()].map(escapeRegExp).join("|"), "g"),
buckets
};
}
const { prefixes, buckets } = compiledMatcher;
const matches = [];
prefixes.lastIndex = 0;
for (let match = prefixes.exec(text); match; match = prefixes.exec(text)) {
const index = match.index;
const value = buckets.get(match[0])?.find((candidate) => text.startsWith(candidate, index));
if (value !== void 0) matches.push({
index,
value
});
prefixes.lastIndex = index + (value?.length ?? 1);
}
let result = "";
let cursor = 0;
for (const match of matches) {
result += `${text.slice(cursor, match.index)}${mask(match.value)}`;
cursor = match.index + match.value.length;
}
return result + text.slice(cursor);
}
function resetSecretRedactionRegistryForTest() {
registeredValues.clear();
invalidateMatcher();
}
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.secretRedactionRegistryTestApi")] = { resetSecretRedactionRegistryForTest };
//#endregion
export { registerSecretValueForRedaction as i, isSecretValueRegisteredForRedaction as n, redactRegisteredSecretValues as r, hasRegisteredSecretValuesForRedaction as t };