update
This commit is contained in:
parent
d9becc67b6
commit
9308795b8b
964 changed files with 104265 additions and 16 deletions
9
node_modules/fast-redact/.travis.yml
generated
vendored
Normal file
9
node_modules/fast-redact/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- 6
|
||||
- 8
|
||||
- 10
|
||||
- 11
|
||||
script:
|
||||
- npm run ci
|
21
node_modules/fast-redact/LICENSE
generated
vendored
Normal file
21
node_modules/fast-redact/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-2020 David Mark Clements
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
148
node_modules/fast-redact/benchmark/index.js
generated
vendored
Normal file
148
node_modules/fast-redact/benchmark/index.js
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
'use strict'
|
||||
const bench = require('fastbench')
|
||||
const noir = require('pino-noir')(['a.b.c'])
|
||||
const fastRedact = require('..')
|
||||
const redactNoSerialize = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const redactWildNoSerialize = fastRedact({ paths: ['a.b.*'], serialize: false })
|
||||
const redactIntermediateWildNoSerialize = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
const redact = fastRedact({ paths: ['a.b.c'] })
|
||||
const noirWild = require('pino-noir')(['a.b.*'])
|
||||
const redactWild = fastRedact({ paths: ['a.b.*'] })
|
||||
const redactIntermediateWild = fastRedact({ paths: ['a.*.c'] })
|
||||
const redactIntermediateWildMatchWildOutcome = fastRedact({ paths: ['a.*.c', 'a.*.b', 'a.*.a'] })
|
||||
const redactStaticMatchWildOutcome = fastRedact({ paths: ['a.b.c', 'a.d.a', 'a.d.b', 'a.d.c'] })
|
||||
const noirCensorFunction = require('pino-noir')(['a.b.*'], (v) => v + '.')
|
||||
const redactCensorFunction = fastRedact({ paths: ['a.b.*'], censor: (v) => v + '.', serialize: false })
|
||||
|
||||
const obj = {
|
||||
a: {
|
||||
b: {
|
||||
c: 's'
|
||||
},
|
||||
d: {
|
||||
a: 's',
|
||||
b: 's',
|
||||
c: 's'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const max = 500
|
||||
|
||||
var run = bench([
|
||||
function benchNoirV2 (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
noir.a(obj.a)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedact (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactNoSerialize(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactRestore (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactNoSerialize(obj)
|
||||
redactNoSerialize.restore(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchNoirV2Wild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
noirWild.a(obj.a)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactWild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactWildNoSerialize(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactWildRestore (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactWildNoSerialize(obj)
|
||||
redactWildNoSerialize.restore(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactIntermediateWild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactIntermediateWildNoSerialize(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactIntermediateWildRestore (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactIntermediateWildNoSerialize(obj)
|
||||
redactIntermediateWildNoSerialize.restore(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchJSONStringify (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
JSON.stringify(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchNoirV2Serialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
noir.a(obj.a)
|
||||
JSON.stringify(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redact(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchNoirV2WildSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
noirWild.a(obj.a)
|
||||
JSON.stringify(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactWildSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactWild(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactIntermediateWildSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactIntermediateWild(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactIntermediateWildMatchWildOutcomeSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactIntermediateWildMatchWildOutcome(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactStaticMatchWildOutcomeSerialize (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactStaticMatchWildOutcome(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchNoirV2CensorFunction (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
noirCensorFunction.a(obj.a)
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchFastRedactCensorFunction (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
redactCensorFunction(obj)
|
||||
}
|
||||
setImmediate(cb)
|
||||
}
|
||||
], 500)
|
||||
|
||||
run(run)
|
14
node_modules/fast-redact/example/default-usage.js
generated
vendored
Normal file
14
node_modules/fast-redact/example/default-usage.js
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
'use strict'
|
||||
const fastRedact = require('..')
|
||||
const fauxRequest = {
|
||||
headers: {
|
||||
host: 'http://example.com',
|
||||
cookie: `oh oh we don't want this exposed in logs in etc.`,
|
||||
referer: `if we're cool maybe we'll even redact this`
|
||||
}
|
||||
}
|
||||
const redact = fastRedact({
|
||||
paths: ['headers.cookie', 'headers.referer']
|
||||
})
|
||||
|
||||
console.log(redact(fauxRequest))
|
11
node_modules/fast-redact/example/intermediate-wildcard-array.js
generated
vendored
Normal file
11
node_modules/fast-redact/example/intermediate-wildcard-array.js
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
'use strict'
|
||||
const fastRedact = require('..')
|
||||
const redact = fastRedact({ paths: ['a[*].c.d'] })
|
||||
const obj = {
|
||||
a: [
|
||||
{ c: { d: 'hide me', e: 'leave me be' } },
|
||||
{ c: { d: 'and me', f: 'I want to live' } },
|
||||
{ c: { d: 'and also I', g: 'I want to run in a stream' } }
|
||||
]
|
||||
}
|
||||
console.log(redact(obj))
|
11
node_modules/fast-redact/example/serialize-false.js
generated
vendored
Normal file
11
node_modules/fast-redact/example/serialize-false.js
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
'use strict'
|
||||
const fastRedact = require('..')
|
||||
const redact = fastRedact({
|
||||
paths: ['a'],
|
||||
serialize: false
|
||||
})
|
||||
const o = { a: 1, b: 2 }
|
||||
console.log(redact(o) === o)
|
||||
console.log(o)
|
||||
console.log(redact.restore(o) === o)
|
||||
console.log(o)
|
4
node_modules/fast-redact/example/serialize-function.js
generated
vendored
Normal file
4
node_modules/fast-redact/example/serialize-function.js
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
'use strict'
|
||||
const fastRedact = require('..')
|
||||
const redact = fastRedact({ paths: ['a'], serialize: (o) => JSON.stringify(o, 0, 2) })
|
||||
console.log(redact({ a: 1, b: 2 }))
|
9
node_modules/fast-redact/example/top-wildcard-object.js
generated
vendored
Normal file
9
node_modules/fast-redact/example/top-wildcard-object.js
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
'use strict'
|
||||
const fastRedact = require('..')
|
||||
const redact = fastRedact({ paths: ['*.c.d'] })
|
||||
const obj = {
|
||||
x: { c: { d: 'hide me', e: 'leave me be' } },
|
||||
y: { c: { d: 'and me', f: 'I want to live' } },
|
||||
z: { c: { d: 'and also I', g: 'I want to run in a stream' } }
|
||||
}
|
||||
console.log(redact(obj))
|
55
node_modules/fast-redact/index.js
generated
vendored
Normal file
55
node_modules/fast-redact/index.js
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use strict'
|
||||
|
||||
const validator = require('./lib/validator')
|
||||
const parse = require('./lib/parse')
|
||||
const redactor = require('./lib/redactor')
|
||||
const restorer = require('./lib/restorer')
|
||||
const { groupRedact, nestedRedact } = require('./lib/modifiers')
|
||||
const state = require('./lib/state')
|
||||
const rx = require('./lib/rx')
|
||||
const validate = validator()
|
||||
const noop = (o) => o
|
||||
noop.restore = noop
|
||||
|
||||
const DEFAULT_CENSOR = '[REDACTED]'
|
||||
fastRedact.rx = rx
|
||||
fastRedact.validator = validator
|
||||
|
||||
module.exports = fastRedact
|
||||
|
||||
function fastRedact (opts = {}) {
|
||||
const paths = Array.from(new Set(opts.paths || []))
|
||||
const serialize = 'serialize' in opts ? (
|
||||
opts.serialize === false ? opts.serialize
|
||||
: (typeof opts.serialize === 'function' ? opts.serialize : JSON.stringify)
|
||||
) : JSON.stringify
|
||||
const remove = opts.remove
|
||||
if (remove === true && serialize !== JSON.stringify) {
|
||||
throw Error('fast-redact – remove option may only be set when serializer is JSON.stringify')
|
||||
}
|
||||
const censor = remove === true
|
||||
? undefined
|
||||
: 'censor' in opts ? opts.censor : DEFAULT_CENSOR
|
||||
|
||||
const isCensorFct = typeof censor === 'function'
|
||||
|
||||
if (paths.length === 0) return serialize || noop
|
||||
|
||||
validate({ paths, serialize, censor })
|
||||
|
||||
const { wildcards, wcLen, secret } = parse({ paths, censor })
|
||||
|
||||
const compileRestore = restorer({ secret, wcLen })
|
||||
const strict = 'strict' in opts ? opts.strict : true
|
||||
|
||||
return redactor({ secret, wcLen, serialize, strict, isCensorFct }, state({
|
||||
secret,
|
||||
censor,
|
||||
compileRestore,
|
||||
serialize,
|
||||
groupRedact,
|
||||
nestedRedact,
|
||||
wildcards,
|
||||
wcLen
|
||||
}))
|
||||
}
|
96
node_modules/fast-redact/lib/modifiers.js
generated
vendored
Normal file
96
node_modules/fast-redact/lib/modifiers.js
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
groupRedact,
|
||||
groupRestore,
|
||||
nestedRedact,
|
||||
nestedRestore
|
||||
}
|
||||
|
||||
function groupRestore ({ keys, values, target }) {
|
||||
if (target == null) return
|
||||
const length = keys.length
|
||||
for (var i = 0; i < length; i++) {
|
||||
const k = keys[i]
|
||||
target[k] = values[i]
|
||||
}
|
||||
}
|
||||
|
||||
function groupRedact (o, path, censor, isCensorFct) {
|
||||
const target = get(o, path)
|
||||
if (target == null) return { keys: null, values: null, target: null, flat: true }
|
||||
const keys = Object.keys(target)
|
||||
const length = keys.length
|
||||
const values = new Array(length)
|
||||
for (var i = 0; i < length; i++) {
|
||||
const k = keys[i]
|
||||
values[i] = target[k]
|
||||
target[k] = isCensorFct ? censor(target[k]) : censor
|
||||
}
|
||||
return { keys, values, target, flat: true }
|
||||
}
|
||||
|
||||
function nestedRestore (arr) {
|
||||
const length = arr.length
|
||||
for (var i = 0; i < length; i++) {
|
||||
const { key, target, value } = arr[i]
|
||||
target[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
function nestedRedact (store, o, path, ns, censor, isCensorFct) {
|
||||
const target = get(o, path)
|
||||
if (target == null) return
|
||||
const keys = Object.keys(target)
|
||||
const length = keys.length
|
||||
for (var i = 0; i < length; i++) {
|
||||
const key = keys[i]
|
||||
const { value, parent, exists } = specialSet(target, key, ns, censor, isCensorFct)
|
||||
|
||||
if (exists === true && parent !== null) {
|
||||
store.push({ key: ns[ns.length - 1], target: parent, value })
|
||||
}
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
function has (obj, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop)
|
||||
}
|
||||
|
||||
function specialSet (o, k, p, v, f) {
|
||||
var i = -1
|
||||
var l = p.length
|
||||
var li = l - 1
|
||||
var n
|
||||
var nv
|
||||
var ov
|
||||
var oov = null
|
||||
var exists = true
|
||||
ov = n = o[k]
|
||||
if (typeof n !== 'object') return { value: null, parent: null, exists }
|
||||
while (n != null && ++i < l) {
|
||||
k = p[i]
|
||||
oov = ov
|
||||
if (!(k in n)) {
|
||||
exists = false
|
||||
break
|
||||
}
|
||||
ov = n[k]
|
||||
nv = f ? v(ov) : v
|
||||
nv = (i !== li) ? ov : nv
|
||||
n[k] = (has(n, k) && nv === ov) || (nv === undefined && v !== undefined) ? n[k] : nv
|
||||
n = n[k]
|
||||
if (typeof n !== 'object') break
|
||||
}
|
||||
return { value: ov, parent: oov, exists }
|
||||
}
|
||||
function get (o, p) {
|
||||
var i = -1
|
||||
var l = p.length
|
||||
var n = o
|
||||
while (n != null && ++i < l) {
|
||||
n = n[p[i]]
|
||||
}
|
||||
return n
|
||||
}
|
45
node_modules/fast-redact/lib/parse.js
generated
vendored
Normal file
45
node_modules/fast-redact/lib/parse.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
'use strict'
|
||||
|
||||
const rx = require('./rx')
|
||||
|
||||
module.exports = parse
|
||||
|
||||
function parse ({ paths }) {
|
||||
const wildcards = []
|
||||
var wcLen = 0
|
||||
const secret = paths.reduce(function (o, strPath, ix) {
|
||||
var path = strPath.match(rx).map((p) => p.replace(/'|"|`/g, ''))
|
||||
const leadingBracket = strPath[0] === '['
|
||||
path = path.map((p) => {
|
||||
if (p[0] === '[') return p.substr(1, p.length - 2)
|
||||
else return p
|
||||
})
|
||||
const star = path.indexOf('*')
|
||||
if (star > -1) {
|
||||
const before = path.slice(0, star)
|
||||
const beforeStr = before.join('.')
|
||||
const after = path.slice(star + 1, path.length)
|
||||
if (after.indexOf('*') > -1) throw Error('fast-redact – Only one wildcard per path is supported')
|
||||
const nested = after.length > 0
|
||||
wcLen++
|
||||
wildcards.push({
|
||||
before,
|
||||
beforeStr,
|
||||
after,
|
||||
nested
|
||||
})
|
||||
} else {
|
||||
o[strPath] = {
|
||||
path: path,
|
||||
val: undefined,
|
||||
precensored: false,
|
||||
circle: '',
|
||||
escPath: JSON.stringify(strPath),
|
||||
leadingBracket: leadingBracket
|
||||
}
|
||||
}
|
||||
return o
|
||||
}, {})
|
||||
|
||||
return { wildcards, wcLen, secret }
|
||||
}
|
94
node_modules/fast-redact/lib/redactor.js
generated
vendored
Normal file
94
node_modules/fast-redact/lib/redactor.js
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
'use strict'
|
||||
|
||||
const rx = require('./rx')
|
||||
|
||||
module.exports = redactor
|
||||
|
||||
function redactor ({ secret, serialize, wcLen, strict, isCensorFct }, state) {
|
||||
/* eslint-disable-next-line */
|
||||
const redact = Function('o', `
|
||||
if (typeof o !== 'object' || o == null) {
|
||||
${strictImpl(strict, serialize)}
|
||||
}
|
||||
const { censor, secret } = this
|
||||
${redactTmpl(secret, isCensorFct)}
|
||||
this.compileRestore()
|
||||
${dynamicRedactTmpl(wcLen > 0, isCensorFct)}
|
||||
${resultTmpl(serialize)}
|
||||
`).bind(state)
|
||||
|
||||
if (serialize === false) {
|
||||
redact.restore = (o) => state.restore(o)
|
||||
}
|
||||
|
||||
return redact
|
||||
}
|
||||
|
||||
function redactTmpl (secret, isCensorFct) {
|
||||
return Object.keys(secret).map((path) => {
|
||||
const { escPath, leadingBracket } = secret[path]
|
||||
const skip = leadingBracket ? 1 : 0
|
||||
const delim = leadingBracket ? '' : '.'
|
||||
const hops = []
|
||||
var match
|
||||
while ((match = rx.exec(path)) !== null) {
|
||||
const [ , ix ] = match
|
||||
const { index, input } = match
|
||||
if (index > skip) hops.push(input.substring(0, index - (ix ? 0 : 1)))
|
||||
}
|
||||
var existence = hops.map((p) => `o${delim}${p}`).join(' && ')
|
||||
if (existence.length === 0) existence += `o${delim}${path} != null`
|
||||
else existence += ` && o${delim}${path} != null`
|
||||
|
||||
const circularDetection = `
|
||||
switch (true) {
|
||||
${hops.reverse().map((p) => `
|
||||
case o${delim}${p} === censor:
|
||||
secret[${escPath}].circle = ${JSON.stringify(p)}
|
||||
break
|
||||
`).join('\n')}
|
||||
}
|
||||
`
|
||||
return `
|
||||
if (${existence}) {
|
||||
const val = o${delim}${path}
|
||||
if (val === censor) {
|
||||
secret[${escPath}].precensored = true
|
||||
} else {
|
||||
secret[${escPath}].val = val
|
||||
o${delim}${path} = ${isCensorFct ? 'censor(val)' : 'censor'}
|
||||
${circularDetection}
|
||||
}
|
||||
}
|
||||
`
|
||||
}).join('\n')
|
||||
}
|
||||
|
||||
function dynamicRedactTmpl (hasWildcards, isCensorFct) {
|
||||
return hasWildcards === true ? `
|
||||
{
|
||||
const { wildcards, wcLen, groupRedact, nestedRedact } = this
|
||||
for (var i = 0; i < wcLen; i++) {
|
||||
const { before, beforeStr, after, nested } = wildcards[i]
|
||||
if (nested === true) {
|
||||
secret[beforeStr] = secret[beforeStr] || []
|
||||
nestedRedact(secret[beforeStr], o, before, after, censor, ${isCensorFct})
|
||||
} else secret[beforeStr] = groupRedact(o, before, censor, ${isCensorFct})
|
||||
}
|
||||
}
|
||||
` : ''
|
||||
}
|
||||
|
||||
function resultTmpl (serialize) {
|
||||
return serialize === false ? `return o` : `
|
||||
var s = this.serialize(o)
|
||||
this.restore(o)
|
||||
return s
|
||||
`
|
||||
}
|
||||
|
||||
function strictImpl (strict, serialize) {
|
||||
return strict === true
|
||||
? `throw Error('fast-redact: primitives cannot be redacted')`
|
||||
: serialize === false ? `return o` : `return this.serialize(o)`
|
||||
}
|
71
node_modules/fast-redact/lib/restorer.js
generated
vendored
Normal file
71
node_modules/fast-redact/lib/restorer.js
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
'use strict'
|
||||
|
||||
const { groupRestore, nestedRestore } = require('./modifiers')
|
||||
|
||||
module.exports = restorer
|
||||
|
||||
function restorer ({ secret, wcLen }) {
|
||||
return function compileRestore () {
|
||||
if (this.restore) return
|
||||
const paths = Object.keys(secret)
|
||||
.filter((path) => secret[path].precensored === false)
|
||||
const resetters = resetTmpl(secret, paths)
|
||||
const hasWildcards = wcLen > 0
|
||||
const state = hasWildcards ? { secret, groupRestore, nestedRestore } : { secret }
|
||||
/* eslint-disable-next-line */
|
||||
this.restore = Function(
|
||||
'o',
|
||||
restoreTmpl(resetters, paths, hasWildcards)
|
||||
).bind(state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutates the original object to be censored by restoring its original values
|
||||
* prior to censoring.
|
||||
*
|
||||
* @param {object} secret Compiled object describing which target fields should
|
||||
* be censored and the field states.
|
||||
* @param {string[]} paths The list of paths to censor as provided at
|
||||
* initialization time.
|
||||
*
|
||||
* @returns {string} String of JavaScript to be used by `Function()`. The
|
||||
* string compiles to the function that does the work in the description.
|
||||
*/
|
||||
function resetTmpl (secret, paths) {
|
||||
return paths.map((path) => {
|
||||
const { circle, escPath, leadingBracket } = secret[path]
|
||||
const delim = leadingBracket ? '' : '.'
|
||||
const reset = circle
|
||||
? `o.${circle} = secret[${escPath}].val`
|
||||
: `o${delim}${path} = secret[${escPath}].val`
|
||||
const clear = `secret[${escPath}].val = undefined`
|
||||
return `
|
||||
if (secret[${escPath}].val !== undefined) {
|
||||
try { ${reset} } catch (e) {}
|
||||
${clear}
|
||||
}
|
||||
`
|
||||
}).join('')
|
||||
}
|
||||
|
||||
function restoreTmpl (resetters, paths, hasWildcards) {
|
||||
const dynamicReset = hasWildcards === true ? `
|
||||
const keys = Object.keys(secret)
|
||||
const len = keys.length
|
||||
for (var i = ${paths.length}; i < len; i++) {
|
||||
const k = keys[i]
|
||||
const o = secret[k]
|
||||
if (o.flat === true) this.groupRestore(o)
|
||||
else this.nestedRestore(o)
|
||||
secret[k] = null
|
||||
}
|
||||
` : ''
|
||||
|
||||
return `
|
||||
const secret = this.secret
|
||||
${resetters}
|
||||
${dynamicReset}
|
||||
return o
|
||||
`
|
||||
}
|
3
node_modules/fast-redact/lib/rx.js
generated
vendored
Normal file
3
node_modules/fast-redact/lib/rx.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = /[^.[\]]+|\[((?:.)*?)\]/g
|
22
node_modules/fast-redact/lib/state.js
generated
vendored
Normal file
22
node_modules/fast-redact/lib/state.js
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = state
|
||||
|
||||
function state (o) {
|
||||
const {
|
||||
secret,
|
||||
censor,
|
||||
isCensorFct,
|
||||
compileRestore,
|
||||
serialize,
|
||||
groupRedact,
|
||||
nestedRedact,
|
||||
wildcards,
|
||||
wcLen
|
||||
} = o
|
||||
const builder = [{ secret, censor, isCensorFct, compileRestore }]
|
||||
builder.push({ secret })
|
||||
if (serialize !== false) builder.push({ serialize })
|
||||
if (wcLen > 0) builder.push({ groupRedact, nestedRedact, wildcards, wcLen })
|
||||
return Object.assign(...builder)
|
||||
}
|
38
node_modules/fast-redact/lib/validator.js
generated
vendored
Normal file
38
node_modules/fast-redact/lib/validator.js
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
'use strict'
|
||||
|
||||
const { createContext, runInContext } = require('vm')
|
||||
|
||||
module.exports = validator
|
||||
|
||||
function validator (opts = {}) {
|
||||
const {
|
||||
ERR_PATHS_MUST_BE_STRINGS = () => 'fast-redact - Paths must be strings',
|
||||
ERR_INVALID_PATH = (s) => `fast-redact – Invalid path (${s})`
|
||||
} = opts
|
||||
|
||||
return function validate ({ paths }) {
|
||||
paths.forEach((s) => {
|
||||
if (typeof s !== 'string') {
|
||||
throw Error(ERR_PATHS_MUST_BE_STRINGS())
|
||||
}
|
||||
try {
|
||||
if (/〇/.test(s)) throw Error()
|
||||
const proxy = new Proxy({}, { get: () => proxy, set: () => { throw Error() } })
|
||||
const expr = (s[0] === '[' ? '' : '.') + s.replace(/^\*/, '〇').replace(/\.\*/g, '.〇').replace(/\[\*\]/g, '[〇]')
|
||||
if (/\n|\r|;/.test(expr)) throw Error()
|
||||
if (/\/\*/.test(expr)) throw Error()
|
||||
runInContext(`
|
||||
(function () {
|
||||
'use strict'
|
||||
o${expr}
|
||||
if ([o${expr}].length !== 1) throw Error()
|
||||
})()
|
||||
`, createContext({ o: proxy, 〇: null }), {
|
||||
codeGeneration: { strings: false, wasm: false }
|
||||
})
|
||||
} catch (e) {
|
||||
throw Error(ERR_INVALID_PATH(s))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
78
node_modules/fast-redact/package.json
generated
vendored
Normal file
78
node_modules/fast-redact/package.json
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"_from": "fast-redact@^2.0.0",
|
||||
"_id": "fast-redact@2.0.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-zxpkULI9W9MNTK2sJ3BpPQrTEXFNESd2X6O1tXMFpK/XM0G5c5Rll2EVYZH2TqI3xRGK/VaJ+eEOt7pnENJpeA==",
|
||||
"_location": "/fast-redact",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "fast-redact@^2.0.0",
|
||||
"name": "fast-redact",
|
||||
"escapedName": "fast-redact",
|
||||
"rawSpec": "^2.0.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^2.0.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/pino"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-2.0.0.tgz",
|
||||
"_shasum": "17bb8f5e1f56ecf4a38c8455985e5eab4c478431",
|
||||
"_spec": "fast-redact@^2.0.0",
|
||||
"_where": "/home/dawidd6/github/dawidd6/action-debian-package/node_modules/pino",
|
||||
"author": {
|
||||
"name": "David Mark Clements",
|
||||
"email": "david.clements@nearform.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/davidmarkclements/fast-redact/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "very fast object redaction",
|
||||
"devDependencies": {
|
||||
"fastbench": "^1.0.1",
|
||||
"pino-noir": "^2.2.1",
|
||||
"snazzy": "^8.0.0",
|
||||
"standard": "^12.0.1",
|
||||
"tap": "^12.5.2"
|
||||
},
|
||||
"directories": {
|
||||
"example": "example",
|
||||
"lib": "lib",
|
||||
"test": "test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"homepage": "https://github.com/davidmarkclements/fast-redact#readme",
|
||||
"keywords": [
|
||||
"redact",
|
||||
"censor",
|
||||
"performance",
|
||||
"performant",
|
||||
"gdpr",
|
||||
"fast",
|
||||
"speed",
|
||||
"serialize",
|
||||
"stringify"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"name": "fast-redact",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/davidmarkclements/fast-redact.git"
|
||||
},
|
||||
"scripts": {
|
||||
"bench": "node benchmark",
|
||||
"ci": "tap --cov --100 test",
|
||||
"cov": "tap --cov test",
|
||||
"cov-ui": "tap --coverage-report=html test",
|
||||
"posttest": "standard index.js 'lib/*.js' 'example/*.js' benchmark/index.js test/index.js | snazzy",
|
||||
"test": "tap test"
|
||||
},
|
||||
"version": "2.0.0"
|
||||
}
|
280
node_modules/fast-redact/readme.md
generated
vendored
Normal file
280
node_modules/fast-redact/readme.md
generated
vendored
Normal file
|
@ -0,0 +1,280 @@
|
|||
# fast-redact
|
||||
|
||||
very fast object redaction
|
||||
|
||||
[](https://travis-ci.org/davidmarkclements/fast-redact)
|
||||
|
||||
## Default Usage
|
||||
|
||||
By default, `fast-redact` serializes an object with `JSON.stringify`, censoring any
|
||||
data at paths specified:
|
||||
|
||||
```js
|
||||
const fastRedact = require('fast-redact')
|
||||
const fauxRequest = {
|
||||
headers: {
|
||||
host: 'http://example.com',
|
||||
cookie: `oh oh we don't want this exposed in logs in etc.`,
|
||||
referer: `if we're cool maybe we'll even redact this`
|
||||
}
|
||||
}
|
||||
const redact = fastRedact({
|
||||
paths: ['headers.cookie', 'headers.referer']
|
||||
})
|
||||
|
||||
console.log(redact(fauxRequest))
|
||||
// {"headers":{"host":"http://example.com","cookie":"[REDACTED]","referer":"[REDACTED]"}}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `require('fast-redact')({paths, censor, serialize}) => Function`
|
||||
|
||||
When called without any options, or with a zero length `paths` array,
|
||||
`fast-redact` will return `JSON.stringify` or the `serialize` option, if set.
|
||||
|
||||
#### `paths` – `Array`
|
||||
|
||||
An array of strings describing the nested location of a key in an object.
|
||||
|
||||
The syntax follows that of the EcmaScript specification, that is any JavaScript
|
||||
path is accepted – both bracket and dot notation is supported. For instance in
|
||||
each of the following cases, the `c` property will be redacted: `a.b.c`,`a['b'].c`,
|
||||
`a["b"].c`, `a[``b``].c`. Since bracket notation is supported, array indices are also
|
||||
supported `a[0].b` would redact the `b` key in the first object of the `a` array.
|
||||
|
||||
Leading brackets are also allowed, for instance `["a"].b.c` will work.
|
||||
|
||||
##### Wildcards
|
||||
|
||||
In addition to static paths, asterisk wildcards are also supported.
|
||||
|
||||
When an asterisk is place in the final position it will redact all keys within the
|
||||
parent object. For instance `a.b.*` will redact all keys in the `b` object. Similarly
|
||||
for arrays `a.b[*]` will redact all elements of an array (in truth it actually doesn't matter
|
||||
whether `b` is in an object or array in either case, both notation styles will work).
|
||||
|
||||
When an asterisk is in an intermediate or first position, the paths following the asterisk will
|
||||
be redacted for every object within the parent.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
const fastRedact = require('fast-redact')
|
||||
const redact = fastRedact({paths: ['*.c.d']})
|
||||
const obj = {
|
||||
x: {c: {d: 'hide me', e: 'leave me be'}},
|
||||
y: {c: {d: 'and me', f: 'I want to live'}},
|
||||
z: {c: {d: 'and also I', g: 'I want to run in a stream'}}
|
||||
}
|
||||
console.log(redact(obj))
|
||||
// {"x":{"c":{"d":"[REDACTED]","e":"leave me be"}},"y":{"c":{"d":"[REDACTED]","f":"I want to live"}},"z":{"c":{"d":"[REDACTED]","g":"I want to run in a stream"}}}
|
||||
```
|
||||
|
||||
Another example with a nested array:
|
||||
|
||||
```js
|
||||
const fastRedact = require('..')
|
||||
const redact = fastRedact({paths: ['a[*].c.d']})
|
||||
const obj = {
|
||||
a: [
|
||||
{c: {d: 'hide me', e: 'leave me be'}},
|
||||
{c: {d: 'and me', f: 'I want to live'}},
|
||||
{c: {d: 'and also I', g: 'I want to run in a stream'}}
|
||||
]
|
||||
}
|
||||
console.log(redact(obj))
|
||||
// {"a":[{"c":{"d":"[REDACTED]","e":"leave me be"}},{"c":{"d":"[REDACTED]","f":"I want to live"}},{"c":{"d":"[REDACTED]","g":"I want to run in a stream"}}]}
|
||||
```
|
||||
|
||||
#### `remove` - `Boolean` - `[false]`
|
||||
|
||||
The `remove` option, when set to `true` will cause keys to be removed from the
|
||||
serialized output.
|
||||
|
||||
Since the implementation exploits the fact that `undefined` keys are ignored
|
||||
by `JSON.stringify` the `remove` option may *only* be used when `JSON.stringify`
|
||||
is the serializer (this is the default) – otherwise `fast-redact` will throw.
|
||||
|
||||
If supplying a custom serializer that has the same behavior (removing keys
|
||||
with `undefined` values), this restriction can be bypassed by explicitly setting
|
||||
the `censor` to `undefined`.
|
||||
|
||||
|
||||
#### `censor` – `<Any type>` – `('[REDACTED]')`
|
||||
|
||||
This is the value which overwrites redacted properties.
|
||||
|
||||
Setting `censor` to `undefined` will cause properties to removed as long as this is
|
||||
the behavior of the `serializer` – which defaults to `JSON.stringify`, which does
|
||||
remove `undefined` properties.
|
||||
|
||||
Setting `censor` to a function will cause `fast-redact` to invoke it with the original
|
||||
value. The output of the `censor` function sets the redacted value.
|
||||
Please note that asynchronous functions are not supported.
|
||||
|
||||
#### `serialize` – `Function | Boolean` – `(JSON.stringify)`
|
||||
|
||||
The `serialize` option may be a function of a boolean. If a function is supplied, this
|
||||
will be used to `serialize` the redacted object. It's important to understand that for
|
||||
performance reasons `fast-redact` *mutates* the original object, then serializes, then
|
||||
restores the original values. So the object passed to the serializer is the exact same
|
||||
object passed to the redacting function.
|
||||
|
||||
The `serialize` option as a function example:
|
||||
|
||||
```js
|
||||
const fastRedact = require('fast-redact')
|
||||
const redact = fastRedact({
|
||||
paths: ['a'],
|
||||
serialize: (o) => JSON.stringify(o, 0, 2)
|
||||
})
|
||||
console.log(redact({a: 1, b: 2}))
|
||||
// {
|
||||
// "a": "[REDACTED]",
|
||||
// "b": 2
|
||||
// }
|
||||
```
|
||||
|
||||
For advanced usage the `serialize` option can be set to `false`. When `serialize` is set to `false`,
|
||||
instead of the serialized object, the output of the redactor function will be the mutated object
|
||||
itself (this is the exact same as the object passed in). In addition a `restore` method is supplied
|
||||
on the redactor function allowing the redacted keys to be restored with the original data.
|
||||
|
||||
```js
|
||||
const fastRedact = require('fast-redact')
|
||||
const redact = fastRedact({
|
||||
paths: ['a'],
|
||||
serialize: false
|
||||
})
|
||||
const o = {a: 1, b: 2}
|
||||
console.log(redact(o) === o) // true
|
||||
console.log(o) // { a: '[REDACTED]', b: 2 }
|
||||
console.log(redact.restore(o) === o) // true
|
||||
console.log(o) // { a: 1, b: 2 }
|
||||
```
|
||||
|
||||
#### `strict` – `Boolean` - `[true]`
|
||||
The `strict` option, when set to `true`, will cause the redactor function to throw if instead
|
||||
of an object it finds a primitive. When `strict` is set to `false`, the redactor function
|
||||
will treat the primitive value as having already been redacted, and return it serialized (with
|
||||
`JSON.stringify` or the user's custom `serialize` function), or as-is if the `serialize` option
|
||||
was set to false.
|
||||
|
||||
## Approach
|
||||
|
||||
In order to achieve lowest cost/highest performance redaction `fast-redact`
|
||||
creates and compiles a function (using the `Function` constructor) on initialization.
|
||||
It's important to distinguish this from the dangers of a runtime eval, no user input
|
||||
is involved in creating the string that compiles into the function. This is as safe
|
||||
as writing code normally and having it compiled by V8 in the usual way.
|
||||
|
||||
Thanks to changes in V8 in recent years, state can be injected into compiled functions
|
||||
using `bind` at very low cost (whereas `bind` used to be expensive, and getting state
|
||||
into a compiled function by any means was difficult without a performance penalty).
|
||||
|
||||
For static paths, this function simply checks that the path exists and then overwrites
|
||||
with the censor. Wildcard paths are processed with normal functions that iterate over
|
||||
the object redacting values as necessary.
|
||||
|
||||
It's important to note, that the original object is mutated – for performance reasons
|
||||
a copy is not made. See [rfdc](https://github.com/davidmarkclements/rfdc) (Really Fast
|
||||
Deep Clone) for the fastest known way to clone – it's not nearly close enough in speed
|
||||
to editing the original object, serializing and then restoring values.
|
||||
|
||||
A `restore` function is also created and compiled to put the original state back on
|
||||
to the object after redaction. This means that in the default usage case, the operation
|
||||
is essentially atomic - the object is mutated, serialized and restored internally which
|
||||
avoids any state management issues.
|
||||
|
||||
## Caveat
|
||||
|
||||
As mentioned in approach, the `paths` array input is dynamically compiled into a function
|
||||
at initialization time. While the `paths` array is vigourously tested for any developer
|
||||
errors, it's strongly recommended against allowing user input to directly supply any
|
||||
paths to redact. It can't be guaranteed that allowing user input for `paths` couldn't
|
||||
feasibly expose an attack vector.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
The fastest known predecessor to `fast-redact` is the non-generic [`pino-noir`](http://npm.im/pino-noir)
|
||||
library (which was also written by myself).
|
||||
|
||||
In the direct calling case, `fast-redact` is ~30x faster than `pino-noir`, however a more realistic
|
||||
comparison is overhead on `JSON.stringify`.
|
||||
|
||||
For a static redaction case (no wildcards) `pino-noir` adds ~25% overhead on top of `JSON.stringify`
|
||||
whereas `fast-redact` adds ~1% overhead.
|
||||
|
||||
In the basic last-position wildcard case,`fast-redact` is ~12% faster than `pino-noir`.
|
||||
|
||||
The `pino-noir` module does not support intermediate wildcards, but `fast-redact` does,
|
||||
the cost of an intermediate wildcard that results in two keys over two nested objects
|
||||
being redacted is about 25% overhead on `JSON.stringify`. The cost of an intermediate
|
||||
wildcard that results in four keys across two objects being redacted is about 55% overhead
|
||||
on `JSON.stringify` and ~50% more expensive that explicitly declaring the keys.
|
||||
|
||||
```sh
|
||||
npm run bench
|
||||
```
|
||||
|
||||
```
|
||||
benchNoirV2*500: 59.108ms
|
||||
benchFastRedact*500: 2.483ms
|
||||
benchFastRedactRestore*500: 10.904ms
|
||||
benchNoirV2Wild*500: 91.399ms
|
||||
benchFastRedactWild*500: 21.200ms
|
||||
benchFastRedactWildRestore*500: 27.304ms
|
||||
benchFastRedactIntermediateWild*500: 92.304ms
|
||||
benchFastRedactIntermediateWildRestore*500: 107.047ms
|
||||
benchJSONStringify*500: 210.573ms
|
||||
benchNoirV2Serialize*500: 281.148ms
|
||||
benchFastRedactSerialize*500: 215.845ms
|
||||
benchNoirV2WildSerialize*500: 281.168ms
|
||||
benchFastRedactWildSerialize*500: 247.140ms
|
||||
benchFastRedactIntermediateWildSerialize*500: 333.722ms
|
||||
benchFastRedactIntermediateWildMatchWildOutcomeSerialize*500: 463.667ms
|
||||
benchFastRedactStaticMatchWildOutcomeSerialize*500: 239.293ms
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
```
|
||||
224 passing (499.544ms)
|
||||
```
|
||||
|
||||
### Coverage
|
||||
|
||||
```
|
||||
npm run cov
|
||||
```
|
||||
|
||||
```
|
||||
-----------------|----------|----------|----------|----------|-------------------|
|
||||
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
|
||||
-----------------|----------|----------|----------|----------|-------------------|
|
||||
All files | 100 | 100 | 100 | 100 | |
|
||||
fast-redact | 100 | 100 | 100 | 100 | |
|
||||
index.js | 100 | 100 | 100 | 100 | |
|
||||
fast-redact/lib | 100 | 100 | 100 | 100 | |
|
||||
modifiers.js | 100 | 100 | 100 | 100 | |
|
||||
parse.js | 100 | 100 | 100 | 100 | |
|
||||
redactor.js | 100 | 100 | 100 | 100 | |
|
||||
restorer.js | 100 | 100 | 100 | 100 | |
|
||||
rx.js | 100 | 100 | 100 | 100 | |
|
||||
state.js | 100 | 100 | 100 | 100 | |
|
||||
validator.js | 100 | 100 | 100 | 100 | |
|
||||
-----------------|----------|----------|----------|----------|-------------------|
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Sponsored by [nearForm](http://www.nearform.com)
|
964
node_modules/fast-redact/test/index.js
generated
vendored
Normal file
964
node_modules/fast-redact/test/index.js
generated
vendored
Normal file
|
@ -0,0 +1,964 @@
|
|||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fastRedact = require('..')
|
||||
|
||||
const censor = '[REDACTED]'
|
||||
const censorFct = value => !value ? value : 'xxx' + value.substr(-2)
|
||||
|
||||
test('returns no-op when passed no paths [serialize: false]', ({ end, doesNotThrow }) => {
|
||||
const redact = fastRedact({ paths: [], serialize: false })
|
||||
doesNotThrow(() => redact({}))
|
||||
doesNotThrow(() => {
|
||||
const o = redact({})
|
||||
redact.restore(o)
|
||||
})
|
||||
end()
|
||||
})
|
||||
|
||||
test('returns serializer when passed no paths [serialize: default]', ({ end, is }) => {
|
||||
is(fastRedact({ paths: [] }), JSON.stringify)
|
||||
is(fastRedact(), JSON.stringify)
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws when passed non-object using defaults', ({ end, throws }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'] })
|
||||
throws(() => redact(1))
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws when passed non-object number using [strict: true]', ({ end, throws }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], strict: true })
|
||||
throws(() => redact(1))
|
||||
end()
|
||||
})
|
||||
|
||||
test('returns JSON.stringified value when passed non-object using [strict: false] and no serialize option', ({ end, is, doesNotThrow }) => {
|
||||
const redactDefaultSerialize = fastRedact({ paths: ['a.b.c'], strict: false })
|
||||
|
||||
// expectedOutputs holds `JSON.stringify`ied versions of each primitive.
|
||||
// We write them out explicitly though to make the test a bit clearer.
|
||||
const primitives = [null, undefined, 'A', 1, false]
|
||||
const expectedOutputs = ['null', undefined, '"A"', '1', 'false']
|
||||
|
||||
primitives.forEach((it, i) => {
|
||||
doesNotThrow(() => redactDefaultSerialize(it))
|
||||
const res = redactDefaultSerialize(it)
|
||||
is(res, expectedOutputs[i])
|
||||
})
|
||||
|
||||
end()
|
||||
})
|
||||
|
||||
test('returns custom serialized value when passed non-object using [strict: false, serialize: fn]', ({ end, is, doesNotThrow }) => {
|
||||
const customSerialize = (v) => `Hello ${v}!`
|
||||
const redactCustomSerialize = fastRedact({
|
||||
paths: ['a.b.c'],
|
||||
strict: false,
|
||||
serialize: customSerialize
|
||||
})
|
||||
|
||||
const primitives = [null, undefined, 'A', 1, false]
|
||||
|
||||
primitives.forEach((it) => {
|
||||
doesNotThrow(() => redactCustomSerialize(it))
|
||||
const res = redactCustomSerialize(it)
|
||||
is(res, customSerialize(it))
|
||||
})
|
||||
|
||||
end()
|
||||
})
|
||||
|
||||
test('returns original value when passed non-object using [strict: false, serialize: false]', ({ end, is, doesNotThrow }) => {
|
||||
const redactSerializeFalse = fastRedact({
|
||||
paths: ['a.b.c'],
|
||||
strict: false,
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const primitives = [null, undefined, 'A', 1, false]
|
||||
|
||||
primitives.forEach((it) => {
|
||||
doesNotThrow(() => redactSerializeFalse(it))
|
||||
const res = redactSerializeFalse(it)
|
||||
is(res, it)
|
||||
})
|
||||
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws if a path is not a string', ({ end, is, throws }) => {
|
||||
throws((e) => {
|
||||
fastRedact({ paths: [1] })
|
||||
}, Error('fast-redact - Paths must be strings'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: [null] })
|
||||
}, Error('fast-redact - Paths must be strings'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: [undefined] })
|
||||
}, Error('fast-redact - Paths must be strings'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: [{}] })
|
||||
}, Error('fast-redact - Paths must be strings'))
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws when passed illegal paths', ({ end, is, throws }) => {
|
||||
const err = (s) => Error(`fast-redact – Invalid path (${s})`)
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['@'] })
|
||||
}, err('@'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['0'] })
|
||||
}, err('0'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['〇'] })
|
||||
}, err('〇'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a.1.c'] })
|
||||
}, err('a.1.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a..c'] })
|
||||
}, err('a..c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['1..c'] })
|
||||
}, err('1..c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a = b'] })
|
||||
}, err('a = b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a(b)'] })
|
||||
}, err('a(b)'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['//a.b.c'] })
|
||||
}, err('//a.b.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['\\a.b.c'] })
|
||||
}, err('\\a.b.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a.#.c'] })
|
||||
}, err('a.#.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['~~a.b.c'] })
|
||||
}, err('~~a.b.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['^a.b.c'] })
|
||||
}, err('^a.b.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a + b'] })
|
||||
}, err('a + b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['return a + b'] })
|
||||
}, err('return a + b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a / b'] })
|
||||
}, err('a / b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a * b'] })
|
||||
}, err('a * b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a - b'] })
|
||||
}, err('a - b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a ** b'] })
|
||||
}, err('a ** b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a % b'] })
|
||||
}, err('a % b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a.b*.c'] })
|
||||
}, err('a.b*.c'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a;global.foo = "bar"'] })
|
||||
}, err('a;global.foo = "bar"'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a;while(1){}'] })
|
||||
}, err('a;while(1){}'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a//'] })
|
||||
}, err('a//'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a/*foo*/'] })
|
||||
}, err('a/*foo*/'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a,o.b'] })
|
||||
}, err('a,o.b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a = o.b'] })
|
||||
}, err('a = o.b'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a\n'] })
|
||||
}, err('a\n'))
|
||||
throws((e) => {
|
||||
fastRedact({ paths: ['a\r'] })
|
||||
}, err('a\r'))
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws if more than one wildcard in a path', ({ end, throws }) => {
|
||||
throws(() => {
|
||||
fastRedact({ paths: ['a.*.x.*'], serialize: false })
|
||||
}, Error('fast-redact – Only one wildcard per path is supported'))
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws if a custom serializer is used and remove is true', ({ end, throws }) => {
|
||||
throws(() => {
|
||||
fastRedact({ paths: ['a'], serialize: (o) => o, remove: true })
|
||||
}, Error('fast-redact – remove option may only be set when serializer is JSON.stringify'))
|
||||
end()
|
||||
})
|
||||
|
||||
test('throws if serialize is false and remove is true', ({ end, throws }) => {
|
||||
throws(() => {
|
||||
fastRedact({ paths: ['a'], serialize: false, remove: true })
|
||||
}, Error('fast-redact – remove option may only be set when serializer is JSON.stringify'))
|
||||
end()
|
||||
})
|
||||
|
||||
test('masks according to supplied censor', ({ end, is }) => {
|
||||
const censor = 'test'
|
||||
const redact = fastRedact({ paths: ['a'], censor, serialize: false })
|
||||
is(redact({ a: 'a' }).a, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('redact.restore function is available when serialize is false', ({ end, is }) => {
|
||||
const censor = 'test'
|
||||
const redact = fastRedact({ paths: ['a'], censor, serialize: false })
|
||||
is(typeof redact.restore, 'function')
|
||||
end()
|
||||
})
|
||||
|
||||
test('redact.restore function places original values back in place', ({ end, is }) => {
|
||||
const censor = 'test'
|
||||
const redact = fastRedact({ paths: ['a'], censor, serialize: false })
|
||||
const o = { a: 'a' }
|
||||
redact(o)
|
||||
is(o.a, censor)
|
||||
redact.restore(o)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('masks according to supplied censor function', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], censor: censorFct, serialize: false })
|
||||
is(redact({ a: '0123456' }).a, 'xxx56')
|
||||
end()
|
||||
})
|
||||
|
||||
test('masks according to supplied censor function with wildcards', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: '*', censor: censorFct, serialize: false })
|
||||
is(redact({ a: '0123456' }).a, 'xxx56')
|
||||
end()
|
||||
})
|
||||
|
||||
test('masks according to supplied censor function with nested wildcards', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['*.b'], censor: censorFct, serialize: false })
|
||||
is(redact({ a: { b: '0123456' } }).a.b, 'xxx56')
|
||||
is(redact({ c: { b: '0123456', d: 'pristine' } }).c.b, 'xxx56')
|
||||
is(redact({ c: { b: '0123456', d: 'pristine' } }).c.d, 'pristine')
|
||||
end()
|
||||
})
|
||||
|
||||
test('redact.restore function places original values back in place with censor function', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], censor: censorFct, serialize: false })
|
||||
const o = { a: 'qwerty' }
|
||||
redact(o)
|
||||
is(o.a, 'xxxty')
|
||||
redact.restore(o)
|
||||
is(o.a, 'qwerty')
|
||||
end()
|
||||
})
|
||||
|
||||
test('serializes with JSON.stringify by default', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'] })
|
||||
const o = { a: 'a' }
|
||||
is(redact(o), `{"a":"${censor}"}`)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('removes during serialization instead of redacting when remove option is true', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], remove: true })
|
||||
const o = { a: 'a', b: 'b' }
|
||||
is(redact(o), `{"b":"b"}`)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('serializes with JSON.stringify if serialize is true', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], serialize: true })
|
||||
const o = { a: 'a' }
|
||||
is(redact(o), `{"a":"${censor}"}`)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('serializes with JSON.stringify if serialize is not a function', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], serialize: {} })
|
||||
const o = { a: 'a' }
|
||||
is(redact(o), `{"a":"${censor}"}`)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('serializes with custom serializer if supplied', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a'], serialize: (o) => JSON.stringify(o, 0, 2) })
|
||||
const o = { a: 'a' }
|
||||
is(redact(o), `{\n "a": "${censor}"\n}`)
|
||||
is(o.a, 'a')
|
||||
end()
|
||||
})
|
||||
|
||||
test('redacts parent keys', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports paths with array indexes', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['insideArray.like[3].this'], serialize: false })
|
||||
same(redact({ insideArray: { like: ['a', 'b', 'c', { this: { foo: 'meow' } }] } }), { insideArray: { like: ['a', 'b', 'c', { this: censor }] } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('censor may be any type, including function', ({ end, same }) => {
|
||||
const redactToString = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: 'censor', serialize: false })
|
||||
const redactToUndefined = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: undefined, serialize: false })
|
||||
const sym = Symbol('sym')
|
||||
const redactToSymbol = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: sym, serialize: false })
|
||||
const redactToNumber = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: 0, serialize: false })
|
||||
const redactToBoolean = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: false, serialize: false })
|
||||
const redactToNull = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: null, serialize: false })
|
||||
const redactToObject = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: { redacted: true }, serialize: false })
|
||||
const redactToArray = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: ['redacted'], serialize: false })
|
||||
const redactToBuffer = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: Buffer.from('redacted'), serialize: false })
|
||||
const redactToError = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: Error('redacted'), serialize: false })
|
||||
const redactToFunction = fastRedact({ paths: ['a.b.c', 'a.b.d.*'], censor: () => 'redacted', serialize: false })
|
||||
same(redactToString({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: 'censor', d: { x: 'censor', y: 'censor' } } } })
|
||||
same(redactToUndefined({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: undefined, d: { x: undefined, y: undefined } } } })
|
||||
same(redactToSymbol({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: sym, d: { x: sym, y: sym } } } })
|
||||
same(redactToNumber({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: 0, d: { x: 0, y: 0 } } } })
|
||||
same(redactToBoolean({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: false, d: { x: false, y: false } } } })
|
||||
same(redactToNull({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: null, d: { x: null, y: null } } } })
|
||||
same(redactToObject({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: { redacted: true }, d: { x: { redacted: true }, y: { redacted: true } } } } })
|
||||
same(redactToArray({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: ['redacted'], d: { x: ['redacted'], y: ['redacted'] } } } })
|
||||
same(redactToBuffer({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: Buffer.from('redacted'), d: { x: Buffer.from('redacted'), y: Buffer.from('redacted') } } } })
|
||||
same(redactToError({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: Error('redacted'), d: { x: Error('redacted'), y: Error('redacted') } } } })
|
||||
same(redactToFunction({ a: { b: { c: 's', d: { x: 's', y: 's' } } } }), { a: { b: { c: 'redacted', d: { x: 'redacted', y: 'redacted' } } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports multiple paths from the same root', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['deep.bar.shoe', 'deep.baz.shoe', 'deep.foo', 'deep.not.there.sooo', 'deep.fum.shoe'], serialize: false })
|
||||
same(redact({ deep: { bar: 'hmm', baz: { shoe: { k: 1 } }, foo: {}, fum: { shoe: 'moo' } } }), { deep: { bar: 'hmm', baz: { shoe: censor }, foo: censor, fum: { shoe: censor } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports strings in bracket notation paths (single quote)', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: [`a['@#!='].c`], serialize: false })
|
||||
const result = redact({ a: { '@#!=': { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a['@#!='].c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports strings in bracket notation paths (double quote)', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: [`a["@#!="].c`], serialize: false })
|
||||
const result = redact({ a: { '@#!=': { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a['@#!='].c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports strings in bracket notation paths (backtick quote)', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a[`@#!=`].c'], serialize: false })
|
||||
const result = redact({ a: { '@#!=': { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a['@#!='].c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('allows * within a bracket notation string', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a["*"].c'], serialize: false })
|
||||
const result = redact({ a: { '*': { c: 's', x: 1 } } })
|
||||
is(result.a['*'].c, censor)
|
||||
is(result.a['*'].x, 1)
|
||||
end()
|
||||
})
|
||||
|
||||
test('redacts parent keys – restore', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
redact.restore(result)
|
||||
is(result.a.b.c, 's')
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles null proto objects', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const result = redact({ __proto__: null, a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles null proto objects – restore', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const result = redact({ __proto__: null, a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
redact.restore(result, 's')
|
||||
is(result.a.b.c, 's')
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles paths that do not match object structure', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['x.y.z'], serialize: false })
|
||||
same(redact({ a: { b: { c: 's' } } }), { a: { b: { c: 's' } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ignores missing paths in object', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.z.d', 'a.b.z'], serialize: false })
|
||||
same(redact({ a: { b: { c: 's' } } }), { a: { b: { c: censor } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ignores missing paths in object – restore', ({ end, doesNotThrow }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.z.d', 'a.b.z'], serialize: false })
|
||||
const o = { a: { b: { c: 's' } } }
|
||||
redact(o)
|
||||
doesNotThrow(() => {
|
||||
redact.restore(o)
|
||||
})
|
||||
|
||||
end()
|
||||
})
|
||||
|
||||
test('gracefully handles primitives that match intermediate keys in paths', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.b.c.d'], serialize: false })
|
||||
same(redact({ a: { b: null } }), { a: { b: null } })
|
||||
same(redact({ a: { b: 's' } }), { a: { b: 's' } })
|
||||
same(redact({ a: { b: 1 } }), { a: { b: 1 } })
|
||||
same(redact({ a: { b: undefined } }), { a: { b: undefined } })
|
||||
same(redact({ a: { b: true } }), { a: { b: true } })
|
||||
const sym = Symbol('sym')
|
||||
same(redact({ a: { b: sym } }), { a: { b: sym } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles circulars', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.baz'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
same(redact(o), { a: 1, bar: { b: 2, baz: censor } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles circulars – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.baz'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
is(o.bar.baz, bar)
|
||||
redact(o)
|
||||
is(o.bar.baz, censor)
|
||||
redact.restore(o)
|
||||
is(o.bar.baz, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles circulars and cross references – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.baz', 'cf.bar'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar, cf: { bar } }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
redact(o)
|
||||
is(o.bar.baz, censor)
|
||||
is(o.cf.bar, censor)
|
||||
redact.restore(o)
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – shallow', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['test.*'], serialize: false })
|
||||
same(redact({ test: { baz: 1, bar: 'private' } }), { test: { baz: censor, bar: censor } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – deep', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['deep.bar.baz.ding.*'], serialize: false })
|
||||
same(redact({ deep: { a: 1, bar: { b: 2, baz: { c: 3, ding: { d: 4, e: 5, f: 'six' } } } } }), { deep: { a: 1, bar: { b: 2, baz: { c: 3, ding: { d: censor, e: censor, f: censor } } } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards - array – shallow', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['array[*]'], serialize: false })
|
||||
same(redact({ array: ['a', 'b', 'c', 'd'] }), { array: [censor, censor, censor, censor] })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – array – deep', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['deepArray.down.here[*]'], serialize: false })
|
||||
same(redact({ deepArray: { down: { here: ['a', 'b', 'c'] } } }), { deepArray: { down: { here: [censor, censor, censor] } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – array – single index', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['insideArray.like[3].this.*'], serialize: false })
|
||||
same(redact({ insideArray: { like: ['a', 'b', 'c', { this: { foo: 'meow' } }] } }), { insideArray: { like: ['a', 'b', 'c', { this: { foo: censor } }] } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards - handles null proto objects', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c'], serialize: false })
|
||||
const result = redact({ __proto__: null, a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards - handles paths that do not match object structure', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['x.y.z'], serialize: false })
|
||||
same(redact({ a: { b: { c: 's' } } }), { a: { b: { c: 's' } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards - gracefully handles primitives that match intermediate keys in paths', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.b.c.d'], serialize: false })
|
||||
same(redact({ a: { b: null } }), { a: { b: null } })
|
||||
same(redact({ a: { b: 's' } }), { a: { b: 's' } })
|
||||
same(redact({ a: { b: 1 } }), { a: { b: 1 } })
|
||||
same(redact({ a: { b: undefined } }), { a: { b: undefined } })
|
||||
same(redact({ a: { b: true } }), { a: { b: true } })
|
||||
const sym = Symbol('sym')
|
||||
same(redact({ a: { b: sym } }), { a: { b: sym } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – handles circulars', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.*'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
same(redact(o), { a: 1, bar: { b: censor, baz: censor } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – handles circulars – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.*'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
is(o.bar.baz, bar)
|
||||
redact(o)
|
||||
is(o.bar.baz, censor)
|
||||
redact.restore(o)
|
||||
is(o.bar.baz, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – handles circulars and cross references – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.*', 'cf.*'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar, cf: { bar } }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
redact(o)
|
||||
is(o.bar.baz, censor)
|
||||
is(o.cf.bar, censor)
|
||||
redact.restore(o)
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('static + wildcards', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.d.*', 'a.b.z.*'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's', z: { x: 's', y: 's' } }, d: { a: 's', b: 's', c: 's' } } })
|
||||
|
||||
is(result.a.b.c, censor)
|
||||
is(result.a.d.a, censor)
|
||||
is(result.a.d.b, censor)
|
||||
is(result.a.d.c, censor)
|
||||
is(result.a.b.z.x, censor)
|
||||
is(result.a.b.z.y, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('static + wildcards reuse', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.c', 'a.d.*'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
|
||||
is(result.a.b.c, censor)
|
||||
is(result.a.d.a, censor)
|
||||
is(result.a.d.b, censor)
|
||||
is(result.a.d.c, censor)
|
||||
|
||||
redact.restore(result)
|
||||
|
||||
const result2 = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result2.a.b.c, censor)
|
||||
is(result2.a.d.a, censor)
|
||||
is(result2.a.d.b, censor)
|
||||
is(result2.a.d.c, censor)
|
||||
|
||||
redact.restore(result2)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard – first position', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['*.c'], serialize: false })
|
||||
const result = redact({ b: { c: 's' }, d: { a: 's', b: 's', c: 's' } })
|
||||
is(result.b.c, censor)
|
||||
is(result.d.a, 's')
|
||||
is(result.d.b, 's')
|
||||
is(result.d.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard – one following key', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
is(result.a.d.a, 's')
|
||||
is(result.a.d.b, 's')
|
||||
is(result.a.d.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('restore parent wildcard – one following key', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
redact.restore(result)
|
||||
is(result.a.b.c, 's')
|
||||
is(result.a.d.a, 's')
|
||||
is(result.a.d.b, 's')
|
||||
is(result.a.d.c, 's')
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard – one following key – reuse', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
is(result.a.d.a, 's')
|
||||
is(result.a.d.b, 's')
|
||||
is(result.a.d.c, censor)
|
||||
const result2 = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result2.a.b.c, censor)
|
||||
is(result2.a.d.a, 's')
|
||||
is(result2.a.d.b, 's')
|
||||
is(result2.a.d.c, censor)
|
||||
redact.restore(result2)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard – two following keys', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.x.c'], serialize: false })
|
||||
const result = redact({ a: { b: { x: { c: 's' } }, d: { x: { a: 's', b: 's', c: 's' } } } })
|
||||
is(result.a.b.x.c, censor)
|
||||
is(result.a.d.x.a, 's')
|
||||
is(result.a.d.x.b, 's')
|
||||
is(result.a.d.x.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard – two following keys – reuse', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.x.c'], serialize: false })
|
||||
const result = redact({ a: { b: { x: { c: 's' } }, d: { x: { a: 's', b: 's', c: 's' } } } })
|
||||
is(result.a.b.x.c, censor)
|
||||
is(result.a.d.x.a, 's')
|
||||
is(result.a.d.x.b, 's')
|
||||
is(result.a.d.x.c, censor)
|
||||
redact.restore(result)
|
||||
const result2 = redact({ a: { b: { x: { c: 's' } }, d: { x: { a: 's', b: 's', c: 's' } } } })
|
||||
is(result2.a.b.x.c, censor)
|
||||
is(result2.a.d.x.a, 's')
|
||||
is(result2.a.d.x.b, 's')
|
||||
is(result2.a.d.x.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('restore parent wildcard – two following keys', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.x.c'], serialize: false })
|
||||
const result = redact({ a: { b: { x: { c: 's' } }, d: { x: { a: 's', b: 's', c: 's' } } } })
|
||||
redact.restore(result)
|
||||
is(result.a.b.x.c, 's')
|
||||
is(result.a.d.x.a, 's')
|
||||
is(result.a.d.x.b, 's')
|
||||
is(result.a.d.x.c, 's')
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcard - array', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b[*].x'], serialize: false })
|
||||
const result = redact({ a: { b: [{ x: 1 }, { a: 2 }], d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b[0].x, censor)
|
||||
is(result.a.b[1].a, 2)
|
||||
is(result.a.d.a, 's')
|
||||
is(result.a.d.b, 's')
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – array – single index', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['insideArray.like[3].*.foo'], serialize: false })
|
||||
same(redact({ insideArray: { like: ['a', 'b', 'c', { this: { foo: 'meow' } }] } }), { insideArray: { like: ['a', 'b', 'c', { this: { foo: censor } }] } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards - handles null proto objects', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
const result = redact({ __proto__: null, a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result.a.b.c, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards - handles paths that do not match object structure', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.y.z'], serialize: false })
|
||||
same(redact({ a: { b: { c: 's' } } }), { a: { b: { c: 's' } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards - gracefully handles primitives that match intermediate keys in paths', ({ end, same }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], serialize: false })
|
||||
same(redact({ a: { b: null } }), { a: { b: null } })
|
||||
same(redact({ a: { b: 's' } }), { a: { b: 's' } })
|
||||
same(redact({ a: { b: 1 } }), { a: { b: 1 } })
|
||||
same(redact({ a: { b: undefined } }), { a: { b: undefined } })
|
||||
same(redact({ a: { b: true } }), { a: { b: true } })
|
||||
const sym = Symbol('sym')
|
||||
same(redact({ a: { b: sym } }), { a: { b: sym } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – handles circulars', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['x.*.baz'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { x: { a: 1, bar } }
|
||||
bar.baz = bar
|
||||
o.x.bar.baz = o.x.bar
|
||||
same(redact(o), { x: { a: 1, bar: { b: 2, baz: censor } } })
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – handles circulars – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['x.*.baz'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { x: { a: 1, bar } }
|
||||
bar.baz = bar
|
||||
o.x.bar.baz = o.x.bar
|
||||
is(o.x.bar.baz, bar)
|
||||
redact(o)
|
||||
is(o.x.a, 1)
|
||||
is(o.x.bar.baz, censor)
|
||||
redact.restore(o)
|
||||
is(o.x.bar.baz, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – handles circulars and cross references – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['x.*.baz', 'x.*.cf.bar'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { x: { a: 1, bar, y: { cf: { bar } } } }
|
||||
bar.baz = bar
|
||||
o.x.bar.baz = o.x.bar
|
||||
is(o.x.bar.baz, bar)
|
||||
is(o.x.y.cf.bar, bar)
|
||||
redact(o)
|
||||
is(o.x.bar.baz, censor)
|
||||
is(o.x.y.cf.bar, censor)
|
||||
redact.restore(o)
|
||||
is(o.x.bar.baz, bar)
|
||||
is(o.x.y.cf.bar, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – handles missing paths', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['z.*.baz'] })
|
||||
const o = { a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } }
|
||||
is(redact(o), JSON.stringify(o))
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – handles missing paths', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['z.*'] })
|
||||
const o = { a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } }
|
||||
is(redact(o), JSON.stringify(o))
|
||||
end()
|
||||
})
|
||||
|
||||
test('parent wildcards – removes during serialization instead of redacting when remove option is true', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.*.c'], remove: true })
|
||||
const o = { a: { b: { c: 'c' }, x: { c: 1 } } }
|
||||
is(redact(o), `{"a":{"b":{},"x":{}}}`)
|
||||
end()
|
||||
})
|
||||
|
||||
test('ultimate wildcards – removes during serialization instead of redacting when remove option is true', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['a.b.*'], remove: true })
|
||||
const o = { a: { b: { c: 'c' }, x: { c: 1 } } }
|
||||
is(redact(o), `{"a":{"b":{},"x":{"c":1}}}`)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports leading bracket notation', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['["a"].b.c'] })
|
||||
const o = { a: { b: { c: 'd' } } }
|
||||
is(redact(o), `{"a":{"b":{"c":"${censor}"}}}`)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports leading bracket notation containing non-legal keyword characters', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['["a-x"].b.c'] })
|
||||
const o = { 'a-x': { b: { c: 'd' } } }
|
||||
is(redact(o), `{"a-x":{"b":{"c":"${censor}"}}}`)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports single leading bracket', ({ end, is }) => {
|
||||
const censor = 'test'
|
||||
const redact = fastRedact({ paths: ['["a"]'], censor, serialize: false })
|
||||
is(redact({ a: 'a' }).a, censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('supports single leading bracket containing non-legal keyword characters', ({ end, is }) => {
|
||||
const censor = 'test'
|
||||
const redact = fastRedact({ paths: ['["a-x"]'], censor, serialize: false })
|
||||
is(redact({ 'a-x': 'a' })['a-x'], censor)
|
||||
end()
|
||||
})
|
||||
|
||||
test('(leading brackets) ultimate wildcards – handles circulars and cross references – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['bar.baz.*', 'cf.*'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { a: 1, bar, cf: { bar } }
|
||||
bar.baz = bar
|
||||
o.bar.baz = o.bar
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
redact(o)
|
||||
is(o.bar.baz, censor)
|
||||
is(o.cf.bar, censor)
|
||||
redact.restore(o)
|
||||
is(o.bar.baz, bar)
|
||||
is(o.cf.bar, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('(leading brackets) parent wildcards – handles circulars and cross references – restore', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['["x"].*.baz', '["x"].*.cf.bar'], serialize: false })
|
||||
const bar = { b: 2 }
|
||||
const o = { x: { a: 1, bar, y: { cf: { bar } } } }
|
||||
bar.baz = bar
|
||||
o.x.bar.baz = o.x.bar
|
||||
is(o.x.bar.baz, bar)
|
||||
is(o.x.y.cf.bar, bar)
|
||||
redact(o)
|
||||
is(o.x.bar.baz, censor)
|
||||
is(o.x.y.cf.bar, censor)
|
||||
redact.restore(o)
|
||||
is(o.x.bar.baz, bar)
|
||||
is(o.x.y.cf.bar, bar)
|
||||
end()
|
||||
})
|
||||
|
||||
test('(leading brackets) ultimate wildcards – handles missing paths', ({ end, is, same }) => {
|
||||
const redact = fastRedact({ paths: ['["z"].*'] })
|
||||
const o = { a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } }
|
||||
is(redact(o), JSON.stringify(o))
|
||||
end()
|
||||
})
|
||||
|
||||
test('(leading brackets) static + wildcards reuse', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['["a"].b.c', '["a"].d.*'], serialize: false })
|
||||
const result = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
|
||||
is(result.a.b.c, censor)
|
||||
is(result.a.d.a, censor)
|
||||
is(result.a.d.b, censor)
|
||||
is(result.a.d.c, censor)
|
||||
|
||||
redact.restore(result)
|
||||
|
||||
const result2 = redact({ a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } })
|
||||
is(result2.a.b.c, censor)
|
||||
is(result2.a.d.a, censor)
|
||||
is(result2.a.d.b, censor)
|
||||
is(result2.a.d.c, censor)
|
||||
|
||||
redact.restore(result2)
|
||||
end()
|
||||
})
|
||||
|
||||
test('correctly restores original object when a path does not match object', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['foo.bar'], strict: false })
|
||||
const o = {}
|
||||
is(redact({ foo: o }), '{"foo":{}}')
|
||||
is(o.hasOwnProperty('bar'), false)
|
||||
end()
|
||||
})
|
||||
|
||||
test('correctly restores original object when a matching path has value of `undefined`', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['foo.bar'], strict: false })
|
||||
const o = { bar: undefined }
|
||||
is(redact({ foo: o }), '{"foo":{}}')
|
||||
is(o.hasOwnProperty('bar'), true)
|
||||
is(o.bar, undefined)
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles multiple paths with leading brackets', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['["x-y"]', '["y-x"]'] })
|
||||
const o = { 'x-y': 'test', 'y-x': 'test2' }
|
||||
is(redact(o), '{"x-y":"[REDACTED]","y-x":"[REDACTED]"}')
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles objects with and then without target paths', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['test'] })
|
||||
const o1 = { test: 'check' }
|
||||
const o2 = {}
|
||||
is(redact(o1), '{"test":"[REDACTED]"}')
|
||||
is(redact(o2), '{}')
|
||||
// run each check twice to ensure no mutations
|
||||
is(redact(o1), '{"test":"[REDACTED]"}')
|
||||
is(redact(o2), '{}')
|
||||
is('test' in o1, true)
|
||||
is('test' in o2, false)
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles leading wildcards and null values', ({ end, is }) => {
|
||||
const redact = fastRedact({ paths: ['*.test'] })
|
||||
const o = { prop: null }
|
||||
is(redact(o), '{"prop":null}')
|
||||
is(o.prop, null)
|
||||
end()
|
||||
})
|
||||
|
||||
test('handles keys with dots', ({ end, is }) => {
|
||||
const redactSingleQ = fastRedact({ paths: [`a['b.c']`], serialize: false })
|
||||
const redactDoubleQ = fastRedact({ paths: [`a["b.c"]`], serialize: false })
|
||||
const redactBacktickQ = fastRedact({ paths: ['a[`b.c`]'], serialize: false })
|
||||
const redactNum = fastRedact({ paths: [`a[-1.2]`], serialize: false })
|
||||
const redactLeading = fastRedact({ paths: [`["b.c"]`], serialize: false })
|
||||
is(redactSingleQ({ a: { 'b.c': 'x', '-1.2': 'x' } }).a['b.c'], censor)
|
||||
is(redactDoubleQ({ a: { 'b.c': 'x', '-1.2': 'x' } }).a['b.c'], censor)
|
||||
is(redactBacktickQ({ a: { 'b.c': 'x', '-1.2': 'x' } }).a['b.c'], censor)
|
||||
is(redactNum({ a: { 'b.c': 'x', '-1.2': 'x' } }).a['-1.2'], censor)
|
||||
is(redactLeading({ 'b.c': 'x', '-1.2': 'x' })['b.c'], censor)
|
||||
end()
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue