field-redactor 1.2.0 → 1.5.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.
Files changed (123) hide show
  1. package/.github/workflows/ci.yml +38 -0
  2. package/CHANGELOG.md +156 -0
  3. package/README.md +64 -670
  4. package/dist/configValidator.d.ts +10 -0
  5. package/dist/configValidator.d.ts.map +1 -0
  6. package/dist/configValidator.js +94 -0
  7. package/dist/configValidator.js.map +1 -0
  8. package/dist/copyOnWriteHelpers.d.ts +20 -0
  9. package/dist/copyOnWriteHelpers.d.ts.map +1 -0
  10. package/dist/copyOnWriteHelpers.js +58 -0
  11. package/dist/copyOnWriteHelpers.js.map +1 -0
  12. package/dist/customObjectManager.d.ts +13 -7
  13. package/dist/customObjectManager.d.ts.map +1 -1
  14. package/dist/customObjectManager.js +30 -29
  15. package/dist/customObjectManager.js.map +1 -1
  16. package/dist/dryRun.d.ts +8 -0
  17. package/dist/dryRun.d.ts.map +1 -0
  18. package/dist/dryRun.js +75 -0
  19. package/dist/dryRun.js.map +1 -0
  20. package/dist/dryRunAttribution.d.ts +8 -0
  21. package/dist/dryRunAttribution.d.ts.map +1 -0
  22. package/dist/dryRunAttribution.js +106 -0
  23. package/dist/dryRunAttribution.js.map +1 -0
  24. package/dist/fieldRedactor.d.ts +43 -5
  25. package/dist/fieldRedactor.d.ts.map +1 -1
  26. package/dist/fieldRedactor.js +124 -24
  27. package/dist/fieldRedactor.js.map +1 -1
  28. package/dist/fieldRedactorConfigBuilder.d.ts +49 -0
  29. package/dist/fieldRedactorConfigBuilder.d.ts.map +1 -0
  30. package/dist/fieldRedactorConfigBuilder.js +98 -0
  31. package/dist/fieldRedactorConfigBuilder.js.map +1 -0
  32. package/dist/fieldRedactorDeps.d.ts +17 -0
  33. package/dist/fieldRedactorDeps.d.ts.map +1 -0
  34. package/dist/fieldRedactorDeps.js +41 -0
  35. package/dist/fieldRedactorDeps.js.map +1 -0
  36. package/dist/index.d.ts +7 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +14 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/jsonWalk.d.ts +11 -0
  41. package/dist/jsonWalk.d.ts.map +1 -0
  42. package/dist/jsonWalk.js +86 -0
  43. package/dist/jsonWalk.js.map +1 -0
  44. package/dist/objectRedactor.d.ts +9 -34
  45. package/dist/objectRedactor.d.ts.map +1 -1
  46. package/dist/objectRedactor.js +11 -298
  47. package/dist/objectRedactor.js.map +1 -1
  48. package/dist/objectRedactorCow.d.ts +35 -0
  49. package/dist/objectRedactorCow.d.ts.map +1 -0
  50. package/dist/objectRedactorCow.js +300 -0
  51. package/dist/objectRedactorCow.js.map +1 -0
  52. package/dist/objectRedactorCustomObject.d.ts +49 -0
  53. package/dist/objectRedactorCustomObject.d.ts.map +1 -0
  54. package/dist/objectRedactorCustomObject.js +138 -0
  55. package/dist/objectRedactorCustomObject.js.map +1 -0
  56. package/dist/objectRedactorHelpers.d.ts +9 -0
  57. package/dist/objectRedactorHelpers.d.ts.map +1 -0
  58. package/dist/objectRedactorHelpers.js +58 -0
  59. package/dist/objectRedactorHelpers.js.map +1 -0
  60. package/dist/objectRedactorMutation.d.ts +10 -0
  61. package/dist/objectRedactorMutation.d.ts.map +1 -0
  62. package/dist/objectRedactorMutation.js +89 -0
  63. package/dist/objectRedactorMutation.js.map +1 -0
  64. package/dist/objectRedactorSync.d.ts +2 -0
  65. package/dist/objectRedactorSync.d.ts.map +1 -0
  66. package/dist/objectRedactorSync.js +7 -0
  67. package/dist/objectRedactorSync.js.map +1 -0
  68. package/dist/objectRedactorTraversal.d.ts +57 -0
  69. package/dist/objectRedactorTraversal.d.ts.map +1 -0
  70. package/dist/objectRedactorTraversal.js +576 -0
  71. package/dist/objectRedactorTraversal.js.map +1 -0
  72. package/dist/presets.d.ts +22 -0
  73. package/dist/presets.d.ts.map +1 -0
  74. package/dist/presets.js +50 -0
  75. package/dist/presets.js.map +1 -0
  76. package/dist/primitiveRedactor.d.ts +13 -8
  77. package/dist/primitiveRedactor.d.ts.map +1 -1
  78. package/dist/primitiveRedactor.js +32 -27
  79. package/dist/primitiveRedactor.js.map +1 -1
  80. package/dist/redactionRules.d.ts +23 -0
  81. package/dist/redactionRules.d.ts.map +1 -0
  82. package/dist/redactionRules.js +76 -0
  83. package/dist/redactionRules.js.map +1 -0
  84. package/dist/regexUtils.d.ts +3 -0
  85. package/dist/regexUtils.d.ts.map +1 -0
  86. package/dist/regexUtils.js +8 -0
  87. package/dist/regexUtils.js.map +1 -0
  88. package/dist/secretManager.d.ts +12 -6
  89. package/dist/secretManager.d.ts.map +1 -1
  90. package/dist/secretManager.js +40 -14
  91. package/dist/secretManager.js.map +1 -1
  92. package/dist/types.d.ts +103 -2
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/types.js +7 -1
  95. package/dist/types.js.map +1 -1
  96. package/dist/valuePatternMatcher.d.ts +15 -0
  97. package/dist/valuePatternMatcher.d.ts.map +1 -0
  98. package/dist/valuePatternMatcher.js +30 -0
  99. package/dist/valuePatternMatcher.js.map +1 -0
  100. package/docs/guides/anti-patterns.md +107 -0
  101. package/docs/guides/metadata-redaction.md +112 -0
  102. package/docs/guides/migration-1.2-to-1.5.md +106 -0
  103. package/docs/guides/secret-key-modes.md +108 -0
  104. package/docs/guides/value-pattern-redaction.md +58 -0
  105. package/docs/reference/config.md +135 -0
  106. package/docs/release-notes/2.0.0.md +19 -0
  107. package/docs/release-notes/2.1.0.md +17 -0
  108. package/docs/release-notes/2.2.0.md +16 -0
  109. package/docs/release-notes/2.3.0.md +17 -0
  110. package/docs/release-notes/2.3.1.md +17 -0
  111. package/docs/release-notes/2.4.0.md +30 -0
  112. package/docs/release-notes/2.5.0.md +33 -0
  113. package/docs/release-notes/2.5.1.md +17 -0
  114. package/docs/release-notes/README.md +30 -0
  115. package/docs/release-notes/v1.0.0.md +17 -0
  116. package/docs/release-notes/v1.1.0.md +18 -0
  117. package/docs/release-notes/v1.2.0.md +18 -0
  118. package/docs/release-notes/v1.2.1.md +16 -0
  119. package/docs/release-notes/v1.2.2.md +19 -0
  120. package/docs/release-notes/v1.3.0.md +112 -0
  121. package/docs/release-notes/v1.5.0.md +88 -0
  122. package/jest.config.js +4 -1
  123. package/package.json +3 -3
