euredact 0.5.1 → 0.6.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.
- package/README.md +262 -13
- package/dist/cjs/cloud/client.js +7 -4
- package/dist/cjs/rules/countries/index.js +75 -38
- package/dist/cjs/rules/suppressors.js +130 -5
- package/dist/cjs/rules/validators.js +53 -0
- package/dist/cjs/sdk.js +133 -38
- package/dist/esm/cloud/client.d.ts +7 -4
- package/dist/esm/cloud/client.js +7 -4
- package/dist/esm/rules/countries/index.js +75 -38
- package/dist/esm/rules/suppressors.js +130 -5
- package/dist/esm/rules/validators.js +53 -0
- package/dist/esm/sdk.d.ts +12 -15
- package/dist/esm/sdk.js +133 -38
- package/package.json +1 -1
|
@@ -105,18 +105,50 @@ const SECRET_CONTEXT = [
|
|
|
105
105
|
// ---------------------------------------------------------------------------
|
|
106
106
|
// DOB context keywords (reused across SHARED patterns)
|
|
107
107
|
// ---------------------------------------------------------------------------
|
|
108
|
+
// Every way the documents this engine sees introduce a date of birth.
|
|
109
|
+
//
|
|
110
|
+
// DOB recall was 62.8% -- 33,441 unmasked birth dates -- because this covered
|
|
111
|
+
// seven languages of thirty-one. Eleven countries scored exactly 100% (the
|
|
112
|
+
// listed languages) while twenty sat at 42-51%, which is what proved it was
|
|
113
|
+
// coverage rather than capability (rules-engine#38).
|
|
114
|
+
//
|
|
115
|
+
// Matching is substring, not word-boundary, so every entry was screened against
|
|
116
|
+
// the 29.2-million-character corpus for occurrences inside a longer word. Two
|
|
117
|
+
// were rejected: `fædd` lives inside `fæddur`, and `born` was found inside
|
|
118
|
+
// `gabornagy` and `gabornemeth` -- Hungarian names as e-mail local parts -- so
|
|
119
|
+
// `"born "` with the trailing space is listed instead.
|
|
120
|
+
//
|
|
121
|
+
// DOB_CONTEXT is gone: it was a second, shorter copy missing `nato il`,
|
|
122
|
+
// `nascido`, `geburtstag` and more, so ISO dates were gated on fewer keywords
|
|
123
|
+
// than DD/MM/YYYY ones for no stated reason.
|
|
108
124
|
const DOB_CONTEXT = [
|
|
125
|
+
// Germanic / Romance -- the original set, kept verbatim.
|
|
109
126
|
"geboren", "geboortedatum", "date de naissance", "né le", "née le",
|
|
110
127
|
"né(e) le", "nee le", "nee(e) le", "date of birth", "DOB",
|
|
111
128
|
"Geburtsdatum", "geboren am", "geboren op", "nascido", "nacido",
|
|
112
129
|
"data di nascita", "nato il", "nata il", "geb.", "geb.datum", "geb ",
|
|
113
130
|
"birth date", "birthday", "naissance", "geboorte", "geburtstag",
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
|
|
131
|
+
// English and Italian forms the list was missing.
|
|
132
|
+
"born ", "d.o.b", "nato/a il",
|
|
133
|
+
// Nordic.
|
|
134
|
+
"født", "fødselsdato", "född", "födelsedatum",
|
|
135
|
+
"syntynyt", "syntymäaika", "fæddur", "fæðingardagur",
|
|
136
|
+
// Greek -- Greece and Cyprus.
|
|
137
|
+
"γεννηθείς", "γεννήθηκε", "ημερομηνία γέννησης",
|
|
138
|
+
// West Slavic.
|
|
139
|
+
"urodzony", "urodzona", "data urodzenia",
|
|
140
|
+
"narozen", "datum narození", "narodený", "dátum narodenia",
|
|
141
|
+
// Hungarian, Romanian, Bulgarian.
|
|
142
|
+
"született", "születési", "născut", "născută", "data nașterii",
|
|
143
|
+
"роден", "родена", "дата на раждане",
|
|
144
|
+
// South Slavic.
|
|
145
|
+
"rođen", "rođena", "datum rođenja", "rojen", "rojena", "datum rojstva",
|
|
146
|
+
// Baltic.
|
|
147
|
+
"sündinud", "sünniaeg", "dzimis", "dzimusi", "dzimšanas datums",
|
|
148
|
+
"gimęs", "gimusi", "gimimo data",
|
|
149
|
+
// Maltese, Irish, Turkish (Cyprus).
|
|
150
|
+
"twieled", "twieldet", "data tat-twelid",
|
|
151
|
+
"rugadh", "dáta breithe", "doğum tarihi",
|
|
120
152
|
];
|
|
121
153
|
const DATE_OF_DEATH_CONTEXT = [
|
|
122
154
|
"overleden", "overlijdensdatum", "date de décès", "décédé le",
|
|
@@ -223,7 +255,12 @@ const SHARED = {
|
|
|
223
255
|
// --- Secret / API Key (connection strings with embedded credentials) ---
|
|
224
256
|
p(types_js_1.EntityType.SECRET, String.raw `(?:mongodb|mysql|postgres(?:ql)?|redis|amqp|rabbitmq):\/\/[^\s:]{1,256}:[^\s@]{1,256}@[^\s]+`, null, "Connection string with credentials"),
|
|
225
257
|
// --- Secret / API Key (assignment-based: KEY=value or KEY: value) ---
|
|
226
|
-
|
|
258
|
+
// The final character may not be sentence punctuation. `\S` does not stop
|
|
259
|
+
// at a full stop, so "Reisepass: CA1234567." was claimed as the
|
|
260
|
+
// ten-character span "CA1234567." -- a passport number plus the sentence's
|
|
261
|
+
// period, masked as a credential because a "credentials" word sat within
|
|
262
|
+
// the context window (rules-engine#35).
|
|
263
|
+
p(types_js_1.EntityType.SECRET, String.raw `(?<=[:=] )\S{7,}[^\s.,;:!?)'"]|(?<=[:=])\S{7,}[^\s.,;:!?)'"]`, "high_entropy", "Assigned secret value", SECRET_CONTEXT, true),
|
|
227
264
|
// --- Secret / API Key (entropy-based fallback for longer tokens) ---
|
|
228
265
|
// Only the *trailing* \b is replaced. It could not match when the token run
|
|
229
266
|
// ended on "-", "+" or "/", so the engine backtracked across two
|
|
@@ -241,7 +278,7 @@ const SHARED = {
|
|
|
241
278
|
p(types_js_1.EntityType.SECRET, String.raw `\b[A-Za-z0-9_\-+/]{24,}[A-Za-z0-9_\-+/=]*(?![A-Za-z0-9_\-+/=])`, "high_entropy", "High-entropy token", SECRET_CONTEXT, true),
|
|
242
279
|
p(types_js_1.EntityType.DOB, String.raw `\b(?:0[1-9]|[12][0-9]|3[01])[/.\-](?:0[1-9]|1[0-2])[/.\-](?:19|20)\d{2}\b`, null, "DD/MM/YYYY", DOB_CONTEXT, true),
|
|
243
280
|
p(types_js_1.EntityType.DATE_OF_DEATH, String.raw `\b(?:0[1-9]|[12][0-9]|3[01])[/.\-](?:0[1-9]|1[0-2])[/.\-](?:19|20)\d{2}\b`, null, "", DATE_OF_DEATH_CONTEXT, true),
|
|
244
|
-
p(types_js_1.EntityType.DOB, String.raw `\b(?:19|20)\d{2}[/.\-](?:0[1-9]|1[0-2])[/.\-](?:0[1-9]|[12][0-9]|3[01])\b`, null, "ISO YYYY-MM-DD",
|
|
281
|
+
p(types_js_1.EntityType.DOB, String.raw `\b(?:19|20)\d{2}[/.\-](?:0[1-9]|1[0-2])[/.\-](?:0[1-9]|[12][0-9]|3[01])\b`, null, "ISO YYYY-MM-DD", DOB_CONTEXT, true),
|
|
245
282
|
],
|
|
246
283
|
};
|
|
247
284
|
const NL = {
|
|
@@ -251,7 +288,7 @@ const NL = {
|
|
|
251
288
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[0-9]{9}\b`, "bsn"),
|
|
252
289
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[0-9]{2,4}\.[0-9]{2,4}\.[0-9]{2,4}\b`, "bsn"),
|
|
253
290
|
p(types_js_1.EntityType.IBAN, String.raw `\bNL\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
|
|
254
|
-
p(types_js_1.EntityType.VAT, String.raw `\bNL\d{9}B\d{2}\b`, "vat_nl"),
|
|
291
|
+
p(types_js_1.EntityType.VAT, String.raw `\bNL[\s.]?\d{9}B\d{2}\b`, "vat_nl"),
|
|
255
292
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{8}\b`, "kvk", "", ["KvK", "Kamer van Koophandel", "kvk-nummer", "KVK-nummer", "handelsregister", "chamber of commerce"], true),
|
|
256
293
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d[\s\-]?\d{3}[\s\-]?\d{4}\b`),
|
|
257
294
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{2}[\s\-]?\d{6,7}\b`),
|
|
@@ -278,7 +315,7 @@ const BE = {
|
|
|
278
315
|
p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
279
316
|
p(types_js_1.EntityType.DRIVERS_LICENSE, String.raw `\b\d{10}\b`, null, "", ["rijbewijs", "permis de conduire", "driving licence", "driving license", "rijbewijsnummer"], true),
|
|
280
317
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[12][\-\s]?[A-Z]{3}[\-\s]?\d{3}\b`),
|
|
281
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["postcode", "code postal", "postnummer", "postal code", "zip", "B-", "adres", "adresse", "wonende", "woonplaats", "rue", "straat", "laan", "avenue", "boulevard", "plein", "steenweg", "chaussée", "domicilié", "gedomicilieerd", "Levering:", "siège"], true),
|
|
318
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["postcode", "code postal", "postnummer", "postal code", "zip", "B-", "adres", "adresse", "wonende", "woonplaats", "domicile", "rue", "straat", "laan", "avenue", "boulevard", "plein", "steenweg", "chaussée", "domicilié", "gedomicilieerd", "Levering:", "siège"], true),
|
|
282
319
|
p(types_js_1.EntityType.POSTAL_CODE, String.raw `(?<=, )[1-9]\d{3}(?= [A-Z])`),
|
|
283
320
|
p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{1}[\-.]?\d{5}[\-.]?\d{2}[\-.]?\d{3}\b`, null, "", ["RIZIV", "INAMI", "arts", "médecin", "zorgverlener", "prestataire", "dokter", "doctor"], true),
|
|
284
321
|
],
|
|
@@ -303,7 +340,7 @@ const DE = {
|
|
|
303
340
|
// separates the city code: "B-AB 1234", "M-XY 99".
|
|
304
341
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-ZÄÖÜ]{1,3}[\s\-][A-Z]{1,2}[\s\-]?\d{1,4}[EH]?\b`),
|
|
305
342
|
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{5}\b`, null, "", ["PLZ", "Postleitzahl", "postal code", "postcode", "Anschrift", "Adresse", "Wohnort", "Ort", "Stadt", "wohnt", "wohnhaft", "Straße", "Str.", "Weg", "Platz", "Allee", "Ring"], true),
|
|
306
|
-
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bHR[AB]\s?\d{4,6}\b
|
|
343
|
+
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bHR[AB]\s?\d{4,6}(?:\s[A-Z]\b)?`),
|
|
307
344
|
p(types_js_1.EntityType.SSN, String.raw `\b\d{2}[\s]?\d{6}[\s]?[A-Z][\s]?\d{3}\b`, null, "", ["Rentenversicherung", "RV-Nummer", "Sozialversicherung", "SV-Nummer", "Versicherungsnummer", "Rentenversicherungsnummer", "Sozialversicherungsnummer", "SVNR", "SV-Nr", "RVNR"], true),
|
|
308
345
|
p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{2,3}/\d{3,4}/\d{4,5}\b`, null, "", ["Steuernummer", "St.-Nr", "StNr", "Finanzamt", "Steuer"], true),
|
|
309
346
|
p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{13}\b`, null, "", ["Steuernummer", "St.-Nr", "StNr", "Finanzamt", "Steuer"], true),
|
|
@@ -316,7 +353,7 @@ const AT = {
|
|
|
316
353
|
patterns: [
|
|
317
354
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{4}\s?\d{6}\b`, "austrian_svnr"),
|
|
318
355
|
p(types_js_1.EntityType.IBAN, String.raw `\bAT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
319
|
-
p(types_js_1.EntityType.VAT, String.raw `\bATU\d{8}\b`),
|
|
356
|
+
p(types_js_1.EntityType.VAT, String.raw `\bATU[\s.]?\d{8}\b`),
|
|
320
357
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{3,4}[\s\-]?\d{5,8}\b`),
|
|
321
358
|
// Short area codes (Vienna is "01") split across three groups:
|
|
322
359
|
// "01 53460 2215". The pattern above requires 3-4 digits after the trunk
|
|
@@ -357,7 +394,7 @@ const FR = {
|
|
|
357
394
|
p(types_js_1.EntityType.PHONE, String.raw `\+33\s?[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
|
|
358
395
|
p(types_js_1.EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
359
396
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]{2}\b`),
|
|
360
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b(?:0[1-9]|[1-8]\d|9[0-5]|97[1-6])\d{3}\b`, null, "", ["code postal", "CP", "postal code", "postcode", "adresse", "domicilié", "résidant", "rue", "avenue", "boulevard", "place", "chemin", "allée", "impasse", "ville"], true),
|
|
397
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b(?:0[1-9]|[1-8]\d|9[0-5]|97[1-6])\d{3}\b`, null, "", ["code postal", "CP", "postal code", "postcode", "adresse", "domicile", "domicilié", "résidant", "rue", "avenue", "boulevard", "place", "chemin", "allée", "impasse", "ville"], true),
|
|
361
398
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{3}\s?\d{3}\s?\d{3}\b`, null, "", ["SIREN", "siren", "RCS", "entreprise", "immatricul", "numéro d'entreprise", "registre du commerce"], true),
|
|
362
399
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{3}\s?\d{3}\s?\d{3}\s?\d{5}\b`, null, "", ["SIRET", "siret", "établissement", "immatricul"], true),
|
|
363
400
|
p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{13}\b`, null, "", ["numéro fiscal", "SPI", "référence fiscale", "avis d'impôt", "impôt sur le revenu", "déclaration fiscale", "fiscal"], true),
|
|
@@ -390,9 +427,9 @@ const SE = {
|
|
|
390
427
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,3}[\-\s]?\d{2,3}[\s]?\d{2,3}[\s]?\d{2}\b`),
|
|
391
428
|
p(types_js_1.EntityType.PHONE, String.raw `\+46\s?\d{1,3}[\s\-]?\d{2,3}[\s\-]?\d{2,3}[\s\-]?\d{2}`),
|
|
392
429
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{3}\s?\d{2}[A-Z0-9]\b`),
|
|
393
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé", "plats", "torg", "stigen", "Bostadsadress"], true),
|
|
394
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\s\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
|
|
395
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{6}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
|
|
430
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé", "plats", "torg", "stigen", "Bostadsadress"], true),
|
|
431
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\s\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
|
|
432
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{6}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
|
|
396
433
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{6}-?\d{4}\b`, null, "", ["organisationsnummer", "org.nr", "org nr", "Bolagsverket", "registreringsnummer"], true),
|
|
397
434
|
],
|
|
398
435
|
};
|
|
@@ -408,7 +445,7 @@ const NO = {
|
|
|
408
445
|
p(types_js_1.EntityType.PHONE, String.raw `\b[2-9]\d\s?\d{2}\s?\d{2}\s?\d{2}\b`),
|
|
409
446
|
p(types_js_1.EntityType.PHONE, String.raw `\+47\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}`),
|
|
410
447
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}\s?\d{5}\b`),
|
|
411
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\b`, null, "", ["postnummer", "poststed", "postnr", "adresse", "bostedsadresse", "gate", "vei", "veien", "gata", "plass", "stien", "allé"], true),
|
|
448
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\b`, null, "", ["postnummer", "poststed", "postnr", "adresse", "bostedsadresse", "gate", "vei", "veien", "gata", "plass", "stien", "allé", "Postal:", "Address:"], true),
|
|
412
449
|
],
|
|
413
450
|
};
|
|
414
451
|
const FI = {
|
|
@@ -417,7 +454,7 @@ const FI = {
|
|
|
417
454
|
patterns: [
|
|
418
455
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}[-+ABCDEFYXWVU]\d{3}[0-9A-FHJK-NPR-Y]\b`, "finnish_hetu"),
|
|
419
456
|
p(types_js_1.EntityType.IBAN, String.raw `\bFI\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
|
|
420
|
-
p(types_js_1.EntityType.VAT, String.raw `\bFI\d{8}\b`),
|
|
457
|
+
p(types_js_1.EntityType.VAT, String.raw `\bFI[\s.]?\d{8}\b`),
|
|
421
458
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{7}-\d\b`, "finnish_business_id"),
|
|
422
459
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,2}\s?\d{3,4}\s?\d{3,4}\b`),
|
|
423
460
|
p(types_js_1.EntityType.PHONE, String.raw `\+358\s?\d{1,3}[\s\-]?\d{3,4}[\s\-]?\d{3,4}`),
|
|
@@ -435,7 +472,7 @@ const IS = {
|
|
|
435
472
|
p(types_js_1.EntityType.PHONE, String.raw `\b[3-9]\d{6}\b`, null, "", ["sími", "símanúmer", "farsími", "gsm", "phone", "tel", "hringja", "hringdu", "ná í", "nás á", "nás", "SMS", "sent to"], true),
|
|
436
473
|
p(types_js_1.EntityType.PHONE, String.raw `\+354\s?\d{3}\s?\d{4}`),
|
|
437
474
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}\s?\d{3}\b`, null, "", ["skráningarnúmer", "bílnúmer", "ökutæki", "bifreið", "plata"], true),
|
|
438
|
-
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{2}\b`, null, "", ["póstnúmer", "póstfang", "staður", "heimilisfang"], true),
|
|
475
|
+
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{2}\b`, null, "", ["póstnúmer", "póstfang", "staður", "heimilisfang", "Postal:", "Address:"], true),
|
|
439
476
|
],
|
|
440
477
|
};
|
|
441
478
|
const IT = {
|
|
@@ -445,7 +482,7 @@ const IT = {
|
|
|
445
482
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[A-Z]{6}\d{2}[ABCDEHLMPRST]\d{2}[A-Z]\d{3}[A-Z]\b`, "italian_cf"),
|
|
446
483
|
p(types_js_1.EntityType.IBAN, String.raw `\bIT\d{2}\s?[A-Z]\d{3}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
|
|
447
484
|
p(types_js_1.EntityType.IBAN, String.raw `\bIT\d{2}[A-Z]\d{22}\b`, "iban"),
|
|
448
|
-
p(types_js_1.EntityType.VAT, String.raw `\bIT\d{11}\b`),
|
|
485
|
+
p(types_js_1.EntityType.VAT, String.raw `\bIT[\s.]?\d{11}\b`),
|
|
449
486
|
p(types_js_1.EntityType.PHONE, String.raw `\b3\d{2}[\s\-]?\d{3}[\s\-]?\d{4}\b`),
|
|
450
487
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,3}[\s\-]?\d{6,8}\b`),
|
|
451
488
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,2}[\s\-]\d{3}[\s\-]\d{4}\b`),
|
|
@@ -461,7 +498,7 @@ const ES = {
|
|
|
461
498
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{8}[A-Z]\b`, "spanish_dni"),
|
|
462
499
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[XYZ]\d{7}[A-Z]\b`, "spanish_nie"),
|
|
463
500
|
p(types_js_1.EntityType.IBAN, String.raw `\bES\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
464
|
-
p(types_js_1.EntityType.VAT, String.raw `\bES[A-Z0-9]\d{7}[A-Z0-9]\b`),
|
|
501
|
+
p(types_js_1.EntityType.VAT, String.raw `\bES[\s.]?[A-Z0-9]\d{7}[A-Z0-9]\b`),
|
|
465
502
|
p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
466
503
|
p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d{8}\b`),
|
|
467
504
|
p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
|
|
@@ -484,7 +521,7 @@ const PT = {
|
|
|
484
521
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-35-9]\d{8}\b`, "portuguese_nif"),
|
|
485
522
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-35-9]\d{2}[\s.]\d{3}[\s.]\d{3}\b`, "portuguese_nif"),
|
|
486
523
|
p(types_js_1.EntityType.IBAN, String.raw `\bPT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{5}\b`, "iban"),
|
|
487
|
-
p(types_js_1.EntityType.VAT, String.raw `\bPT\d{9}\b`),
|
|
524
|
+
p(types_js_1.EntityType.VAT, String.raw `\bPT[\s.]?\d{9}\b`),
|
|
488
525
|
p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
489
526
|
p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{8}\b`),
|
|
490
527
|
p(types_js_1.EntityType.PHONE, String.raw `\+351\s?[29]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
|
|
@@ -517,7 +554,7 @@ const PL = {
|
|
|
517
554
|
p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{3}-?\d{3}-?\d{2}-?\d{2}\b`, "polish_nip"),
|
|
518
555
|
p(types_js_1.EntityType.IBAN, String.raw `\bPL\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
519
556
|
p(types_js_1.EntityType.IBAN, String.raw `\bPL\d{26}\b`, "iban"),
|
|
520
|
-
p(types_js_1.EntityType.VAT, String.raw `\bPL\d{10}\b`),
|
|
557
|
+
p(types_js_1.EntityType.VAT, String.raw `\bPL[\s.]?\d{10}\b`),
|
|
521
558
|
p(types_js_1.EntityType.PHONE, String.raw `\b[5-8]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
522
559
|
p(types_js_1.EntityType.PHONE, String.raw `\b[5-8]\d{8}\b`),
|
|
523
560
|
p(types_js_1.EntityType.PHONE, String.raw `\+48\s?[5-8]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
|
|
@@ -531,7 +568,7 @@ const IE = {
|
|
|
531
568
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{7}[A-W][ABWTXZ]?\b`, "irish_pps"),
|
|
532
569
|
p(types_js_1.EntityType.IBAN, String.raw `\bIE\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
|
|
533
570
|
p(types_js_1.EntityType.IBAN, String.raw `\bIE\d{2}[A-Z]{4}\d{14}\b`, "iban"),
|
|
534
|
-
p(types_js_1.EntityType.VAT, String.raw `\bIE\d{7}[A-Z]\b`),
|
|
571
|
+
p(types_js_1.EntityType.VAT, String.raw `\bIE[\s.]?\d{7}[A-Z]\b`),
|
|
535
572
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[89]\d[\s\-]?\d{3}[\s\-]?\d{4}\b`),
|
|
536
573
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[89]\d{8}\b`),
|
|
537
574
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{0,2}[\s\-]?\d{5,8}\b`),
|
|
@@ -547,7 +584,7 @@ const UK = {
|
|
|
547
584
|
p(types_js_1.EntityType.HEALTH_INSURANCE, String.raw `\b\d{3}\s?\d{3}\s?\d{4}\b`, "uk_nhs", "", ["NHS", "NHS number", "health number", "NHS no"], true),
|
|
548
585
|
p(types_js_1.EntityType.IBAN, String.raw `\bGB\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
|
|
549
586
|
p(types_js_1.EntityType.IBAN, String.raw `\bGB\d{2}[A-Z]{4}\d{14}\b`, "iban"),
|
|
550
|
-
p(types_js_1.EntityType.VAT, String.raw `\bGB\d{
|
|
587
|
+
p(types_js_1.EntityType.VAT, String.raw `\bGB[\s.]?\d{3}[\s.]?\d{4}[\s.]?\d{2}\b`),
|
|
551
588
|
p(types_js_1.EntityType.PHONE, String.raw `\b07[4-9]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
552
589
|
p(types_js_1.EntityType.PHONE, String.raw `\b07[4-9]\d{8}\b`),
|
|
553
590
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[12]\d{2}[\s\-]?\d{3}[\s\-]?\d{4}\b`),
|
|
@@ -563,7 +600,7 @@ const BG = {
|
|
|
563
600
|
patterns: [
|
|
564
601
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{10}\b`, "bulgarian_egn"),
|
|
565
602
|
p(types_js_1.EntityType.IBAN, String.raw `\bBG\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
|
|
566
|
-
p(types_js_1.EntityType.VAT, String.raw `\bBG\d{9,10}\b`),
|
|
603
|
+
p(types_js_1.EntityType.VAT, String.raw `\bBG[\s.]?\d{9,10}\b`),
|
|
567
604
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?[89][789][\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
|
|
568
605
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?[89][789]\d{7,8}\b`),
|
|
569
606
|
p(types_js_1.EntityType.PHONE, String.raw `\+359\s?[89][789][\s\-]?\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -578,7 +615,7 @@ const CY = {
|
|
|
578
615
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{8}[A-Z]\b`),
|
|
579
616
|
p(types_js_1.EntityType.IBAN, String.raw `\bCY\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
580
617
|
p(types_js_1.EntityType.IBAN, String.raw `\bCY\d{26}\b`, "iban"),
|
|
581
|
-
p(types_js_1.EntityType.VAT, String.raw `\bCY\d{8}[A-Z]\b`),
|
|
618
|
+
p(types_js_1.EntityType.VAT, String.raw `\bCY[\s.]?\d{8}[A-Z]\b`),
|
|
582
619
|
p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{3}[\s\-]?\d{4}\b`),
|
|
583
620
|
p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
584
621
|
p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{7}\b`),
|
|
@@ -594,7 +631,7 @@ const CZ = {
|
|
|
594
631
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}/?\d{3,4}\b`, "czech_birth_number"),
|
|
595
632
|
p(types_js_1.EntityType.IBAN, String.raw `\bCZ\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
596
633
|
p(types_js_1.EntityType.IBAN, String.raw `\bCZ\d{22}\b`, "iban"),
|
|
597
|
-
p(types_js_1.EntityType.VAT, String.raw `\bCZ\d{8,10}\b`),
|
|
634
|
+
p(types_js_1.EntityType.VAT, String.raw `\bCZ[\s.]?\d{8,10}\b`),
|
|
598
635
|
p(types_js_1.EntityType.PHONE, String.raw `\b[67]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
599
636
|
p(types_js_1.EntityType.PHONE, String.raw `\b[67]\d{8}\b`),
|
|
600
637
|
p(types_js_1.EntityType.PHONE, String.raw `\+420\s?[67]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
|
|
@@ -608,7 +645,7 @@ const SK = {
|
|
|
608
645
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}/?\d{3,4}\b`, "czech_birth_number"),
|
|
609
646
|
p(types_js_1.EntityType.IBAN, String.raw `\bSK\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
610
647
|
p(types_js_1.EntityType.IBAN, String.raw `\bSK\d{22}\b`, "iban"),
|
|
611
|
-
p(types_js_1.EntityType.VAT, String.raw `\bSK\d{10}\b`),
|
|
648
|
+
p(types_js_1.EntityType.VAT, String.raw `\bSK[\s.]?\d{10}\b`),
|
|
612
649
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?9\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
613
650
|
p(types_js_1.EntityType.PHONE, String.raw `\+421\s?9\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
|
|
614
651
|
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["PSČ", "poštové", "adresa", "ulica", "Postal:", "Address:"], true),
|
|
@@ -621,7 +658,7 @@ const EE = {
|
|
|
621
658
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-6]\d{10}\b`, "estonian_id"),
|
|
622
659
|
p(types_js_1.EntityType.IBAN, String.raw `\bEE\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
623
660
|
p(types_js_1.EntityType.IBAN, String.raw `\bEE\d{18}\b`, "iban"),
|
|
624
|
-
p(types_js_1.EntityType.VAT, String.raw `\bEE\d{9}\b`),
|
|
661
|
+
p(types_js_1.EntityType.VAT, String.raw `\bEE[\s.]?\d{9}\b`),
|
|
625
662
|
p(types_js_1.EntityType.PHONE, String.raw `\b5\d{3}[\s\-]?\d{4}\b`),
|
|
626
663
|
p(types_js_1.EntityType.PHONE, String.raw `\b5\d{7}\b`),
|
|
627
664
|
p(types_js_1.EntityType.PHONE, String.raw `\+372\s?5\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -635,7 +672,7 @@ const LT = {
|
|
|
635
672
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-6]\d{10}\b`, "estonian_id"),
|
|
636
673
|
p(types_js_1.EntityType.IBAN, String.raw `\bLT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
637
674
|
p(types_js_1.EntityType.IBAN, String.raw `\bLT\d{18}\b`, "iban"),
|
|
638
|
-
p(types_js_1.EntityType.VAT, String.raw `\bLT\d{9,12}\b`),
|
|
675
|
+
p(types_js_1.EntityType.VAT, String.raw `\bLT[\s.]?\d{9,12}\b`),
|
|
639
676
|
p(types_js_1.EntityType.PHONE, String.raw `\b6\d{2}[\s\-]?\d{2}[\s\-]?\d{3}\b`),
|
|
640
677
|
p(types_js_1.EntityType.PHONE, String.raw `\b6\d{7}\b`),
|
|
641
678
|
p(types_js_1.EntityType.PHONE, String.raw `\b8[\s\-]?6\d{2}[\s\-]?\d{2}[\s\-]?\d{3}\b`),
|
|
@@ -651,7 +688,7 @@ const LV = {
|
|
|
651
688
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b(?:0[1-9]|[12]\d|3[01])(?:0[1-9]|1[0-2])\d{7}\b`, null, "", ["personas kods", "PK", "identifikācijas"], true),
|
|
652
689
|
p(types_js_1.EntityType.IBAN, String.raw `\bLV\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d\b`, "iban"),
|
|
653
690
|
p(types_js_1.EntityType.IBAN, String.raw `\bLV\d{2}[A-Z]{4}\d{13}\b`, "iban"),
|
|
654
|
-
p(types_js_1.EntityType.VAT, String.raw `\bLV\d{11}\b`),
|
|
691
|
+
p(types_js_1.EntityType.VAT, String.raw `\bLV[\s.]?\d{11}\b`),
|
|
655
692
|
p(types_js_1.EntityType.PHONE, String.raw `\b[2]\d{3}[\s\-]?\d{4}\b`),
|
|
656
693
|
p(types_js_1.EntityType.PHONE, String.raw `\b[2]\d{7}\b`),
|
|
657
694
|
p(types_js_1.EntityType.PHONE, String.raw `\+371\s?[2]\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -666,7 +703,7 @@ const HR = {
|
|
|
666
703
|
p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
|
|
667
704
|
p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{2}\s\d{4}\s\d{4}\s\d{4}\s\d{5}\b`, "iban"),
|
|
668
705
|
p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{19}\b`, "iban"),
|
|
669
|
-
p(types_js_1.EntityType.VAT, String.raw `\bHR\d{11}\b`),
|
|
706
|
+
p(types_js_1.EntityType.VAT, String.raw `\bHR[\s.]?\d{11}\b`),
|
|
670
707
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?9[12579][\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
|
|
671
708
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?9[12579]\d{7,8}\b`),
|
|
672
709
|
p(types_js_1.EntityType.PHONE, String.raw `\+385\s?9[12579][\s\-]?\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -680,7 +717,7 @@ const HU = {
|
|
|
680
717
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{9}\b`, "hungarian_taj"),
|
|
681
718
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{3}\s\d{3}\s\d{3}\b`, "hungarian_taj"),
|
|
682
719
|
p(types_js_1.EntityType.TAX_ID, String.raw `\b8\d{9}\b`, null, "", ["adóazonosító", "adószám", "adóazonosító jel"], true),
|
|
683
|
-
p(types_js_1.EntityType.VAT, String.raw `\bHU\d{8,11}\b`, null, "", ["adószám", "ÁFA"]),
|
|
720
|
+
p(types_js_1.EntityType.VAT, String.raw `\bHU[\s.]?\d{8,11}\b`, null, "", ["adószám", "ÁFA"]),
|
|
684
721
|
p(types_js_1.EntityType.IBAN, String.raw `\bHU\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
|
|
685
722
|
p(types_js_1.EntityType.IBAN, String.raw `\bHU\d{26}\b`, "iban"),
|
|
686
723
|
p(types_js_1.EntityType.PHONE, String.raw `\b06\s?\d{1,2}[\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
|
|
@@ -695,7 +732,7 @@ const RO = {
|
|
|
695
732
|
patterns: [
|
|
696
733
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-8]\d{12}\b`, "romanian_cnp"),
|
|
697
734
|
p(types_js_1.EntityType.IBAN, String.raw `\bRO\d{2}[A-Z]{4}\d{16}\b`, "iban"),
|
|
698
|
-
p(types_js_1.EntityType.VAT, String.raw `\bRO\d{2,10}\b`),
|
|
735
|
+
p(types_js_1.EntityType.VAT, String.raw `\bRO[\s.]?\d{2,10}\b`),
|
|
699
736
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?7[2-9]\d{1}[\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
|
|
700
737
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?7[2-9]\d{7,8}\b`),
|
|
701
738
|
p(types_js_1.EntityType.PHONE, String.raw `\+40\s?7[2-9]\d{1}[\s\-]?\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -710,7 +747,7 @@ const EL = {
|
|
|
710
747
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{11}\b`, "greek_amka"),
|
|
711
748
|
p(types_js_1.EntityType.IBAN, String.raw `\bGR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
|
|
712
749
|
p(types_js_1.EntityType.IBAN, String.raw `\bGR\d{25}\b`, "iban"),
|
|
713
|
-
p(types_js_1.EntityType.VAT, String.raw `\bEL\d{9}\b`),
|
|
750
|
+
p(types_js_1.EntityType.VAT, String.raw `\bEL[\s.]?\d{9}\b`),
|
|
714
751
|
p(types_js_1.EntityType.PHONE, String.raw `\b69\d[\s\-]?\d{3,4}[\s\-]?\d{4}\b`),
|
|
715
752
|
p(types_js_1.EntityType.PHONE, String.raw `\b69\d{8}\b`),
|
|
716
753
|
p(types_js_1.EntityType.PHONE, String.raw `\b2\d{1,3}[\s\-]?\d{3,4}[\s\-]?\d{3,4}\b`),
|
|
@@ -725,7 +762,7 @@ const SI = {
|
|
|
725
762
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{13}\b`, "slovenian_emso"),
|
|
726
763
|
p(types_js_1.EntityType.IBAN, String.raw `\bSI\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
|
|
727
764
|
p(types_js_1.EntityType.IBAN, String.raw `\bSI\d{17}\b`, "iban"),
|
|
728
|
-
p(types_js_1.EntityType.VAT, String.raw `\bSI\d{8}\b`),
|
|
765
|
+
p(types_js_1.EntityType.VAT, String.raw `\bSI[\s.]?\d{8}\b`),
|
|
729
766
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?[34567]\d{1}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
|
|
730
767
|
p(types_js_1.EntityType.PHONE, String.raw `\b0?[34567]\d{7}\b`),
|
|
731
768
|
p(types_js_1.EntityType.PHONE, String.raw `\+386\s?[34567]\d{1}[\s\-]?\d{3}[\s\-]?\d{3}`),
|
|
@@ -740,7 +777,7 @@ const MT = {
|
|
|
740
777
|
p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{5,7}[MGAPLHBZ]\b`),
|
|
741
778
|
p(types_js_1.EntityType.IBAN, String.raw `\bMT\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{3}\b`, "iban"),
|
|
742
779
|
p(types_js_1.EntityType.IBAN, String.raw `\bMT\d{2}[A-Z]{4}\d{5}[A-Z0-9]{18}\b`, "iban"),
|
|
743
|
-
p(types_js_1.EntityType.VAT, String.raw `\bMT\d{8}\b`),
|
|
780
|
+
p(types_js_1.EntityType.VAT, String.raw `\bMT[\s.]?\d{8}\b`),
|
|
744
781
|
p(types_js_1.EntityType.PHONE, String.raw `\b[79]\d{3}[\s\-]?\d{4}\b`),
|
|
745
782
|
p(types_js_1.EntityType.PHONE, String.raw `\b[79]\d{7}\b`),
|
|
746
783
|
p(types_js_1.EntityType.PHONE, String.raw `\+356\s?[79]\d{3}[\s\-]?\d{3,4}`),
|
|
@@ -313,9 +313,85 @@ function suppressSequential(_text, match) {
|
|
|
313
313
|
const clean = match.text.replace(/[\s.\-]/g, "");
|
|
314
314
|
return SEQUENTIAL_PATTERNS.test(clean);
|
|
315
315
|
}
|
|
316
|
+
// ── The engine's own output ───────────────────────────────────────────────
|
|
317
|
+
//
|
|
318
|
+
// Redaction has to be idempotent: running it over a document it already
|
|
319
|
+
// produced must leave the placeholders it wrote alone. It did not.
|
|
320
|
+
// `[POSTAL_CODE]` is thirteen characters of mixed case with an underscore, so
|
|
321
|
+
// the entropy-based SECRET rule read it as a credential and a second pass
|
|
322
|
+
// replaced it with `[SECRET]` (rules-engine#33).
|
|
323
|
+
//
|
|
324
|
+
// All three emitted forms are guarded, because all three come back as input in
|
|
325
|
+
// a re-processing pipeline: `[TYPE]`, `TYPE_1` (referentialIntegrity) and
|
|
326
|
+
// `TYPE_K7Q2` (tokenize -- where a false detection breaks restore()).
|
|
327
|
+
//
|
|
328
|
+
// Only real entity-type names count. Guarding any bracketed upper-case token
|
|
329
|
+
// would be simpler and wrong: `[AKIAIOSFODNN7EXAMPLE]` is one, and also a live
|
|
330
|
+
// AWS key. A placeholder for a *custom* pattern is therefore not covered.
|
|
331
|
+
// The closing bracket is optional: another pattern's span need not agree with
|
|
332
|
+
// this one about where the marker ends. The assigned-secret rule stops before
|
|
333
|
+
// sentence punctuation (rules-engine#35), so it claims "[POSTAL_CODE" without
|
|
334
|
+
// the "]". The opening bracket is still required.
|
|
335
|
+
const PLACEHOLDER = /^(?:\[([A-Z][A-Z0-9_]*)\]?|([A-Z][A-Z0-9_]*?)_(?:\d+|[A-HJ-NP-Z2-9]{4}))$/;
|
|
336
|
+
const KNOWN_TYPE_NAMES = new Set(Object.values(types_js_1.EntityType));
|
|
337
|
+
function suppressRedactionPlaceholder(_text, match) {
|
|
338
|
+
const found = PLACEHOLDER.exec(match.text);
|
|
339
|
+
if (found === null)
|
|
340
|
+
return false;
|
|
341
|
+
const name = found[1] ?? found[2];
|
|
342
|
+
return KNOWN_TYPE_NAMES.has(name);
|
|
343
|
+
}
|
|
316
344
|
const RECENT_YEAR = /^(?:19[5-9]\d|20[0-3]\d)$/;
|
|
317
345
|
const POSTAL_CONTEXT_NEAR = /(?:postcode|postal|code\s*postal|PLZ|Postleitzahl|postnummer|postinumero|póstnúmer|zip|straat|straße|strasse|rue\s|via\s|calle\s|rua\s|ulica|utca|street|avenue|laan\s|weg\s|plein|adres|adresse|address|woonplaats|wonende|woonachtig|gevestigd|domicili|demeurant|résidant|residant|bosatt|bopæl|wohnhaft|ansässig|stad\b|ville\b|city\b|Stadt|città|ciudad|cidade|miasto|město|város)/i;
|
|
318
346
|
const DATE_KEYWORD_NEAR = /(?:født|fødselsdato|fødsel|Fødselsdato|född|födelsedatum|födelsedag|syntynyt|syntymäaika|fæddur|fæðingardagur|geboren|geboortedatum|Geburtsdatum|nascido|nacido|data di nascita|nato il|nata il|Tiltr[æa]delsesdato|Tiltredelsesdato|datum|date\b|Datum|jaar|year|année|Jahr|since|sinds|depuis|seit|\d{2}\.\d{2}\.|(?:januar|februar|marts|april|maj|juni|juli|august|september|oktober|november|december|januari|februari|mars|mei|juin|juillet|août))/i;
|
|
347
|
+
// A sentence boundary: a full stop, question or exclamation mark followed by
|
|
348
|
+
// space, or a line break. Written so it cannot fire inside "11.03.1970", where
|
|
349
|
+
// the periods are not followed by whitespace -- that date is one of the false
|
|
350
|
+
// positives this gate has to reject.
|
|
351
|
+
const SENTENCE_BREAK = /[.!?][ \t\n]|\n/;
|
|
352
|
+
const SENTENCE_BREAK_G = /[.!?][ \t\n]|\n/g;
|
|
353
|
+
// The value's own sentence, bounded either side.
|
|
354
|
+
function sentenceAround(text, start, end, reach = 160) {
|
|
355
|
+
let before = text.slice(Math.max(0, start - reach), start);
|
|
356
|
+
let after = text.slice(end, end + reach);
|
|
357
|
+
let lastEnd = -1;
|
|
358
|
+
SENTENCE_BREAK_G.lastIndex = 0;
|
|
359
|
+
for (let m = SENTENCE_BREAK_G.exec(before); m !== null; m = SENTENCE_BREAK_G.exec(before)) {
|
|
360
|
+
lastEnd = m.index + m[0].length;
|
|
361
|
+
}
|
|
362
|
+
if (lastEnd >= 0)
|
|
363
|
+
before = before.slice(lastEnd);
|
|
364
|
+
const found = SENTENCE_BREAK.exec(after);
|
|
365
|
+
if (found !== null)
|
|
366
|
+
after = after.slice(0, found.index);
|
|
367
|
+
return before + after;
|
|
368
|
+
}
|
|
369
|
+
// A postal label whose only distance from the value is punctuation:
|
|
370
|
+
// "PLZ: 2011", "code postal : 2011", "Woonplaats: 1950".
|
|
371
|
+
const POSTAL_LABEL_TOUCHING = new RegExp(`(?:${POSTAL_CONTEXT_NEAR.source})[\\s:.\\-]{0,4}$`, "i");
|
|
372
|
+
// A capitalised place name after the code: "2000 Antwerpen". A following label
|
|
373
|
+
// ("1970 Fødselsnummer:") is excluded by the colon.
|
|
374
|
+
const CITY_AFTER = /^[ \t](?!\w+\s*:)[A-ZÀ-ÞŁŠŽ][\w\-']{2,}/u;
|
|
375
|
+
// Every position is local to the value or inside its own sentence. Proximity to
|
|
376
|
+
// an address *word* anywhere in a 300-character window is what the old rescue
|
|
377
|
+
// asked, and that is a neighbourhood rather than a position (rules-engine#32).
|
|
378
|
+
function yearSitsInAddressStructure(text, start, end) {
|
|
379
|
+
const before = text.slice(Math.max(0, start - 28), start);
|
|
380
|
+
// "Amsterdam, 2026" -- the comma of an address line.
|
|
381
|
+
if (/,[ \t]*$/.test(before))
|
|
382
|
+
return true;
|
|
383
|
+
// "PLZ: 2011" -- a postal label with nothing but punctuation between.
|
|
384
|
+
if (POSTAL_LABEL_TOUCHING.test(before))
|
|
385
|
+
return true;
|
|
386
|
+
// A capitalised place name immediately after.
|
|
387
|
+
if (CITY_AFTER.test(text.slice(end, end + 24)))
|
|
388
|
+
return true;
|
|
389
|
+
// An address word in the value's own sentence. "Te huur: Lange Nieuwstraat
|
|
390
|
+
// 12, rustige ligging in 2018, vlakbij openbaar vervoer." is an Antwerp
|
|
391
|
+
// postal code in prose; the false positives put the year and the address in
|
|
392
|
+
// different sentences.
|
|
393
|
+
return POSTAL_CONTEXT_NEAR.test(sentenceAround(text, start, end));
|
|
394
|
+
}
|
|
319
395
|
function suppressYearAsPostal(text, match) {
|
|
320
396
|
if (match.patternDef.entityType !== types_js_1.EntityType.POSTAL_CODE)
|
|
321
397
|
return false;
|
|
@@ -334,8 +410,10 @@ function suppressYearAsPostal(text, match) {
|
|
|
334
410
|
return true;
|
|
335
411
|
const [before, after] = getContext(text, match.start, match.end);
|
|
336
412
|
const context = before + after;
|
|
337
|
-
// Keep as postal code
|
|
338
|
-
|
|
413
|
+
// Keep as postal code only when the value sits in address *structure* --
|
|
414
|
+
// see yearSitsInAddressStructure for why a 300-character window of address
|
|
415
|
+
// words was the defect rather than the keyword list.
|
|
416
|
+
if (yearSitsInAddressStructure(text, match.start, match.end))
|
|
339
417
|
return false;
|
|
340
418
|
// Keep if preceded by comma+space (address pattern: "Amsterdam, 2026")
|
|
341
419
|
const immediateBefore = text.slice(Math.max(0, match.start - 3), match.start);
|
|
@@ -510,6 +588,35 @@ function suppressPostalInsideIban(text, match) {
|
|
|
510
588
|
}
|
|
511
589
|
return false;
|
|
512
590
|
}
|
|
591
|
+
// An IBAN-shaped run: two letters, two check digits, then grouped
|
|
592
|
+
// alphanumerics. Deliberately not checksum-gated -- the point is to describe the
|
|
593
|
+
// run a human wrote as one account number, including the ones that fail their
|
|
594
|
+
// checksum, because those are exactly the ones no structured detector claims.
|
|
595
|
+
const ACCOUNT_RUN = /\b[A-Z]{2}\d{2}(?:[ ]?[A-Z0-9]{2,4}){2,8}\b/g;
|
|
596
|
+
// "Compte: FR76 3000 4008 0300 0109 5374" fails the IBAN checksum, so no
|
|
597
|
+
// BANK_ACCOUNT candidate survives and the Danish eight-digit phone pattern took
|
|
598
|
+
// "3000 4008" out of the middle of it, leaving the rest in the clear
|
|
599
|
+
// (rules-engine#30). POSTAL_CODE has carried its own version of this guard
|
|
600
|
+
// since 0.3.3, in suppressPostalInsideIban.
|
|
601
|
+
function suppressPhoneInsideAccountRun(text, match) {
|
|
602
|
+
if (match.patternDef.entityType !== types_js_1.EntityType.PHONE)
|
|
603
|
+
return false;
|
|
604
|
+
const lineStart = text.lastIndexOf("\n", match.start) + 1;
|
|
605
|
+
let lineEnd = text.indexOf("\n", match.end);
|
|
606
|
+
if (lineEnd < 0)
|
|
607
|
+
lineEnd = text.length;
|
|
608
|
+
const line = text.slice(lineStart, lineEnd);
|
|
609
|
+
ACCOUNT_RUN.lastIndex = 0;
|
|
610
|
+
for (let m = ACCOUNT_RUN.exec(line); m !== null; m = ACCOUNT_RUN.exec(line)) {
|
|
611
|
+
const runStart = lineStart + m.index;
|
|
612
|
+
const runEnd = runStart + m[0].length;
|
|
613
|
+
if (runStart <= match.start && match.end <= runEnd
|
|
614
|
+
&& runEnd - runStart > match.end - match.start) {
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
513
620
|
function suppressPostalAsHouseNumber(text, match) {
|
|
514
621
|
if (match.patternDef.entityType !== types_js_1.EntityType.POSTAL_CODE)
|
|
515
622
|
return false;
|
|
@@ -738,12 +845,19 @@ function suppressBicWithoutEvidence(text, match, scratch) {
|
|
|
738
845
|
// Repeated per alternative, the engine retried the same unbounded prefix five
|
|
739
846
|
// times per starting position — the most expensive single regex in the engine
|
|
740
847
|
// at 197 ms per 1 MB document. Same language, 8x faster on the worst case.
|
|
848
|
+
// An identifier cue that is really a postal label -- see the use below.
|
|
849
|
+
const POSTAL_LABEL_CUE = /^post/i;
|
|
741
850
|
const ID_CUE_BEFORE = /(?:[\w\-]*(?:Nr|N[°ºo]|Nummer|Numero|Numéro)|No|number|num|Kennzahl|Aktenzeichen|Az|e-?card|Polizze|Police|Policen)\.?\s*:?\s*$/i;
|
|
742
851
|
// An international dialling prefix earlier on the same line, with nothing but
|
|
743
852
|
// number punctuation in between: these digits belong to the phone detector.
|
|
744
853
|
const DIALLING_PREFIX_BEFORE = /\+\d{1,3}[\d\s\-().]*$/;
|
|
745
854
|
// A country prefix on a postal code — "A-1010 Wien", "B-2000", "L-1234".
|
|
746
|
-
|
|
855
|
+
// The boundary excludes a hyphen, which is what makes this a *prefix* rather
|
|
856
|
+
// than a segment. It did not, so any `XX-NNNNN` tail of a hyphenated reference
|
|
857
|
+
// read as a country-prefixed address: in "PV-2026-LU-09143" the `LU` is
|
|
858
|
+
// preceded by `-` and the German five-digit postal pattern kept `09143`
|
|
859
|
+
// (rules-engine#31).
|
|
860
|
+
const COUNTRY_PREFIXED = /(?:^|[^A-Za-z0-9\-])[A-Z]{1,2}$/;
|
|
747
861
|
/**
|
|
748
862
|
* Suppress bare digit runs that belong to a longer number, not an address.
|
|
749
863
|
*
|
|
@@ -787,7 +901,16 @@ function suppressPostalInLongerIdentifier(text, match) {
|
|
|
787
901
|
if (/^[ \t]+\d/.test(text.slice(match.end, match.end + 4)))
|
|
788
902
|
return true;
|
|
789
903
|
// An identifier label introduces the digits: "DiNr. 4471", "Policen-Nr."
|
|
790
|
-
|
|
904
|
+
// Unless the label is a *postal* one. ID_CUE_BEFORE matches any word ending
|
|
905
|
+
// in "Nummer", "Nr", "Numero" or "Numéro" -- the wildcard is `[\w\-]*` -- so
|
|
906
|
+
// it matches `postnummer`, `postnr` and `postinumero`, the canonical postal
|
|
907
|
+
// labels of Norway, Denmark and Finland. Those countries write a bare
|
|
908
|
+
// four- or five-digit code, so the isdigit() guard above lets this run and
|
|
909
|
+
// the code was suppressed by its own label: `postnummer: 5020 Bergen`
|
|
910
|
+
// produced nothing at all (rules-engine#41).
|
|
911
|
+
const before = text.slice(Math.max(0, match.start - 40), match.start);
|
|
912
|
+
const cue = ID_CUE_BEFORE.exec(before);
|
|
913
|
+
if (cue !== null && !POSTAL_LABEL_CUE.test(cue[0].trimStart()))
|
|
791
914
|
return true;
|
|
792
915
|
// Digits after an international dialling prefix belong to PHONE
|
|
793
916
|
const [lineStart] = enclosingLine(text, match.start, match.end);
|
|
@@ -839,7 +962,7 @@ function suppressRequiresContext(text, match) {
|
|
|
839
962
|
return !match.patternDef.contextKeywords.some(kw => context.includes(kw.toLowerCase()));
|
|
840
963
|
}
|
|
841
964
|
const TYPE_SUPPRESSORS = {
|
|
842
|
-
[types_js_1.EntityType.PHONE]: [suppressCurrency, suppressUnits, suppressReference, suppressMath, suppressPhoneServiceNumber, suppressPhoneDateOverlap, suppressPhoneAsNumberRange],
|
|
965
|
+
[types_js_1.EntityType.PHONE]: [suppressCurrency, suppressUnits, suppressReference, suppressMath, suppressPhoneServiceNumber, suppressPhoneInsideAccountRun, suppressPhoneDateOverlap, suppressPhoneAsNumberRange],
|
|
843
966
|
[types_js_1.EntityType.NATIONAL_ID]: [suppressCurrency, suppressUnits, suppressReference, suppressLegal, suppressMath, suppressNatidAsPassport, suppressSeNatidAsOrg],
|
|
844
967
|
[types_js_1.EntityType.SSN]: [suppressCurrency, suppressUnits, suppressReference, suppressMath],
|
|
845
968
|
[types_js_1.EntityType.TAX_ID]: [suppressCurrency, suppressUnits, suppressReference, suppressMath, suppressTaxidAsIpAddress],
|
|
@@ -853,6 +976,8 @@ const TYPE_SUPPRESSORS = {
|
|
|
853
976
|
function shouldSuppress(text, match, scratch) {
|
|
854
977
|
if (suppressSequential(text, match))
|
|
855
978
|
return true;
|
|
979
|
+
if (suppressRedactionPlaceholder(text, match))
|
|
980
|
+
return true;
|
|
856
981
|
const typeSups = TYPE_SUPPRESSORS[match.patternDef.entityType];
|
|
857
982
|
if (typeSups) {
|
|
858
983
|
for (const s of typeSups) {
|
|
@@ -513,10 +513,24 @@ function validatePortugueseNif(candidate) {
|
|
|
513
513
|
const check = remainder < 2 ? 0 : 11 - remainder;
|
|
514
514
|
return parseInt(c[8]) === check;
|
|
515
515
|
}
|
|
516
|
+
// PESEL carries the century in the month field: +0 is the 1900s, +20 the 2000s,
|
|
517
|
+
// +40 the 2100s, +60 the 2200s, +80 the 1800s. Without a date check this
|
|
518
|
+
// accepted 80666603210 -- day 66 -- as NATIONAL_ID at high confidence
|
|
519
|
+
// (rules-engine#43).
|
|
516
520
|
function validatePolishPesel(candidate) {
|
|
517
521
|
const c = candidate.replace(/[\s\-]/g, "");
|
|
518
522
|
if (c.length !== 11 || !/^\d{11}$/.test(c))
|
|
519
523
|
return false;
|
|
524
|
+
const rawMonth = parseInt(c.slice(2, 4), 10);
|
|
525
|
+
let month = null;
|
|
526
|
+
for (const off of [0, 20, 40, 60, 80]) {
|
|
527
|
+
if (rawMonth - off >= 1 && rawMonth - off <= 12) {
|
|
528
|
+
month = rawMonth - off;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (month === null || !isRealDay(month, parseInt(c.slice(4, 6), 10)))
|
|
533
|
+
return false;
|
|
520
534
|
const d = c.split("").map(Number);
|
|
521
535
|
const weights = [1, 3, 7, 9, 1, 3, 7, 9, 1, 3];
|
|
522
536
|
let total = 0;
|
|
@@ -535,18 +549,53 @@ function validatePolishNip(candidate) {
|
|
|
535
549
|
total += d[i] * weights[i];
|
|
536
550
|
return total % 11 === d[9];
|
|
537
551
|
}
|
|
552
|
+
// Days per month for the rodné číslo date check. February is 29 because the
|
|
553
|
+
// century is not recoverable from a two-digit year, so a leap year cannot be
|
|
554
|
+
// ruled out -- being permissive by one day is the right direction here.
|
|
555
|
+
const RC_DAYS = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
556
|
+
// The calendar month a rodné číslo month field encodes, or null. Four ranges
|
|
557
|
+
// are legal: 1-12 is a man, +50 a woman, and since 2004 +20 marks a birth on a
|
|
558
|
+
// day whose sequence numbers were exhausted (so +70 is a woman on such a day).
|
|
559
|
+
// Whether `day` exists in `month`, February permissive at 29 because a
|
|
560
|
+
// two-digit year does not reveal the century.
|
|
561
|
+
function isRealDay(month, day) {
|
|
562
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= RC_DAYS[month - 1];
|
|
563
|
+
}
|
|
564
|
+
function czechMonth(raw) {
|
|
565
|
+
for (const offset of [0, 20, 50, 70]) {
|
|
566
|
+
if (raw - offset >= 1 && raw - offset <= 12)
|
|
567
|
+
return raw - offset;
|
|
568
|
+
}
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
// The date is checked, not only the checksum. Without it the validator accepted
|
|
572
|
+
// an impossible month, and a Czech mobile number is the same shape -- nine
|
|
573
|
+
// digits opening 6 or 7 -- so any mobile that happened to pass mod 11 was typed
|
|
574
|
+
// NATIONAL_ID at high confidence: 606666032 (MM=66), 778836400 (MM=88),
|
|
575
|
+
// 728990603 (MM=89). 284 per corpus pass, the largest single false-positive
|
|
576
|
+
// bucket in the evaluation (rules-engine#37).
|
|
538
577
|
function validateCzechBirthNumber(candidate) {
|
|
539
578
|
const c = candidate.replace(/[/\s]/g, "");
|
|
540
579
|
if (!/^\d+$/.test(c) || (c.length !== 9 && c.length !== 10))
|
|
541
580
|
return false;
|
|
581
|
+
const month = czechMonth(parseInt(c.slice(2, 4), 10));
|
|
582
|
+
if (month === null)
|
|
583
|
+
return false;
|
|
584
|
+
const day = parseInt(c.slice(4, 6), 10);
|
|
585
|
+
if (day < 1 || day > RC_DAYS[month - 1])
|
|
586
|
+
return false;
|
|
542
587
|
if (c.length === 10)
|
|
543
588
|
return parseInt(c) % 11 === 0;
|
|
544
589
|
return true;
|
|
545
590
|
}
|
|
591
|
+
// S YY MM DD JJ NNN C -- the leading S carries the century, so the month needs
|
|
592
|
+
// no offset convention here (rules-engine#43).
|
|
546
593
|
function validateRomanianCnp(candidate) {
|
|
547
594
|
const c = candidate.replace(/[\s\-]/g, "");
|
|
548
595
|
if (c.length !== 13 || !/^\d{13}$/.test(c) || !"12345678".includes(c[0]))
|
|
549
596
|
return false;
|
|
597
|
+
if (!isRealDay(parseInt(c.slice(3, 5), 10), parseInt(c.slice(5, 7), 10)))
|
|
598
|
+
return false;
|
|
550
599
|
const key = "279146358279";
|
|
551
600
|
let total = 0;
|
|
552
601
|
for (let i = 0; i < 12; i++)
|
|
@@ -593,10 +642,14 @@ function validateCroatianOib(candidate) {
|
|
|
593
642
|
const check = (11 - product) % 10;
|
|
594
643
|
return check === parseInt(c[10]);
|
|
595
644
|
}
|
|
645
|
+
// DD MM YYY RR BBB K -- note EMSO opens with the *day*, where the Czech, Polish
|
|
646
|
+
// and Romanian forms open with the year or a century digit (rules-engine#43).
|
|
596
647
|
function validateSlovenianEmso(candidate) {
|
|
597
648
|
const c = candidate.replace(/[\s\-]/g, "");
|
|
598
649
|
if (c.length !== 13 || !/^\d{13}$/.test(c))
|
|
599
650
|
return false;
|
|
651
|
+
if (!isRealDay(parseInt(c.slice(2, 4), 10), parseInt(c.slice(0, 2), 10)))
|
|
652
|
+
return false;
|
|
600
653
|
const d = c.split("").map(Number);
|
|
601
654
|
const weights = [7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
|
|
602
655
|
let total = 0;
|