1
import { getRedactedString, REDACTED_TEXT } from '@directus/utils';
2
import { merge } from 'lodash-es';
3
import { describe, expect, test } from 'vitest';
4
import { getReplacer, redactObject } from './redact-object.js';
10
first_name: 'Example',
12
email: 'user@example.com',
15
key: 'eb641950-fffa-4388-8606-aede594ae487',
16
collection: 'directus_users',
20
event: 'users.create',
22
first_name: 'Example',
24
email: 'user@example.com',
27
key: 'eb641950-fffa-4388-8606-aede594ae487',
28
collection: 'directus_users',
31
event: 'users.create',
33
first_name: 'Example',
35
email: 'user@example.com',
38
key: 'eb641950-fffa-4388-8606-aede594ae487',
39
collection: 'directus_users',
44
test('should not mutate input', () => {
45
const result = redactObject(input, { keys: [['$trigger']] }, getRedactedString);
47
expect(result).not.toBe(input);
50
test('should support single level path', () => {
51
const result = redactObject(input, { keys: [['$trigger']] }, getRedactedString);
53
expect(result).toEqual(
55
$trigger: REDACTED_TEXT,
60
test('should support multi level path', () => {
61
const result = redactObject(input, { keys: [['$trigger', 'payload', 'password']] }, getRedactedString);
63
expect(result).toEqual(
66
payload: { password: REDACTED_TEXT },
72
test('should support wildcard path', () => {
73
const result = redactObject(input, { keys: [['*', 'payload']] }, getRedactedString);
75
expect(result).toEqual(
78
payload: REDACTED_TEXT,
84
test('should support deep path', () => {
85
const result = redactObject(input, { keys: [['**', 'password']] }, getRedactedString);
87
expect(result).toMatchObject(
91
password: REDACTED_TEXT,
97
password: REDACTED_TEXT,
102
password: REDACTED_TEXT,
110
test('should support multiple paths', () => {
111
const result = redactObject(
116
['*', 'payload', 'email'],
123
expect(result).toEqual(
128
email: REDACTED_TEXT,
129
password: REDACTED_TEXT,
135
password: REDACTED_TEXT,
140
password: REDACTED_TEXT,
148
describe('getReplacer tests', () => {
149
test('Returns parsed error object', () => {
150
const errorMessage = 'Error Message';
151
const errorCause = 'Error Cause';
152
const replacer = getReplacer(getRedactedString);
153
const result: any = replacer('', new Error(errorMessage, { cause: errorCause }));
154
expect(result.name).toBe('Error');
155
expect(result.message).toBe(errorMessage);
156
expect(result.stack).toBeDefined();
157
expect(result.cause).toBe(errorCause);
160
test('Returns other types as is', () => {
179
const replacer = getReplacer(getRedactedString);
181
for (const value of values) {
182
expect(replacer('', value)).toEqual(value);
186
test('Correctly parses object with circular structure when used with JSON.stringify()', () => {
187
const obj: Record<string, any> = {
195
const expectedResult = {
198
c: { obj: '[Circular]' },
202
const result = JSON.parse(JSON.stringify(obj, getReplacer(getRedactedString)));
204
expect(result).toStrictEqual(expectedResult);
207
test('Correctly parses object with repeatedly occurring same refs', () => {
210
const obj: Record<string, any> = {
215
const expectedResult = {
220
const result = JSON.parse(JSON.stringify(obj, getReplacer(getRedactedString)));
222
expect(result).toStrictEqual(expectedResult);
225
test('Correctly parses error object when used with JSON.stringify()', () => {
226
const errorMessage = 'Error Message';
227
const errorCause = 'Error Cause';
236
const objWithError = {
238
error: new Error(errorMessage, { cause: errorCause }),
241
const expectedResult = {
243
error: { name: 'Error', message: errorMessage, cause: errorCause },
246
const result = JSON.parse(JSON.stringify(objWithError, getReplacer(getRedactedString)));
248
// Stack changes depending on env
249
expect(result.error.stack).toBeDefined();
250
delete result.error.stack;
252
expect(result).toStrictEqual(expectedResult);
255
test('Correctly redacts values when used with JSON.stringify()', () => {
260
string_ignore: `No error Cause it's case sensitive~~`,
263
const objWithError = {
265
string: `Replace cause case matches Errors~~`,
266
nested: { another_str: 'just because of safety 123456' },
267
nested_array: [{ str_a: 'cause surely' }, { str_b: 'not an Error' }, { str_ignore: 'nothing here' }],
268
array: ['something', 'no Error', 'just because', 'all is good'],
269
error: new Error('This is an Error message.', { cause: 'Here is an Error cause!' }),
272
const expectedResult = {
274
string: `Replace ${getRedactedString('cause')} case matches ${getRedactedString('ERROR')}s~~`,
275
nested: { another_str: `just be${getRedactedString('cause')} of safety 123456` },
277
{ str_a: `${getRedactedString('cause')} surely` },
278
{ str_b: `not an ${getRedactedString('ERROR')}` },
279
{ str_ignore: 'nothing here' },
281
array: ['something', `no ${getRedactedString('ERROR')}`, `just be${getRedactedString('cause')}`, 'all is good'],
283
name: getRedactedString('ERROR'),
284
message: `This is an ${getRedactedString('ERROR')} message.`,
285
cause: `Here is an ${getRedactedString('ERROR')} ${getRedactedString('cause')}!`,
289
const result = JSON.parse(
292
getReplacer(getRedactedString, {
301
// Stack changes depending on env
302
expect(result.error.stack).toBeDefined();
303
delete result.error.stack;
305
expect(result).toStrictEqual(expectedResult);