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.
Files changed (61) hide show
  1. package/LICENSE +42 -0
  2. package/README.dev.md +153 -0
  3. package/README.md +443 -0
  4. package/README.npm.md +443 -0
  5. package/dist/nodes/PiiRedactor/PiiRedactor.node.d.ts +5 -0
  6. package/dist/nodes/PiiRedactor/PiiRedactor.node.js +1093 -0
  7. package/dist/nodes/PiiRedactor/__tests__/encryption.test.d.ts +1 -0
  8. package/dist/nodes/PiiRedactor/__tests__/encryption.test.js +200 -0
  9. package/dist/nodes/PiiRedactor/__tests__/engine.test.d.ts +1 -0
  10. package/dist/nodes/PiiRedactor/__tests__/engine.test.js +524 -0
  11. package/dist/nodes/PiiRedactor/__tests__/operations.test.d.ts +1 -0
  12. package/dist/nodes/PiiRedactor/__tests__/operations.test.js +316 -0
  13. package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.d.ts +1 -0
  14. package/dist/nodes/PiiRedactor/__tests__/patterns-global.test.js +427 -0
  15. package/dist/nodes/PiiRedactor/__tests__/patterns.test.d.ts +1 -0
  16. package/dist/nodes/PiiRedactor/__tests__/patterns.test.js +481 -0
  17. package/dist/nodes/PiiRedactor/__tests__/phase1.test.d.ts +1 -0
  18. package/dist/nodes/PiiRedactor/__tests__/phase1.test.js +343 -0
  19. package/dist/nodes/PiiRedactor/__tests__/phase3.test.d.ts +1 -0
  20. package/dist/nodes/PiiRedactor/__tests__/phase3.test.js +275 -0
  21. package/dist/nodes/PiiRedactor/__tests__/phase4.test.d.ts +1 -0
  22. package/dist/nodes/PiiRedactor/__tests__/phase4.test.js +184 -0
  23. package/dist/nodes/PiiRedactor/__tests__/presidio.test.d.ts +1 -0
  24. package/dist/nodes/PiiRedactor/__tests__/presidio.test.js +170 -0
  25. package/dist/nodes/PiiRedactor/__tests__/security.test.d.ts +1 -0
  26. package/dist/nodes/PiiRedactor/__tests__/security.test.js +178 -0
  27. package/dist/nodes/PiiRedactor/__tests__/semantic.test.d.ts +1 -0
  28. package/dist/nodes/PiiRedactor/__tests__/semantic.test.js +319 -0
  29. package/dist/nodes/PiiRedactor/__tests__/vault.test.d.ts +1 -0
  30. package/dist/nodes/PiiRedactor/__tests__/vault.test.js +247 -0
  31. package/dist/nodes/PiiRedactor/audit.d.ts +48 -0
  32. package/dist/nodes/PiiRedactor/audit.js +192 -0
  33. package/dist/nodes/PiiRedactor/classification.d.ts +33 -0
  34. package/dist/nodes/PiiRedactor/classification.js +118 -0
  35. package/dist/nodes/PiiRedactor/context.d.ts +57 -0
  36. package/dist/nodes/PiiRedactor/context.js +260 -0
  37. package/dist/nodes/PiiRedactor/encryption.d.ts +45 -0
  38. package/dist/nodes/PiiRedactor/encryption.js +158 -0
  39. package/dist/nodes/PiiRedactor/engine.d.ts +23 -0
  40. package/dist/nodes/PiiRedactor/engine.js +888 -0
  41. package/dist/nodes/PiiRedactor/injection.d.ts +46 -0
  42. package/dist/nodes/PiiRedactor/injection.js +425 -0
  43. package/dist/nodes/PiiRedactor/names.d.ts +25 -0
  44. package/dist/nodes/PiiRedactor/names.js +188 -0
  45. package/dist/nodes/PiiRedactor/patterns.d.ts +17 -0
  46. package/dist/nodes/PiiRedactor/patterns.js +1742 -0
  47. package/dist/nodes/PiiRedactor/presidio.d.ts +77 -0
  48. package/dist/nodes/PiiRedactor/presidio.js +264 -0
  49. package/dist/nodes/PiiRedactor/profiles.d.ts +47 -0
  50. package/dist/nodes/PiiRedactor/profiles.js +139 -0
  51. package/dist/nodes/PiiRedactor/pseudonymize.d.ts +20 -0
  52. package/dist/nodes/PiiRedactor/pseudonymize.js +203 -0
  53. package/dist/nodes/PiiRedactor/redact.png +0 -0
  54. package/dist/nodes/PiiRedactor/redact.svg +3 -0
  55. package/dist/nodes/PiiRedactor/ropa.d.ts +63 -0
  56. package/dist/nodes/PiiRedactor/ropa.js +70 -0
  57. package/dist/nodes/PiiRedactor/types.d.ts +82 -0
  58. package/dist/nodes/PiiRedactor/types.js +3 -0
  59. package/dist/nodes/PiiRedactor/vault.d.ts +61 -0
  60. package/dist/nodes/PiiRedactor/vault.js +352 -0
  61. package/package.json +87 -0
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * Pseudonymization Engine
4
+ *
5
+ * Replaces PII with realistic-looking fake data that preserves format.
6
+ * GDPR Article 4(5): "processing personal data so it can no longer be
7
+ * attributed to a specific data subject without additional information."
8
+ *
9
+ * Key properties:
10
+ * - Format-preserving: emails stay email-shaped, phones stay phone-shaped
11
+ * - Deterministic: same input + same session = same output (seeded)
12
+ * - Reversible: mapping stored in vault for restoration
13
+ * - GDPR-compliant: mapping stored separately from pseudonymized data
14
+ *
15
+ * No external dependencies (no Faker.js). Uses built-in deterministic generation.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.generatePseudonym = generatePseudonym;
52
+ const crypto = __importStar(require("crypto"));
53
+ /** Generate a deterministic seed from session ID + original value */
54
+ function getSeed(sessionId, original) {
55
+ const hash = crypto.createHash('sha256').update(`${sessionId}:${original}`).digest();
56
+ return hash.readUInt32BE(0);
57
+ }
58
+ /** Seeded random number generator (Mulberry32) */
59
+ function seededRandom(seed) {
60
+ let s = seed;
61
+ return () => {
62
+ s |= 0;
63
+ s = (s + 0x6d2b79f5) | 0;
64
+ let t = Math.imul(s ^ (s >>> 15), 1 | s);
65
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
66
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
67
+ };
68
+ }
69
+ // ═══════════════════════════════════════════
70
+ // FAKE DATA POOLS
71
+ // ═══════════════════════════════════════════
72
+ const FIRST_NAMES = [
73
+ 'Emma', 'Liam', 'Olivia', 'Noah', 'Ava', 'William', 'Sophia', 'James',
74
+ 'Isabella', 'Oliver', 'Mia', 'Benjamin', 'Charlotte', 'Elijah', 'Amelia',
75
+ 'Lucas', 'Harper', 'Mason', 'Evelyn', 'Logan', 'Abigail', 'Alexander',
76
+ 'Emily', 'Ethan', 'Elizabeth', 'Jacob', 'Sofia', 'Michael', 'Avery',
77
+ 'Daniel', 'Ella', 'Henry', 'Scarlett', 'Jackson', 'Grace', 'Sebastian',
78
+ 'Victoria', 'Aiden', 'Riley', 'Matthew', 'Aria', 'Samuel', 'Lily',
79
+ 'David', 'Layla', 'Joseph', 'Zoe', 'Carter', 'Penelope', 'Owen',
80
+ 'Anna', 'Klaus', 'Marie', 'Stefan', 'Laura', 'Marco', 'Giulia',
81
+ 'Pierre', 'Camille', 'Pablo', 'Elena', 'Jan', 'Sophie', 'Andrzej',
82
+ ];
83
+ const LAST_NAMES = [
84
+ 'Anderson', 'Baker', 'Campbell', 'Davis', 'Edwards', 'Foster', 'Garcia',
85
+ 'Harrison', 'Ingram', 'Johnson', 'Kelly', 'Lambert', 'Mitchell', 'Nelson',
86
+ 'Owens', 'Parker', 'Quinn', 'Reynolds', 'Stewart', 'Turner', 'Underwood',
87
+ 'Vincent', 'Watson', 'Xavier', 'Young', 'Zimmerman', 'Adams', 'Brooks',
88
+ 'Cooper', 'Dixon', 'Evans', 'Fisher', 'Grant', 'Hayes', 'Irving',
89
+ 'Jordan', 'Klein', 'Lewis', 'Morgan', 'Norton', 'Palmer', 'Ross',
90
+ 'Schmidt', 'Weber', 'Mueller', 'Fischer', 'Schneider', 'Hoffmann',
91
+ 'Martin', 'Bernard', 'Dubois', 'Rossi', 'Ferrari', 'Kowalski',
92
+ ];
93
+ const EMAIL_DOMAINS = [
94
+ 'example.com', 'sample.org', 'demo.net', 'test.io', 'mock.dev',
95
+ 'placeholder.com', 'pseudo.org', 'anon.net', 'redacted.io', 'safe.dev',
96
+ ];
97
+ const STREET_NAMES = [
98
+ 'Oak', 'Maple', 'Cedar', 'Pine', 'Elm', 'Birch', 'Willow', 'Ash',
99
+ 'Cherry', 'Walnut', 'Spruce', 'Cypress', 'Magnolia', 'Jasmine', 'Holly',
100
+ ];
101
+ const STREET_TYPES = ['Street', 'Avenue', 'Road', 'Drive', 'Lane', 'Boulevard', 'Way', 'Place'];
102
+ const CITIES = [
103
+ 'Springfield', 'Riverside', 'Fairview', 'Greenville', 'Oakland',
104
+ 'Brighton', 'Westport', 'Lakewood', 'Northfield', 'Eastwood',
105
+ ];
106
+ const COMPANY_PREFIXES = [
107
+ 'Global', 'United', 'Premier', 'Pacific', 'Atlas', 'Apex', 'Nova',
108
+ 'Pinnacle', 'Vertex', 'Meridian', 'Horizon', 'Summit', 'Delta',
109
+ ];
110
+ const COMPANY_SUFFIXES = [
111
+ 'Solutions', 'Technologies', 'Industries', 'Services', 'Group',
112
+ 'Systems', 'Enterprises', 'Partners', 'Holdings', 'Associates',
113
+ ];
114
+ /**
115
+ * Generate a pseudonym for a given PII value based on its type.
116
+ * Deterministic: same sessionId + original = same pseudonym.
117
+ */
118
+ function generatePseudonym(sessionId, original, label) {
119
+ const seed = getSeed(sessionId, original);
120
+ const rng = seededRandom(seed);
121
+ const pick = (arr) => arr[Math.floor(rng() * arr.length)];
122
+ const digit = () => String(Math.floor(rng() * 10));
123
+ const digits = (n) => Array.from({ length: n }, digit).join('');
124
+ switch (label) {
125
+ case 'EMAIL':
126
+ case 'EDU_EMAIL': {
127
+ const first = pick(FIRST_NAMES).toLowerCase();
128
+ const last = pick(LAST_NAMES).toLowerCase();
129
+ const domain = pick(EMAIL_DOMAINS);
130
+ return `${first}.${last}@${domain}`;
131
+ }
132
+ case 'PERSON_NAME':
133
+ case 'PERSON':
134
+ case 'FULL_NAME': {
135
+ return `${pick(FIRST_NAMES)} ${pick(LAST_NAMES)}`;
136
+ }
137
+ case 'PHONE':
138
+ case 'PHONE_DE':
139
+ case 'PHONE_UK':
140
+ case 'PHONE_FR':
141
+ case 'PHONE_US':
142
+ case 'FAX': {
143
+ // Preserve general phone format
144
+ if (original.startsWith('+49'))
145
+ return `+49 ${digits(3)} ${digits(4)}-${digits(4)}`;
146
+ if (original.startsWith('+44'))
147
+ return `+44 ${digits(4)} ${digits(6)}`;
148
+ if (original.startsWith('+33'))
149
+ return `+33 ${digit()} ${digits(2)} ${digits(2)} ${digits(2)} ${digits(2)}`;
150
+ if (original.startsWith('+1'))
151
+ return `+1-${digits(3)}-${digits(3)}-${digits(4)}`;
152
+ return `+${digits(2)} ${digits(3)} ${digits(4)}-${digits(4)}`;
153
+ }
154
+ case 'SSN': {
155
+ return `${digits(3)}-${digits(2)}-${digits(4)}`;
156
+ }
157
+ case 'CREDIT_CARD':
158
+ case 'AMEX': {
159
+ return `${digits(4)}-${digits(4)}-${digits(4)}-${digits(4)}`;
160
+ }
161
+ case 'IBAN': {
162
+ const country = original.slice(0, 2) || 'DE';
163
+ return `${country}${digits(20)}`;
164
+ }
165
+ case 'ADDRESS':
166
+ case 'ADDRESS_DE':
167
+ case 'STREET_ADDRESS': {
168
+ return `${digits(3)} ${pick(STREET_NAMES)} ${pick(STREET_TYPES)}, ${pick(CITIES)}`;
169
+ }
170
+ case 'COMPANY':
171
+ case 'ORGANIZATION': {
172
+ return `${pick(COMPANY_PREFIXES)} ${pick(COMPANY_SUFFIXES)}`;
173
+ }
174
+ case 'DOB':
175
+ case 'DATE':
176
+ case 'EMPLOYMENT_DATE':
177
+ case 'HIRE_DATE':
178
+ case 'DATE_OF_DEATH': {
179
+ // Generate a random date preserving format
180
+ const year = 1950 + Math.floor(rng() * 60);
181
+ const month = 1 + Math.floor(rng() * 12);
182
+ const day = 1 + Math.floor(rng() * 28);
183
+ if (original.includes('/'))
184
+ return `${String(day).padStart(2, '0')}/${String(month).padStart(2, '0')}/${year}`;
185
+ if (original.includes('.'))
186
+ return `${String(day).padStart(2, '0')}.${String(month).padStart(2, '0')}.${year}`;
187
+ return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
188
+ }
189
+ case 'IP_ADDRESS':
190
+ case 'PRIVATE_IP': {
191
+ return `${10 + Math.floor(rng() * 240)}.${Math.floor(rng() * 256)}.${Math.floor(rng() * 256)}.${Math.floor(rng() * 256)}`;
192
+ }
193
+ case 'SALARY':
194
+ case 'FINANCIAL_AMOUNT': {
195
+ return String(30000 + Math.floor(rng() * 170000));
196
+ }
197
+ default: {
198
+ // For unknown types, generate a hash-like pseudonym
199
+ const hash = crypto.createHash('sha256').update(`pseudo:${sessionId}:${original}`).digest('hex');
200
+ return `[PSEUDO_${hash.slice(0, 8).toUpperCase()}]`;
201
+ }
202
+ }
203
+ }
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="60" viewBox="0 0 60 60">
2
+ <image width="60" height="60" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAIAAAAP3aGbAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgBAABAAAAkAEAAAOgBAABAAAAkAEAAAAAAAA1qXZBAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFwGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTAzLTE0PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUc2anFFdGhHMCZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBRWt2VDU2eXVJJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O01JICZhbXA7IEFaJnF1b3Q7LCZxdW90O3RlbXBsYXRlJnF1b3Q7OiZxdW90O09yYW5nZSBCbHVlIE1pbmltYWxpc3QgTW9kZXJuIFByb3RlY3QgTG9nbyAmcXVvdDt9PC9BdHRyaWI6RGF0YT4KICAgICA8QXR0cmliOkV4dElkPjMxYTE3OWFiLWFhZDUtNDM1My05MjM5LWZmNzMxOWUwMGY0NTwvQXR0cmliOkV4dElkPgogICAgIDxBdHRyaWI6RmJJZD41MjUyNjU5MTQxNzk1ODA8L0F0dHJpYjpGYklkPgogICAgIDxBdHRyaWI6VG91Y2hUeXBlPjI8L0F0dHJpYjpUb3VjaFR5cGU+CiAgICA8L3JkZjpsaT4KICAgPC9yZGY6U2VxPgogIDwvQXR0cmliOkFkcz4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6ZGM9J2h0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6dGl0bGU+CiAgIDxyZGY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz5mYXZpY29uIC0gcmVkYWN0PC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOnRpdGxlPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpwZGY9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGRmLzEuMy8nPgogIDxwZGY6QXV0aG9yPk1JICZhbXA7IEE8L3BkZjpBdXRob3I+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOnhtcD0naHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyc+CiAgPHhtcDpDcmVhdG9yVG9vbD5DYW52YSBkb2M9REFHNmpxRXRoRzAgdXNlcj1VQUVrdlQ1Nnl1SSBicmFuZD1NSSAmYW1wOyBBWiB0ZW1wbGF0ZT1PcmFuZ2UgQmx1ZSBNaW5pbWFsaXN0IE1vZGVybiBQcm90ZWN0IExvZ28gPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic/PlPvXF4AACAASURBVHic7Z0JuBXVle+ZRQTJ5WpM0oma+KXzuu0kL+l0vu5093vpdL8kGpUZ04IIEUdwiHMcouLQJnBn7mVGJkWQ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"/>
3
+ </svg>
@@ -0,0 +1,63 @@
1
+ /**
2
+ * GDPR Article 30 ROPA (Records of Processing Activities) Report Generator
3
+ *
4
+ * Generates structured compliance reports as required by GDPR Art.30(1).
5
+ * Auto-populates technical fields from redaction results.
6
+ * User provides business context (controller name, purposes, legal basis).
7
+ *
8
+ * Output: JSON conforming to Art.30(1)(a)-(h) requirements.
9
+ */
10
+ import { RedactionHit } from './types';
11
+ import { ClassificationLabel } from './classification';
12
+ export interface RopaController {
13
+ name: string;
14
+ contactEmail: string;
15
+ representative?: string;
16
+ dpo?: string;
17
+ }
18
+ export interface RopaThirdCountryTransfer {
19
+ country: string;
20
+ organization: string;
21
+ safeguards: string;
22
+ }
23
+ export interface RopaRecord {
24
+ recordId: string;
25
+ version: string;
26
+ generatedAt: string;
27
+ generatedBy: string;
28
+ controller: RopaController;
29
+ processingPurposes: string[];
30
+ legalBasis: string;
31
+ dataSubjectCategories: string[];
32
+ personalDataCategories: Array<{
33
+ category: string;
34
+ count: number;
35
+ patternLabels: string[];
36
+ }>;
37
+ recipientCategories: string[];
38
+ thirdCountryTransfers: RopaThirdCountryTransfer[];
39
+ retentionPolicy: {
40
+ vaultTtlMinutes: number;
41
+ auditLogRetentionDays: number;
42
+ description: string;
43
+ };
44
+ securityMeasures: {
45
+ encryptionAtRest: boolean;
46
+ encryptionAlgorithm: string;
47
+ pseudonymization: boolean;
48
+ accessControl: string;
49
+ auditLogging: boolean;
50
+ };
51
+ processingSummary: {
52
+ totalItemsProcessed: number;
53
+ totalPiiDetected: number;
54
+ redactionMode: string;
55
+ patternsUsed: number;
56
+ classificationLevel: string;
57
+ hitsByCategory: Record<string, number>;
58
+ };
59
+ }
60
+ /**
61
+ * Generate a ROPA record from redaction results and user-provided context.
62
+ */
63
+ export declare function generateRopaRecord(hits: RedactionHit[], classification: ClassificationLabel, itemCount: number, patternsUsed: number, redactionMode: string, vaultTtlMinutes: number, encryptionEnabled: boolean, auditLogEnabled: boolean, controller: RopaController, processingPurposes: string[], legalBasis: string, dataSubjectCategories: string[], recipientCategories: string[], thirdCountryTransfers: RopaThirdCountryTransfer[]): RopaRecord;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * GDPR Article 30 ROPA (Records of Processing Activities) Report Generator
4
+ *
5
+ * Generates structured compliance reports as required by GDPR Art.30(1).
6
+ * Auto-populates technical fields from redaction results.
7
+ * User provides business context (controller name, purposes, legal basis).
8
+ *
9
+ * Output: JSON conforming to Art.30(1)(a)-(h) requirements.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.generateRopaRecord = generateRopaRecord;
13
+ /**
14
+ * Generate a ROPA record from redaction results and user-provided context.
15
+ */
16
+ function generateRopaRecord(
17
+ // Auto-populated from redaction
18
+ hits, classification, itemCount, patternsUsed, redactionMode, vaultTtlMinutes, encryptionEnabled, auditLogEnabled,
19
+ // User-provided context
20
+ controller, processingPurposes, legalBasis, dataSubjectCategories, recipientCategories, thirdCountryTransfers) {
21
+ // Auto-derive personal data categories from hits
22
+ const categoryMap = {};
23
+ for (const hit of hits) {
24
+ if (!categoryMap[hit.category]) {
25
+ categoryMap[hit.category] = { count: 0, labels: new Set() };
26
+ }
27
+ categoryMap[hit.category].count++;
28
+ categoryMap[hit.category].labels.add(hit.patternLabel);
29
+ }
30
+ const personalDataCategories = Object.entries(categoryMap).map(([cat, data]) => ({
31
+ category: cat,
32
+ count: data.count,
33
+ patternLabels: Array.from(data.labels),
34
+ }));
35
+ // Generate record ID
36
+ const recordId = `ropa-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
37
+ return {
38
+ recordId,
39
+ version: '1.0',
40
+ generatedAt: new Date().toISOString(),
41
+ generatedBy: 'n8n-nodes-redactor',
42
+ controller,
43
+ processingPurposes,
44
+ legalBasis,
45
+ dataSubjectCategories,
46
+ recipientCategories,
47
+ thirdCountryTransfers,
48
+ personalDataCategories,
49
+ retentionPolicy: {
50
+ vaultTtlMinutes,
51
+ auditLogRetentionDays: 2555, // 7 years (SOX)
52
+ description: `Vault sessions auto-expire after ${vaultTtlMinutes} minutes. Audit logs retained for 7 years.`,
53
+ },
54
+ securityMeasures: {
55
+ encryptionAtRest: encryptionEnabled,
56
+ encryptionAlgorithm: encryptionEnabled ? 'AES-256-GCM' : 'none',
57
+ pseudonymization: redactionMode === 'pseudonymize',
58
+ accessControl: 'n8n workflow execution context with optional vault passphrase',
59
+ auditLogging: auditLogEnabled,
60
+ },
61
+ processingSummary: {
62
+ totalItemsProcessed: itemCount,
63
+ totalPiiDetected: hits.length,
64
+ redactionMode,
65
+ patternsUsed,
66
+ classificationLevel: classification.level,
67
+ hitsByCategory: classification.hitCountByLevel,
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,82 @@
1
+ /** Shared type definitions for the PII Redactor node */
2
+ export interface PiiPattern {
3
+ name: string;
4
+ label: string;
5
+ category: PiiCategory;
6
+ regex: RegExp;
7
+ /** Optional validator to reduce false positives */
8
+ validate?: (match: string) => boolean;
9
+ /** Description shown in the UI */
10
+ description?: string;
11
+ /** Context words that boost confidence when found near a match */
12
+ contextWords?: string[];
13
+ /** Base confidence score (0.0 to 1.0) before context boosting */
14
+ baseConfidence?: number;
15
+ }
16
+ export type PiiCategory = 'identity' | 'financial' | 'contact' | 'network' | 'location' | 'temporal' | 'medical' | 'crypto' | 'enterprise' | 'vehicle' | 'biometric' | 'other';
17
+ export type RedactionMode = 'token' | 'mask' | 'hash' | 'redact' | 'pseudonymize' | 'blackout' | 'remove';
18
+ export type VaultStorage = 'memory' | 'file';
19
+ export interface RedactionHit {
20
+ token: string;
21
+ original: string;
22
+ patternName: string;
23
+ patternLabel: string;
24
+ category: PiiCategory;
25
+ field: string;
26
+ itemIndex: number;
27
+ /** Confidence score 0.0 to 1.0 */
28
+ confidence: number;
29
+ }
30
+ export interface RedactionReport {
31
+ sessionId: string;
32
+ timestamp: string;
33
+ totalHits: number;
34
+ hitsByCategory: Record<string, number>;
35
+ hitsByPattern: Record<string, number>;
36
+ hits: RedactionHit[];
37
+ }
38
+ export interface VaultEntry {
39
+ token: string;
40
+ original: string;
41
+ patternLabel: string;
42
+ category: PiiCategory;
43
+ createdAt: string;
44
+ }
45
+ export interface VaultSession {
46
+ sessionId: string;
47
+ entries: Record<string, VaultEntry>;
48
+ createdAt: string;
49
+ ttl: number;
50
+ }
51
+ export interface CustomPattern {
52
+ label: string;
53
+ regex: string;
54
+ category?: string;
55
+ }
56
+ export interface FieldRule {
57
+ field: string;
58
+ mode: 'include' | 'exclude';
59
+ }
60
+ export interface AllowDenyEntry {
61
+ value: string;
62
+ type: 'exact' | 'contains' | 'regex';
63
+ }
64
+ export interface RedactionContext {
65
+ enabledPatterns: string[];
66
+ customPatterns: CustomPattern[];
67
+ mode: RedactionMode;
68
+ dedup: boolean;
69
+ fieldRules: FieldRule[];
70
+ fieldMode: 'all' | 'allowlist' | 'denylist';
71
+ skipSemantic?: boolean;
72
+ /** Values to NEVER redact (bypass) */
73
+ allowList?: AllowDenyEntry[];
74
+ /** Values to ALWAYS redact even if no pattern matches */
75
+ denyList?: AllowDenyEntry[];
76
+ /** Minimum confidence score to trigger redaction (0.0 to 1.0) */
77
+ confidenceThreshold?: number;
78
+ /** Presidio Analyzer base URL (e.g., http://localhost:5002) */
79
+ presidioUrl?: string;
80
+ /** Language for Presidio analysis (default: en) */
81
+ presidioLanguage?: string;
82
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ /** Shared type definitions for the PII Redactor node */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,61 @@
1
+ import { VaultEntry, VaultSession } from './types';
2
+ /**
3
+ * Abstract vault interface — storage backend for token ↔ original mappings.
4
+ */
5
+ export interface IVault {
6
+ getOrCreateSession(sessionId: string, ttl: number): VaultSession;
7
+ getSession(sessionId: string): VaultSession | null;
8
+ addEntry(sessionId: string, entry: VaultEntry): void;
9
+ findByOriginal(sessionId: string, original: string): VaultEntry | undefined;
10
+ deleteSession(sessionId: string): void;
11
+ listSessions(): Array<{
12
+ sessionId: string;
13
+ entryCount: number;
14
+ createdAt: string;
15
+ ttl: number;
16
+ }>;
17
+ save(sessionId: string): void;
18
+ cleanup(): number;
19
+ }
20
+ export declare class MemoryVault implements IVault {
21
+ getOrCreateSession(sessionId: string, ttl: number): VaultSession;
22
+ getSession(sessionId: string): VaultSession | null;
23
+ addEntry(sessionId: string, entry: VaultEntry): void;
24
+ findByOriginal(sessionId: string, original: string): VaultEntry | undefined;
25
+ deleteSession(sessionId: string): void;
26
+ listSessions(): Array<{
27
+ sessionId: string;
28
+ entryCount: number;
29
+ createdAt: string;
30
+ ttl: number;
31
+ }>;
32
+ save(): void;
33
+ cleanup(): number;
34
+ }
35
+ export declare class FileVault implements IVault {
36
+ private dir;
37
+ private cache;
38
+ private passphrase;
39
+ constructor(dir?: string, passphrase?: string);
40
+ private filePath;
41
+ private load;
42
+ getOrCreateSession(sessionId: string, ttl: number): VaultSession;
43
+ getSession(sessionId: string): VaultSession | null;
44
+ addEntry(sessionId: string, entry: VaultEntry): void;
45
+ findByOriginal(sessionId: string, original: string): VaultEntry | undefined;
46
+ deleteSession(sessionId: string): void;
47
+ listSessions(): Array<{
48
+ sessionId: string;
49
+ entryCount: number;
50
+ createdAt: string;
51
+ ttl: number;
52
+ }>;
53
+ save(sessionId: string): void;
54
+ cleanup(): number;
55
+ }
56
+ /**
57
+ * Factory to get the right vault implementation.
58
+ * If file vault fails (read-only filesystem, no permissions, Docker, cloud),
59
+ * automatically falls back to memory vault so the node never crashes.
60
+ */
61
+ export declare function createVault(storage: string, vaultDir?: string, passphrase?: string): IVault;