@visulima/redact
Version:
Detect whether a terminal or browser supports ansi colors.
587 lines (580 loc) • 19.5 kB
JavaScript
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 };