n8n-nodes-redactor 2.0.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.
Potentially problematic release.
This version of n8n-nodes-redactor might be problematic. Click here for more details.
- package/LICENSE +42 -0
- package/README.dev.md +134 -0
- package/README.md +376 -0
- package/README.npm.md +376 -0
- package/dist/nodes/PiiRedactor/PiiRedactor.node.d.ts +5 -0
- package/dist/nodes/PiiRedactor/PiiRedactor.node.js +872 -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__/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/context.d.ts +57 -0
- package/dist/nodes/PiiRedactor/context.js +260 -0
- package/dist/nodes/PiiRedactor/engine.d.ts +17 -0
- package/dist/nodes/PiiRedactor/engine.js +813 -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 +1741 -0
- package/dist/nodes/PiiRedactor/redact.png +0 -0
- package/dist/nodes/PiiRedactor/redact.svg +3 -0
- package/dist/nodes/PiiRedactor/types.d.ts +78 -0
- package/dist/nodes/PiiRedactor/types.js +3 -0
- package/dist/nodes/PiiRedactor/vault.d.ts +60 -0
- package/dist/nodes/PiiRedactor/vault.js +299 -0
- package/package.json +87 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Context words and confidence scoring for PII patterns.
|
|
4
|
+
*
|
|
5
|
+
* Context words are keywords that appear near a PII match and boost confidence.
|
|
6
|
+
* For example, "SSN" appearing before "123-45-6789" boosts confidence from 0.60 to 0.85.
|
|
7
|
+
*
|
|
8
|
+
* Base confidence scores reflect how reliable a pattern is:
|
|
9
|
+
* - 0.95: Checksum validated (Luhn, IBAN, PESEL, etc.)
|
|
10
|
+
* - 0.90: Semantic field-name match
|
|
11
|
+
* - 0.85: Context-aware ambiguous field
|
|
12
|
+
* - 0.80: Regex match WITH context words nearby
|
|
13
|
+
* - 0.60: Regex match WITHOUT context words (bare pattern)
|
|
14
|
+
* - 0.70: Custom pattern match
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.CONTEXT_WINDOW = exports.DENY_LIST_CONFIDENCE = exports.CUSTOM_PATTERN_CONFIDENCE = exports.AMBIGUOUS_FIELD_CONFIDENCE = exports.SEMANTIC_CONFIDENCE = exports.CONTEXT_BOOST = exports.DEFAULT_BASE_CONFIDENCE = exports.BASE_CONFIDENCE = exports.CONTEXT_WORDS = void 0;
|
|
18
|
+
exports.hasContextWords = hasContextWords;
|
|
19
|
+
exports.calculateConfidence = calculateConfidence;
|
|
20
|
+
/** Context words by pattern name. Case-insensitive matching. */
|
|
21
|
+
exports.CONTEXT_WORDS = {
|
|
22
|
+
// Contact
|
|
23
|
+
email: ['email', 'e-mail', 'mail', 'contact', 'reach', 'send', 'Kontakt', 'correo', 'posta'],
|
|
24
|
+
phone: ['phone', 'call', 'mobile', 'cell', 'tel', 'telephone', 'dial', 'ring', 'Telefon', 'anrufen', 'Handy', 'appeler', 'llamar'],
|
|
25
|
+
phoneDE: ['Telefon', 'anrufen', 'Handy', 'Rufnummer', 'Festnetz', 'mobil'],
|
|
26
|
+
phoneUK: ['phone', 'call', 'mobile', 'ring', 'dial', 'landline'],
|
|
27
|
+
phoneAT: ['Telefon', 'anrufen', 'Handy'],
|
|
28
|
+
phoneCH: ['Telefon', 'anrufen', 'Natel', 'Handy'],
|
|
29
|
+
phoneFR: ['telephone', 'appeler', 'portable', 'fixe', 'numero'],
|
|
30
|
+
phoneNL: ['telefoon', 'bellen', 'mobiel'],
|
|
31
|
+
phoneES: ['telefono', 'llamar', 'movil', 'celular'],
|
|
32
|
+
phoneIT: ['telefono', 'chiamare', 'cellulare'],
|
|
33
|
+
phoneAU: ['phone', 'call', 'mobile'],
|
|
34
|
+
phoneIN: ['phone', 'call', 'mobile'],
|
|
35
|
+
phoneBR: ['telefone', 'ligar', 'celular'],
|
|
36
|
+
phoneCN: ['phone', 'call', 'mobile'],
|
|
37
|
+
phoneKR: ['phone', 'call'],
|
|
38
|
+
phoneRU: ['telefon', 'pozvonit'],
|
|
39
|
+
phoneMX: ['telefono', 'llamar', 'celular'],
|
|
40
|
+
phoneZA: ['phone', 'call', 'mobile'],
|
|
41
|
+
// Identity - person names
|
|
42
|
+
personName: ['name', 'person', 'contact', 'customer', 'client', 'patient', 'employee', 'user', 'Mr', 'Mrs', 'Ms', 'Dr', 'Herr', 'Frau'],
|
|
43
|
+
// Identity - government IDs
|
|
44
|
+
ssn: ['social security', 'SSN', 'social', 'security number', 'Sozialversicherung'],
|
|
45
|
+
itinUS: ['ITIN', 'taxpayer', 'individual taxpayer'],
|
|
46
|
+
sinCA: ['SIN', 'social insurance', 'insurance number'],
|
|
47
|
+
ninoUK: ['national insurance', 'NINO', 'NI number'],
|
|
48
|
+
nhsNumber: ['NHS', 'national health', 'health service', 'patient'],
|
|
49
|
+
passportUS: ['passport', 'travel document', 'Reisepass'],
|
|
50
|
+
passportEU: ['passport', 'travel document', 'Reisepass', 'passeport'],
|
|
51
|
+
passportDE: ['Reisepass', 'passport', 'Passnummer'],
|
|
52
|
+
nationalIdDE: ['Personalausweis', 'Ausweis', 'ID card', 'identity card'],
|
|
53
|
+
taxIdUS: ['EIN', 'employer identification', 'tax ID', 'federal tax'],
|
|
54
|
+
taxIdDE: ['Steuer', 'Identifikationsnummer', 'Finanzamt'],
|
|
55
|
+
sozialversicherungDE: ['Sozialversicherung', 'Rentenversicherung', 'SV-Nummer'],
|
|
56
|
+
ahvCH: ['AHV', 'AVS', 'Sozialversicherung', 'assurance'],
|
|
57
|
+
nationalIdFR: ['securite sociale', 'NIR', 'numero national'],
|
|
58
|
+
codiceFiscaleIT: ['codice fiscale', 'fiscal code', 'CF'],
|
|
59
|
+
dniES: ['DNI', 'documento nacional', 'identidad'],
|
|
60
|
+
nieES: ['NIE', 'extranjero', 'foreigner'],
|
|
61
|
+
peselPL: ['PESEL', 'numer identyfikacyjny'],
|
|
62
|
+
bsnNL: ['BSN', 'burgerservicenummer', 'burger service'],
|
|
63
|
+
ppsIE: ['PPS', 'personal public service'],
|
|
64
|
+
tfnAU: ['TFN', 'tax file', 'tax number'],
|
|
65
|
+
nricSG: ['NRIC', 'identity card', 'IC'],
|
|
66
|
+
panIN: ['PAN', 'permanent account', 'income tax'],
|
|
67
|
+
aadhaarIN: ['Aadhaar', 'UID', 'unique identification'],
|
|
68
|
+
cpfBR: ['CPF', 'cadastro', 'pessoa fisica'],
|
|
69
|
+
hetuFI: ['HETU', 'henkilotunnus', 'personal identity'],
|
|
70
|
+
personnummerSE: ['personnummer', 'personal number'],
|
|
71
|
+
fodselsnummerNO: ['fodselsnummer', 'personal number'],
|
|
72
|
+
cprDK: ['CPR', 'personnummer'],
|
|
73
|
+
nifPT: ['NIF', 'numero fiscal', 'contribuinte'],
|
|
74
|
+
nationalIdCN: ['identity card', 'resident card', 'shenfenzheng'],
|
|
75
|
+
nationalIdTR: ['TC Kimlik', 'kimlik no', 'identity'],
|
|
76
|
+
curpMX: ['CURP', 'poblacion'],
|
|
77
|
+
rfcMX: ['RFC', 'registro federal'],
|
|
78
|
+
emiratesIdUAE: ['emirates ID', 'UAE ID'],
|
|
79
|
+
// Financial
|
|
80
|
+
creditCard: ['card', 'credit', 'debit', 'visa', 'mastercard', 'amex', 'payment', 'Kreditkarte', 'carte', 'tarjeta'],
|
|
81
|
+
amex: ['amex', 'american express', 'card', 'payment'],
|
|
82
|
+
iban: ['IBAN', 'bank account', 'Kontonummer', 'compte', 'cuenta', 'conto'],
|
|
83
|
+
bic: ['BIC', 'SWIFT', 'bank', 'routing'],
|
|
84
|
+
vatEU: ['VAT', 'MwSt', 'Umsatzsteuer', 'TVA', 'IVA', 'BTW'],
|
|
85
|
+
abaRouting: ['routing', 'ABA', 'bank', 'transfer'],
|
|
86
|
+
sortCodeUK: ['sort code', 'bank', 'branch'],
|
|
87
|
+
cardExpiry: ['expiry', 'expiration', 'valid', 'exp', 'Gultig'],
|
|
88
|
+
cvvCtx: ['CVV', 'CVC', 'security code', 'verification'],
|
|
89
|
+
bankAccountCtx: ['account', 'bank', 'checking', 'savings', 'Konto'],
|
|
90
|
+
insurancePolicyCtx: ['insurance', 'policy', 'Versicherung', 'assurance', 'polizza'],
|
|
91
|
+
salaryCtx: ['salary', 'compensation', 'pay', 'wage', 'Gehalt', 'salaire'],
|
|
92
|
+
// Network
|
|
93
|
+
ipv4: ['IP', 'address', 'server', 'host', 'network'],
|
|
94
|
+
ipv6: ['IP', 'address', 'server', 'IPv6'],
|
|
95
|
+
macAddress: ['MAC', 'hardware', 'network', 'device', 'adapter'],
|
|
96
|
+
url: ['URL', 'link', 'website', 'http', 'visit'],
|
|
97
|
+
privateIp: ['internal', 'private', 'LAN', 'intranet', 'network'],
|
|
98
|
+
// Location
|
|
99
|
+
addressDE: ['Adresse', 'Anschrift', 'wohnt', 'Straße', 'address'],
|
|
100
|
+
addressLabeledEN: ['address', 'lives', 'located', 'residing'],
|
|
101
|
+
addressLabeledDE: ['Adresse', 'Anschrift', 'wohnt', 'Wohnort'],
|
|
102
|
+
gpsCoordinates: ['GPS', 'coordinates', 'location', 'latitude', 'longitude', 'Standort'],
|
|
103
|
+
// Medical
|
|
104
|
+
medicalRecordNumber: ['MRN', 'medical record', 'patient', 'chart', 'Krankenakte'],
|
|
105
|
+
deaNumber: ['DEA', 'drug enforcement', 'prescriber'],
|
|
106
|
+
npiNumber: ['NPI', 'provider', 'physician', 'doctor'],
|
|
107
|
+
rxNumber: ['prescription', 'Rx', 'pharmacy', 'medication', 'Rezept'],
|
|
108
|
+
healthPlanCtx: ['health plan', 'insurance', 'member', 'subscriber', 'beneficiary'],
|
|
109
|
+
bloodTypeCtx: ['blood', 'type', 'group', 'Blutgruppe'],
|
|
110
|
+
// Enterprise
|
|
111
|
+
awsAccessKey: ['AWS', 'amazon', 'access key', 'credential'],
|
|
112
|
+
gcpApiKey: ['Google', 'GCP', 'API key', 'cloud'],
|
|
113
|
+
stripeKey: ['Stripe', 'payment', 'API key'],
|
|
114
|
+
openaiKey: ['OpenAI', 'API key', 'GPT'],
|
|
115
|
+
githubToken: ['GitHub', 'token', 'personal access'],
|
|
116
|
+
slackToken: ['Slack', 'token', 'bot'],
|
|
117
|
+
jwtToken: ['JWT', 'token', 'auth', 'bearer', 'session'],
|
|
118
|
+
pemPrivateKey: ['private key', 'RSA', 'certificate', 'SSL', 'TLS'],
|
|
119
|
+
sshPublicKey: ['SSH', 'key', 'public key', 'authorized'],
|
|
120
|
+
genericSecret: ['password', 'secret', 'credential', 'API key', 'token'],
|
|
121
|
+
dbConnectionString: ['database', 'connection', 'JDBC', 'mongo', 'postgres', 'mysql'],
|
|
122
|
+
internalHostname: ['server', 'host', 'internal', 'intranet'],
|
|
123
|
+
// Vehicle
|
|
124
|
+
vin: ['VIN', 'vehicle', 'chassis', 'Fahrgestell', 'vehicule'],
|
|
125
|
+
// Crypto
|
|
126
|
+
bitcoinAddress: ['bitcoin', 'BTC', 'wallet', 'crypto'],
|
|
127
|
+
ethereumAddress: ['ethereum', 'ETH', 'wallet', 'crypto', 'contract'],
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Base confidence scores by pattern name.
|
|
131
|
+
* Patterns with checksum validation get higher scores.
|
|
132
|
+
*/
|
|
133
|
+
exports.BASE_CONFIDENCE = {
|
|
134
|
+
// Checksum validated = 0.95
|
|
135
|
+
creditCard: 0.95,
|
|
136
|
+
amex: 0.95,
|
|
137
|
+
iban: 0.95,
|
|
138
|
+
nhsNumber: 0.95,
|
|
139
|
+
sinCA: 0.95,
|
|
140
|
+
peselPL: 0.95,
|
|
141
|
+
bsnNL: 0.95,
|
|
142
|
+
nifPT: 0.95,
|
|
143
|
+
tfnAU: 0.95,
|
|
144
|
+
abaRouting: 0.95,
|
|
145
|
+
// Strong format (unique structure) = 0.85
|
|
146
|
+
email: 0.90,
|
|
147
|
+
codiceFiscaleIT: 0.90,
|
|
148
|
+
hetuFI: 0.90,
|
|
149
|
+
ahvCH: 0.90,
|
|
150
|
+
nricSG: 0.90,
|
|
151
|
+
panIN: 0.90,
|
|
152
|
+
passportUS: 0.85,
|
|
153
|
+
passportEU: 0.85,
|
|
154
|
+
passportDE: 0.85,
|
|
155
|
+
ssn: 0.85,
|
|
156
|
+
ninoUK: 0.85,
|
|
157
|
+
dniES: 0.85,
|
|
158
|
+
nieES: 0.85,
|
|
159
|
+
nationalIdCN: 0.85,
|
|
160
|
+
nationalIdTH: 0.85,
|
|
161
|
+
emiratesIdUAE: 0.85,
|
|
162
|
+
curpMX: 0.85,
|
|
163
|
+
bitcoinAddress: 0.85,
|
|
164
|
+
ethereumAddress: 0.85,
|
|
165
|
+
vin: 0.85,
|
|
166
|
+
jwtToken: 0.85,
|
|
167
|
+
awsAccessKey: 0.90,
|
|
168
|
+
gcpApiKey: 0.90,
|
|
169
|
+
stripeKey: 0.90,
|
|
170
|
+
openaiKey: 0.90,
|
|
171
|
+
githubToken: 0.90,
|
|
172
|
+
pemPrivateKey: 0.90,
|
|
173
|
+
sshPublicKey: 0.90,
|
|
174
|
+
// Medium format = 0.70
|
|
175
|
+
phone: 0.70,
|
|
176
|
+
phoneDE: 0.75,
|
|
177
|
+
phoneUK: 0.75,
|
|
178
|
+
phoneAT: 0.75,
|
|
179
|
+
phoneCH: 0.75,
|
|
180
|
+
phoneFR: 0.75,
|
|
181
|
+
personName: 0.80,
|
|
182
|
+
ipv4: 0.70,
|
|
183
|
+
ipv6: 0.80,
|
|
184
|
+
macAddress: 0.80,
|
|
185
|
+
url: 0.75,
|
|
186
|
+
vatEU: 0.80,
|
|
187
|
+
bic: 0.65,
|
|
188
|
+
// Broad patterns (high false positive risk) = 0.50-0.60
|
|
189
|
+
postalCodeDE: 0.40,
|
|
190
|
+
postalCodeAT: 0.40,
|
|
191
|
+
postalCodeCH: 0.40,
|
|
192
|
+
postalCodeUS: 0.50,
|
|
193
|
+
postalCodeIT: 0.40,
|
|
194
|
+
postalCodeES: 0.50,
|
|
195
|
+
postalCodeIN: 0.40,
|
|
196
|
+
dateSlash: 0.50,
|
|
197
|
+
dateDash: 0.50,
|
|
198
|
+
dateDot: 0.50,
|
|
199
|
+
sortCodeUK: 0.50,
|
|
200
|
+
cardExpiry: 0.50,
|
|
201
|
+
socialHandle: 0.60,
|
|
202
|
+
licensePlateDE: 0.55,
|
|
203
|
+
dnaSequence: 0.50,
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Default base confidence for patterns not in the BASE_CONFIDENCE map.
|
|
207
|
+
*/
|
|
208
|
+
exports.DEFAULT_BASE_CONFIDENCE = 0.65;
|
|
209
|
+
/**
|
|
210
|
+
* Confidence boost when context words are found nearby.
|
|
211
|
+
*/
|
|
212
|
+
exports.CONTEXT_BOOST = 0.20;
|
|
213
|
+
/**
|
|
214
|
+
* Confidence score for semantic field-name matches.
|
|
215
|
+
*/
|
|
216
|
+
exports.SEMANTIC_CONFIDENCE = 0.90;
|
|
217
|
+
/**
|
|
218
|
+
* Confidence score for context-aware ambiguous field matches.
|
|
219
|
+
*/
|
|
220
|
+
exports.AMBIGUOUS_FIELD_CONFIDENCE = 0.85;
|
|
221
|
+
/**
|
|
222
|
+
* Confidence score for custom pattern matches.
|
|
223
|
+
*/
|
|
224
|
+
exports.CUSTOM_PATTERN_CONFIDENCE = 0.70;
|
|
225
|
+
/**
|
|
226
|
+
* Confidence score for deny list matches.
|
|
227
|
+
*/
|
|
228
|
+
exports.DENY_LIST_CONFIDENCE = 1.0;
|
|
229
|
+
/**
|
|
230
|
+
* Window size (characters) to search for context words around a match.
|
|
231
|
+
*/
|
|
232
|
+
exports.CONTEXT_WINDOW = 80;
|
|
233
|
+
/**
|
|
234
|
+
* Check if any context words appear near a match position in the text.
|
|
235
|
+
*/
|
|
236
|
+
function hasContextWords(text, matchStart, matchEnd, words) {
|
|
237
|
+
if (!words || words.length === 0)
|
|
238
|
+
return false;
|
|
239
|
+
const windowStart = Math.max(0, matchStart - exports.CONTEXT_WINDOW);
|
|
240
|
+
const windowEnd = Math.min(text.length, matchEnd + exports.CONTEXT_WINDOW);
|
|
241
|
+
const surrounding = text.slice(windowStart, windowEnd).toLowerCase();
|
|
242
|
+
return words.some((word) => surrounding.includes(word.toLowerCase()));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Calculate confidence score for a pattern match.
|
|
246
|
+
*/
|
|
247
|
+
function calculateConfidence(patternName, hasValidator, validatorPassed, text, matchStart, matchEnd) {
|
|
248
|
+
// Start with base confidence
|
|
249
|
+
let confidence = exports.BASE_CONFIDENCE[patternName] ?? exports.DEFAULT_BASE_CONFIDENCE;
|
|
250
|
+
// If has validator and passed, boost to at least 0.95
|
|
251
|
+
if (hasValidator && validatorPassed) {
|
|
252
|
+
confidence = Math.max(confidence, 0.95);
|
|
253
|
+
}
|
|
254
|
+
// Check context words
|
|
255
|
+
const contextWords = exports.CONTEXT_WORDS[patternName];
|
|
256
|
+
if (contextWords && hasContextWords(text, matchStart, matchEnd, contextWords)) {
|
|
257
|
+
confidence = Math.min(1.0, confidence + exports.CONTEXT_BOOST);
|
|
258
|
+
}
|
|
259
|
+
return Math.round(confidence * 100) / 100;
|
|
260
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IVault } from './vault';
|
|
2
|
+
import { RedactionContext, RedactionHit, RedactionReport } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Main redaction engine - recursively walks JSON and applies redaction.
|
|
5
|
+
* Uses BOTH semantic field-name detection AND regex pattern matching.
|
|
6
|
+
*/
|
|
7
|
+
export declare function redactValue(value: unknown, ctx: RedactionContext, vault: IVault, sessionId: string, hits: RedactionHit[], itemIndex: number, currentPath?: string, siblingKeys?: string[]): unknown;
|
|
8
|
+
/**
|
|
9
|
+
* Restore redacted tokens back to original values.
|
|
10
|
+
* Uses single-pass replacement to avoid infinite loops.
|
|
11
|
+
*/
|
|
12
|
+
export declare function restoreValue(value: unknown, vault: IVault, sessionId: string): unknown;
|
|
13
|
+
/**
|
|
14
|
+
* Build a redaction audit report from collected hits.
|
|
15
|
+
* SECURITY: Original PII values are NEVER included in the report.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildReport(sessionId: string, hits: RedactionHit[]): RedactionReport;
|