package/README.md CHANGED
@@ -1,703 +1,97 @@
1
1
  # FieldRedactor
2
2
 
3
- A utility `npm` module for redacting sensitive data from JSON objects using complex, recursive evaluation logic.
3
+ A TypeScript library for redacting PII from nested JSON using regex key rules, object **schemas** (sibling-key patterns), and configurable redaction modes.
4
4
 
5
- It can redact JSON values based on Regular Expression matching of key values, custom object logic for PII values identified by fields other than their key, deeply redact sensitive objects, and other PII redaction strategies that more simplistic approaches cannot handle.
5
+ ## Install
6
6
 
7
- # History
8
- In many instances, redaction of sensitive data from log output is fairly straightforward and can be accomplished through existing redaction libraries. However, I often encountered scenarios where such simplistic approaches were not sufficient. Take, for example, the following object:
9
-
10
- ```json
11
- {
12
- "name": "email",
13
- "type": "String",
14
- "value": "foo.bar@example.com"
15
- }
16
- ```
17
-
18
- If this object is placed in a data set with many others, and only some of these objects contain PII, redaction becomes quite troublesome. One could redact all `value` fields from these objects - but this merely renders the log output useless for debugging or tracing, as all values are now redacted. The value of the `name` field determines whether or not the `value` field should be redacted. I often found myself writing custom logic to make this determination, which led to brittle code that was difficult to maintain. In the end I decided to write a new, highly configurable plug-and-play solution that could redact sensitive data in various manners while requiring only minor configuration tweaks. Enter FieldRedactor!
19
-
20
- ## Basic Usage
21
- Basic usage of the FieldRedactor is straightforward but not recommended. Object redaction can be performed either in-place or return the redacted object. If redactor is given a string, primitive, Date, function, or null/undefined value then it returns the value without modification.
22
-
23
- ```typescript
24
- import { FieldRedactor } from 'field-redactor';
25
- const myJsonObject = { foo: "bar", fizz: null };
26
- const fieldRedactor = new FieldRedactor();
27
-
28
- // return redacted result
29
- const result = await fieldRedactor.redact(myJsonObject);
30
- const primitiveResult = await fieldRedactor.redact("foobar");
31
- console.log(myJsonObject); // { foo: "bar", fizz: null }
32
- console.log(result); // { foo: "REDACTED", fizz: null }
33
- console.log(primitiveResult); // "foobar"
34
-
35
- // redact in place
36
- await fieldRedactor.redactInPlace(myJsonObject);
37
- console.log(myJsonObject); // { foo: "REDACTED", fizz: null }
38
- ```
39
-
40
- > **Note:** `null`, `undefined`, `string`, `Date`, `Function`, and primitive value inputs are completely ignored.
41
- >
42
- > **Note:** `null` and `undefined` values are not redacted by default.
43
-
44
-
45
- ## Customization
46
- The true power of this tool comes from its customization. FieldRedactor can be customized to redact only primtive values for certain keys, deeply redact others, while stringifying and fully redacting other keys. The redactor itself can be configured, and "Custom Objects" can be specified to handle certain object shapes in a highly specific and configurable way.
47
-
48
- ### Overview
49
- | Config Field | Type | Default | Effect |
50
- |-----------------------|---------------------------------|--------------------------------|-----------------------------------------------------------------------|
51
- | `redactor` | `(val: any) => Promise<string>` | `(val) => Promise.resolve("REDACTED")` | The function to use when redacting values. |
52
- | `secretKeys` | `RegExp[]` | `null` | Specifies which values at any level of the JSON object should be redacted. Objects are not deeply redacted, but primitive values in arrays are. If not specified, all values are considered secret. |
53
- | `deepSecretKeys` | `RegExp[]` | `[]` | Specifies keys at any level of the JSON object to be deeply redacted. All values within matching objects are fully redacted unless matching a custom object. |
54
- | `fullSecretKeys` | `RegExp[]` | `[]` | Specifies keys at any level of the JSON object to be stringified and fully redacted. Primarily used for objects and arrays. |
55
- | `deleteSecretKeys` | `RegExp[]` | `[]` | Specifies keys at any level of the JSON object to be completely deleted. |
56
- | `customObjects` | `CustomObject[]` | `[]` | Specifies custom objects requiring fine-tuned redaction logic, such as referencing sibling keys. See the "Custom Objects" section for details. |
57
- | `ignoreBooleans` | `boolean` | `false` | If `true`, booleans will not be redacted even if secret. |
58
- | `ignoreNullOrUndefined` | `boolean` | `true` | If `true`, `null` and `undefined` values will not be redacted. |
59
-
60
- ### `redactor` Configuration
61
- Configures the redactor function used when a secret is encountered. Users should typically provide this configuration.
62
-
63
- #### Details
64
- - **Type:** `(val: any) => Promise<string>`
65
- - **Effect:** The function used to redact values.
66
- - Value can only be null or undefined if `ignoreNullOrUndefined` is set to false.
67
- - Defaults to `() => Promise.resolve('REDACTED')`.
68
-
69
- #### Example
70
- ##### Code
71
- ```typescript
72
- import { FieldRedactor } from 'field-redactor';
73
- import * as crypto from 'crypto';
74
- const redactor: Redactor = (val: any) => Promise.resolve(crypto.createHash('sha256').update(val.toString()).digest('hex'));
75
- const fieldRedactor = new FieldRedactor({
76
- redactor
77
- });
78
- const result = await redactor.redact({foo: "bar"});
79
- console.log(result);
80
- ```
81
-
82
- ##### Output
83
7
  ```bash
84
- 'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9'
8
+ npm install field-redactor
9
+ # or
10
+ yarn add field-redactor
85
11
  ```
