From 121eba2cdcd5f32fbb2fbe17b091e61c6ea5ea60 Mon Sep 17 00:00:00 2001 From: Anthony Lauro Date: Mon, 3 May 2021 08:53:47 -0600 Subject: [PATCH 01/11] PR for issue 27 - adding multiple wildcard support --- .gitignore | 3 +- example/mulit-wildcard-array.js | 11 ++ example/multi-wildcard-array-depth.js | 11 ++ example/multi-wildcard-array-end.js | 11 ++ lib/modifiers.js | 73 ++++++++++-- lib/parse.js | 1 - test/index.js | 156 +++++++++++++++++++++++--- 7 files changed, 237 insertions(+), 29 deletions(-) create mode 100644 example/mulit-wildcard-array.js create mode 100644 example/multi-wildcard-array-depth.js create mode 100644 example/multi-wildcard-array-end.js diff --git a/.gitignore b/.gitignore index 7cddf33..1a49a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules .nyc_output package-lock.json -coverage \ No newline at end of file +coverage +.idea diff --git a/example/mulit-wildcard-array.js b/example/mulit-wildcard-array.js new file mode 100644 index 0000000..a0152a5 --- /dev/null +++ b/example/mulit-wildcard-array.js @@ -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' }, { d: 'hide me too', e: 'leave me be' }, { d: 'hide me 3', 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)) diff --git a/example/multi-wildcard-array-depth.js b/example/multi-wildcard-array-depth.js new file mode 100644 index 0000000..8a22b5d --- /dev/null +++ b/example/multi-wildcard-array-depth.js @@ -0,0 +1,11 @@ +'use strict' +const fastRedact = require('..') +const redact = fastRedact({ paths: ['a[*].c.d[*].i'] }) +const obj = { + a: [ + { c: { d: [ { i: 'redact me', j: 'not me' } ], e: 'leave me be' } }, + { c: { d: [ { i: 'redact me too', j: 'not me' }, { i: 'redact me too', j: 'not me' } ], f: 'I want to live' } }, + { c: { d: [ { i: 'redact me 3', j: 'not me' } ], g: 'I want to run in a stream' } } + ] +} +console.log(redact(obj)) diff --git a/example/multi-wildcard-array-end.js b/example/multi-wildcard-array-end.js new file mode 100644 index 0000000..eaae35a --- /dev/null +++ b/example/multi-wildcard-array-end.js @@ -0,0 +1,11 @@ +'use strict' +const fastRedact = require('..') +const redact = fastRedact({ paths: ['a[*].c.d[*]'] }) +const obj = { + a: [ + { c: { d: ['hide me', '2'], 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)) diff --git a/lib/modifiers.js b/lib/modifiers.js index 42c16f7..13a727a 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -45,7 +45,19 @@ function nestedRestore (arr) { const length = arr.length for (var i = 0; i < length; i++) { const { key, target, value } = arr[i] - target[key] = value + if (has(target, key)) { + target[key] = value + } + /* istanbul ignore else */ + if (typeof target === 'object') { + const targetKeys = Object.keys(target) + targetKeys.forEach((tKey) => { + const subTarget = target[tKey] + if (has(subTarget, key)) { + subTarget[key] = value + } + }) + } } } @@ -70,6 +82,14 @@ function has (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop) } +function isIn (obj, prop) { + try { + return prop in obj + } catch (e) { + return false + } +} + function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesPath) { const afterPathLen = afterPath.length const lastPathIndex = afterPathLen - 1 @@ -80,23 +100,56 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP var ov var oov = null var exists = true + var wc = null ov = n = o[k] if (typeof n !== 'object') return { value: null, parent: null, exists } while (n != null && ++i < afterPathLen) { k = afterPath[i] oov = ov - if (!(k in n)) { + if (k !== '*' && !wc && !isIn(n, k)) { exists = false break } - ov = n[k] - nv = (i !== lastPathIndex) - ? ov - : (isCensorFct - ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) - : censor) - n[k] = (has(n, k) && nv === ov) || (nv === undefined && censor !== undefined) ? n[k] : nv - n = n[k] + if (k === '*') { + wc = k + if (i !== lastPathIndex) { + continue + } + } + if (wc) { + const wcKeys = Object.keys(n) + wcKeys.forEach((wck) => { + const wcov = n[wck] + const kIsWc = k === '*' + if (kIsWc || isIn(wcov, k)) { + if (kIsWc) { + ov = wcov + } else { + ov = wcov[k] + } + nv = (i !== lastPathIndex) + ? ov + : (isCensorFct + ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) + : censor) + if (kIsWc) { + n[wck] = nv + } else { + wcov[k] = (has(wcov, k) && nv === ov) || (nv === undefined && censor !== undefined) ? wcov[k] : nv + } + } + }) + wc = null + } else { + ov = n[k] + nv = (i !== lastPathIndex) + ? ov + : (isCensorFct + ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) + : censor) + n[k] = (has(n, k) && nv === ov) || (nv === undefined && censor !== undefined) ? n[k] : nv + n = n[k] + } if (typeof n !== 'object') break } return { value: ov, parent: oov, exists } diff --git a/lib/parse.js b/lib/parse.js index ccbd980..abdb9e4 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -19,7 +19,6 @@ function parse ({ paths }) { 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({ diff --git a/test/index.js b/test/index.js index baf2481..32334fa 100644 --- a/test/index.js +++ b/test/index.js @@ -207,13 +207,6 @@ test('throws when passed illegal paths', ({ end, throws }) => { 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 }) @@ -620,7 +613,7 @@ test('ultimate wildcards – handles circulars', ({ end, is, same }) => { end() }) -test('ultimate wildcards – handles circulars – restore', ({ end, is, same }) => { +test('ultimate wildcards – handles circulars – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['bar.baz.*'], serialize: false }) const bar = { b: 2 } const o = { a: 1, bar } @@ -634,7 +627,21 @@ test('ultimate wildcards – handles circulars – restore', ({ end, is, same } end() }) -test('ultimate wildcards – handles circulars and cross references – restore', ({ end, is, same }) => { +test('ultimate multi wildcards – handles circulars – restore', ({ end, is, same }) => { + const redact = fastRedact({ paths: ['bar.*.baz.*.b'], 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.b, censor) + redact.restore(o) + same(o.bar.baz, bar) + end() +}) + +test('ultimate wildcards – handles circulars and cross references – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['bar.baz.*', 'cf.*'], serialize: false }) const bar = { b: 2 } const o = { a: 1, bar, cf: { bar } } @@ -742,6 +749,21 @@ test('parent wildcard – two following keys', ({ end, is }) => { end() }) +test('multi object wildcard', ({ end, is }) => { + const redact = fastRedact({ paths: ['a.*.x.*.c'], serialize: false }) + const result = redact({ a: { b: { x: { z: { c: 's' } } }, d: { x: { u: { a: 's', b: 's', c: 's' } } } } }) + is(result.a.b.x.z.c, censor) + is(result.a.d.x.u.a, 's') + is(result.a.d.x.u.b, 's') + is(result.a.d.x.u.c, censor) + redact.restore(result) + is(result.a.b.x.z.c, 's') + is(result.a.d.x.u.a, 's') + is(result.a.d.x.u.b, 's') + is(result.a.d.x.u.c, 's') + 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' } } } }) @@ -779,6 +801,106 @@ test('parent wildcard - array', ({ end, is }) => { end() }) +test('multiple wildcards', ({ end, is }) => { + const redact = fastRedact({ paths: ['a[*].c[*].d'], serialize: false }) + const obj = { + a: [ + { c: [{ d: '1', e: '2' }, { d: '1', e: '3' }, { d: '1', e: '4' }] }, + { c: [{ d: '1', f: '5' }] }, + { c: [{ d: '2', g: '6' }] } + ] + } + const result = redact(obj) + is(result.a[0].c[0].d, censor) + is(result.a[0].c[0].e, '2') + is(result.a[0].c[1].d, censor) + is(result.a[0].c[1].e, '3') + is(result.a[0].c[2].d, censor) + is(result.a[0].c[2].e, '4') + is(result.a[1].c[0].d, censor) + is(result.a[1].c[0].f, '5') + is(result.a[2].c[0].d, censor) + is(result.a[2].c[0].g, '6') + redact.restore(result) + is(result.a[0].c[0].d, '1') + is(result.a[0].c[0].e, '2') + is(result.a[0].c[1].d, '1') + is(result.a[0].c[1].e, '3') + is(result.a[0].c[2].d, '1') + is(result.a[0].c[2].e, '4') + is(result.a[1].c[0].d, '1') + is(result.a[1].c[0].f, '5') + is(result.a[2].c[0].d, '2') + is(result.a[2].c[0].g, '6') + end() +}) + +test('multiple wildcards - censor function', ({ end, is }) => { + const redact = fastRedact({ paths: ['a[*].c[*].d'], censor: censorFct, serialize: false }) + const obj = { + a: [ + { c: [{ d: '1', e: '2' }, { d: '1', e: '3' }, { d: '1', e: '4' }] }, + { c: [{ d: '1', f: '5' }] }, + { c: [{ d: '2', g: '6' }] } + ] + } + const result = redact(obj) + is(result.a[0].c[0].d, 'xxx1') + is(result.a[0].c[0].e, '2') + is(result.a[0].c[1].d, 'xxx1') + is(result.a[0].c[1].e, '3') + is(result.a[0].c[2].d, 'xxx1') + is(result.a[0].c[2].e, '4') + is(result.a[1].c[0].d, 'xxx1') + is(result.a[1].c[0].f, '5') + is(result.a[2].c[0].d, 'xxx2') + is(result.a[2].c[0].g, '6') + end() +}) + +test('multiple wildcards end', ({ end, is, same }) => { + const redact = fastRedact({ paths: ['a[*].c.d[*]'], serialize: false }) + const obj = { + a: [ + { c: { d: [ '1', '2' ], e: '3' } }, + { c: { d: [ '1' ], f: '4' } }, + { c: { d: [ '1' ], g: '5' } } + ] + } + const result = redact(obj) + same(result.a[0].c.d, [censor, censor]) + is(result.a[0].c.e, '3') + same(result.a[1].c.d, [censor]) + is(result.a[1].c.f, '4') + same(result.a[2].c.d, [censor]) + is(result.a[2].c.g, '5') + end() +}) + +test('multiple wildcards depth after n wildcard', ({ end, is }) => { + const redact = fastRedact({ paths: ['a[*].c.d[*].i'], serialize: false }) + const obj = { + a: [ + { c: { d: [ { i: '1', j: '2' } ], e: '3' } }, + { c: { d: [ { i: '1', j: '2' }, { i: '1', j: '3' } ], f: '4' } }, + { c: { d: [ { i: '1', j: '2' } ], g: '5' } } + ] + } + const result = redact(obj) + is(result.a[0].c.d[0].i, censor) + is(result.a[0].c.d[0].j, '2') + is(result.a[0].c.e, '3') + is(result.a[1].c.d[0].i, censor) + is(result.a[1].c.d[0].j, '2') + is(result.a[1].c.d[1].i, censor) + is(result.a[1].c.d[1].j, '3') + is(result.a[1].c.f, '4') + is(result.a[2].c.d[0].i, censor) + is(result.a[2].c.d[0].j, '2') + is(result.a[2].c.g, '5') + 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 } }] } }) @@ -810,7 +932,7 @@ test('parent wildcards - gracefully handles primitives that match intermediate k end() }) -test('parent wildcards – handles circulars', ({ end, is, same }) => { +test('parent wildcards – handles circulars', ({ end, same }) => { const redact = fastRedact({ paths: ['x.*.baz'], serialize: false }) const bar = { b: 2 } const o = { x: { a: 1, bar } } @@ -820,7 +942,7 @@ test('parent wildcards – handles circulars', ({ end, is, same }) => { end() }) -test('parent wildcards – handles circulars – restore', ({ end, is, same }) => { +test('parent wildcards – handles circulars – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['x.*.baz'], serialize: false }) const bar = { b: 2 } const o = { x: { a: 1, bar } } @@ -835,7 +957,7 @@ test('parent wildcards – handles circulars – restore', ({ end, is, same }) end() }) -test('parent wildcards – handles circulars and cross references – restore', ({ end, is, same }) => { +test('parent wildcards – handles circulars and cross references – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['x.*.baz', 'x.*.cf.bar'], serialize: false }) const bar = { b: 2 } const o = { x: { a: 1, bar, y: { cf: { bar } } } } @@ -852,14 +974,14 @@ test('parent wildcards – handles circulars and cross references – restore', end() }) -test('parent wildcards – handles missing paths', ({ end, is, same }) => { +test('parent wildcards – handles missing paths', ({ end, is }) => { 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 }) => { +test('ultimate wildcards – handles missing paths', ({ end, is }) => { const redact = fastRedact({ paths: ['z.*'] }) const o = { a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } } is(redact(o), JSON.stringify(o)) @@ -908,7 +1030,7 @@ test('supports single leading bracket containing non-legal keyword characters', end() }) -test('(leading brackets) ultimate wildcards – handles circulars and cross references – restore', ({ end, is, same }) => { +test('(leading brackets) ultimate wildcards – handles circulars and cross references – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['bar.baz.*', 'cf.*'], serialize: false }) const bar = { b: 2 } const o = { a: 1, bar, cf: { bar } } @@ -925,7 +1047,7 @@ test('(leading brackets) ultimate wildcards – handles circulars and cross refe end() }) -test('(leading brackets) parent wildcards – handles circulars and cross references – restore', ({ end, is, same }) => { +test('(leading brackets) parent wildcards – handles circulars and cross references – restore', ({ end, is }) => { const redact = fastRedact({ paths: ['["x"].*.baz', '["x"].*.cf.bar'], serialize: false }) const bar = { b: 2 } const o = { x: { a: 1, bar, y: { cf: { bar } } } } @@ -942,7 +1064,7 @@ test('(leading brackets) parent wildcards – handles circulars and cross refere end() }) -test('(leading brackets) ultimate wildcards – handles missing paths', ({ end, is, same }) => { +test('(leading brackets) ultimate wildcards – handles missing paths', ({ end, is }) => { const redact = fastRedact({ paths: ['["z"].*'] }) const o = { a: { b: { c: 's' }, d: { a: 's', b: 's', c: 's' } } } is(redact(o), JSON.stringify(o)) From 2ba4f49bba213d0cbccfc5b1512b4bd39a66f753 Mon Sep 17 00:00:00 2001 From: Anthony Lauro Date: Mon, 3 May 2021 10:17:49 -0600 Subject: [PATCH 02/11] PR comments re loops --- lib/modifiers.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/modifiers.js b/lib/modifiers.js index 13a727a..e08b4ac 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -51,12 +51,13 @@ function nestedRestore (arr) { /* istanbul ignore else */ if (typeof target === 'object') { const targetKeys = Object.keys(target) - targetKeys.forEach((tKey) => { + for (var j = 0; j < targetKeys.length; j++) { + const tKey = targetKeys[j] const subTarget = target[tKey] if (has(subTarget, key)) { subTarget[key] = value } - }) + } } } } @@ -118,7 +119,8 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP } if (wc) { const wcKeys = Object.keys(n) - wcKeys.forEach((wck) => { + for (var j = 0; j < wcKeys.length; j++) { + const wck = wcKeys[j] const wcov = n[wck] const kIsWc = k === '*' if (kIsWc || isIn(wcov, k)) { @@ -138,7 +140,7 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP wcov[k] = (has(wcov, k) && nv === ov) || (nv === undefined && censor !== undefined) ? wcov[k] : nv } } - }) + } wc = null } else { ov = n[k] From f8130accb75200e1f130ad219995bf0d2dffef55 Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Fri, 14 Jan 2022 07:57:35 +0100 Subject: [PATCH 03/11] finish multi-wc test coverage --- lib/modifiers.js | 2 +- test/index.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/modifiers.js b/lib/modifiers.js index e08b4ac..f00b9df 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -137,7 +137,7 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP if (kIsWc) { n[wck] = nv } else { - wcov[k] = (has(wcov, k) && nv === ov) || (nv === undefined && censor !== undefined) ? wcov[k] : nv + wcov[k] = (nv === undefined && censor !== undefined) || (has(wcov, k) && nv === ov) ? wcov[k] : nv } } } diff --git a/test/index.js b/test/index.js index 32334fa..b791f63 100644 --- a/test/index.js +++ b/test/index.js @@ -1183,3 +1183,29 @@ test('handles keys with dots', ({ end, is }) => { is(redactLeading({ 'b.c': 'x', '-1.2': 'x' })['b.c'], censor) end() }) + +test('handles multi wildcards within arrays', ({ end, is }) => { + const redact = fastRedact({ + paths: ['a[*].x.d[*].i.*'] + }) + const o = { + a: [ { x: { d: [ { j: { i: 'R' } }, { i: 'R', j: 'NR' } ] } } ] + } + is(redact(o), '{"a":[{"x":{"d":["[REDACTED]","[REDACTED]"]}}]}') + end() +}) + +test('handles multi wildcards within arrays with a censorFct', ({ end, is }) => { + const redact = fastRedact({ + paths: ['a[*].x.d[*].i.*.i'], + censor: censorWithPath + }) + const o = { + a: [ + { x: { d: [ { i: 'R', j: 'NR' } ] } } + ] + } + is(redact(o), '{"a":[{"x":{"d":[{"i":"a.0.x.d.*.i.*.i xxxR","j":"NR"}]}}]}') + end() +}) + From 2da193217b1db2efd41838e349091f916187363c Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Fri, 14 Jan 2022 07:58:03 +0100 Subject: [PATCH 04/11] add test and check for undefined / null values with multi-wc --- lib/modifiers.js | 4 +++- test/index.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/modifiers.js b/lib/modifiers.js index f00b9df..bd68738 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -80,7 +80,9 @@ function nestedRedact (store, o, path, ns, censor, isCensorFct, censorFctTakesPa } function has (obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop) + return obj !== undefined && obj !== null + ? Object.prototype.hasOwnProperty.call(obj, prop) + : false } function isIn (obj, prop) { diff --git a/test/index.js b/test/index.js index b791f63..0ad3b1f 100644 --- a/test/index.js +++ b/test/index.js @@ -1209,3 +1209,15 @@ test('handles multi wildcards within arrays with a censorFct', ({ end, is }) => end() }) +test('handles multi wildcards within arrays with undefined values', ({ end, is }) => { + const redact = fastRedact({ + paths: ['a[*].x.d[*].i.*.i'] + }) + const o = { + a: [ + { x: { d: [ { i: undefined, j: 'NR' } ] } } + ] + } + is(redact(o), '{"a":[{"x":{"d":[{"i":"[REDACTED]","j":"NR"}]}}]}') + end() +}) From 0684a469e05db20c240b0b3118728932070c843a Mon Sep 17 00:00:00 2001 From: Luke Hedger Date: Sat, 28 Aug 2021 10:29:51 +0100 Subject: [PATCH 05/11] Fix precensored restore (#38) --- lib/restorer.js | 1 - test/index.js | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/restorer.js b/lib/restorer.js index d75ffed..a8a06b9 100644 --- a/lib/restorer.js +++ b/lib/restorer.js @@ -8,7 +8,6 @@ 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 } diff --git a/test/index.js b/test/index.js index 0ad3b1f..c86ff85 100644 --- a/test/index.js +++ b/test/index.js @@ -282,6 +282,22 @@ test('redact.restore function places original values back in place', ({ end, is end() }) +test('redact.restore function places original values back in place when called twice and the first call is precensored', ({ end, is }) => { + const censor = 'test' + const redact = fastRedact({ paths: ['a'], censor, serialize: false }) + const o1 = { a: censor } + const o2 = { a: 'a' } + redact(o1) + is(o1.a, censor) + redact.restore(o1) + is(o1.a, censor) + redact(o2) + is(o2.a, censor) + redact.restore(o2) + is(o2.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') From 47dc8093ae752291f068dec684afe1f630550212 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sat, 28 Aug 2021 11:31:21 +0200 Subject: [PATCH 06/11] Bumped v3.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a71752..786b6ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fast-redact", - "version": "3.0.1", + "version": "3.0.2", "description": "very fast object redaction", "main": "index.js", "scripts": { From a2031b980d3a2fcd0fc5f4e0d2bf78d2e263cf44 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 18 Jan 2022 18:31:47 +0100 Subject: [PATCH 07/11] Move to github actions (#44) --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ .travis.yml | 11 ----------- 2 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5240588 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: ci + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [6.x, 8.x, 10.x, 12.x, 14.x, 16.x] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install + run: | + npm install + + - name: Run tests + run: | + npm run test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bfbc621..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: node_js -sudo: false -node_js: - - 6 - - 8 - - 10 - - 12 - - 13 - - 14 -script: - - npm run ci From 69022d2d735df776389090a57cf5eaa6cb46d568 Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Wed, 19 Jan 2022 13:08:19 +0100 Subject: [PATCH 08/11] fix example filename typo --- example/{mulit-wildcard-array.js => multi-wildcard-array.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename example/{mulit-wildcard-array.js => multi-wildcard-array.js} (100%) diff --git a/example/mulit-wildcard-array.js b/example/multi-wildcard-array.js similarity index 100% rename from example/mulit-wildcard-array.js rename to example/multi-wildcard-array.js From cb0490a03847dbcabade421041469a7662a1563b Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Wed, 19 Jan 2022 13:30:23 +0100 Subject: [PATCH 09/11] Apply suggested new hasOwn if available for prop in obj check Co-authored-by: David Mark Clements --- lib/modifiers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modifiers.js b/lib/modifiers.js index bd68738..1417dc4 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -81,7 +81,7 @@ function nestedRedact (store, o, path, ns, censor, isCensorFct, censorFctTakesPa function has (obj, prop) { return obj !== undefined && obj !== null - ? Object.prototype.hasOwnProperty.call(obj, prop) + ? ('hasOwn' in Object ? Object.hasOwn(obj, prop) : Object.prototype.hasOwnProperty.call(obj, prop)) : false } From 5fb8cfaf3306022e17067d85e7d750c4a62d3e0e Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Wed, 19 Jan 2022 13:35:46 +0100 Subject: [PATCH 10/11] inline isIn check as suggested --- lib/modifiers.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/modifiers.js b/lib/modifiers.js index 1417dc4..c94eb85 100644 --- a/lib/modifiers.js +++ b/lib/modifiers.js @@ -85,14 +85,6 @@ function has (obj, prop) { : false } -function isIn (obj, prop) { - try { - return prop in obj - } catch (e) { - return false - } -} - function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesPath) { const afterPathLen = afterPath.length const lastPathIndex = afterPathLen - 1 @@ -109,7 +101,7 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP while (n != null && ++i < afterPathLen) { k = afterPath[i] oov = ov - if (k !== '*' && !wc && !isIn(n, k)) { + if (k !== '*' && !wc && !(typeof n === 'object' && k in n)) { exists = false break } @@ -125,7 +117,7 @@ function specialSet (o, k, path, afterPath, censor, isCensorFct, censorFctTakesP const wck = wcKeys[j] const wcov = n[wck] const kIsWc = k === '*' - if (kIsWc || isIn(wcov, k)) { + if (kIsWc || (typeof wcov === 'object' && k in wcov)) { if (kIsWc) { ov = wcov } else { From c906c98c3e42abd78c62107adb291595bcfa66b3 Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Wed, 19 Jan 2022 15:31:49 +0100 Subject: [PATCH 11/11] add basic multi-wc benchmarks --- benchmark/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/benchmark/index.js b/benchmark/index.js index b599492..2409927 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -26,6 +26,8 @@ const redactIntermediateWildCensorFunction = fastRedact({ paths: ['as.*.c'], cen const redactCensorFunctionWithPath = fastRedact({ paths: ['at.d.b'], censor: censorFn, serialize: false }) const redactWildCensorFunctionWithPath = fastRedact({ paths: ['au.d.*'], censor: censorFnWithPath, serialize: false }) const redactIntermediateWildCensorFunctionWithPath = fastRedact({ paths: ['av.*.c'], censorFnWithPath, serialize: false }) +const redactMultiWild = fastRedact({ paths: ['aw.*.*'] }) +const redactMultiWildCensorFunction = fastRedact({ paths: ['ax.*.*'], censor: censorFn, serialize: false }) const getObj = (outerKey) => ({ [outerKey]: { @@ -201,6 +203,20 @@ var run = bench([ redactIntermediateWildCensorFunctionWithPath(obj) } setImmediate(cb) + }, + function benchFastRedactMultiWild (cb) { + const obj = getObj('aw') + for (var i = 0; i < max; i++) { + redactMultiWild(obj) + } + setImmediate(cb) + }, + function benchFastRedactMultiWildCensorFunction (cb) { + const obj = getObj('ax') + for (var i = 0; i < max; i++) { + redactMultiWildCensorFunction(obj) + } + setImmediate(cb) } ], 500)