@hackylabs/deep-redact 2.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -25
- package/dist/cjs/index.js +23 -2
- package/dist/cjs/utils/redactorUtils.js +50 -6
- package/dist/esm/index.mjs +23 -2
- package/dist/esm/utils/redactorUtils.mjs +48 -3
- package/dist/types/types.d.ts +18 -1
- package/dist/types/utils/redactorUtils.d.ts +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/@hackylabs%2Fdeep-redact)
|
|
4
4
|
[](https://github.com/hackylabs/deep-redact/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
Faster than Fast Redact <sup>1</sup> as well as being safer and more configurable than many other redaction
|
|
6
|
+
Faster than Fast Redact <sup>1</sup> as well as being safer and more configurable than many other redaction solutions,
|
|
7
7
|
Deep Redact is a zero-dependency tool that redacts sensitive information from strings and objects. It is designed to be
|
|
8
8
|
used in a production environment where sensitive information needs to be redacted from logs, error messages, files,
|
|
9
|
-
and other outputs.
|
|
9
|
+
and other outputs. Supporting both strings and objects or a mix of both, Deep Redact can be used to redact sensitive
|
|
10
|
+
information from more data structures than any other redaction library. Even partially redacting sensitive information
|
|
11
|
+
from strings is supported, by way of custom regex patterns and replacers.
|
|
10
12
|
|
|
11
13
|
Circular references and other unsupported values are handled gracefully, and the library is designed to be as fast as
|
|
12
|
-
possible while still being
|
|
14
|
+
possible while still being easy to use and configure.
|
|
13
15
|
|
|
14
16
|
Supporting both CommonJS and ESM, with named and default exports, Deep Redact is designed to be versatile and easy to
|
|
15
17
|
use in any modern JavaScript or TypeScript project in Node or the browser.
|
|
@@ -31,9 +33,8 @@ library outside of your global logging/error-reporting libraries.</h4>
|
|
|
31
33
|
// ./src/example.ts
|
|
32
34
|
import {DeepRedact} from '@hackylabs/deep-redact'; // If you're using CommonJS, import with require('@hackylabs/deep-redact') instead. Both CommonJS and ESM support named and default imports.
|
|
33
35
|
|
|
34
|
-
const
|
|
36
|
+
const objRedaction = new DeepRedact({
|
|
35
37
|
blacklistedKeys: ['sensitive', 'password', /name/i],
|
|
36
|
-
serialise: false,
|
|
37
38
|
})
|
|
38
39
|
|
|
39
40
|
const obj = {
|
|
@@ -46,7 +47,8 @@ const obj = {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
// Recursively redact sensitive information from an object
|
|
51
|
+
objRedaction.redact(obj)
|
|
50
52
|
// {
|
|
51
53
|
// keepThis: 'This is fine',
|
|
52
54
|
// sensitive: '[REDACTED]',
|
|
@@ -56,6 +58,19 @@ redaction.redact(obj)
|
|
|
56
58
|
// firstName: '[REDACTED]'
|
|
57
59
|
// }
|
|
58
60
|
// }
|
|
61
|
+
|
|
62
|
+
const strRedaction = new DeepRedact({
|
|
63
|
+
partialStringTests: [
|
|
64
|
+
{
|
|
65
|
+
pattern: /<(email|password)>([^<]+)<\/\1>/gi,
|
|
66
|
+
replacer: (value: string, pattern: RegExp) => value.replace(pattern, '<$1>[REDACTED]</$1>'),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Partially redact sensitive information from a string
|
|
72
|
+
strRedaction.redact('<email>someone@somewhere.com</email><keepThis>This is fine</keepThis><password>secret</password>')
|
|
73
|
+
// '<email>[REDACTED]</email><keepThis>This is fine</keepThis><password>[REDACTED]</password>'
|
|
59
74
|
```
|
|
60
75
|
|
|
61
76
|
## Configuration
|
|
@@ -65,7 +80,8 @@ redaction.redact(obj)
|
|
|
65
80
|
| key | description | type | options | default | required |
|
|
66
81
|
| --- | --- | --- | --- | --- | --- |
|
|
67
82
|
| blacklistedKeys | Deeply compare names of these keys against the keys in your object. | array | Array<string│RegExp│BlacklistKeyConfig> | [] | N |
|
|
68
|
-
| stringTests | Array of regular expressions to perform against string values, whether that value is a flat string or nested within an object. | array | RegExp
|
|
83
|
+
| stringTests | Array of regular expressions to perform against string values, whether that value is a flat string or nested within an object. Will redact whole string values. If you want to redact only part of the string, use `partialStringTests` instead. If a replacer function is provided in the config for the associated test, it will be used to redact the value. | array | Array<RegExp│StringTestConfig> | [] | N |
|
|
84
|
+
| partialStringTests | Array of regular expressions to perform against string values, whether that value is a flat string or nested within an object. Will redact only the matched part of the string using the replacer function provided in the config for the associated test. | array | StringTestConfig[] | [] | N |
|
|
69
85
|
| fuzzyKeyMatch | Loosely compare key names by checking if the key name of your unredacted object is included anywhere within the name of your blacklisted key. For example, is "pass" (your key) included in "password" (from config). | boolean | | false | N |
|
|
70
86
|
| caseSensitiveKeyMatch | Loosely compare key names by normalising the strings. This involves removing non-word characters and transforms the string to lowercase. This means you never have to worry having to list duplicate keys in different formats such as snake_case, camelCase, PascalCase or any other case. | boolean | | true | N |
|
|
71
87
|
| remove | Determines whether or not to remove the key from the object when it is redacted. | boolean | | false | N |
|
|
@@ -86,6 +102,13 @@ redaction.redact(obj)
|
|
|
86
102
|
| remove | boolean | Main options `remove` | N |
|
|
87
103
|
| retainStructure | boolean | Main options `retainStructure` | N |
|
|
88
104
|
|
|
105
|
+
### StringTestConfig
|
|
106
|
+
|
|
107
|
+
| key | description | type | required |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| pattern | A regular expression to perform against a string value, whether that value is a flat string or nested within an object. | RegExp | Y |
|
|
110
|
+
| replacer | A function that will be called with the value of the string that matched the pattern and the pattern itself. This function should return the new (redacted) value to replace the original value. | function | Y |
|
|
111
|
+
|
|
89
112
|
### Benchmark
|
|
90
113
|
Comparisons are made against JSON.stringify, Regex.replace, Fast Redact &
|
|
91
114
|
(one of my other creations, [@hackylabs/obglob](https://npmjs.com/package/@hackylabs/obglob)) as well as different
|
|
@@ -111,21 +134,23 @@ Redact and Obglob are slower and rely on dependencies.
|
|
|
111
134
|
|
|
112
135
|
| scenario | ops / sec | op duration (ms) | margin of error | sample count |
|
|
113
136
|
| --- | --- | --- | --- | --- |
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
| DeepRedact,
|
|
117
|
-
| DeepRedact,
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
| DeepRedact,
|
|
121
|
-
| DeepRedact,
|
|
122
|
-
| DeepRedact,
|
|
123
|
-
| DeepRedact,
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
| ObGlob, large object |
|
|
127
|
-
| DeepRedact,
|
|
128
|
-
|
|
|
129
|
-
| ObGlob, 1000 large objects |
|
|
130
|
-
|
|
|
131
|
-
|
|
|
137
|
+
| DeepRedact, partial redaction | 178183.03 | 0.0056122067 | 0.00003 | 89092 |
|
|
138
|
+
| JSON.stringify, large object | 161672.63 | 0.0061853388 | 0.00004 | 80837 |
|
|
139
|
+
| DeepRedact, remove item, single object | 25085.95 | 0.039862959 | 0.00033 | 12543 |
|
|
140
|
+
| DeepRedact, default config, large object | 23164.57 | 0.0431693713 | 0.00023 | 11583 |
|
|
141
|
+
| Regex replace, large object | 22828.32 | 0.043805244 | 0.00031 | 11415 |
|
|
142
|
+
| DeepRedact, custom replacer function, single object | 22520.52 | 0.0444039553 | 0.00039 | 11261 |
|
|
143
|
+
| DeepRedact, replace string by length, single object | 22470.94 | 0.0445019191 | 0.00025 | 11236 |
|
|
144
|
+
| DeepRedact, retain structure, single object | 18315.67 | 0.0545980591 | 0.00026 | 9158 |
|
|
145
|
+
| DeepRedact, fuzzy matching, single object | 18077.45 | 0.0553175285 | 0.00035 | 9039 |
|
|
146
|
+
| DeepRedact, config per key, single object | 16055.96 | 0.0622821745 | 0.00031 | 8028 |
|
|
147
|
+
| DeepRedact, default config, 1000 large objects | 8077.4 | 0.1238022 | 0.00187 | 4039 |
|
|
148
|
+
| fast redact, large object | 5918.61 | 0.1689584628 | 0.00151 | 2960 |
|
|
149
|
+
| ObGlob, large object | 5193.29 | 0.1925563273 | 0.00972 | 2597 |
|
|
150
|
+
| DeepRedact, case insensitive matching, single object | 3477.6 | 0.2875549482 | 0.00324 | 1739 |
|
|
151
|
+
| DeepRedact, fuzzy and case insensitive matching, single object | 3437.73 | 0.2908896131 | 0.00239 | 1719 |
|
|
152
|
+
| ObGlob, 1000 large objects | 237.69 | 4.2072005126 | 0.05005 | 119 |
|
|
153
|
+
| JSON.stringify, 1000 large objects | 230.33 | 4.3416907672 | 0.04373 | 116 |
|
|
154
|
+
| DeepRedact, partial redaction large string | 127.18 | 7.8628400156 | 0.4176 | 64 |
|
|
155
|
+
| fast redact, 1000 large objects | 119.33 | 8.3803393 | 0.31943 | 60 |
|
|
156
|
+
| Regex replace, 1000 large objects | 97.12 | 10.2963537755 | 0.29341 | 49 |
|
package/dist/cjs/index.js
CHANGED
|
@@ -53,7 +53,7 @@ class DeepRedact {
|
|
|
53
53
|
return {
|
|
54
54
|
__unsupported: {
|
|
55
55
|
type: 'bigint',
|
|
56
|
-
value: value.toString(),
|
|
56
|
+
value: value.toString(10),
|
|
57
57
|
radix: 10,
|
|
58
58
|
},
|
|
59
59
|
};
|
|
@@ -77,6 +77,24 @@ class DeepRedact {
|
|
|
77
77
|
},
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
if (value instanceof Set) {
|
|
81
|
+
return {
|
|
82
|
+
__unsupported: {
|
|
83
|
+
type: 'set',
|
|
84
|
+
values: Array.from(value),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (value instanceof Map) {
|
|
89
|
+
return {
|
|
90
|
+
__unsupported: {
|
|
91
|
+
type: 'map',
|
|
92
|
+
entries: Object.fromEntries(value.entries()),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (value instanceof URL)
|
|
97
|
+
return value.toString();
|
|
80
98
|
if (value instanceof Date)
|
|
81
99
|
return value.toISOString();
|
|
82
100
|
return value;
|
|
@@ -130,7 +148,10 @@ class DeepRedact {
|
|
|
130
148
|
*/
|
|
131
149
|
this.maybeSerialise = (value) => {
|
|
132
150
|
this.circularReference = null;
|
|
133
|
-
|
|
151
|
+
const result = this.redactorUtils.partialStringRedact(value);
|
|
152
|
+
if (!this.config.serialise)
|
|
153
|
+
return result;
|
|
154
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
134
155
|
};
|
|
135
156
|
/**
|
|
136
157
|
* Redact the provided value. The value will be stripped of any circular references and other unsupported data types, before being redacted according to the configuration and finally serialised if required.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const defaultConfig = {
|
|
4
4
|
stringTests: [],
|
|
5
5
|
blacklistedKeys: [],
|
|
6
|
+
partialStringTests: [],
|
|
6
7
|
blacklistedKeysTransformed: [],
|
|
7
8
|
fuzzyKeyMatch: false,
|
|
8
9
|
caseSensitiveKeyMatch: true,
|
|
@@ -14,7 +15,7 @@ const defaultConfig = {
|
|
|
14
15
|
};
|
|
15
16
|
class RedactorUtils {
|
|
16
17
|
constructor(customConfig) {
|
|
17
|
-
var _a, _b, _c;
|
|
18
|
+
var _a, _b, _c, _d;
|
|
18
19
|
/**
|
|
19
20
|
* The configuration for the redaction.
|
|
20
21
|
* @private
|
|
@@ -80,11 +81,32 @@ class RedactorUtils {
|
|
|
80
81
|
* @param shouldRedact
|
|
81
82
|
*/
|
|
82
83
|
this.redactString = (value, replacement, remove, shouldRedact) => {
|
|
83
|
-
if (!value)
|
|
84
|
+
if (!value || typeof value !== 'string')
|
|
84
85
|
return value;
|
|
85
86
|
const { stringTests } = this.config;
|
|
86
|
-
if (!shouldRedact
|
|
87
|
+
if (!shouldRedact) {
|
|
88
|
+
const result = stringTests === null || stringTests === void 0 ? void 0 : stringTests.map((test) => {
|
|
89
|
+
if (test instanceof RegExp) {
|
|
90
|
+
if (!test.test(value))
|
|
91
|
+
return value;
|
|
92
|
+
if (remove)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof replacement === 'function')
|
|
95
|
+
return replacement(value);
|
|
96
|
+
if (this.config.replaceStringByLength)
|
|
97
|
+
return replacement.repeat(value.length);
|
|
98
|
+
return replacement;
|
|
99
|
+
}
|
|
100
|
+
if (remove && test.pattern.test(value))
|
|
101
|
+
return undefined;
|
|
102
|
+
return test.replacer(value, test.pattern);
|
|
103
|
+
}).filter(Boolean)[0];
|
|
104
|
+
if (result)
|
|
105
|
+
return result;
|
|
106
|
+
if (remove)
|
|
107
|
+
return undefined;
|
|
87
108
|
return value;
|
|
109
|
+
}
|
|
88
110
|
if (remove)
|
|
89
111
|
return undefined;
|
|
90
112
|
if (typeof replacement === 'function')
|
|
@@ -139,6 +161,28 @@ class RedactorUtils {
|
|
|
139
161
|
return [prop, this.recurse(val, key !== null && key !== void 0 ? key : prop, shouldRedact)];
|
|
140
162
|
}).filter(([prop]) => prop !== undefined));
|
|
141
163
|
};
|
|
164
|
+
this.partialStringRedact = (value) => {
|
|
165
|
+
const { partialStringTests } = this.config;
|
|
166
|
+
if (partialStringTests.length === 0)
|
|
167
|
+
return value;
|
|
168
|
+
let result;
|
|
169
|
+
if (typeof value === 'string') {
|
|
170
|
+
result = value;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
try {
|
|
174
|
+
result = JSON.stringify(value);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// It should never reach this point, but if it does, it will throw an error that must not contain sensitive data.
|
|
178
|
+
throw new Error('Failed to stringify value for partialStringRedact. Did you replace the rewriteUnsupported method with something that returns non-serialisable data?');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
partialStringTests.forEach((test) => {
|
|
182
|
+
result = test.replacer(result, test.pattern);
|
|
183
|
+
});
|
|
184
|
+
return typeof value === 'string' ? result : JSON.parse(result);
|
|
185
|
+
};
|
|
142
186
|
/**
|
|
143
187
|
* Redact a value. If the value is an object or array, the redaction will be performed recursively, otherwise the value will be redacted if it is a supported type using the `replace` method.
|
|
144
188
|
* @private
|
|
@@ -164,7 +208,7 @@ class RedactorUtils {
|
|
|
164
208
|
return this.redactArray(value);
|
|
165
209
|
return this.redactObject(value, key, parentShouldRedact);
|
|
166
210
|
};
|
|
167
|
-
this.config = Object.assign(Object.assign(Object.assign({}, defaultConfig), customConfig), {
|
|
211
|
+
this.config = Object.assign(Object.assign(Object.assign({}, defaultConfig), customConfig), { partialStringTests: (_a = customConfig.partialStringTests) !== null && _a !== void 0 ? _a : [], blacklistedKeys: (_b = customConfig.blacklistedKeys) !== null && _b !== void 0 ? _b : [], blacklistedKeysTransformed: (_d = (_c = customConfig.blacklistedKeys) === null || _c === void 0 ? void 0 : _c.map((key) => {
|
|
168
212
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
169
213
|
const isObject = !(typeof key === 'string' || key instanceof RegExp);
|
|
170
214
|
const setKey = isObject ? key.key : key;
|
|
@@ -187,7 +231,7 @@ class RedactorUtils {
|
|
|
187
231
|
};
|
|
188
232
|
}
|
|
189
233
|
return fallback;
|
|
190
|
-
})) !== null &&
|
|
234
|
+
})) !== null && _d !== void 0 ? _d : [] });
|
|
191
235
|
}
|
|
192
236
|
}
|
|
193
237
|
/**
|
|
@@ -196,7 +240,7 @@ class RedactorUtils {
|
|
|
196
240
|
* @param str The string to normalise.
|
|
197
241
|
* @returns {string} The normalised string.
|
|
198
242
|
*/
|
|
199
|
-
RedactorUtils.normaliseString = (str) => str.toLowerCase().
|
|
243
|
+
RedactorUtils.normaliseString = (str) => str.toLowerCase().replaceAll(/\W/g, '');
|
|
200
244
|
/**
|
|
201
245
|
* Determine if a key matches a given blacklistedKeyConfig. This will check the key against the blacklisted keys,
|
|
202
246
|
* using the configuration option for the given key falling back to the default configuration.
|
package/dist/esm/index.mjs
CHANGED
|
@@ -48,7 +48,7 @@ class DeepRedact {
|
|
|
48
48
|
return {
|
|
49
49
|
__unsupported: {
|
|
50
50
|
type: 'bigint',
|
|
51
|
-
value: value.toString(),
|
|
51
|
+
value: value.toString(10),
|
|
52
52
|
radix: 10,
|
|
53
53
|
},
|
|
54
54
|
};
|
|
@@ -72,6 +72,24 @@ class DeepRedact {
|
|
|
72
72
|
},
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
+
if (value instanceof Set) {
|
|
76
|
+
return {
|
|
77
|
+
__unsupported: {
|
|
78
|
+
type: 'set',
|
|
79
|
+
values: Array.from(value),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (value instanceof Map) {
|
|
84
|
+
return {
|
|
85
|
+
__unsupported: {
|
|
86
|
+
type: 'map',
|
|
87
|
+
entries: Object.fromEntries(value.entries()),
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (value instanceof URL)
|
|
92
|
+
return value.toString();
|
|
75
93
|
if (value instanceof Date)
|
|
76
94
|
return value.toISOString();
|
|
77
95
|
return value;
|
|
@@ -123,7 +141,10 @@ class DeepRedact {
|
|
|
123
141
|
*/
|
|
124
142
|
maybeSerialise = (value) => {
|
|
125
143
|
this.circularReference = null;
|
|
126
|
-
|
|
144
|
+
const result = this.redactorUtils.partialStringRedact(value);
|
|
145
|
+
if (!this.config.serialise)
|
|
146
|
+
return result;
|
|
147
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
127
148
|
};
|
|
128
149
|
/**
|
|
129
150
|
* Redact the provided value. The value will be stripped of any circular references and other unsupported data types, before being redacted according to the configuration and finally serialised if required.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const defaultConfig = {
|
|
2
2
|
stringTests: [],
|
|
3
3
|
blacklistedKeys: [],
|
|
4
|
+
partialStringTests: [],
|
|
4
5
|
blacklistedKeysTransformed: [],
|
|
5
6
|
fuzzyKeyMatch: false,
|
|
6
7
|
caseSensitiveKeyMatch: true,
|
|
@@ -20,6 +21,7 @@ class RedactorUtils {
|
|
|
20
21
|
this.config = {
|
|
21
22
|
...defaultConfig,
|
|
22
23
|
...customConfig,
|
|
24
|
+
partialStringTests: customConfig.partialStringTests ?? [],
|
|
23
25
|
blacklistedKeys: customConfig.blacklistedKeys ?? [],
|
|
24
26
|
blacklistedKeysTransformed: customConfig.blacklistedKeys?.map((key) => {
|
|
25
27
|
const isObject = !(typeof key === 'string' || key instanceof RegExp);
|
|
@@ -52,7 +54,7 @@ class RedactorUtils {
|
|
|
52
54
|
* @param str The string to normalise.
|
|
53
55
|
* @returns {string} The normalised string.
|
|
54
56
|
*/
|
|
55
|
-
static normaliseString = (str) => str.toLowerCase().
|
|
57
|
+
static normaliseString = (str) => str.toLowerCase().replaceAll(/\W/g, '');
|
|
56
58
|
/**
|
|
57
59
|
* Determine if a key matches a given blacklistedKeyConfig. This will check the key against the blacklisted keys,
|
|
58
60
|
* using the configuration option for the given key falling back to the default configuration.
|
|
@@ -131,11 +133,32 @@ class RedactorUtils {
|
|
|
131
133
|
* @param shouldRedact
|
|
132
134
|
*/
|
|
133
135
|
redactString = (value, replacement, remove, shouldRedact) => {
|
|
134
|
-
if (!value)
|
|
136
|
+
if (!value || typeof value !== 'string')
|
|
135
137
|
return value;
|
|
136
138
|
const { stringTests } = this.config;
|
|
137
|
-
if (!shouldRedact
|
|
139
|
+
if (!shouldRedact) {
|
|
140
|
+
const result = stringTests?.map((test) => {
|
|
141
|
+
if (test instanceof RegExp) {
|
|
142
|
+
if (!test.test(value))
|
|
143
|
+
return value;
|
|
144
|
+
if (remove)
|
|
145
|
+
return undefined;
|
|
146
|
+
if (typeof replacement === 'function')
|
|
147
|
+
return replacement(value);
|
|
148
|
+
if (this.config.replaceStringByLength)
|
|
149
|
+
return replacement.repeat(value.length);
|
|
150
|
+
return replacement;
|
|
151
|
+
}
|
|
152
|
+
if (remove && test.pattern.test(value))
|
|
153
|
+
return undefined;
|
|
154
|
+
return test.replacer(value, test.pattern);
|
|
155
|
+
}).filter(Boolean)[0];
|
|
156
|
+
if (result)
|
|
157
|
+
return result;
|
|
158
|
+
if (remove)
|
|
159
|
+
return undefined;
|
|
138
160
|
return value;
|
|
161
|
+
}
|
|
139
162
|
if (remove)
|
|
140
163
|
return undefined;
|
|
141
164
|
if (typeof replacement === 'function')
|
|
@@ -190,6 +213,28 @@ class RedactorUtils {
|
|
|
190
213
|
return [prop, this.recurse(val, key ?? prop, shouldRedact)];
|
|
191
214
|
}).filter(([prop]) => prop !== undefined));
|
|
192
215
|
};
|
|
216
|
+
partialStringRedact = (value) => {
|
|
217
|
+
const { partialStringTests } = this.config;
|
|
218
|
+
if (partialStringTests.length === 0)
|
|
219
|
+
return value;
|
|
220
|
+
let result;
|
|
221
|
+
if (typeof value === 'string') {
|
|
222
|
+
result = value;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
try {
|
|
226
|
+
result = JSON.stringify(value);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
// It should never reach this point, but if it does, it will throw an error that must not contain sensitive data.
|
|
230
|
+
throw new Error('Failed to stringify value for partialStringRedact. Did you replace the rewriteUnsupported method with something that returns non-serialisable data?');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
partialStringTests.forEach((test) => {
|
|
234
|
+
result = test.replacer(result, test.pattern);
|
|
235
|
+
});
|
|
236
|
+
return typeof value === 'string' ? result : JSON.parse(result);
|
|
237
|
+
};
|
|
193
238
|
/**
|
|
194
239
|
* Redact a value. If the value is an object or array, the redaction will be performed recursively, otherwise the value will be redacted if it is a supported type using the `replace` method.
|
|
195
240
|
* @private
|
package/dist/types/types.d.ts
CHANGED
|
@@ -42,6 +42,10 @@ export interface BlacklistKeyConfig {
|
|
|
42
42
|
*/
|
|
43
43
|
key: string | RegExp;
|
|
44
44
|
}
|
|
45
|
+
export interface ComplexStringTest {
|
|
46
|
+
pattern: RegExp;
|
|
47
|
+
replacer: (value: string, pattern: RegExp) => string;
|
|
48
|
+
}
|
|
45
49
|
export interface BaseDeepRedactConfig {
|
|
46
50
|
/**
|
|
47
51
|
* Keys that should be redacted. Can be a string, or an object with additional configuration options.
|
|
@@ -58,7 +62,8 @@ export interface BaseDeepRedactConfig {
|
|
|
58
62
|
* /^[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}$/, // redact any string that looks like an IP address.
|
|
59
63
|
* ]
|
|
60
64
|
*/
|
|
61
|
-
stringTests?: RegExp
|
|
65
|
+
stringTests?: Array<RegExp | ComplexStringTest>;
|
|
66
|
+
partialStringTests?: Array<ComplexStringTest>;
|
|
62
67
|
/**
|
|
63
68
|
* Perform a fuzzy match on the key. This will match any key that contains the string, rather than a case-sensitive match.
|
|
64
69
|
* @default false
|
|
@@ -119,8 +124,20 @@ export interface BaseDeepRedactConfig {
|
|
|
119
124
|
serialize?: boolean;
|
|
120
125
|
}
|
|
121
126
|
export type DeepRedactConfig = Partial<Omit<BaseDeepRedactConfig, 'blacklistedKeysTransformed' | 'blacklistedKeys' | 'stringTests'>> & ({
|
|
127
|
+
partialStringTests: BaseDeepRedactConfig['partialStringTests'];
|
|
122
128
|
blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
|
|
123
129
|
stringTests: BaseDeepRedactConfig['stringTests'];
|
|
130
|
+
} | {
|
|
131
|
+
partialStringTests: BaseDeepRedactConfig['partialStringTests'];
|
|
132
|
+
blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
|
|
133
|
+
} | {
|
|
134
|
+
blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
|
|
135
|
+
stringTests: BaseDeepRedactConfig['stringTests'];
|
|
136
|
+
} | {
|
|
137
|
+
partialStringTests: BaseDeepRedactConfig['partialStringTests'];
|
|
138
|
+
stringTests: BaseDeepRedactConfig['stringTests'];
|
|
139
|
+
} | {
|
|
140
|
+
partialStringTests: BaseDeepRedactConfig['partialStringTests'];
|
|
124
141
|
} | {
|
|
125
142
|
blacklistedKeys: BaseDeepRedactConfig['blacklistedKeys'];
|
|
126
143
|
} | {
|
|
@@ -77,6 +77,7 @@ declare class RedactorUtils {
|
|
|
77
77
|
* @param {boolean} parentShouldRedact Whether the item should be redacted based on the key within the parent object.
|
|
78
78
|
*/
|
|
79
79
|
private redactObject;
|
|
80
|
+
partialStringRedact: (value: unknown) => unknown;
|
|
80
81
|
/**
|
|
81
82
|
* Redact a value. If the value is an object or array, the redaction will be performed recursively, otherwise the value will be redacted if it is a supported type using the `replace` method.
|
|
82
83
|
* @private
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hackylabs/deep-redact",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A fast, safe and configurable zero-dependency library for redacting strings or deeply redacting arrays and objects.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
27
|
"keywords": [
|
|
28
|
+
"json",
|
|
29
|
+
"xml",
|
|
30
|
+
"document",
|
|
31
|
+
"fast",
|
|
28
32
|
"redact",
|
|
29
33
|
"redaction",
|
|
30
34
|
"redactor",
|