86
12
 
87
- ### `secretKeys` Configuration
88
- * Specifies values which should be redacted.
89
- * __All values are considered secret if no secrets of any type are specified.__
90
- * has lowest precedence of all secret specifiers.
91
-
92
- #### Details
93
- - **Type:** `RegExp[]`
94
- - **Default:** All values considered secret unless a secret field is specified.
95
- - **Effect:** Matches keys in objects, child objects, or array value primitives. Assessed recursively.
13
+ ## Quick start
96
14
 
97
- #### Example
98
- ##### Code
99
15
  ```typescript
100
- import { FieldRedactor } from 'field-redactor';
101
- const myJsonObject = {
102
- timestamp: "2024-12-01T22:07:26.448Z",
103
- userId: 271,
104
- contactInfo: {
105
- email: "foo.bar@example.com",
106
- firstName: "Foo",
107
- lastName: "Bar",
108
- Salutation: "Mr.",
109
- },
110
- someSecretData: ["fizz", "buzz", { deep: "is not redacted", name: "foobar" }],
111
- }
112
- const fieldRedactor = new FieldRedactor({
113
- secretKeys: [/email/i, /name/i, /userid/i, /someSecretData/i],
16
+ import { CustomObjectMatchType, FieldRedactor, FieldRedactorConfigBuilder } from 'field-redactor';
17
+
18
+ // Recommended: explicit rules via createSafe or the builder
19
+ const redactor = FieldRedactorConfigBuilder.create()
20
+ .shallow(/email/i, /password/i)
21
+ .remove(/authKey/i)
22
+ .schema(
23
+ { name: CustomObjectMatchType.Ignore, type: CustomObjectMatchType.Ignore, value: 'name' },
24
+ { name: 'metadata-entry' }
25
+ )
26
+ .buildSafeRedactor();
27
+
28
+ const result = redactor.redactSync({
29
+ email: 'alice@example.com',
30
+ authKey: 'secret',
31
+ metadata: [{ name: 'email', type: 'String', value: 'alice@example.com' }]
114
32
  });
115
- const result = await fieldRedactor.redact(myJsonObject);
116
- console.log(result);
117
33
  ```
