Buckets:
| const { test } = require('node:test') | |
| const { strict: assert } = require('node:assert') | |
| const slowRedact = require('../index.js') | |
| test('basic path redaction', () => { | |
| const obj = { | |
| headers: { | |
| cookie: 'secret-cookie', | |
| authorization: 'Bearer token' | |
| }, | |
| body: { message: 'hello' } | |
| } | |
| const redact = slowRedact({ paths: ['headers.cookie'] }) | |
| const result = redact(obj) | |
| // Original object should remain unchanged | |
| assert.strictEqual(obj.headers.cookie, 'secret-cookie') | |
| // Result should have redacted path | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.headers.cookie, '[REDACTED]') | |
| assert.strictEqual(parsed.headers.authorization, 'Bearer token') | |
| assert.strictEqual(parsed.body.message, 'hello') | |
| }) | |
| test('multiple paths redaction', () => { | |
| const obj = { | |
| user: { name: 'john', password: 'secret' }, | |
| session: { token: 'abc123' } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['user.password', 'session.token'] | |
| }) | |
| const result = redact(obj) | |
| // Original unchanged | |
| assert.strictEqual(obj.user.password, 'secret') | |
| assert.strictEqual(obj.session.token, 'abc123') | |
| // Result redacted | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.user.password, '[REDACTED]') | |
| assert.strictEqual(parsed.session.token, '[REDACTED]') | |
| assert.strictEqual(parsed.user.name, 'john') | |
| }) | |
| test('custom censor value', () => { | |
| const obj = { secret: 'hidden' } | |
| const redact = slowRedact({ | |
| paths: ['secret'], | |
| censor: '***' | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.secret, '***') | |
| }) | |
| test('serialize: false returns object with restore method', () => { | |
| const obj = { secret: 'hidden' } | |
| const redact = slowRedact({ | |
| paths: ['secret'], | |
| serialize: false | |
| }) | |
| const result = redact(obj) | |
| // Should be object, not string | |
| assert.strictEqual(typeof result, 'object') | |
| assert.strictEqual(result.secret, '[REDACTED]') | |
| // Should have restore method | |
| assert.strictEqual(typeof result.restore, 'function') | |
| const restored = result.restore() | |
| assert.strictEqual(restored.secret, 'hidden') | |
| }) | |
| test('bracket notation paths', () => { | |
| const obj = { | |
| 'weird-key': { 'another-weird': 'secret' }, | |
| normal: 'public' | |
| } | |
| const redact = slowRedact({ | |
| paths: ['["weird-key"]["another-weird"]'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed['weird-key']['another-weird'], '[REDACTED]') | |
| assert.strictEqual(parsed.normal, 'public') | |
| }) | |
| test('array paths', () => { | |
| const obj = { | |
| users: [ | |
| { name: 'john', password: 'secret1' }, | |
| { name: 'jane', password: 'secret2' } | |
| ] | |
| } | |
| const redact = slowRedact({ | |
| paths: ['users[0].password', 'users[1].password'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.users[0].password, '[REDACTED]') | |
| assert.strictEqual(parsed.users[1].password, '[REDACTED]') | |
| assert.strictEqual(parsed.users[0].name, 'john') | |
| assert.strictEqual(parsed.users[1].name, 'jane') | |
| }) | |
| test('wildcard at end of path', () => { | |
| const obj = { | |
| secrets: { | |
| key1: 'secret1', | |
| key2: 'secret2' | |
| }, | |
| public: 'data' | |
| } | |
| const redact = slowRedact({ | |
| paths: ['secrets.*'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.secrets.key1, '[REDACTED]') | |
| assert.strictEqual(parsed.secrets.key2, '[REDACTED]') | |
| assert.strictEqual(parsed.public, 'data') | |
| }) | |
| test('wildcard with arrays', () => { | |
| const obj = { | |
| items: ['secret1', 'secret2', 'secret3'] | |
| } | |
| const redact = slowRedact({ | |
| paths: ['items.*'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.items[0], '[REDACTED]') | |
| assert.strictEqual(parsed.items[1], '[REDACTED]') | |
| assert.strictEqual(parsed.items[2], '[REDACTED]') | |
| }) | |
| test('intermediate wildcard', () => { | |
| const obj = { | |
| users: { | |
| user1: { password: 'secret1' }, | |
| user2: { password: 'secret2' } | |
| } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['users.*.password'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.users.user1.password, '[REDACTED]') | |
| assert.strictEqual(parsed.users.user2.password, '[REDACTED]') | |
| }) | |
| test('censor function', () => { | |
| const obj = { secret: 'hidden' } | |
| const redact = slowRedact({ | |
| paths: ['secret'], | |
| censor: (value, path) => `REDACTED:${path.join('.')}` | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.secret, 'REDACTED:secret') | |
| }) | |
| test('custom serialize function', () => { | |
| const obj = { secret: 'hidden', public: 'data' } | |
| const redact = slowRedact({ | |
| paths: ['secret'], | |
| serialize: (obj) => `custom:${JSON.stringify(obj)}` | |
| }) | |
| const result = redact(obj) | |
| assert(result.startsWith('custom:')) | |
| const parsed = JSON.parse(result.slice(7)) | |
| assert.strictEqual(parsed.secret, '[REDACTED]') | |
| assert.strictEqual(parsed.public, 'data') | |
| }) | |
| test('nested paths', () => { | |
| const obj = { | |
| level1: { | |
| level2: { | |
| level3: { | |
| secret: 'hidden' | |
| } | |
| } | |
| } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['level1.level2.level3.secret'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.level1.level2.level3.secret, '[REDACTED]') | |
| }) | |
| test('non-existent paths are ignored', () => { | |
| const obj = { existing: 'value' } | |
| const redact = slowRedact({ | |
| paths: ['nonexistent.path'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.existing, 'value') | |
| assert.strictEqual(parsed.nonexistent, undefined) | |
| }) | |
| test('null and undefined handling', () => { | |
| const obj = { | |
| nullValue: null, | |
| undefinedValue: undefined, | |
| nested: { | |
| nullValue: null | |
| } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['nullValue', 'nested.nullValue'] | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.nullValue, '[REDACTED]') | |
| assert.strictEqual(parsed.nested.nullValue, '[REDACTED]') | |
| }) | |
| test('original object remains unchanged', () => { | |
| const original = { | |
| secret: 'hidden', | |
| nested: { secret: 'hidden2' } | |
| } | |
| const copy = JSON.parse(JSON.stringify(original)) | |
| const redact = slowRedact({ | |
| paths: ['secret', 'nested.secret'] | |
| }) | |
| redact(original) | |
| // Original should be completely unchanged | |
| assert.deepStrictEqual(original, copy) | |
| }) | |
| test('strict mode with primitives', () => { | |
| const redact = slowRedact({ | |
| paths: ['test'], | |
| strict: true | |
| }) | |
| const stringResult = redact('primitive') | |
| assert.strictEqual(stringResult, '"primitive"') | |
| const numberResult = redact(42) | |
| assert.strictEqual(numberResult, '42') | |
| }) | |
| // Path validation tests to match fast-redact behavior | |
| test('path validation - non-string paths should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: [123] }) | |
| }, { | |
| message: 'Paths must be (non-empty) strings' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: [null] }) | |
| }, { | |
| message: 'Paths must be (non-empty) strings' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: [undefined] }) | |
| }, { | |
| message: 'Paths must be (non-empty) strings' | |
| }) | |
| }) | |
| test('path validation - empty string should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: [''] }) | |
| }, { | |
| message: 'Invalid redaction path ()' | |
| }) | |
| }) | |
| test('path validation - double dots should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: ['invalid..path'] }) | |
| }, { | |
| message: 'Invalid redaction path (invalid..path)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['a..b..c'] }) | |
| }, { | |
| message: 'Invalid redaction path (a..b..c)' | |
| }) | |
| }) | |
| test('path validation - unmatched brackets should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: ['invalid[unclosed'] }) | |
| }, { | |
| message: 'Invalid redaction path (invalid[unclosed)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['invalid]unopened'] }) | |
| }, { | |
| message: 'Invalid redaction path (invalid]unopened)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['nested[a[b]'] }) | |
| }, { | |
| message: 'Invalid redaction path (nested[a[b])' | |
| }) | |
| }) | |
| test('path validation - comma-separated paths should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: ['req,headers.cookie'] }) | |
| }, { | |
| message: 'Invalid redaction path (req,headers.cookie)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['user,profile,name'] }) | |
| }, { | |
| message: 'Invalid redaction path (user,profile,name)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['a,b'] }) | |
| }, { | |
| message: 'Invalid redaction path (a,b)' | |
| }) | |
| }) | |
| test('path validation - mixed valid and invalid should throw', () => { | |
| assert.throws(() => { | |
| slowRedact({ paths: ['valid.path', 123, 'another.valid'] }) | |
| }, { | |
| message: 'Paths must be (non-empty) strings' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['valid.path', 'invalid..path'] }) | |
| }, { | |
| message: 'Invalid redaction path (invalid..path)' | |
| }) | |
| assert.throws(() => { | |
| slowRedact({ paths: ['valid.path', 'req,headers.cookie'] }) | |
| }, { | |
| message: 'Invalid redaction path (req,headers.cookie)' | |
| }) | |
| }) | |
| test('path validation - valid paths should work', () => { | |
| // These should not throw | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: [] }) | |
| }) | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: ['valid.path'] }) | |
| }) | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: ['user.password', 'data[0].secret'] }) | |
| }) | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: ['["quoted-key"].value'] }) | |
| }) | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: ["['single-quoted'].value"] }) | |
| }) | |
| assert.doesNotThrow(() => { | |
| slowRedact({ paths: ['array[0]', 'object.property', 'wildcard.*'] }) | |
| }) | |
| }) | |
| // fast-redact compatibility tests | |
| test('censor function receives path as array (fast-redact compatibility)', () => { | |
| const obj = { | |
| headers: { | |
| authorization: 'Bearer token', | |
| 'x-api-key': 'secret-key' | |
| } | |
| } | |
| const pathsReceived = [] | |
| const redact = slowRedact({ | |
| paths: ['headers.authorization', 'headers["x-api-key"]'], | |
| censor: (value, path) => { | |
| pathsReceived.push(path) | |
| assert(Array.isArray(path), 'Path should be an array') | |
| return '[REDACTED]' | |
| } | |
| }) | |
| redact(obj) | |
| // Verify paths are arrays | |
| assert.strictEqual(pathsReceived.length, 2) | |
| assert.deepStrictEqual(pathsReceived[0], ['headers', 'authorization']) | |
| assert.deepStrictEqual(pathsReceived[1], ['headers', 'x-api-key']) | |
| }) | |
| test('censor function with nested paths receives correct array', () => { | |
| const obj = { | |
| user: { | |
| profile: { | |
| credentials: { | |
| password: 'secret123' | |
| } | |
| } | |
| } | |
| } | |
| let receivedPath | |
| const redact = slowRedact({ | |
| paths: ['user.profile.credentials.password'], | |
| censor: (value, path) => { | |
| receivedPath = path | |
| assert.strictEqual(value, 'secret123') | |
| assert(Array.isArray(path)) | |
| return '[REDACTED]' | |
| } | |
| }) | |
| redact(obj) | |
| assert.deepStrictEqual(receivedPath, ['user', 'profile', 'credentials', 'password']) | |
| }) | |
| test('censor function with wildcards receives correct array paths', () => { | |
| const obj = { | |
| users: { | |
| user1: { password: 'secret1' }, | |
| user2: { password: 'secret2' } | |
| } | |
| } | |
| const pathsReceived = [] | |
| const redact = slowRedact({ | |
| paths: ['users.*.password'], | |
| censor: (value, path) => { | |
| pathsReceived.push([...path]) // copy the array | |
| assert(Array.isArray(path)) | |
| return '[REDACTED]' | |
| } | |
| }) | |
| redact(obj) | |
| assert.strictEqual(pathsReceived.length, 2) | |
| assert.deepStrictEqual(pathsReceived[0], ['users', 'user1', 'password']) | |
| assert.deepStrictEqual(pathsReceived[1], ['users', 'user2', 'password']) | |
| }) | |
| test('censor function with array wildcard receives correct array paths', () => { | |
| const obj = { | |
| items: [ | |
| { secret: 'value1' }, | |
| { secret: 'value2' } | |
| ] | |
| } | |
| const pathsReceived = [] | |
| const redact = slowRedact({ | |
| paths: ['items.*.secret'], | |
| censor: (value, path) => { | |
| pathsReceived.push([...path]) | |
| assert(Array.isArray(path)) | |
| return '[REDACTED]' | |
| } | |
| }) | |
| redact(obj) | |
| assert.strictEqual(pathsReceived.length, 2) | |
| assert.deepStrictEqual(pathsReceived[0], ['items', '0', 'secret']) | |
| assert.deepStrictEqual(pathsReceived[1], ['items', '1', 'secret']) | |
| }) | |
| test('censor function with end wildcard receives correct array paths', () => { | |
| const obj = { | |
| secrets: { | |
| key1: 'secret1', | |
| key2: 'secret2' | |
| } | |
| } | |
| const pathsReceived = [] | |
| const redact = slowRedact({ | |
| paths: ['secrets.*'], | |
| censor: (value, path) => { | |
| pathsReceived.push([...path]) | |
| assert(Array.isArray(path)) | |
| return '[REDACTED]' | |
| } | |
| }) | |
| redact(obj) | |
| assert.strictEqual(pathsReceived.length, 2) | |
| // Sort paths for consistent testing since object iteration order isn't guaranteed | |
| pathsReceived.sort((a, b) => a[1].localeCompare(b[1])) | |
| assert.deepStrictEqual(pathsReceived[0], ['secrets', 'key1']) | |
| assert.deepStrictEqual(pathsReceived[1], ['secrets', 'key2']) | |
| }) | |
| test('type safety: accessing properties on primitive values should not throw', () => { | |
| // Test case from GitHub issue #5 | |
| const redactor = slowRedact({ paths: ['headers.authorization'] }) | |
| const data = { | |
| headers: 123 // primitive value | |
| } | |
| assert.doesNotThrow(() => { | |
| const result = redactor(data) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.headers, 123) // Should remain unchanged | |
| }) | |
| // Test wildcards with primitives | |
| const redactor2 = slowRedact({ paths: ['data.*.nested'] }) | |
| const data2 = { | |
| data: { | |
| item1: 123, // primitive, trying to access .nested on it | |
| item2: { nested: 'secret' } | |
| } | |
| } | |
| assert.doesNotThrow(() => { | |
| const result2 = redactor2(data2) | |
| const parsed2 = JSON.parse(result2) | |
| assert.strictEqual(parsed2.data.item1, 123) // Primitive unchanged | |
| assert.strictEqual(parsed2.data.item2.nested, '[REDACTED]') // Object property redacted | |
| }) | |
| // Test deep nested access on primitives | |
| const redactor3 = slowRedact({ paths: ['user.name.first.charAt'] }) | |
| const data3 = { | |
| user: { | |
| name: 'John' // string primitive | |
| } | |
| } | |
| assert.doesNotThrow(() => { | |
| const result3 = redactor3(data3) | |
| const parsed3 = JSON.parse(result3) | |
| assert.strictEqual(parsed3.user.name, 'John') // Should remain unchanged | |
| }) | |
| }) | |
| // Remove option tests | |
| test('remove option: basic key removal', () => { | |
| const obj = { username: 'john', password: 'secret123' } | |
| const redact = slowRedact({ paths: ['password'], remove: true }) | |
| const result = redact(obj) | |
| // Original object should remain unchanged | |
| assert.strictEqual(obj.password, 'secret123') | |
| // Result should have password completely removed | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.username, 'john') | |
| assert.strictEqual('password' in parsed, false) | |
| assert.strictEqual(parsed.password, undefined) | |
| }) | |
| test('remove option: multiple paths removal', () => { | |
| const obj = { | |
| user: { name: 'john', password: 'secret' }, | |
| session: { token: 'abc123', id: 'session1' } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['user.password', 'session.token'], | |
| remove: true | |
| }) | |
| const result = redact(obj) | |
| // Original unchanged | |
| assert.strictEqual(obj.user.password, 'secret') | |
| assert.strictEqual(obj.session.token, 'abc123') | |
| // Result has keys completely removed | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.user.name, 'john') | |
| assert.strictEqual(parsed.session.id, 'session1') | |
| assert.strictEqual('password' in parsed.user, false) | |
| assert.strictEqual('token' in parsed.session, false) | |
| }) | |
| test('remove option: wildcard removal', () => { | |
| const obj = { | |
| secrets: { | |
| key1: 'secret1', | |
| key2: 'secret2' | |
| }, | |
| public: 'data' | |
| } | |
| const redact = slowRedact({ | |
| paths: ['secrets.*'], | |
| remove: true | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.public, 'data') | |
| assert.deepStrictEqual(parsed.secrets, {}) // All keys removed | |
| }) | |
| test('remove option: array wildcard removal', () => { | |
| const obj = { | |
| items: ['secret1', 'secret2', 'secret3'], | |
| meta: 'data' | |
| } | |
| const redact = slowRedact({ | |
| paths: ['items.*'], | |
| remove: true | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.meta, 'data') | |
| // Array items set to undefined are omitted by JSON.stringify | |
| assert.deepStrictEqual(parsed.items, [null, null, null]) | |
| }) | |
| test('remove option: intermediate wildcard removal', () => { | |
| const obj = { | |
| users: { | |
| user1: { password: 'secret1', name: 'john' }, | |
| user2: { password: 'secret2', name: 'jane' } | |
| } | |
| } | |
| const redact = slowRedact({ | |
| paths: ['users.*.password'], | |
| remove: true | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.users.user1.name, 'john') | |
| assert.strictEqual(parsed.users.user2.name, 'jane') | |
| assert.strictEqual('password' in parsed.users.user1, false) | |
| assert.strictEqual('password' in parsed.users.user2, false) | |
| }) | |
| test('remove option: serialize false returns object with removed keys', () => { | |
| const obj = { secret: 'hidden', public: 'data' } | |
| const redact = slowRedact({ | |
| paths: ['secret'], | |
| remove: true, | |
| serialize: false | |
| }) | |
| const result = redact(obj) | |
| // Should be object, not string | |
| assert.strictEqual(typeof result, 'object') | |
| assert.strictEqual(result.public, 'data') | |
| assert.strictEqual('secret' in result, false) | |
| // Should have restore method | |
| assert.strictEqual(typeof result.restore, 'function') | |
| const restored = result.restore() | |
| assert.strictEqual(restored.secret, 'hidden') | |
| }) | |
| test('remove option: non-existent paths are ignored', () => { | |
| const obj = { existing: 'value' } | |
| const redact = slowRedact({ | |
| paths: ['nonexistent.path'], | |
| remove: true | |
| }) | |
| const result = redact(obj) | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed.existing, 'value') | |
| assert.strictEqual(parsed.nonexistent, undefined) | |
| }) | |
| // Test for Issue #13: Empty string bracket notation paths not being redacted correctly | |
| test('empty string bracket notation path', () => { | |
| const obj = { '': { c: 'sensitive-data' } } | |
| const redact = slowRedact({ paths: ["[''].c"] }) | |
| const result = redact(obj) | |
| // Original object should remain unchanged | |
| assert.strictEqual(obj[''].c, 'sensitive-data') | |
| // Result should have redacted path | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed[''].c, '[REDACTED]') | |
| }) | |
| test('empty string bracket notation with double quotes', () => { | |
| const obj = { '': { c: 'sensitive-data' } } | |
| const redact = slowRedact({ paths: ['[""].c'] }) | |
| const result = redact(obj) | |
| // Original object should remain unchanged | |
| assert.strictEqual(obj[''].c, 'sensitive-data') | |
| // Result should have redacted path | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed[''].c, '[REDACTED]') | |
| }) | |
| test('empty string key with nested bracket notation', () => { | |
| const obj = { '': { '': { secret: 'value' } } } | |
| const redact = slowRedact({ paths: ["[''][''].secret"] }) | |
| const result = redact(obj) | |
| // Original object should remain unchanged | |
| assert.strictEqual(obj[''][''].secret, 'value') | |
| // Result should have redacted path | |
| const parsed = JSON.parse(result) | |
| assert.strictEqual(parsed[''][''].secret, '[REDACTED]') | |
| }) | |
| // Test for Pino issue #2313: censor should only be called when path exists | |
| test('censor function not called for non-existent paths', () => { | |
| let censorCallCount = 0 | |
| const censorCalls = [] | |
| const redact = slowRedact({ | |
| paths: ['a.b.c', 'req.authorization', 'url'], | |
| serialize: false, | |
| censor (value, path) { | |
| censorCallCount++ | |
| censorCalls.push({ value, path: path.slice() }) | |
| return '***' | |
| } | |
| }) | |
| // Test case 1: { req: { id: 'test' } } | |
| // req.authorization doesn't exist, censor should not be called for it | |
| censorCallCount = 0 | |
| censorCalls.length = 0 | |
| redact({ req: { id: 'test' } }) | |
| // Should not have been called for any path since none exist | |
| assert.strictEqual(censorCallCount, 0, 'censor should not be called when paths do not exist') | |
| // Test case 2: { a: { d: 'test' } } | |
| // a.b.c doesn't exist (a.d exists, but not a.b.c) | |
| censorCallCount = 0 | |
| redact({ a: { d: 'test' } }) | |
| assert.strictEqual(censorCallCount, 0) | |
| // Test case 3: paths that do exist should still call censor | |
| censorCallCount = 0 | |
| censorCalls.length = 0 | |
| const result = redact({ req: { authorization: 'bearer token' } }) | |
| assert.strictEqual(censorCallCount, 1, 'censor should be called when path exists') | |
| assert.deepStrictEqual(censorCalls[0].path, ['req', 'authorization']) | |
| assert.strictEqual(censorCalls[0].value, 'bearer token') | |
| assert.strictEqual(result.req.authorization, '***') | |
| }) | |
| test('censor function not called for non-existent nested paths', () => { | |
| let censorCallCount = 0 | |
| const redact = slowRedact({ | |
| paths: ['headers.authorization'], | |
| serialize: false, | |
| censor (value, path) { | |
| censorCallCount++ | |
| return '[REDACTED]' | |
| } | |
| }) | |
| // headers exists but authorization doesn't | |
| censorCallCount = 0 | |
| const result1 = redact({ headers: { 'content-type': 'application/json' } }) | |
| assert.strictEqual(censorCallCount, 0) | |
| assert.deepStrictEqual(result1.headers, { 'content-type': 'application/json' }) | |
| // headers doesn't exist at all | |
| censorCallCount = 0 | |
| const result2 = redact({ body: 'data' }) | |
| assert.strictEqual(censorCallCount, 0) | |
| assert.strictEqual(result2.body, 'data') | |
| assert.strictEqual(typeof result2.restore, 'function') | |
| // headers.authorization exists - should call censor | |
| censorCallCount = 0 | |
| const result3 = redact({ headers: { authorization: 'Bearer token' } }) | |
| assert.strictEqual(censorCallCount, 1) | |
| assert.strictEqual(result3.headers.authorization, '[REDACTED]') | |
| }) | |
Xet Storage Details
- Size:
- 22 kB
- Xet hash:
- 2979d68852d6393b2c1ce74902b0d327d3b4088ca4e3f5a63c0a209125bd9052
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.