894 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			894 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
'use strict'
 | 
						||
 | 
						||
const test = require('node:test')
 | 
						||
const assert = require('node:assert')
 | 
						||
 | 
						||
const { sink, once } = require('./helper')
 | 
						||
const pino = require('../')
 | 
						||
 | 
						||
test('redact option – throws if not array', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: 'req.headers.cookie' })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – throws if array does not only contain strings', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: ['req.headers.cookie', {}] })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – throws if array contains an invalid path', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: ['req,headers.cookie'] })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – throws if not array', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: { paths: 'req.headers.cookie' } })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – throws if array does not only contain strings', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: { paths: ['req.headers.cookie', {}] } })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – throws if array contains an invalid path', async () => {
 | 
						||
  assert.throws(() => {
 | 
						||
    pino({ redact: { paths: ['req,headers.cookie'] } })
 | 
						||
  })
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – top level key', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['key'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: { redact: 'me' }
 | 
						||
  })
 | 
						||
  const { key } = await once(stream, 'data')
 | 
						||
  assert.equal(key, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – top level key next level key', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['key', 'key.foo'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: { redact: 'me' }
 | 
						||
  })
 | 
						||
  const { key } = await once(stream, 'data')
 | 
						||
  assert.equal(key, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – next level key then top level key', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['key.foo', 'key'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: { redact: 'me' }
 | 
						||
  })
 | 
						||
  const { key } = await once(stream, 'data')
 | 
						||
  assert.equal(key, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.headers.cookie'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – child object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.headers.cookie'] }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact option – interpolated object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.headers.cookie'] }, stream)
 | 
						||
 | 
						||
  instance.info('test %j', {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { msg } = await once(stream, 'data')
 | 
						||
  assert.equal(JSON.parse(msg.replace(/test /, '')).req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'] } }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – child object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'] } }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths option – interpolated object', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'] } }, stream)
 | 
						||
 | 
						||
  instance.info('test %j', {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { msg } = await once(stream, 'data')
 | 
						||
  assert.equal(JSON.parse(msg.replace(/test /, '')).req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.censor option – sets the redact value', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'], censor: 'test' } }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, 'test')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.censor option – can be a function that accepts value and path arguments', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['topLevel'], censor: (value, path) => value + ' ' + path.join('.') } }, stream)
 | 
						||
  instance.info({
 | 
						||
    topLevel: 'test'
 | 
						||
  })
 | 
						||
  const { topLevel } = await once(stream, 'data')
 | 
						||
  assert.equal(topLevel, 'test topLevel')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.censor option – can be a function that accepts value and path arguments (nested path)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'], censor: (value, path) => value + ' ' + path.join('.') } }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1; req.headers.cookie')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove option – removes both key and value', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'], remove: true } }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal('cookie' in req.headers, false)
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove – top level key - object value', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['key'], remove: true } }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: { redact: 'me' }
 | 
						||
  })
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal('key' in o, false)
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove – top level key - number value', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['key'], remove: true } }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: 1
 | 
						||
  })
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal('key' in o, false)
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove – top level key - boolean value', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['key'], remove: true } }, stream)
 | 
						||
  instance.info({
 | 
						||
    key: false
 | 
						||
  })
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal('key' in o, false)
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove – top level key in child logger', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const opts = { redact: { paths: ['key'], remove: true } }
 | 
						||
  const instance = pino(opts, stream).child({ key: { redact: 'me' } })
 | 
						||
  instance.info('test')
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal('key' in o, false)
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths preserves original object values after the log write', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.headers.cookie'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(obj.req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.paths preserves original object values after the log write', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'] } }, stream)
 | 
						||
  const obj = {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(obj.req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.censor preserves original object values after the log write', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'], censor: 'test' } }, stream)
 | 
						||
  const obj = {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.req.headers.cookie, 'test')
 | 
						||
  assert.equal(obj.req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;')
 | 
						||
})
 | 
						||
 | 
						||