118
34
 
119
- ##### Output
120
- ```json
121
- {
122
- "timestamp": "2024-12-01T22:07:26.448Z",
123
- "userId": "REDACTED",
124
- "contactInfo": {
125
- "email": "REDACTED",
126
- "firstName": "REDACTED",
127
- "lastName": "REDACTED",
128
- "Salutation": "Mr.",
129
- },
130
- "someSecretData": ["REDACTED", "REDACTED", { "deep": "is not redacted", "name": "REDACTED" }]
131
- }
132
- ```
133
-
134
- > - `email` and `name` fields were all redacted
135
- > - primitives in `someSecretData` array were redacted
136
- > - object values in `someSecretData` was evaluated and redacted if applicable
137
-
138
- ### `deepSecretKeys` Configuration
139
- * Specifies values which should be deeply redacted
140
- * Has higher precedence than `secretKeys`
141
- * All children of the key will have their values redacted unless `fullSecretKey` or `customObject` has precedence.
35
+ `redact()` / `redactSync()` leave the input untouched by default (copy-on-write). Primitives, `Date`, and `null`/`undefined` at the root are returned unchanged.
142
36
 
143
- #### Details
144
- - **Type:** `RegExp[]`
145
- - **Default:** `[]`
146
- - **Effect:** Matches keys in objects or child objects and deeply redacts all values, including values in child objects or objects within arrays.
147
- - _Note: has higher precedence than `secretKeys`._
37
+ ### Preview changes with `dryRun`
148
38
 
149
- #### Example
150
- ##### Code
151
39
  ```typescript
152
- import { FieldRedactor } from 'field-redactor';
153
- const myJsonObject = {
154
- timestamp: "2024-12-01T22:07:26.448Z",
155
- userId: 271,
156
- contactInfo: {
157
- email: "foo.bar@example.com",
158
- firstName: "Foo",
159
- lastName: "Bar",
160
- Salutation: "Mr.",
161
- lastUpdatedBy: {
162
- id: 1
163
- }
164
- },
165
- someSecretData: ["fizz", "buzz", { deep: "FOO", name: "BAR" }]
166
- }
167
- const fieldRedactor = new FieldRedactor({
168
- deepSecretKeys: [/someSecretData/i, /contactInfo/i]
169
- });
170
- const result = await fieldRedactor.redact(myJsonObject);
171
- console.log(result);
172
- ```
173
-
174
- ##### Output
175
- ```json
176
- {
177
- "timestamp": "2024-12-01T22:07:26.448Z",
178
- "userId": 271,
179
- "contactInfo": {
180
- "email": "REDACTED",
181
- "firstName": "REDACTED",
182
- "lastName": "REDACTED",
183
- "Salutation": "REDACTED",
184
- "lastUpdatedBy": {
185
- "id": "REDACTED"
186
- }
187
- },
188
- "someSecretData": ["REDACTED", "REDACTED", { "deep": "REDACTED", "name": "REDACTED" }]
189
- }
40
+ const { result, report } = redactor.dryRunSync(payload);
41
+ // report.redactedPaths, report.deletedPaths, report.matchedSchemas, report.pathRules
190
42
  ```
191
- > - All values in `contactInfo` were redacted
192
- > - All values in `someSecretData` were redacted
193
-
194
- ### `fullSecretKeys` Configuration
195
- * Specifies values which should be stringified and redacted
196
- * _Note: has higher precedence than `secretKeys` and `deepSecretKeys`_
197
43
 
198
- #### Details
199
- - **Type:** `RegExp[]`
200
- - **Default:** `[]`
201
- - **Effect:** Matches keys and stringifies + redacts their values.
202
- - Has higher precedence than `secretKeys` and `deepSecretKeys` but lower precedence than `customObjects`.
44
+ ## Start here
203
45
 
