diff --git a/lib/deep-map.js b/lib/deep-map.js index c14857c..26727ee 100644 --- a/lib/deep-map.js +++ b/lib/deep-map.js @@ -38,12 +38,18 @@ const deepMap = (input, handler = v => v, path = ['$'], seen = new Set([input])) } else if (typeof input === 'object' || typeof input === 'function') { const result = {} - for (const propertyName of Object.getOwnPropertyNames(input)) { - // skip logging internal properties - if (propertyName.startsWith('_')) { - continue + const publicPropertyNames = Object.getOwnPropertyNames(input).filter((name) => !name.startsWith('_')) + + // if there are no public properties, check for custom serialization + if (publicPropertyNames.length === 0 && typeof input.toJSON === 'function') { + try { + return handler(input.toJSON(), path) + } catch (err) { + return `[error getting JSON value: ${err.message}]` } + } + for (const propertyName of publicPropertyNames) { try { const property = input[propertyName] const propertyPath = [...path, propertyName] diff --git a/tap-snapshots/test/suite/server-redact-nonstring.test.cjs b/tap-snapshots/test/suite/server-redact-nonstring.test.cjs index 314bf23..4660761 100644 --- a/tap-snapshots/test/suite/server-redact-nonstring.test.cjs +++ b/tap-snapshots/test/suite/server-redact-nonstring.test.cjs @@ -29,6 +29,10 @@ exports[`test/suite.js TAP server redact nonstring nonString emptyObject just se Object {} ` +exports[`test/suite.js TAP server redact nonstring nonString exampleCustomSerializerThrows just serverRedact > must match snapshot 1`] = ` +[error getting JSON value: Error in toJSON] +` + exports[`test/suite.js TAP server redact nonstring nonString exampleGetterThrows just serverRedact > must match snapshot 1`] = ` Object { "getter": "[error getting value: Error in getter]", @@ -148,6 +152,10 @@ Object { } ` +exports[`test/suite.js TAP server redact nonstring nonStringSensitve exampleCustomSerializer just serverRedact > must match snapshot 1`] = ` +latitude: 37.7822689; longitude: -122.3937449; secret: [REDACTED_NPM_SECRET] +` + exports[`test/suite.js TAP server redact nonstring nonStringSensitve exampleGetter just serverRedact > must match snapshot 1`] = ` Object { "getter": "[REDACTED_NPM_SECRET]", diff --git a/test/fixtures/complex.js b/test/fixtures/complex.js index 0090210..7b15ecf 100644 --- a/test/fixtures/complex.js +++ b/test/fixtures/complex.js @@ -27,6 +27,15 @@ const exampleGetter = (() => { return example })() +const exampleCustomSerializer = (() => { + class ExampleClass { + toJSON () { + return `latitude: 37.7822689; longitude: -122.3937449; secret: ${examples.NPM_SECRET.npm_36}` + } + } + return new ExampleClass() +})() + const exampleGetterThrows = (() => { const example = {} Object.defineProperty(example, 'getter', { @@ -37,6 +46,15 @@ const exampleGetterThrows = (() => { return example })() +const exampleCustomSerializerThrows = (() => { + class ExampleClass { + toJSON () { + throw new Error('Error in toJSON') + } + } + return new ExampleClass() +})() + const circular = (() => { const example = exampleObject exampleObject.nested = exampleObject @@ -64,6 +82,7 @@ const examplesNonString = { objectNumbers: { a: 1, b: 2, c: 3 }, objectStrings: { a: 'a', b: 'b', c: 'c' }, exampleGetterThrows, + exampleCustomSerializerThrows, } const error = (() => { @@ -81,6 +100,7 @@ const examplesNonStringSensitive = { circular, circularArray, exampleGetter, + exampleCustomSerializer, error, privateProperty: { ...exampleObject, diff --git a/test/utils.js b/test/utils.js index a439a0d..daa77ce 100644 --- a/test/utils.js +++ b/test/utils.js @@ -119,6 +119,30 @@ t.test('error with buffer', async () => { t.same(deepMap(new TextEncoder().encode('hello')), '[unable to log instanceof Uint8Array]') +t.test('deepMap date', async (t) => { + const date = new Date('2010-01-12T00:00:00.000Z') + const result = deepMap(date) + t.same(result, '2010-01-12T00:00:00.000Z') +}) + +t.test('deepMap nested date', async (t) => { + const date = new Date('2010-01-12T00:00:00.000Z') + const result = deepMap({ message: 'npm version 0.0.1 published', date }) + t.same(result, ({ message: 'npm version 0.0.1 published', date: '2010-01-12T00:00:00.000Z' })) +}) + +t.test('deepMap URL', async (t) => { + const url = new URL('https://www.npmjs.com/package/@npmcli/redact?activeTab=versions') + const result = deepMap(url) + t.same(result, 'https://www.npmjs.com/package/@npmcli/redact?activeTab=versions') +}) + +t.test('deepMap nested URL', async (t) => { + const url = new URL('https://www.npmjs.com/package/@npmcli/redact?activeTab=versions') + const result = deepMap({ message: 'request start', url }) + t.same(result, ({ message: 'request start', url: 'https://www.npmjs.com/package/@npmcli/redact?activeTab=versions' })) +}) + const redactUrl = redactMatchers( redactUrlMatcher( redactUrlHostnameMatcher({ hostname: 'example.com', replacement: 'example.net' }),