UNPKG

3.06 kBJavaScriptView Raw
1'use strict'
2
3const fastRedact = require('fast-redact')
4const { redactFmtSym, wildcardFirstSym } = require('./symbols')
5const { rx, validator } = fastRedact
6
7const validate = validator({
8 ERR_PATHS_MUST_BE_STRINGS: () => 'pino – redacted paths must be strings',
9 ERR_INVALID_PATH: (s) => `pino – redact paths array contains an invalid path (${s})`
10})
11
12const CENSOR = '[Redacted]'
13const strict = false // TODO should this be configurable?
14
15function redaction (opts, serialize) {
16 const { paths, censor } = handle(opts)
17
18 const shape = paths.reduce((o, str) => {
19 rx.lastIndex = 0
20 const first = rx.exec(str)
21 const next = rx.exec(str)
22
23 // ns is the top-level path segment, brackets + quoting removed.
24 let ns = first[1] !== undefined
25 ? first[1].replace(/^(?:"|'|`)(.*)(?:"|'|`)$/, '$1')
26 : first[0]
27
28 if (ns === '*') {
29 ns = wildcardFirstSym
30 }
31
32 // top level key:
33 if (next === null) {
34 o[ns] = null
35 return o
36 }
37
38 // path with at least two segments:
39 // if ns is already redacted at the top level, ignore lower level redactions
40 if (o[ns] === null) {
41 return o
42 }
43
44 const { index } = next
45 const nextPath = `${str.substr(index, str.length - 1)}`
46
47 o[ns] = o[ns] || []
48
49 // shape is a mix of paths beginning with literal values and wildcard
50 // paths [ "a.b.c", "*.b.z" ] should reduce to a shape of
51 // { "a": [ "b.c", "b.z" ], *: [ "b.z" ] }
52 // note: "b.z" is in both "a" and * arrays because "a" matches the wildcard.
53 // (* entry has wildcardFirstSym as key)
54 if (ns !== wildcardFirstSym && o[ns].length === 0) {
55 // first time ns's get all '*' redactions so far
56 o[ns].push(...(o[wildcardFirstSym] || []))
57 }
58
59 if (ns === wildcardFirstSym) {
60 // new * path gets added to all previously registered literal ns's.
61 Object.keys(o).forEach(function (k) {
62 if (o[k]) {
63 o[k].push(nextPath)
64 }
65 })
66 }
67
68 o[ns].push(nextPath)
69 return o
70 }, {})
71
72 // the redactor assigned to the format symbol key
73 // provides top level redaction for instances where
74 // an object is interpolated into the msg string
75 const result = {
76 [redactFmtSym]: fastRedact({ paths, censor, serialize, strict })
77 }
78
79 const topCensor = (...args) =>
80 typeof censor === 'function' ? serialize(censor(...args)) : serialize(censor)
81
82 return [...Object.keys(shape), ...Object.getOwnPropertySymbols(shape)].reduce((o, k) => {
83 // top level key:
84 if (shape[k] === null) o[k] = topCensor
85 else o[k] = fastRedact({ paths: shape[k], censor, serialize, strict })
86 return o
87 }, result)
88}
89
90function handle (opts) {
91 if (Array.isArray(opts)) {
92 opts = { paths: opts, censor: CENSOR }
93 validate(opts)
94 return opts
95 }
96 var { paths, censor = CENSOR, remove } = opts
97 if (Array.isArray(paths) === false) { throw Error('pino – redact must contain an array of strings') }
98 if (remove === true) censor = undefined
99 validate({ paths, censor })
100
101 return { paths, censor }
102}
103
104module.exports = redaction