204
- #### Example
205
- ##### Code
206
- ```typescript
207
- import { FieldRedactor } from 'field-redactor';
208
-
209
- const myJsonObject = {
210
- timestamp: "2024-12-01T22:07:26.448Z",
211
- userId: 271,
212
- contactInfo: {
213
- email: "foo.bar@example.com",
214
- firstName: "Foo",
215
- lastName: "Bar",
216
- Salutation: "Mr.",
217
- preference: "email",
218
- lastUpdated: "2024-12-01T22:07:26.448Z"
219
- }
220
- }
221
- const fieldRedactor = new FieldRedactor({
222
- fullSecretKeys: [/someSecretData/i],
223
- });
224
- const result = await fieldRedactor.redact(myJsonObject);
225
- console.log(result);
226
46
  ```
227
-
228
- ##### Output
229
- ```json
230
- {
231
- "timestamp": "2024-12-01T22:07:26.448Z",
232
- "userId": 271,
233
- "contactInfo": "REDACTED"
234
- }
47
+ Do you know which JSON keys are always sensitive?
48
+ ├─ yes → Shallow / Deep / Opaque / Remove (see secret key modes guide)
49
+ └─ only sometimes → shaped objects like { name, value }? → Schema rules (see metadata guide)
235
50
  ```
236
- > - Entirity of `contactInfo` was redacted.
237
51
 
238
- ### `deleteSecretKeys` Configuration
239
- * Specifies values which should be completely deleted.
52
+ **Precedence:** Schema → Opaque → Deep → Remove → Shallow → Value-pattern
240
53
 
241
- #### Details
242
- - **Type:** `RegExp[]`
243
- - **Default:** `[]`
244
- - **Effect:** Matches keys and deletes them from output.
245
- - Has lower precedence than `customObjects`
54
+ | Concept | Config field |
55
+ | --- | --- |
56
+ | Shallow | `secretKeys` |
57
+ | Deep | `deepSecretKeys` |
58
+ | Opaque | `fullSecretKeys` |
59
+ | Remove | `deleteSecretKeys` |
60
+ | Schema | `customObjects` |
246
61
 
247
- #### Example
248
- ##### Code
249
- ```typescript
250
- import { FieldRedactor } from 'field-redactor';
62
+ Use `FieldRedactor.createSafe({ ... })` or `FieldRedactorConfigBuilder` so you never accidentally redact every field. `new FieldRedactor()` without rules still redacts all values (legacy default).
251
63
 
252
- const myJsonObject = {
253
- timestamp: "2024-12-01T22:07:26.448Z",
254
- userId: 271,
255
- appAuthKey: "12345-67890"
256
- }
257
- const fieldRedactor = new FieldRedactor({
258
- deleteSecretKeys: [/authKey/i],
259
- });
260
- const result = await fieldRedactor.redact(myJsonObject);
261
- console.log(result);
262
- ```
263
-
264
- ##### Output
265
- ```json
266
- {
267
- "timestamp": "2024-12-01T22:07:26.448Z",
268
- "userId": 271
269
- }
270
- ```
271
- > - `appAuthKey` was deleted
64
+ ## Documentation
272
65
 
273
- ### `customObjects` Configuration
274
- * **One of the most powerful features of this library and why it was written in the first place.**
275
- * Combining CustomObjects with secrets yields a powerful and highly customizable redaction tool capable of conditionally redacting a broad variety of input JSON objects correctly according to a user-specified schema.
66
+ | Guide | Description |
67
+ | --- | --- |
68
+ | [Secret key modes](docs/guides/secret-key-modes.md) | Shallow, Deep, Opaque, Remove with examples |
69
+ | [Metadata redaction](docs/guides/metadata-redaction.md) | `{ name, value }` schemas and sibling-key rules |
70
+ | [Anti-patterns](docs/guides/anti-patterns.md) | Common config mistakes and fixes |
71
+ | [Value-pattern redaction](docs/guides/value-pattern-redaction.md) | Detect PII in free-text field values |
72
+ | [Configuration reference](docs/reference/config.md) | Full option table, API, presets, validation |
73
+ | [Migration 1.2 → 1.5](docs/guides/migration-1.2-to-1.5.md) | Upgrade from the previous npm line |
74
+ | [Release notes](docs/release-notes/README.md) | Per-version notes for every published tag |
276
75
 
277
- #### Details
278
- - **Type:** `CustomObject` (See `CustomObject` Schema Section)
279
- - **Default:** `[]`
280
- - **Effect:** Any object that matches the schema will be redacted based on its schema.
281
- - _Note: has highest precedence_
282
- - -_Note: If a custom object is nested inside another, the child takes precedence_
76
+ ## API surface
283
77
 
