n8n-nodes-redactor 3.0.1
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/LICENSE +42 -0
- package/README.dev.md +153 -0
- package/README.md +443 -0
- package/README.npm.md +443 -0
- package/dist/nodes/PiiRedactor/PiiRedactor.node.d.ts +5 -0
- package/dist/nodes/PiiRedactor/PiiRedactor.node.js +1093 -0
- package/dist/nodes/PiiRedactor/__tests__/encryption.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/encryption.test.js +200 -0
- package/dist/nodes/PiiRedactor/__tests__/engine.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/engine.test.js +524 -0
- package/dist/nodes/PiiRedactor/__tests__/operations.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/operations.test.js +316 -0
- package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.js +427 -0
- package/dist/nodes/PiiRedactor/__tests__/patterns.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/patterns.test.js +481 -0
- package/dist/nodes/PiiRedactor/__tests__/phase1.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/phase1.test.js +343 -0
- package/dist/nodes/PiiRedactor/__tests__/phase3.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/phase3.test.js +275 -0
- package/dist/nodes/PiiRedactor/__tests__/phase4.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/phase4.test.js +184 -0
- package/dist/nodes/PiiRedactor/__tests__/presidio.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/presidio.test.js +170 -0
- package/dist/nodes/PiiRedactor/__tests__/security.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/security.test.js +178 -0
- package/dist/nodes/PiiRedactor/__tests__/semantic.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/semantic.test.js +319 -0
- package/dist/nodes/PiiRedactor/__tests__/vault.test.d.ts +1 -0
- package/dist/nodes/PiiRedactor/__tests__/vault.test.js +247 -0
- package/dist/nodes/PiiRedactor/audit.d.ts +48 -0
- package/dist/nodes/PiiRedactor/audit.js +192 -0
- package/dist/nodes/PiiRedactor/classification.d.ts +33 -0
- package/dist/nodes/PiiRedactor/classification.js +118 -0
- package/dist/nodes/PiiRedactor/context.d.ts +57 -0
- package/dist/nodes/PiiRedactor/context.js +260 -0
- package/dist/nodes/PiiRedactor/encryption.d.ts +45 -0
- package/dist/nodes/PiiRedactor/encryption.js +158 -0
- package/dist/nodes/PiiRedactor/engine.d.ts +23 -0
- package/dist/nodes/PiiRedactor/engine.js +888 -0
- package/dist/nodes/PiiRedactor/injection.d.ts +46 -0
- package/dist/nodes/PiiRedactor/injection.js +425 -0
- package/dist/nodes/PiiRedactor/names.d.ts +25 -0
- package/dist/nodes/PiiRedactor/names.js +188 -0
- package/dist/nodes/PiiRedactor/patterns.d.ts +17 -0
- package/dist/nodes/PiiRedactor/patterns.js +1742 -0
- package/dist/nodes/PiiRedactor/presidio.d.ts +77 -0
- package/dist/nodes/PiiRedactor/presidio.js +264 -0
- package/dist/nodes/PiiRedactor/profiles.d.ts +47 -0
- package/dist/nodes/PiiRedactor/profiles.js +139 -0
- package/dist/nodes/PiiRedactor/pseudonymize.d.ts +20 -0
- package/dist/nodes/PiiRedactor/pseudonymize.js +203 -0
- package/dist/nodes/PiiRedactor/redact.png +0 -0
- package/dist/nodes/PiiRedactor/redact.svg +3 -0
- package/dist/nodes/PiiRedactor/ropa.d.ts +63 -0
- package/dist/nodes/PiiRedactor/ropa.js +70 -0
- package/dist/nodes/PiiRedactor/types.d.ts +82 -0
- package/dist/nodes/PiiRedactor/types.js +3 -0
- package/dist/nodes/PiiRedactor/vault.d.ts +61 -0
- package/dist/nodes/PiiRedactor/vault.js +352 -0
- package/package.json +87 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vault_1 = require("../vault");
|
|
4
|
+
const engine_1 = require("../engine");
|
|
5
|
+
function ctx(overrides = {}) {
|
|
6
|
+
return {
|
|
7
|
+
enabledPatterns: ['email', 'phone', 'ssn', 'creditCard'],
|
|
8
|
+
customPatterns: [],
|
|
9
|
+
mode: 'token',
|
|
10
|
+
dedup: true,
|
|
11
|
+
fieldRules: [],
|
|
12
|
+
fieldMode: 'all',
|
|
13
|
+
...overrides,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// ═══════════════════════════════════════════════════════
|
|
17
|
+
// REDACT OPERATION - Enterprise Tests
|
|
18
|
+
// ═══════════════════════════════════════════════════════
|
|
19
|
+
describe('Redact operation', () => {
|
|
20
|
+
let vault;
|
|
21
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
22
|
+
test('handles empty input (0 items)', () => {
|
|
23
|
+
vault.getOrCreateSession('empty', 0);
|
|
24
|
+
const hits = [];
|
|
25
|
+
const result = (0, engine_1.redactValue)({}, ctx(), vault, 'empty', hits, 0);
|
|
26
|
+
expect(result).toEqual({});
|
|
27
|
+
expect(hits).toHaveLength(0);
|
|
28
|
+
});
|
|
29
|
+
test('handles null values in data', () => {
|
|
30
|
+
vault.getOrCreateSession('nulls', 0);
|
|
31
|
+
const hits = [];
|
|
32
|
+
const result = (0, engine_1.redactValue)({ name: null, email: 'test@test.com', count: null }, ctx(), vault, 'nulls', hits, 0);
|
|
33
|
+
expect(result.name).toBeNull();
|
|
34
|
+
expect(result.email).toContain('[');
|
|
35
|
+
expect(result.count).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
test('handles undefined values in data', () => {
|
|
38
|
+
vault.getOrCreateSession('undef', 0);
|
|
39
|
+
const hits = [];
|
|
40
|
+
const data = { name: undefined, email: 'test@test.com' };
|
|
41
|
+
const result = (0, engine_1.redactValue)(data, ctx(), vault, 'undef', hits, 0);
|
|
42
|
+
expect(result.name).toBeUndefined();
|
|
43
|
+
expect(result.email).toContain('[');
|
|
44
|
+
});
|
|
45
|
+
test('handles boolean values', () => {
|
|
46
|
+
vault.getOrCreateSession('bool', 0);
|
|
47
|
+
const hits = [];
|
|
48
|
+
const result = (0, engine_1.redactValue)({ active: true, deleted: false }, ctx(), vault, 'bool', hits, 0);
|
|
49
|
+
expect(result.active).toBe(true);
|
|
50
|
+
expect(result.deleted).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
test('handles large batch (100 items)', () => {
|
|
53
|
+
vault.getOrCreateSession('batch', 0);
|
|
54
|
+
const allHits = [];
|
|
55
|
+
for (let i = 0; i < 100; i++) {
|
|
56
|
+
const hits = [];
|
|
57
|
+
(0, engine_1.redactValue)({ email: `user${i}@test.com`, ssn: '123-45-6789' }, ctx(), vault, 'batch', hits, i);
|
|
58
|
+
allHits.push(...hits);
|
|
59
|
+
}
|
|
60
|
+
expect(allHits.length).toBeGreaterThanOrEqual(100);
|
|
61
|
+
});
|
|
62
|
+
test('handles deeply nested data (5 levels)', () => {
|
|
63
|
+
vault.getOrCreateSession('deep', 0);
|
|
64
|
+
const hits = [];
|
|
65
|
+
const result = (0, engine_1.redactValue)({ a: { b: { c: { d: { e: { email: 'deep@test.com' } } } } } }, ctx(), vault, 'deep', hits, 0);
|
|
66
|
+
expect(result.a.b.c.d.e.email).toContain('[');
|
|
67
|
+
});
|
|
68
|
+
test('handles mixed arrays and objects', () => {
|
|
69
|
+
vault.getOrCreateSession('mixed', 0);
|
|
70
|
+
const hits = [];
|
|
71
|
+
const result = (0, engine_1.redactValue)({ items: [{ email: 'a@b.com' }, 'plain text', 42, null, { nested: { ssn: '111-22-3333' } }] }, ctx(), vault, 'mixed', hits, 0);
|
|
72
|
+
expect(result.items[0].email).toContain('[');
|
|
73
|
+
expect(result.items[1]).toBe('plain text');
|
|
74
|
+
expect(result.items[2]).toBe(42);
|
|
75
|
+
expect(result.items[3]).toBeNull();
|
|
76
|
+
expect(result.items[4].nested.ssn).toContain('[');
|
|
77
|
+
});
|
|
78
|
+
test('custom patterns work alongside built-in patterns', () => {
|
|
79
|
+
vault.getOrCreateSession('custom', 0);
|
|
80
|
+
const hits = [];
|
|
81
|
+
const result = (0, engine_1.redactValue)({ text: 'Order ORD-123456 for user@test.com' }, ctx({ customPatterns: [{ label: 'ORDER', regex: 'ORD-\\d{6}' }] }), vault, 'custom', hits, 0);
|
|
82
|
+
expect(result.text).not.toContain('ORD-123456');
|
|
83
|
+
expect(result.text).not.toContain('user@test.com');
|
|
84
|
+
expect(hits.length).toBeGreaterThanOrEqual(2);
|
|
85
|
+
});
|
|
86
|
+
test('TTL 0 means no expiry', () => {
|
|
87
|
+
vault.getOrCreateSession('no-ttl', 0);
|
|
88
|
+
const session = vault.getSession('no-ttl');
|
|
89
|
+
expect(session).not.toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// ═══════════════════════════════════════════════════════
|
|
93
|
+
// RESTORE OPERATION - Enterprise Tests
|
|
94
|
+
// ═══════════════════════════════════════════════════════
|
|
95
|
+
describe('Restore operation', () => {
|
|
96
|
+
let vault;
|
|
97
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
98
|
+
test('full round-trip with multiple data types', () => {
|
|
99
|
+
vault.getOrCreateSession('rt', 0);
|
|
100
|
+
const hits = [];
|
|
101
|
+
const original = {
|
|
102
|
+
name: 'John Doe',
|
|
103
|
+
email: 'john@company.com',
|
|
104
|
+
ssn: '123-45-6789',
|
|
105
|
+
notes: 'Called from 555-123-4567',
|
|
106
|
+
meta: { active: true, count: 5 },
|
|
107
|
+
};
|
|
108
|
+
const redacted = (0, engine_1.redactValue)(original, ctx({ enabledPatterns: ['email', 'ssn', 'phone'] }), vault, 'rt', hits, 0);
|
|
109
|
+
const restored = (0, engine_1.restoreValue)(redacted, vault, 'rt');
|
|
110
|
+
expect(restored.email).toBe('john@company.com');
|
|
111
|
+
expect(restored.ssn).toBe('123-45-6789');
|
|
112
|
+
expect(restored.meta.active).toBe(true);
|
|
113
|
+
expect(restored.meta.count).toBe(5);
|
|
114
|
+
});
|
|
115
|
+
test('restore handles empty object', () => {
|
|
116
|
+
vault.getOrCreateSession('empty-rt', 0);
|
|
117
|
+
const result = (0, engine_1.restoreValue)({}, vault, 'empty-rt');
|
|
118
|
+
expect(result).toEqual({});
|
|
119
|
+
});
|
|
120
|
+
test('restore handles null', () => {
|
|
121
|
+
vault.getOrCreateSession('null-rt', 0);
|
|
122
|
+
const result = (0, engine_1.restoreValue)(null, vault, 'null-rt');
|
|
123
|
+
expect(result).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
test('restore with nonexistent session returns data as-is', () => {
|
|
126
|
+
const result = (0, engine_1.restoreValue)({ text: '[EMAIL_0]' }, vault, 'nope');
|
|
127
|
+
expect(result.text).toBe('[EMAIL_0]');
|
|
128
|
+
});
|
|
129
|
+
test('multiple restores with deleteAfterRestore=false', () => {
|
|
130
|
+
vault.getOrCreateSession('multi', 0);
|
|
131
|
+
const hits = [];
|
|
132
|
+
(0, engine_1.redactValue)({ email: 'test@test.com' }, ctx(), vault, 'multi', hits, 0);
|
|
133
|
+
// First restore
|
|
134
|
+
const r1 = (0, engine_1.restoreValue)({ text: '[EMAIL_0]' }, vault, 'multi');
|
|
135
|
+
expect(r1.text).toBe('test@test.com');
|
|
136
|
+
// Second restore (session not deleted)
|
|
137
|
+
const r2 = (0, engine_1.restoreValue)({ text: '[EMAIL_0]' }, vault, 'multi');
|
|
138
|
+
expect(r2.text).toBe('test@test.com');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
// ═══════════════════════════════════════════════════════
|
|
142
|
+
// DETECT OPERATION - Enterprise Tests
|
|
143
|
+
// ═══════════════════════════════════════════════════════
|
|
144
|
+
describe('Detect operation (scan-only)', () => {
|
|
145
|
+
let vault;
|
|
146
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
147
|
+
test('detects PII without modifying data', () => {
|
|
148
|
+
vault.getOrCreateSession('det', 0);
|
|
149
|
+
const hits = [];
|
|
150
|
+
const original = { email: 'secret@company.com', text: 'SSN: 123-45-6789' };
|
|
151
|
+
// Run redaction to collect hits (simulating detect)
|
|
152
|
+
(0, engine_1.redactValue)(original, ctx(), vault, 'det', hits, 0);
|
|
153
|
+
expect(hits.length).toBeGreaterThanOrEqual(2);
|
|
154
|
+
// Original data should be untouched (detect returns original)
|
|
155
|
+
expect(original.email).toBe('secret@company.com');
|
|
156
|
+
expect(original.text).toBe('SSN: 123-45-6789');
|
|
157
|
+
});
|
|
158
|
+
test('detect produces valid report', () => {
|
|
159
|
+
vault.getOrCreateSession('det-rpt', 0);
|
|
160
|
+
const hits = [];
|
|
161
|
+
(0, engine_1.redactValue)({ email: 'a@b.com', ssn: '111-22-3333' }, ctx(), vault, 'det-rpt', hits, 0);
|
|
162
|
+
const report = (0, engine_1.buildReport)('det-rpt', hits);
|
|
163
|
+
expect(report.totalHits).toBeGreaterThanOrEqual(2);
|
|
164
|
+
expect(report.hitsByPattern).toBeDefined();
|
|
165
|
+
expect(report.hitsByCategory).toBeDefined();
|
|
166
|
+
expect(report.timestamp).toBeDefined();
|
|
167
|
+
});
|
|
168
|
+
test('detect with clean data returns 0 hits', () => {
|
|
169
|
+
vault.getOrCreateSession('det-clean', 0);
|
|
170
|
+
const hits = [];
|
|
171
|
+
(0, engine_1.redactValue)({ status: 'ok', count: 42 }, ctx(), vault, 'det-clean', hits, 0);
|
|
172
|
+
expect(hits).toHaveLength(0);
|
|
173
|
+
});
|
|
174
|
+
test('detect with custom patterns', () => {
|
|
175
|
+
vault.getOrCreateSession('det-custom', 0);
|
|
176
|
+
const hits = [];
|
|
177
|
+
(0, engine_1.redactValue)({ order: 'ORD-999888' }, ctx({ enabledPatterns: [], customPatterns: [{ label: 'ORDER', regex: 'ORD-\\d{6}' }] }), vault, 'det-custom', hits, 0);
|
|
178
|
+
expect(hits).toHaveLength(1);
|
|
179
|
+
expect(hits[0].patternLabel).toBe('ORDER');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
// ═══════════════════════════════════════════════════════
|
|
183
|
+
// VERIFY OPERATION - Enterprise Tests
|
|
184
|
+
// ═══════════════════════════════════════════════════════
|
|
185
|
+
describe('Verify operation (leak check)', () => {
|
|
186
|
+
let vault;
|
|
187
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
188
|
+
test('PASS: clean data after redaction', () => {
|
|
189
|
+
const tempVault = new vault_1.MemoryVault();
|
|
190
|
+
tempVault.getOrCreateSession('verify-pass', 0);
|
|
191
|
+
const hits = [];
|
|
192
|
+
// Simulate already-redacted data
|
|
193
|
+
const redactedData = { employeeId: '[EMPLOYEE_ID_0]', name: '[PERSON_NAME_1]', dept: '[DEPARTMENT_2]' };
|
|
194
|
+
(0, engine_1.redactValue)(redactedData, ctx(), tempVault, 'verify-pass', hits, 0);
|
|
195
|
+
// Tokens should NOT be detected as PII (they start with [ and end with ])
|
|
196
|
+
// Only semantic detection on field names would fire
|
|
197
|
+
// The key test is that actual PII values are not present
|
|
198
|
+
expect(redactedData.employeeId).toBe('[EMPLOYEE_ID_0]');
|
|
199
|
+
});
|
|
200
|
+
test('FAIL: PII leaked through', () => {
|
|
201
|
+
const tempVault = new vault_1.MemoryVault();
|
|
202
|
+
tempVault.getOrCreateSession('verify-fail', 0);
|
|
203
|
+
const hits = [];
|
|
204
|
+
// Data with PII that should have been redacted
|
|
205
|
+
const leakyData = { email: 'leaked@company.com', text: 'Call 555-123-4567' };
|
|
206
|
+
(0, engine_1.redactValue)(leakyData, ctx(), tempVault, 'verify-fail', hits, 0);
|
|
207
|
+
expect(hits.length).toBeGreaterThanOrEqual(1);
|
|
208
|
+
});
|
|
209
|
+
test('verify with empty data passes', () => {
|
|
210
|
+
const tempVault = new vault_1.MemoryVault();
|
|
211
|
+
tempVault.getOrCreateSession('verify-empty', 0);
|
|
212
|
+
const hits = [];
|
|
213
|
+
(0, engine_1.redactValue)({}, ctx(), tempVault, 'verify-empty', hits, 0);
|
|
214
|
+
expect(hits).toHaveLength(0);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
// ═══════════════════════════════════════════════════════
|
|
218
|
+
// PURGE OPERATION - Enterprise Tests
|
|
219
|
+
// ═══════════════════════════════════════════════════════
|
|
220
|
+
describe('Purge operation', () => {
|
|
221
|
+
let vault;
|
|
222
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
223
|
+
test('purge specific session', () => {
|
|
224
|
+
vault.getOrCreateSession('purge-me', 0);
|
|
225
|
+
vault.addEntry('purge-me', {
|
|
226
|
+
token: '[EMAIL_0]',
|
|
227
|
+
original: 'test@test.com',
|
|
228
|
+
patternLabel: 'EMAIL',
|
|
229
|
+
category: 'contact',
|
|
230
|
+
createdAt: new Date().toISOString(),
|
|
231
|
+
});
|
|
232
|
+
expect(vault.getSession('purge-me')).not.toBeNull();
|
|
233
|
+
vault.deleteSession('purge-me');
|
|
234
|
+
expect(vault.getSession('purge-me')).toBeNull();
|
|
235
|
+
});
|
|
236
|
+
test('purge all sessions', () => {
|
|
237
|
+
vault.getOrCreateSession('s1', 0);
|
|
238
|
+
vault.getOrCreateSession('s2', 0);
|
|
239
|
+
vault.getOrCreateSession('s3', 0);
|
|
240
|
+
const sessions = vault.listSessions();
|
|
241
|
+
for (const s of sessions) {
|
|
242
|
+
vault.deleteSession(s.sessionId);
|
|
243
|
+
}
|
|
244
|
+
expect(vault.listSessions().length).toBe(0);
|
|
245
|
+
});
|
|
246
|
+
test('purge nonexistent session is safe', () => {
|
|
247
|
+
vault.deleteSession('does-not-exist');
|
|
248
|
+
// Should not throw
|
|
249
|
+
expect(true).toBe(true);
|
|
250
|
+
});
|
|
251
|
+
test('purge with zero sessions', () => {
|
|
252
|
+
const sessions = vault.listSessions();
|
|
253
|
+
// Filter only our test sessions (vault is shared)
|
|
254
|
+
expect(Array.isArray(sessions)).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
// ═══════════════════════════════════════════════════════
|
|
258
|
+
// STATS OPERATION - Enterprise Tests
|
|
259
|
+
// ═══════════════════════════════════════════════════════
|
|
260
|
+
describe('Stats operation', () => {
|
|
261
|
+
let vault;
|
|
262
|
+
beforeEach(() => { vault = new vault_1.MemoryVault(); });
|
|
263
|
+
test('lists sessions with entry counts', () => {
|
|
264
|
+
vault.getOrCreateSession('stats-1', 0);
|
|
265
|
+
vault.addEntry('stats-1', {
|
|
266
|
+
token: '[EMAIL_0]',
|
|
267
|
+
original: 'a@b.com',
|
|
268
|
+
patternLabel: 'EMAIL',
|
|
269
|
+
category: 'contact',
|
|
270
|
+
createdAt: new Date().toISOString(),
|
|
271
|
+
});
|
|
272
|
+
vault.addEntry('stats-1', {
|
|
273
|
+
token: '[SSN_1]',
|
|
274
|
+
original: '123-45-6789',
|
|
275
|
+
patternLabel: 'SSN',
|
|
276
|
+
category: 'identity',
|
|
277
|
+
createdAt: new Date().toISOString(),
|
|
278
|
+
});
|
|
279
|
+
const sessions = vault.listSessions();
|
|
280
|
+
const s1 = sessions.find((s) => s.sessionId === 'stats-1');
|
|
281
|
+
expect(s1).toBeDefined();
|
|
282
|
+
expect(s1.entryCount).toBe(2);
|
|
283
|
+
});
|
|
284
|
+
test('cleanup removes expired before listing', () => {
|
|
285
|
+
vault.getOrCreateSession('expired-stat', 1);
|
|
286
|
+
const start = Date.now();
|
|
287
|
+
while (Date.now() - start < 10) { /* wait */ }
|
|
288
|
+
vault.cleanup();
|
|
289
|
+
expect(vault.getSession('expired-stat')).toBeNull();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
// ═══════════════════════════════════════════════════════
|
|
293
|
+
// AUDIT REPORT - Enterprise Tests
|
|
294
|
+
// ═══════════════════════════════════════════════════════
|
|
295
|
+
describe('Audit report', () => {
|
|
296
|
+
test('report never contains original PII', () => {
|
|
297
|
+
const vault = new vault_1.MemoryVault();
|
|
298
|
+
vault.getOrCreateSession('audit', 0);
|
|
299
|
+
const hits = [];
|
|
300
|
+
(0, engine_1.redactValue)({ email: 'secret@corp.com', ssn: '999-88-7777' }, ctx(), vault, 'audit', hits, 0);
|
|
301
|
+
const report = (0, engine_1.buildReport)('audit', hits);
|
|
302
|
+
const json = JSON.stringify(report);
|
|
303
|
+
expect(json).not.toContain('secret@corp.com');
|
|
304
|
+
expect(json).not.toContain('999-88-7777');
|
|
305
|
+
report.hits.forEach((h) => expect(h.original).toBe('***'));
|
|
306
|
+
});
|
|
307
|
+
test('report structure is complete', () => {
|
|
308
|
+
const report = (0, engine_1.buildReport)('test', []);
|
|
309
|
+
expect(report).toHaveProperty('sessionId');
|
|
310
|
+
expect(report).toHaveProperty('timestamp');
|
|
311
|
+
expect(report).toHaveProperty('totalHits');
|
|
312
|
+
expect(report).toHaveProperty('hitsByCategory');
|
|
313
|
+
expect(report).toHaveProperty('hitsByPattern');
|
|
314
|
+
expect(report).toHaveProperty('hits');
|
|
315
|
+
});
|
|
316
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|