UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

96 lines (94 loc) 3.67 kB
import { isObject } from "@directus/utils"; //#region src/utils/redact-object.ts /** * Redact values in an object. * * @param input Input object in which values should be redacted. * @param redact The key paths at which and values itself which should be redacted. * @param redact.keys Nested array of key paths at which values should be redacted. (Supports `*` for shallow matching, `**` for deep matching.) * @param redact.values Value names and the corresponding values that should be redacted. * @param replacement Replacement function with which the values are redacted. * @returns Redacted object. */ function redactObject(input, redact, replacement) { const wildcardChars = ["*", "**"]; const clone = JSON.parse(JSON.stringify(input, getReplacer(replacement, redact.values))); if (redact.keys) traverse(clone, redact.keys); return clone; function traverse(object, checkKeyPaths) { if (checkKeyPaths.length === 0) return; const REDACTED_TEXT$1 = replacement(); const globalCheckPaths = []; for (const key of Object.keys(object)) { const localCheckPaths = []; for (const [index, path] of [...checkKeyPaths].entries()) { const [current, ...remaining] = path; const escapedKey = wildcardChars.includes(key) ? `\\${key}` : key; switch (current) { case escapedKey: if (remaining.length > 0) localCheckPaths.push(remaining); else { object[key] = REDACTED_TEXT$1; checkKeyPaths.splice(index, 1); } break; case "*": if (remaining.length > 0) { globalCheckPaths.push(remaining); checkKeyPaths.splice(index, 1); } else object[key] = REDACTED_TEXT$1; break; case "**": if (remaining.length > 0) { const [next, ...nextRemaining] = remaining; if (next === escapedKey) if (nextRemaining.length === 0) object[key] = REDACTED_TEXT$1; else localCheckPaths.push(nextRemaining); else if (next !== void 0 && wildcardChars.includes(next)) localCheckPaths.push(remaining); else localCheckPaths.push(path); } else object[key] = REDACTED_TEXT$1; break; } } const value = object[key]; if (isObject(value)) traverse(value, [...globalCheckPaths, ...localCheckPaths]); } } } /** * Replace values and extract Error objects for use with JSON.stringify() */ function getReplacer(replacement, values) { const filteredValues = values ? Object.entries(values).filter(([_k, v]) => typeof v === "string" && v.length > 0) : []; const replacer = (seen) => { return function(_key, value) { if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack, cause: value.cause }; if (value !== null && typeof value === "object") { if (seen.has(value)) return "[Circular]"; seen.add(value); const newValue = Array.isArray(value) ? [] : {}; for (const [key2, value2] of Object.entries(value)) if (typeof value2 === "string") newValue[key2] = value2; else newValue[key2] = replacer(seen)(key2, value2); seen.delete(value); return newValue; } if (!values || filteredValues.length === 0 || typeof value !== "string") return value; let finalValue = value; for (const [redactKey, valueToRedact] of filteredValues) if (finalValue.includes(valueToRedact)) { const regexp = new RegExp(escapeRegexString(valueToRedact), "g"); finalValue = finalValue.replace(regexp, replacement(redactKey)); } return finalValue; }; }; return replacer(/* @__PURE__ */ new WeakSet()); } function escapeRegexString(string) { return string.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"); } //#endregion export { getReplacer, redactObject };