284
- #### `CustomObject` Schema
285
78
  ```typescript
286
- {
287
- [key]: CustomObjectMatchType | string
288
- }
289
- ```
290
-
291
- - `key`
292
- - **Type:** `string`
293
- - **Effect:** Specifies the key to match on.
294
- - `value`:
295
- - **Type:** `CustomObjectMatchType | string`
296
- - **Effect:** Specifies how the value should be redacted
297
- - `string` - checks if a sibling key with this name exists and contains a secret value. If so, redacts according to the secret specifier.
298
- - `CustomObjectMatchType` - Redacts according to the match type.
299
-
300
-
301
- #### `CustomObjectMatchType` Enum
302
- Specifies how a value should be redacted in a CustomObject if not using a `string` sibling specifier.
303
-
304
- | Key | Description |
305
- | --- | ----------- |
306
- | `Delete` | Delete the value from the result. |
307
- | `Full` | Stringify value and redact. |
308
- | `Deep` | Redact if primitive and deeply redact if object or array. |
309
- | `Shallow` | Redact if primitive or array of primitives and revert to normal rules otherwise, including objects in arrays. |
310
- | `Pass` | Do not redact, but revert to normal rules for child objects or objects in arrays. |
311
- | `Ignore` | Skip evaluation entirely. |
312
-
313
- #### Example
314
- ```typescript
315
- import { FieldRedactor, CustomObjectMatchType } from 'field-redactor';
316
-
317
- const myCustomObject1 = {
318
- shallow: CustomObjectMatchType.Shallow,
319
- deep: CustomObjectMatchType.Deep,
320
- full: CustomObjectMatchType.Full,
321
- delete: CustomObjectMatchType.Delete
322
- pass: CustomObjectMatchType.Pass,
323
- ignore: CustomObjectMatchType.Ignore
324
- };
325
-
326
- const myCustomObject2 = {
327
- name: CustomObjectMatchType.Ignore,
328
- type: CustomObjectMatchType.Ignore,
329
- shallowValue: "name",
330
- deepValue: "type",
331
- }
332
-
333
- const myJsonObject = {
334
- timestamp: "2024-12-01T22:07:26.448Z",
335
- userId: 271,
336
- data: [
337
- {
338
- shallow: "hello",
339
- deep: "hello",
340
- full: "hello",
341
- delete: "hello",
342
- pass: "hello",
343
- ignore: "hello"
344
- },
345
- {
346
- name: "email",
347
- type: "Secure",
348
- shallowValue: "foo.bar@example.com",
349
- deepValue: "foobar",
350
- },
351
- {
352
- shallow: {
353
- hello: "world",
354
- email: "foobar"
355
- },
356
- deep: {
357
- hello: "world",
358
- email: "foobar"
359
- },
360
- full: {
361
- hello: "world",
362
- email: "foobar"
363
- },
364
- delete: {
365
- hello: "world",
366
- email: "foobar"
367
- },
368
- pass: {
369
- hello: "world",
370
- email: "foobar"
371
- },
372
- ignore: {
373
- hello: "world",
374
- email: "foobar"
375
- }
376
- }
377
- {
378
- name: "email",
379
- type: "Secure",
380
- shallowValue: {
381
- hello: "world",
382
- email: "foobar"
383
- },
384
- deepValue: {
385
- hello: "world",
386
- email: "foobar"
387
- }
388
- }
389
- ]
390
- };
391
- const fieldRedactor = new FieldRedactor({
392
- secretKeys: [/email/],
393
- deepSecretKeys: [/secure/i],
394
- fullSecretKeys: [/meta/i],
395
- customObjects: [myCustomObject1, myCustomObject2]
396
- });
397
- const result = await fieldRedactor.redact(myJsonObject);
398
- console.log(result);
399
- ```
400
-
401
- ##### Output
402
- ```json
403
- {
404
- "timestamp": "2024-12-01T22:07:26.448Z",
405
- "userId": 271,
406
- "data": [
407
- {
408
- "shallow": "REDACTED",
409
- "deep": "REDACTED",
410
- "full": "REDACTED",
411
- "pass": "hello",
412
- "ignore": "hello"
413
- },
414
- {
415
- "name": "email",
416
- "type": "Secure",
417
- "shallowValue": "REDACTED",
418
- "deepValue": "REDACTED"
419
- },
420
- {
421
- "shallow": {
422
- "hello": "world",
423
- "email": "REDACTED"
424
- },
425
- "deep": {
426
- "hello": "REDACTED",
427
- "email": "REDACTED"
428
- },
429
- "full": "REDACTED",
430
- "pass": {
431
- "hello": "world",
432
- "email": "REDACTED"
433
- },
434
- "ignore": {
435
- "hello": "world",
436
- "email": "foobar"
437
- }
438
- }
439
- {
440
- "name": "email",
441
- "type": "Secure",
442
- "shallowValue": {
443
- "hello": "world",
444
- "email": "REDACTED"
445
- },
446
- "deepValue": {
447
- "hello": "REDACTED",
448
- "email": "REDACTED"
449
- }
450
- }
451
- ]
452
- }
453
- ```
454
- > - Example shows both primitives and objects to highlight differences
455
-
456
- ### `ignoreBooleans` Configuration
457
- - **Type:** `boolean`
458
- - **Default:** `false`
459
- - **Effect:** Specifies if boolean values should be redacted.
460
-
461
- ### Example
462
- #### Code
463
- ```typescript
464
- import { FieldRedactor } from 'field-redactor';
465
-
466
- const myJsonObject = {
467
- foo: "bar",
468
- fizz: false,
469
- buzz: true
470
- };
471
- const fieldRedactor = new FieldRedactor({
472
- ignoreBooleans: true,
473
- secretKeys: [/foo/, /fizz/, /buzz/]
474
- });
475
-
476
- const result = await fieldRedactor.redact(myJsonObject);
477
- console.log(result);
478
- ```
479
-
480
- #### Output
481
- ```json
482
- {
483
- "foo": "REDACTED",
484
- "fizz": false,
485
- "buzz": true
486
- }
487
- ```
488
-
489
- ### `ignoreNullOrUndefined` Configuration
490
- - **Type:** `boolean`
491
- - **Default:** `true`
492
- - **Effect:** Specifies if null or undefined values shold be redacted.
493
- - *Note: Ensure custom redaction function can appropriately handle null or undefined if set to false.*
494
-
495
- ### Example
496
- #### Code
497
- ```typescript
498
- const myJsonObject = {
499
- foo: "bar",
500
- fizz: null,
501
- buzz: undefined
502
- };
503
- const fieldRedactor = new FieldRedactor({
504
- ignoreNullOrUndefined: false
505
- });
79
+ import {
80
+ FieldRedactor,
81
+ FieldRedactorConfigBuilder,
82
+ FieldRedactorConfigurationError,
83
+ FieldRedactorError,
84
+ presets,
85
+ validateFieldRedactorConfig
86
+ } from 'field-redactor';
506
87
 
507
- const result = await fieldRedactor.redact(myJsonObject);
508
- console.log(result);
88
+ import type { DryRunPathRule, RedactionRuleLabel, MatchedSchemaReport } from 'field-redactor';
509
89
  ```