test('redact.remove preserves original object values after the log write', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['req.headers.cookie'], remove: true } }, stream)
 | 
						||
  const obj = {
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal('cookie' in o.req.headers, false)
 | 
						||
  assert.equal('cookie' in obj.req.headers, true)
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports last position wildcard paths', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.headers.*'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(req.headers.host, '[Redacted]')
 | 
						||
  assert.equal(req.headers.connection, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports first position wildcard paths', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['*.headers'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports first position wildcards before other paths', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['*.headers.cookie', 'req.id'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(req.id, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports first position wildcards after other paths', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.id', '*.headers.cookie'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(req.id, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports first position wildcards after top level keys', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['key', '*.headers.cookie'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports top level wildcard', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['*'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports top level wildcard with a censor function', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({
 | 
						||
    redact: {
 | 
						||
      paths: ['*'],
 | 
						||
      censor: () => '[Redacted]'
 | 
						||
    }
 | 
						||
  }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports top level wildcard and leading wildcard', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['*', '*.req'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redact – supports intermediate wildcard paths', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.*.cookie'] }, stream)
 | 
						||
  instance.info({
 | 
						||
    req: {
 | 
						||
      id: 7915,
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        host: 'localhost:3000',
 | 
						||
        connection: 'keep-alive',
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      },
 | 
						||
      remoteAddress: '::ffff:127.0.0.1',
 | 
						||
      remotePort: 58022
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redacts numbers at the top level', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['id'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    id: 7915
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.id, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redacts booleans at the top level', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['maybe'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    maybe: true
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.maybe, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redacts strings at the top level', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['s'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    s: 's'
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.s, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('does not redact primitives if not objects', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['a.b'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    a: 42
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.a, 42)
 | 
						||
})
 | 
						||
 | 
						||
test('redacts null at the top level', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['n'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    n: null
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.n, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports bracket notation', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['a["b.b"]'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    a: { 'b.b': 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.a['b.b'], '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports bracket notation with further nesting', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['a["b.b"].c'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    a: { 'b.b': { c: 'd' } }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.a['b.b'].c, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports bracket notation with empty string as path segment', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['a[""].c'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    a: { '': { c: 'd' } }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.a[''].c, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports leading bracket notation (single quote)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['[\'a.a\'].b'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    'a.a': { b: 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o['a.a'].b, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports leading bracket notation (double quote)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['["a.a"].b'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    'a.a': { b: 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o['a.a'].b, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports leading bracket notation (backtick quote)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['[`a.a`].b'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    'a.a': { b: 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o['a.a'].b, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports leading bracket notation (single-segment path)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['[`a.a`]'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    'a.a': { b: 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o['a.a'], '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('supports leading bracket notation (single-segment path, wildcard)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['[*]'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    'a.a': { b: 'c' }
 | 
						||
  }
 | 
						||
  instance.info(obj)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o['a.a'], '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('child bindings are redacted using wildcard path', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['*.headers.cookie'] }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('child bindings are redacted using wildcard and plain path keys', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.method', '*.headers.cookie'] }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, '[Redacted]')
 | 
						||
  assert.equal(req.method, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('redacts boolean at the top level', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['msg'] }, stream)
 | 
						||
  const obj = {
 | 
						||
    s: 's'
 | 
						||
  }
 | 
						||
  instance.info(obj, true)
 | 
						||
  const o = await once(stream, 'data')
 | 
						||
  assert.equal(o.s, 's')
 | 
						||
  assert.equal(o.msg, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('child can customize redact', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.method', '*.headers.cookie'] }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }, {
 | 
						||
    redact: ['req.url']
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;')
 | 
						||
  assert.equal(req.method, 'GET')
 | 
						||
  assert.equal(req.url, '[Redacted]')
 | 
						||
})
 | 
						||
 | 
						||
test('child can remove parent redact by array', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: ['req.method', '*.headers.cookie'] }, stream)
 | 
						||
  instance.child({
 | 
						||
    req: {
 | 
						||
      method: 'GET',
 | 
						||
      url: '/',
 | 
						||
      headers: {
 | 
						||
        cookie: 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;'
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }, {
 | 
						||
    redact: []
 | 
						||
  }).info('message completed')
 | 
						||
  const { req } = await once(stream, 'data')
 | 
						||
  assert.equal(req.headers.cookie, 'SESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;')
 | 
						||
  assert.equal(req.method, 'GET')
 | 
						||
})
 | 
						||
 | 
						||
test('redact safe stringify', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const instance = pino({ redact: { paths: ['that.secret'] } }, stream)
 | 
						||
 | 
						||
  instance.info({
 | 
						||
    that: {
 | 
						||
      secret: 'please hide me',
 | 
						||
      myBigInt: 123n
 | 
						||
    },
 | 
						||
    other: {
 | 
						||
      mySecondBigInt: 222n
 | 
						||
    }
 | 
						||
  })
 | 
						||
  const { that, other } = await once(stream, 'data')
 | 
						||
  assert.equal(that.secret, '[Redacted]')
 | 
						||
  assert.equal(that.myBigInt, 123)
 | 
						||
  assert.equal(other.mySecondBigInt, 222)
 | 
						||
})
 | 
						||
 | 
						||
test('censor function should not be called for non-existent nested paths (issue #2313)', async () => {
 | 
						||
  const stream = sink()
 | 
						||
  const censorCalls = []
 | 
						||
 | 
						||
  const instance = pino({
 | 
						||
    redact: {
 | 
						||
      paths: ['a.b.c', 'req.authorization', 'url'],
 | 
						||
      censor (value, path) {
 | 
						||
        censorCalls.push({ value, path: path.join('.') })
 | 
						||
        if (typeof value !== 'string') {
 | 
						||
          return '***'
 | 
						||
        }
 | 
						||
        return '***'
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }, stream)
 | 
						||
 | 
						||
  // Test case 1: parent exists but nested path doesn't
 | 
						||
  censorCalls.length = 0
 | 
						||
  instance.info({ req: { id: 'test' } }, 'test message')
 | 
						||
  await once(stream, 'data')
 | 
						||
  assert.equal(censorCalls.length, 0, 'censor should not be called when req.authorization does not exist')
 | 
						||
 | 
						||
  // Test case 2: parent exists but deeply nested path doesn't
 | 
						||
  censorCalls.length = 0
 | 
						||
  instance.info({ a: { d: 'test' } }, 'test message')
 | 
						||
  await once(stream, 'data')
 | 
						||
  assert.equal(censorCalls.length, 0, 'censor should not be called when a.b.c does not exist')
 | 
						||
 | 
						||
  // Test case 3: multiple parent keys exist but nested paths don't
 | 
						||
  censorCalls.length = 0
 | 
						||
  instance.info({ a: { c: 'should-not-show-me' }, req: { id: 'test' } }, 'test message')
 | 
						||
  await once(stream, 'data')
 | 
						||
  assert.equal(censorCalls.length, 0, 'censor should not be called when neither a.b.c nor req.authorization exist')
 | 
						||
 | 
						||
  // Test case 4: verify censor IS called when path exists
 | 
						||
  censorCalls.length = 0
 | 
						||
  instance.info({ req: { authorization: 'bearer token' } }, 'test message')
 | 
						||
  await once(stream, 'data')
 | 
						||
  assert.equal(censorCalls.length, 1, 'censor should be called when req.authorization exists')
 | 
						||
  assert.equal(censorCalls[0].path, 'req.authorization')
 | 
						||
  assert.equal(censorCalls[0].value, 'bearer token')
 | 
						||
})
 |