UNPKG

@visulima/redact

Version:

Detect whether a terminal or browser supports ansi colors.

587 lines (580 loc) 19.5 kB
import stringAnonymize from './packem_shared/stringAnonymize-DiI6bOue.mjs'; export { default as standardRules } from './packem_shared/standardRules-Ba9quAar.mjs'; var __defProp$4 = Object.defineProperty; var __name$4 = (target, value) => __defProp$4(target, "name", { value, configurable: true }); const isObject = /* @__PURE__ */ __name$4((value) => { const type = typeof value; return value !== null && (type === "object" || type === "function"); }, "isObject"); const isEmptyObject = /* @__PURE__ */ __name$4((value) => isObject(value) && Object.keys(value).length === 0, "isEmptyObject"); const disallowedKeys = /* @__PURE__ */ new Set([ "__proto__", "prototype", "constructor" ]); const digits = new Set("0123456789"); function getPathSegments(path) { const parts = []; let currentSegment = ""; let currentPart = "start"; let isIgnoring = false; for (const character of path) { switch (character) { case "\\": { if (currentPart === "index") { throw new Error("Invalid character in an index"); } if (currentPart === "indexEnd") { throw new Error("Invalid character after an index"); } if (isIgnoring) { currentSegment += character; } currentPart = "property"; isIgnoring = !isIgnoring; break; } case ".": { if (currentPart === "index") { throw new Error("Invalid character in an index"); } if (currentPart === "indexEnd") { currentPart = "property"; break; } if (isIgnoring) { isIgnoring = false; currentSegment += character; break; } if (disallowedKeys.has(currentSegment)) { return []; } parts.push(currentSegment); currentSegment = ""; currentPart = "property"; break; } case "[": { if (currentPart === "index") { throw new Error("Invalid character in an index"); } if (currentPart === "indexEnd") { currentPart = "index"; break; } if (isIgnoring) { isIgnoring = false; currentSegment += character; break; } if (currentPart === "property") { if (disallowedKeys.has(currentSegment)) { return []; } parts.push(currentSegment); currentSegment = ""; } currentPart = "index"; break; } case "]": { if (currentPart === "index") { parts.push(Number.parseInt(currentSegment, 10)); currentSegment = ""; currentPart = "indexEnd"; break; } if (currentPart === "indexEnd") { throw new Error("Invalid character after an index"); } } default: { if (currentPart === "index" && !digits.has(character)) { throw new Error("Invalid character in an index"); } if (currentPart === "indexEnd") { throw new Error("Invalid character after an index"); } if (currentPart === "start") { currentPart = "property"; } if (isIgnoring) { isIgnoring = false; currentSegment += "\\"; } currentSegment += character; } } } if (isIgnoring) { currentSegment += "\\"; } switch (currentPart) { case "property": { if (disallowedKeys.has(currentSegment)) { return []; } parts.push(currentSegment); break; } case "index": { throw new Error("Index was not closed"); } case "start": { parts.push(""); break; } } return parts; } __name$4(getPathSegments, "getPathSegments"); function isStringIndex(object, key) { if (typeof key !== "number" && Array.isArray(object)) { const index = Number.parseInt(key, 10); return Number.isInteger(index) && object[index] === object[key]; } return false; } __name$4(isStringIndex, "isStringIndex"); function assertNotStringIndex(object, key) { if (isStringIndex(object, key)) { throw new Error("Cannot use string index"); } } __name$4(assertNotStringIndex, "assertNotStringIndex"); function getProperty(object, path, value) { if (!isObject(object) || typeof path !== "string") { return value === void 0 ? object : value; } const pathArray = getPathSegments(path); if (pathArray.length === 0) { return value; } for (let index = 0; index < pathArray.length; index++) { const key = pathArray[index]; if (isStringIndex(object, key)) { object = index === pathArray.length - 1 ? void 0 : null; } else { object = object[key]; } if (object === void 0 || object === null) { if (index !== pathArray.length - 1) { return value; } break; } } return object === void 0 ? value : object; } __name$4(getProperty, "getProperty"); function setProperty(object, path, value) { if (!isObject(object) || typeof path !== "string") { return object; } const root = object; const pathArray = getPathSegments(path); for (let index = 0; index < pathArray.length; index++) { const key = pathArray[index]; assertNotStringIndex(object, key); if (index === pathArray.length - 1) { object[key] = value; } else if (!isObject(object[key])) { object[key] = typeof pathArray[index + 1] === "number" ? [] : {}; } object = object[key]; } return root; } __name$4(setProperty, "setProperty"); function deleteProperty(object, path) { if (!isObject(object) || typeof path !== "string") { return false; } const pathArray = getPathSegments(path); for (let index = 0; index < pathArray.length; index++) { const key = pathArray[index]; assertNotStringIndex(object, key); if (index === pathArray.length - 1) { delete object[key]; return true; } object = object[key]; if (!isObject(object)) { return false; } } } __name$4(deleteProperty, "deleteProperty"); function hasProperty(object, path) { if (!isObject(object) || typeof path !== "string") { return false; } const pathArray = getPathSegments(path); if (pathArray.length === 0) { return false; } for (const key of pathArray) { if (!isObject(object) || !(key in object) || isStringIndex(object, key)) { return false; } object = object[key]; } return true; } __name$4(hasProperty, "hasProperty"); function escapePath(path) { if (typeof path !== "string") { throw new TypeError("Expected a string"); } return path.replaceAll(/[\\.[]/g, "\\$&"); } __name$4(escapePath, "escapePath"); function entries(value) { const result = Object.entries(value); if (Array.isArray(value)) { return result.map(([key, value2]) => [Number(key), value2]); } return result; } __name$4(entries, "entries"); function stringifyPath(pathSegments) { let result = ""; for (let [index, segment] of entries(pathSegments)) { if (typeof segment === "number") { result += `[${segment}]`; } else { segment = escapePath(segment); result += index === 0 ? segment : `.${segment}`; } } return result; } __name$4(stringifyPath, "stringifyPath"); function* deepKeysIterator(object, currentPath = []) { if (!isObject(object) || isEmptyObject(object)) { if (currentPath.length > 0) { yield stringifyPath(currentPath); } return; } for (const [key, value] of entries(object)) { yield* deepKeysIterator(value, [...currentPath, key]); } } __name$4(deepKeysIterator, "deepKeysIterator"); function deepKeys(object) { return [...deepKeysIterator(object)]; } __name$4(deepKeys, "deepKeys"); var __defProp$3 = Object.defineProperty; var __name$3 = (target, value) => __defProp$3(target, "name", { value, configurable: true }); const isJson = /* @__PURE__ */ __name$3((value) => { try { JSON.parse(value); } catch { return false; } return true; }, "isJson"); var __defProp$2 = Object.defineProperty; var __name$2 = (target, value) => __defProp$2(target, "name", { value, configurable: true }); const urlDelimiters = "#;/?:@&"; const urlParameterRegex = new RegExp(`([${urlDelimiters}][^${urlDelimiters}=\\s]+=[^${urlDelimiters}=\\s]*)`, "g"); const parseUrlParameters = /* @__PURE__ */ __name$2((input) => { const segments = []; let previousEndIndex = 0; urlParameterRegex.lastIndex = 0; let match = urlParameterRegex.exec(input); while (match != null) { const { 0: text, index } = match; segments.push({ key: null, value: input.slice(previousEndIndex, index + 1) }); previousEndIndex = index + text.length; segments.push({ key: text.slice(1, text.indexOf("=")), value: text.slice(text.indexOf("=") + 1, text.length) }); match = urlParameterRegex.exec(input); } const lastSegment = input.slice(previousEndIndex, input.length); if (lastSegment.length > 0) { segments.push({ key: null, value: lastSegment }); } return segments; }, "parseUrlParameters"); var __defProp$1 = Object.defineProperty; var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true }); const checkRollbackStrings = /* @__PURE__ */ __name$1((rollbackStrings, patternSubstrings) => { for (let s = 0; s < rollbackStrings.length; ++s) { let currentString = rollbackStrings[s].string; let patternIndex = rollbackStrings[s].index; while (patternIndex < patternSubstrings.length) { const patternSubstring = patternSubstrings[patternIndex]; if (!currentString.includes(patternSubstring)) { break; } const testString = currentString.slice(1); rollbackStrings.push({ index: patternIndex, string: testString }); if (!testString.includes(patternSubstring)) { rollbackStrings.pop(); } currentString = currentString.slice(currentString.indexOf(patternSubstring) + patternSubstring.length); patternIndex++; while (patternSubstrings[patternIndex] === "") { patternIndex++; } if (patternIndex >= patternSubstrings.length) { if (patternSubstrings.at(-1) !== "" && currentString.length > 0) { break; } else { return true; } } } } return false; }, "checkRollbackStrings"); const wildcard = /* @__PURE__ */ __name$1((input, pattern) => { if (!pattern.includes("*")) { return pattern === input; } const patternSubstrings = pattern.split("*"); let patternIndex = 0; let currentString = input; while (patternSubstrings[patternIndex] === "") { patternIndex++; if (patternIndex === pattern.length) { return true; } } if (patternIndex === 0 && !input.startsWith(patternSubstrings[0])) { return false; } const rollbackStrings = []; while (patternIndex < patternSubstrings.length) { const patternSubstring = patternSubstrings[patternIndex]; if (!currentString.includes(patternSubstring)) { return checkRollbackStrings(rollbackStrings, patternSubstrings); } const testString = currentString.slice(1); rollbackStrings.push({ index: patternIndex, string: testString }); if (!testString.includes(patternSubstring)) { rollbackStrings.pop(); } currentString = currentString.slice(currentString.indexOf(patternSubstring) + patternSubstring.length); patternIndex++; while (patternSubstrings[patternIndex] === "") { patternIndex++; } } if (patternIndex >= patternSubstrings.length && patternSubstrings.at(-1) !== "" && currentString.length > 0) { if (currentString === input) { return false; } return checkRollbackStrings(rollbackStrings, patternSubstrings); } return true; }, "wildcard"); var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); const circularReferenceKey = "__redact_circular_reference__"; const recursivelyFilterAttributes = /* @__PURE__ */ __name((copy, examinedObjects, saveCopy, rules, options, identifier) => { for (const modifier of rules) { if (!modifier.wildcard && !modifier.deep && hasProperty(copy, modifier.key)) { setProperty(copy, modifier.key, modifier.replacement); } else { for (const key in copy) { const currentIdentifier = identifier ? `${identifier}.${key.toLowerCase()}` : key.toLowerCase(); if (!modifier.wildcard && key.toLowerCase() === modifier.key) { copy[key] = modifier.replacement; } else if (modifier.wildcard && (wildcard(key.toLowerCase(), modifier.key) || wildcard(currentIdentifier.toLowerCase(), modifier.key))) { copy[key] = modifier.replacement; } else { copy[key] = recursiveFilter(copy[key], examinedObjects, saveCopy, [modifier], options, currentIdentifier.toLowerCase()); } } } } }, "recursivelyFilterAttributes"); const recursiveFilter = /* @__PURE__ */ __name((input, examinedObjects, saveCopy, rules, options, identifier) => { if (input == null) { return input; } const id = input[circularReferenceKey]; if (id != null || id === 0) { return examinedObjects[id]?.copy; } if (typeof input === "object" && !Array.isArray(input)) { if (input instanceof Error) { const copy2 = new Error(input.message); Object.defineProperties(copy2, { name: { configurable: true, enumerable: false, value: input.name, writable: true }, stack: { configurable: true, enumerable: false, value: input.stack, writable: true } }); if (input.code != null) { copy2.code = input.code; } for (const key in input) { copy2[key] = input[key]; } saveCopy(input, copy2); recursivelyFilterAttributes(copy2, examinedObjects, saveCopy, rules, options, identifier); return copy2; } if (input instanceof Map) { const copy2 = /* @__PURE__ */ new Map(); const iterator = input.entries(); let result = iterator.next(); while (result.done != null && !result.done) { const [key, value] = result.value; if (typeof key === "string" || key instanceof String) { let modifierFound = false; for (const modifier of rules) { const lowerCaseKey = key.toLowerCase(); if (modifier.key === lowerCaseKey || modifier.wildcard && wildcard(lowerCaseKey, modifier.key)) { modifierFound = true; copy2.set(key, modifier.replacement); } } if (!modifierFound) { copy2.set(key, recursiveFilter(value, examinedObjects, saveCopy, rules, options)); } } else { copy2.set( recursiveFilter(key, examinedObjects, saveCopy, rules, options), recursiveFilter(value, examinedObjects, saveCopy, rules, options) ); } result = iterator.next(); } saveCopy(input, copy2); return copy2; } if (input instanceof Set) { const copy2 = /* @__PURE__ */ new Set(); const iterator = input.values(); let result = iterator.next(); while (result.done != null && !result.done) { copy2.add(recursiveFilter(result.value, examinedObjects, saveCopy, rules, options, identifier)); result = iterator.next(); } saveCopy(input, copy2); return copy2; } const copy = { ...input }; saveCopy(input, copy); recursivelyFilterAttributes(copy, examinedObjects, saveCopy, rules, options, identifier); return copy; } if (typeof input === "string" || input instanceof String) { if (isJson(input)) { try { const parsed = JSON.parse(input); if (typeof parsed !== "object" && typeof parsed !== "string" || parsed == null) { return input; } const filtered = recursiveFilter(parsed, examinedObjects, saveCopy, rules, options, identifier); return JSON.stringify(filtered); } catch (error) { options?.logger?.debug(error); } } if (/(?:http|https):\/\/?/.test(input)) { const parsedUrlParameters = parseUrlParameters(input); const filtered = []; for (const { key, value } of parsedUrlParameters) { if (key == null) { const foundModifier = rules.find((modifier) => modifier.key === value.toLowerCase()); if (foundModifier) { filtered.push(foundModifier.replacement); } else { filtered.push(value); } } else { const foundModifier = rules.find((modifier) => modifier.key === key.toLowerCase()); if (foundModifier) { filtered.push(`${key}=${foundModifier.replacement}`); } else { filtered.push(`${key}=${value}`); } } } return filtered.join(""); } return stringAnonymize(input, rules, { logger: options?.logger }); } if (Array.isArray(input)) { const copy = []; saveCopy(input, copy); for (const [index, item] of input.entries()) { const currentIdentifier = identifier ? `${identifier}.${index.toString()}`.toLowerCase() : index.toString().toLowerCase(); const foundModifier = rules.find((modifier) => modifier.key === index.toString().toLowerCase() || modifier.key === currentIdentifier); if (foundModifier) { copy.push(foundModifier.replacement); identifier = void 0; } else { copy.push(recursiveFilter(item, examinedObjects, saveCopy, rules, options, currentIdentifier)); } } return copy; } return input; }, "recursiveFilter"); function redact(input, rules, options) { if (input == null || typeof input === "number" || typeof input === "boolean") { return input; } const examinedObjects = []; const saveCopy = /* @__PURE__ */ __name((original, copy) => { const id = examinedObjects.length; original[circularReferenceKey] = id; examinedObjects.push({ copy, original }); }, "saveCopy"); const preparedModifiers = []; for (const modifier of rules) { if (options?.exclude && (typeof modifier === "string" && options.exclude.includes(modifier) || typeof modifier === "number" && options.exclude.includes(modifier) || typeof modifier === "object" && options.exclude.includes(modifier.key))) { continue; } if (typeof modifier === "string") { const hasWildcard = modifier.includes("*"); preparedModifiers.push({ deep: false, key: modifier.toLowerCase(), replacement: "<" + modifier.toUpperCase() + ">", wildcard: hasWildcard }); } else if (typeof modifier === "number") { preparedModifiers.push({ deep: false, key: modifier.toString(), replacement: "<REDACTED>" }); } else { modifier.key = modifier.key.toLowerCase(); if (modifier.key.includes("*")) { modifier.wildcard = true; } if (!modifier.replacement) { modifier.replacement = "<" + modifier.key.toUpperCase() + ">"; } preparedModifiers.push(modifier); } } const returnValue = recursiveFilter(input, examinedObjects, saveCopy, preparedModifiers, options); for (const examinedObject of examinedObjects) { Reflect.deleteProperty(examinedObject.original, circularReferenceKey); } return returnValue; } __name(redact, "redact"); export { redact, stringAnonymize };