510
- #### Output
511
- ```json
512
- {
513
- "foo": "REDACTED",
514
- "fizz": "REDACTED",
515
- "buzz": "REDACTED"
516
- }
517
- ```
518
-
519
- ## Full Example
520
- The following example illustrates the power and utility of this library when conditionally redacting JSON output for logging or other purposes. It allows users to specify the manner of redaction, which fields should be redacted and how, and specify custom object schemas with highly configurable redaction logic based on sibling keys or set rules. I find it a quite useful tool!
521
90
 
522
- ##### Code
523
- ```typescript
524
- import { FieldRedactor } from 'field-redactor';
525
- const myRedactor = (text: string) => Promise.resolve("REDACTED");
526
- const metadataCustomObject: CustomObject = {
527
- name: CustomObjectMatchTypes.Pass,
528
- type: CustomObjectMatchTypes.Pass,
529
- id: CustomObjectMatchTypes.Shallow,
530
- value: "name"
531
- };
91
+ - **Sync:** `redactSync()`, `redactInPlaceSync()`, `dryRunSync()`
92
+ - **Async:** `redact()`, `redactInPlace()`, `dryRun()` (sync path when no async-only `redactor`)
93
+ - **Presets:** `presets.loggingMetadata()`, `presets.applicationLogging()`, `presets.keyValueEntries()`
532
94
 
533
- const actionsCustomObject: CustomObject = {
534
- userId: CustomObjectMatchTypes.Pass,
535
- field: CustomObjectMatchTypes.Pass,
536
- action: CustomObjectMatchTypes.Pass,
537
- value: "field"
538
- }
539
-
540
- const fieldRedactor: FieldRedactor = new FieldRedactor({
541
- redactor: myRedactor,
542
- secretKeys: [/email/, /name/i, /someSecretData/, /children/],
543
- deepSecretKeys: [/accountInfo/i, /someDeepSecretData/i, /privateInfo/i],
544
- deleteSecretKeys: [/authKey/i],
545
- customObjects: [metadataCustomObject, actionsCustomObject],
546
- ignoreNullOrUndefined: false
547
- });
95
+ ## Why this library exists
548
96
 
549
- const myJsonObjectToRedact = {
550
- timestamp: "2024-12-01T22:07:26.448Z",
551
- userId: 271,
552
- authKey: 12345,
553
- contactInfo: {
554
- email: "foo.bar@example.com",
555
- salutation: "Mr.",
556
- firstName: "Foo",
557
- lastName: "Bar",
558
- dob: "1980-01-01",
559
- backupEmail: undefined,
560
- preference: "email",
561
- lastUpdated: "2024-12-01T22:07:26.448Z"
562
- },
563
- someSecretData: ["fizz", "buzz", { deep: "is not redacted", name: "foobar" }],
564
- accountInfo: {
565
- balance: 123.45,
566
- institution: "FizzBuz International"
567
- information: {
568
- routingNumber: 11111111,
569
- acctNumber: 222222
570
- }
571
- },
572
- actions: [
573
- {
574
- userId: 271,
575
- field: "email",
576
- action: "CREATE",
577
- value: "foo.bar@example.com"
578
- },
579
- {
580
- userId: 271,
581
- field: "preference",
582
- action: "UPDATE",
583
- value: "email"
584
- }
585
- ],
586
- metadata: [
587
- {
588
- name: "mdn",
589
- type: "Number",
590
- id: 12,
591
- value: 16151112222
592
- },
593
- {
594
- name: "children",
595
- type: "Array",
596
- id: 20,
597
- value: ["John", "Paul","Ringo", "George"]
598
- },
599
- {
600
- name: "traceId",
601
- type: "String",
602
- id: 10,
603
- value: "1234-6587"
604
- },
605
- {
606
- name: "privateInfo",
607
- type: "Object",
608
- id: 20,
609
- value: {
610
- mySecretThings: {
611
- a: "foo",
612
- b: "bar"
613
- }
614
- }
615
- }
616
- ],
617
- hobbies: ["Basketball", "Baseball", "Tennis"],
618
- someDeepSecretData: ["fizz", "buzz", { deep: "is redacted", name: "foobar" }],
619
- someFullSecretData: {
620
- foo: "bar"
621
- },
622
- someFullSecretData2: ["a", 1, "12"]
623
- };
624
-
625
- const result = await fieldRedactor.redact(myJsonObject);
626
- console.log(result);
627
- ```
628
-
629
- ##### Output
630
- ```json
631
- {
632
- "timestamp": "2024-12-01T22:07:26.448Z",
633
- "userId": 271,
634
- "contactInfo": {
635
- "email": "REDACTED",
636
- "salutation": "Mr.",
637
- "firstName": "REDACTED",
638
- "lastName": "REDACTED",
639
- "dob": "REDACTED",
640
- "backupEmail": "REDACTED",
641
- "preference": "email",
642
- "lastUpdated": "2024-12-01T22:07:26.448Z",
643
- },
644
- "someSecretData": ["REDACTED", "REDACTED", { "deep": "is not redacted", "name": "REDACTED" }],
645
- "accountInfo": {
646
- "balance": "REDACTED",
647
- "institution": "REDACTED",
648
- "information": {
649
- "routingNumber": "REDACTED",
650
- "acctNumber": "REDACTED"
651
- }
652
- },
653
- "actions": [
654
- {
655
- "userId": 271,
656
- "field": "email",
657
- "action": "CREATE",
658
- "value": "REDACTED"
659
- },
660
- {
661
- "userId": 271,
662
- "field": "preference",
663
- "action": "UPDATE",
664
- "value": "email"
665
- }
666
- ],
667
- "metadata": [
668
- {
669
- "name": "mdn",
670
- "type": "Number",
671
- "id": "REDACTED",
672
- "value": "REDACTED"
673
- },
674
- {
675
- "name": "children",
676
- "type": "Array",
677
- "id": "REDACTED",
678
- "value": ["REDACTED", "REDACTED", "REDACTED", "REDACTED"]
679
- },
680
- {
681
- "name": "traceId",
682
- "type": "String",
683
- "id": 10,
684
- "value": "1234-6587"
685
- },
686
- {
687
- "name": "privateInfo",
688
- "type": "Object",
689
- "id": "REDACTED",
690
- "value": {
691
- "mySecretThings": {
692
- "a": "REDACTED",
693
- "b": "REDACTED"
694
- }
695
- }
696
- }
697
- ],
698
- "hobbies": ["Basketball", "Baseball", "Tennis"],
699
- "someDeepSecretData": ["REDACTED", "REDACTED", { "deep": "REDACTED", "name": "REDACTED" }],
700
- "someFullSecretData": "REDACTED",
701
- "someFullSecretData2": "REDACTED"
702
- }
703
- ```
97
+ Many log payloads encode sensitivity in sibling fields rather than key names — for example `{ name: "email", value: "user@example.com" }`. FieldRedactor matches object **schemas** and applies rules based on a sibling's value. See [Metadata redaction](docs/guides/metadata-redaction.md) for the full pattern.