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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const encryption_1 = require("../encryption");
|
|
40
|
+
const vault_1 = require("../vault");
|
|
41
|
+
describe('Encryption Engine', () => {
|
|
42
|
+
const testData = '{"sessionId":"test","entries":{},"createdAt":"2026-01-01T00:00:00Z","ttl":3600000}';
|
|
43
|
+
test('encrypt then decrypt returns original data', () => {
|
|
44
|
+
const encrypted = (0, encryption_1.encryptVaultData)(testData, 'my-secret-passphrase');
|
|
45
|
+
const decrypted = (0, encryption_1.decryptVaultData)(encrypted, 'my-secret-passphrase');
|
|
46
|
+
expect(decrypted).toBe(testData);
|
|
47
|
+
});
|
|
48
|
+
test('encrypted data starts with version byte 0x01', () => {
|
|
49
|
+
const encrypted = (0, encryption_1.encryptVaultData)(testData, 'pass');
|
|
50
|
+
expect(encrypted[0]).toBe(0x01);
|
|
51
|
+
expect((0, encryption_1.isEncryptedVault)(encrypted)).toBe(true);
|
|
52
|
+
expect((0, encryption_1.isPlaintextVault)(encrypted)).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
test('plaintext JSON is detected correctly', () => {
|
|
55
|
+
const plain = Buffer.from(testData, 'utf-8');
|
|
56
|
+
expect((0, encryption_1.isPlaintextVault)(plain)).toBe(true);
|
|
57
|
+
expect((0, encryption_1.isEncryptedVault)(plain)).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
test('wrong passphrase throws error', () => {
|
|
60
|
+
const encrypted = (0, encryption_1.encryptVaultData)(testData, 'correct-pass');
|
|
61
|
+
expect(() => (0, encryption_1.decryptVaultData)(encrypted, 'wrong-pass')).toThrow('decryption failed');
|
|
62
|
+
});
|
|
63
|
+
test('tampered ciphertext throws error', () => {
|
|
64
|
+
const encrypted = (0, encryption_1.encryptVaultData)(testData, 'pass');
|
|
65
|
+
// Flip a byte in the ciphertext
|
|
66
|
+
encrypted[encrypted.length - 1] ^= 0xFF;
|
|
67
|
+
expect(() => (0, encryption_1.decryptVaultData)(encrypted, 'pass')).toThrow();
|
|
68
|
+
});
|
|
69
|
+
test('truncated data throws error', () => {
|
|
70
|
+
const encrypted = (0, encryption_1.encryptVaultData)(testData, 'pass');
|
|
71
|
+
const truncated = encrypted.subarray(0, 10);
|
|
72
|
+
expect(() => (0, encryption_1.decryptVaultData)(truncated, 'pass')).toThrow('too short');
|
|
73
|
+
});
|
|
74
|
+
test('different passphrases produce different ciphertext', () => {
|
|
75
|
+
const e1 = (0, encryption_1.encryptVaultData)(testData, 'pass1');
|
|
76
|
+
const e2 = (0, encryption_1.encryptVaultData)(testData, 'pass2');
|
|
77
|
+
expect(e1.equals(e2)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
test('same passphrase produces different ciphertext (random IV)', () => {
|
|
80
|
+
const e1 = (0, encryption_1.encryptVaultData)(testData, 'same-pass');
|
|
81
|
+
const e2 = (0, encryption_1.encryptVaultData)(testData, 'same-pass');
|
|
82
|
+
expect(e1.equals(e2)).toBe(false);
|
|
83
|
+
// But both decrypt to the same data
|
|
84
|
+
expect((0, encryption_1.decryptVaultData)(e1, 'same-pass')).toBe(testData);
|
|
85
|
+
expect((0, encryption_1.decryptVaultData)(e2, 'same-pass')).toBe(testData);
|
|
86
|
+
});
|
|
87
|
+
test('empty buffer returns false for both checks', () => {
|
|
88
|
+
expect((0, encryption_1.isEncryptedVault)(Buffer.alloc(0))).toBe(false);
|
|
89
|
+
expect((0, encryption_1.isPlaintextVault)(Buffer.alloc(0))).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('FileVault with Encryption', () => {
|
|
93
|
+
let testDir;
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vault-enc-test-'));
|
|
96
|
+
});
|
|
97
|
+
afterEach(() => {
|
|
98
|
+
try {
|
|
99
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
catch { /* ignore */ }
|
|
102
|
+
});
|
|
103
|
+
test('encrypted vault: save and reload', () => {
|
|
104
|
+
const vault = new vault_1.FileVault(testDir, 'my-secret');
|
|
105
|
+
vault.getOrCreateSession('enc-test', 0);
|
|
106
|
+
vault.addEntry('enc-test', {
|
|
107
|
+
token: '[EMAIL_0]',
|
|
108
|
+
original: 'secret@company.com',
|
|
109
|
+
patternLabel: 'EMAIL',
|
|
110
|
+
category: 'contact',
|
|
111
|
+
createdAt: new Date().toISOString(),
|
|
112
|
+
});
|
|
113
|
+
vault.save('enc-test');
|
|
114
|
+
// New vault instance with same passphrase should read it
|
|
115
|
+
const vault2 = new vault_1.FileVault(testDir, 'my-secret');
|
|
116
|
+
const session = vault2.getSession('enc-test');
|
|
117
|
+
expect(session).not.toBeNull();
|
|
118
|
+
expect(session.entries['[EMAIL_0]'].original).toBe('secret@company.com');
|
|
119
|
+
});
|
|
120
|
+
test('wrong passphrase cannot read vault', () => {
|
|
121
|
+
const vault = new vault_1.FileVault(testDir, 'correct-pass');
|
|
122
|
+
vault.getOrCreateSession('secure', 0);
|
|
123
|
+
vault.addEntry('secure', {
|
|
124
|
+
token: '[SSN_0]',
|
|
125
|
+
original: '123-45-6789',
|
|
126
|
+
patternLabel: 'SSN',
|
|
127
|
+
category: 'identity',
|
|
128
|
+
createdAt: new Date().toISOString(),
|
|
129
|
+
});
|
|
130
|
+
vault.save('secure');
|
|
131
|
+
// Different passphrase should NOT read the data
|
|
132
|
+
const vault2 = new vault_1.FileVault(testDir, 'wrong-pass');
|
|
133
|
+
const session = vault2.getSession('secure');
|
|
134
|
+
expect(session).toBeNull();
|
|
135
|
+
});
|
|
136
|
+
test('no passphrase cannot read encrypted vault', () => {
|
|
137
|
+
const vault = new vault_1.FileVault(testDir, 'my-secret');
|
|
138
|
+
vault.getOrCreateSession('protected', 0);
|
|
139
|
+
vault.save('protected');
|
|
140
|
+
// No passphrase — cannot read encrypted file
|
|
141
|
+
const vault2 = new vault_1.FileVault(testDir);
|
|
142
|
+
const session = vault2.getSession('protected');
|
|
143
|
+
expect(session).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
test('vault without passphrase writes plaintext', () => {
|
|
146
|
+
const vault = new vault_1.FileVault(testDir);
|
|
147
|
+
vault.getOrCreateSession('plain', 0);
|
|
148
|
+
vault.save('plain');
|
|
149
|
+
// Should be readable without passphrase
|
|
150
|
+
const vault2 = new vault_1.FileVault(testDir);
|
|
151
|
+
const session = vault2.getSession('plain');
|
|
152
|
+
expect(session).not.toBeNull();
|
|
153
|
+
});
|
|
154
|
+
test('tenant A cannot read tenant B vault (different passphrases)', () => {
|
|
155
|
+
// Tenant A
|
|
156
|
+
const vaultA = new vault_1.FileVault(testDir, 'tenant-A-secret');
|
|
157
|
+
vaultA.getOrCreateSession('tenant-a-data', 0);
|
|
158
|
+
vaultA.addEntry('tenant-a-data', {
|
|
159
|
+
token: '[NAME_0]',
|
|
160
|
+
original: 'Alice Smith',
|
|
161
|
+
patternLabel: 'PERSON',
|
|
162
|
+
category: 'identity',
|
|
163
|
+
createdAt: new Date().toISOString(),
|
|
164
|
+
});
|
|
165
|
+
vaultA.save('tenant-a-data');
|
|
166
|
+
// Tenant B tries to read Tenant A's vault
|
|
167
|
+
const vaultB = new vault_1.FileVault(testDir, 'tenant-B-secret');
|
|
168
|
+
const sessionB = vaultB.getSession('tenant-a-data');
|
|
169
|
+
// MUST be null — cross-tenant isolation
|
|
170
|
+
expect(sessionB).toBeNull();
|
|
171
|
+
// Tenant A can still read their own vault
|
|
172
|
+
const vaultA2 = new vault_1.FileVault(testDir, 'tenant-A-secret');
|
|
173
|
+
const sessionA = vaultA2.getSession('tenant-a-data');
|
|
174
|
+
expect(sessionA).not.toBeNull();
|
|
175
|
+
expect(sessionA.entries['[NAME_0]'].original).toBe('Alice Smith');
|
|
176
|
+
});
|
|
177
|
+
test('encrypted vault file on disk is not human-readable', () => {
|
|
178
|
+
const vault = new vault_1.FileVault(testDir, 'encrypt-me');
|
|
179
|
+
vault.getOrCreateSession('secret-session', 0);
|
|
180
|
+
vault.addEntry('secret-session', {
|
|
181
|
+
token: '[CC_0]',
|
|
182
|
+
original: '4532-0151-1283-0366',
|
|
183
|
+
patternLabel: 'CREDIT_CARD',
|
|
184
|
+
category: 'financial',
|
|
185
|
+
createdAt: new Date().toISOString(),
|
|
186
|
+
});
|
|
187
|
+
vault.save('secret-session');
|
|
188
|
+
// Read raw file from disk
|
|
189
|
+
const files = fs.readdirSync(testDir).filter((f) => f.startsWith('vault_'));
|
|
190
|
+
expect(files.length).toBe(1);
|
|
191
|
+
const rawContent = fs.readFileSync(path.join(testDir, files[0]));
|
|
192
|
+
// Raw content should NOT contain the original PII
|
|
193
|
+
const asString = rawContent.toString('utf-8');
|
|
194
|
+
expect(asString).not.toContain('4532-0151-1283-0366');
|
|
195
|
+
expect(asString).not.toContain('secret-session');
|
|
196
|
+
expect(asString).not.toContain('CREDIT_CARD');
|
|
197
|
+
// First byte should be the version marker
|
|
198
|
+
expect(rawContent[0]).toBe(0x01);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|