euredact 0.5.0 → 0.5.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.
|
@@ -31,6 +31,41 @@ function p(entityType, pattern, validator = null, description = "", contextKeywo
|
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
32
|
// Secret context keywords (consolidated, reused across SECRET patterns)
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Words that name a travel document, in the official languages of the 31
|
|
36
|
+
* countries this engine covers, plus English.
|
|
37
|
+
*
|
|
38
|
+
* Shared by every passport pattern. Before this existed each country kept its
|
|
39
|
+
* own list, so a passport was only recognised when its shape and its label
|
|
40
|
+
* came from the same country: `Reisepass: CA1234567` was missed because the
|
|
41
|
+
* German pattern rejects that alphabet while the Dutch pattern, whose shape
|
|
42
|
+
* fits, had never heard of "Reisepass" (issue rules-engine#23).
|
|
43
|
+
*
|
|
44
|
+
* The bare Scandinavian "pass", Finnish "passi" and Latvian "pase" are
|
|
45
|
+
* omitted deliberately: context matching is substring, not word-boundary, so
|
|
46
|
+
* they fire inside "password", "passenger", "passive" and "phase". Only the
|
|
47
|
+
* compound forms are listed. Mirrors PASSPORT_CONTEXT in Python.
|
|
48
|
+
*/
|
|
49
|
+
const PASSPORT_CONTEXT = [
|
|
50
|
+
"passport", "passport no", "passport number", "travel document",
|
|
51
|
+
"reisepass", "passnummer", "reisepassnummer", "reisepass-nr", "pass nr",
|
|
52
|
+
"reisedokument",
|
|
53
|
+
"paspoort", "paspoortnummer", "reisdocument", "identiteitsbewijs",
|
|
54
|
+
"passeport", "numéro de passeport", "document de voyage",
|
|
55
|
+
"passaporto", "numero di passaporto", "documento di viaggio",
|
|
56
|
+
"pasaporte", "número de pasaporte", "documento de viaje",
|
|
57
|
+
"passaporte", "número do passaporte",
|
|
58
|
+
"passnr", "passets nummer", "passin numero", "passinumero",
|
|
59
|
+
"passi number", "passinumber",
|
|
60
|
+
"vegabréf", "vegabréfsnúmer",
|
|
61
|
+
"paso numeris", "pases numurs",
|
|
62
|
+
"paszport", "numer paszportu", "cestovní pas", "číslo pasu",
|
|
63
|
+
"cestovný pas", "potni list", "številka potnega lista",
|
|
64
|
+
"putovnica", "broj putovnice",
|
|
65
|
+
"útlevél", "útlevélszám", "pașaport", "numărul pașaportului",
|
|
66
|
+
"паспорт", "номер на паспорт", "διαβατήριο",
|
|
67
|
+
"αριθμός διαβατηρίου", "passaport",
|
|
68
|
+
];
|
|
34
69
|
const SECRET_CONTEXT = [
|
|
35
70
|
// English
|
|
36
71
|
"key", "token", "secret", "password", "credential",
|
|
@@ -121,6 +156,17 @@ const SHARED = {
|
|
|
121
156
|
// "Call (+32 475 12 34 56) today." also masked the ")" (issue
|
|
122
157
|
// rules-engine#3). Mirrors the Python pattern character for character.
|
|
123
158
|
p(types_js_1.EntityType.PHONE, String.raw `\+\d{1,3}(?:[\s\-/]?(?:\(\d{1,4}\)|\d{1,4})){2,7}`, "e164", "International phone number (E.164) — any country"),
|
|
159
|
+
// Passport (any country). The per-country patterns cover BE, DE, FR and NL
|
|
160
|
+
// only; the other 26 had none. Same argument as the E.164 phone rule: one
|
|
161
|
+
// country-independent shape, with the label carrying the precision.
|
|
162
|
+
// Shapes from the project canon (prompts/pii_definitions.md, PASSPORT):
|
|
163
|
+
// EU is 1-2 letters + 7 digits, UK is 9 digits optionally GBR-prefixed.
|
|
164
|
+
// The canon's position is that the EU converged on one shape, so national
|
|
165
|
+
// regexes would contradict it as well as being unverifiable. An earlier
|
|
166
|
+
// draft required a leading letter and missed all-numeric passports, the
|
|
167
|
+
// UK's own format among them; requiring exactly nine digits for the
|
|
168
|
+
// numeric form rejects eight-digit dates on shape (issue rules-engine#23).
|
|
169
|
+
p(types_js_1.EntityType.PASSPORT, String.raw `\b(?:GBR\d{9}|[A-Z]{1,2}\d{7}|\d{9})\b`, null, "Passport number — any country, label-gated", PASSPORT_CONTEXT, true),
|
|
124
170
|
p(types_js_1.EntityType.BIC, String.raw `\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b`, "bic"),
|
|
125
171
|
// Space-separated form: "RZBA AT WW", "NICA BE BB". Written as a separate
|
|
126
172
|
// pattern requiring a space between *every* group, rather than making the
|
|
@@ -214,7 +260,7 @@ const NL = {
|
|
|
214
260
|
p(types_js_1.EntityType.PHONE, String.raw `\b06[\s\-]?\d{2}[\s\-]?\d{2}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
|
|
215
261
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{1,2}[\s\-]?\d{2,3}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
|
|
216
262
|
p(types_js_1.EntityType.PHONE, String.raw `\+31\s?[1-9]\d{0,2}[\s\-]?\d{3,4}[\s\-]?\d{3,4}\b`),
|
|
217
|
-
p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z][A-Z0-9]{8}\b`, null, "",
|
|
263
|
+
p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z][A-Z0-9]{8}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
218
264
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b(?:[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]|\d[\-\s]?[A-Z]{3}[\-\s]?\d{2}|\d{2}[\-\s]?[A-Z]{3}[\-\s]?\d|[A-Z]{2}[\-\s]?\d{2}[\-\s]?[A-Z]{2}|\d{2}[\-\s]?[A-Z]{2}[\-\s]?\d{2}|\d[\-\s]?[A-Z]{2}[\-\s]?\d{3})\b`),
|
|
219
265
|
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\s?[A-Z]{2}\b`),
|
|
220
266
|
],
|
|
@@ -229,7 +275,7 @@ const BE = {
|
|
|
229
275
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b0\d{3}\.?\d{3}\.?\d{3}\b`, null, "", ["KBO", "BCE", "ondernemingsnummer", "numéro d'entreprise", "enterprise number", "bedrijfsnummer", "Kruispuntbank", "Banque-Carrefour", "Banque Carrefour", "Ondernemingen", "Entreprises", "onder nummer"], true),
|
|
230
276
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{0,2}[/\s.\-]?\d{2,3}[.\s\-]?\d{2,3}[.\s\-]?\d{2,3}\b`),
|
|
231
277
|
p(types_js_1.EntityType.PHONE, String.raw `\+32\s?\d{1,3}[\s.\-]?\d{2,3}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
|
|
232
|
-
p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "",
|
|
278
|
+
p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
233
279
|
p(types_js_1.EntityType.DRIVERS_LICENSE, String.raw `\b\d{10}\b`, null, "", ["rijbewijs", "permis de conduire", "driving licence", "driving license", "rijbewijsnummer"], true),
|
|
234
280
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[12][\-\s]?[A-Z]{3}[\-\s]?\d{3}\b`),
|
|
235
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),
|
|
@@ -248,7 +294,7 @@ const DE = {
|
|
|
248
294
|
p(types_js_1.EntityType.VAT, String.raw `\bDE\s?\d{9}\b`, "vat_de"),
|
|
249
295
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{1,4}[\s/\-]?\d{3,8}\b`),
|
|
250
296
|
p(types_js_1.EntityType.PHONE, String.raw `\+49\s?\d{2,5}[\s/\-]?\d{3,8}\b`),
|
|
251
|
-
p(types_js_1.EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "",
|
|
297
|
+
p(types_js_1.EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "", PASSPORT_CONTEXT, true),
|
|
252
298
|
// The separator after the city code is mandatory. When it was optional, a
|
|
253
299
|
// contiguous letter run split across both groups and the hyphen was
|
|
254
300
|
// consumed as the *second* separator, so any "LETTERS-DIGITS" token
|
|
@@ -309,7 +355,7 @@ const FR = {
|
|
|
309
355
|
p(types_js_1.EntityType.VAT, String.raw `\bFR\s?[0-9A-HJ-NP-Z]{2}\s?\d{9}\b`, "vat_fr"),
|
|
310
356
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}\b`),
|
|
311
357
|
p(types_js_1.EntityType.PHONE, String.raw `\+33\s?[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
|
|
312
|
-
p(types_js_1.EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "",
|
|
358
|
+
p(types_js_1.EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
313
359
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]{2}\b`),
|
|
314
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),
|
|
315
361
|
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),
|
package/dist/cjs/rules/cues.js
CHANGED
|
@@ -69,8 +69,20 @@ const types_js_1 = require("../types.js");
|
|
|
69
69
|
* How far back a cue may sit. Short on purpose: a cue is only evidence about
|
|
70
70
|
* the value it introduces, which is the lesson of the postal-code year defect,
|
|
71
71
|
* where a cue 150 characters away licensed every number in the window.
|
|
72
|
+
*
|
|
73
|
+
* This bounds how long a *label* may be, not how far it may sit from the
|
|
74
|
+
* value: every pattern is anchored with `$`, so the label still has to reach
|
|
75
|
+
* the span through a run-on or one qualifier word.
|
|
76
|
+
*
|
|
77
|
+
* 32 was too short for German tax labels, the longest the engine carries.
|
|
78
|
+
* "Steuerliche Identifikationsnummer " is 34 characters and
|
|
79
|
+
* "Steueridentifikationsnummer lautet: " is 36, so the label's own start sat
|
|
80
|
+
* outside the window and `(?<![A-Za-z0-9_])` had nothing to anchor against,
|
|
81
|
+
* leaving a critical-tier identifier in the clear (rules-engine#26). 44
|
|
82
|
+
* admits the longest with a qualifier: "Steuerliche Identifikationsnummer
|
|
83
|
+
* lautet: " is 42.
|
|
72
84
|
*/
|
|
73
|
-
exports.CUE_WINDOW =
|
|
85
|
+
exports.CUE_WINDOW = 44;
|
|
74
86
|
/**
|
|
75
87
|
* (entity type, label pattern). Ordered: the first match wins, so the narrower
|
|
76
88
|
* label comes first where two could read the same text.
|
|
@@ -93,8 +105,15 @@ exports.CUES = [
|
|
|
93
105
|
/(?<![A-Za-z0-9_])(?:ssn|social\s*security|sozialversicherungsnummer|svnr|sofi)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
94
106
|
// Tax number. Greece's ΑΦΜ is issued by the tax authority; the identity-card
|
|
95
107
|
// number is the ΑΔΤ, above.
|
|
108
|
+
//
|
|
109
|
+
// The German long forms are spelled out rather than left to the run-on after
|
|
110
|
+
// `steuer-?id`. A cue may use its run-on *or* one qualifier word, never both,
|
|
111
|
+
// and "Steuer-IdNr lautet:" needs both: the run-on for "Nr" and the qualifier
|
|
112
|
+
// for "lautet". Naming the whole label leaves the qualifier free.
|
|
113
|
+
// "Steuerliche Identifikationsnummer" matched nothing at all — it is two
|
|
114
|
+
// words, and `steuer-?id` cannot reach across "liche " (rules-engine#26).
|
|
96
115
|
[types_js_1.EntityType.TAX_ID,
|
|
97
|
-
/(?<![A-Za-z0-9_])(?:αφμ|α\.φ\.μ\.|steuer-?id|steuernummer|st\.?-?nr|stnr|tin|finanzamt\s+ist|tax\s*(?:id|no|number)|daňové\s*číslo|numer\s*podatkowy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
116
|
+
/(?<![A-Za-z0-9_])(?:αφμ|α\.φ\.μ\.|steuerliche\s*identifikations-?nummer|steuer-?identifikations-?nummer|steuer-?id-?nr\.?|steuer-?id|steuernummer|st\.?-?nr|stnr|tin|finanzamt\s+ist|tax\s*(?:id|no|number)|daňové\s*číslo|numer\s*podatkowy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
98
117
|
// Health insurance: the insured person.
|
|
99
118
|
[types_js_1.EntityType.HEALTH_INSURANCE,
|
|
100
119
|
/(?<![A-Za-z0-9_])(?:versichertennummer|krankenversicherung|kvnr|kv-?nr|mutualité|ziekenfonds|aansluitingsnummer|medical\s*card(?:\s*(?:no|number))?)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
@@ -28,6 +28,41 @@ function p(entityType, pattern, validator = null, description = "", contextKeywo
|
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
// Secret context keywords (consolidated, reused across SECRET patterns)
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Words that name a travel document, in the official languages of the 31
|
|
33
|
+
* countries this engine covers, plus English.
|
|
34
|
+
*
|
|
35
|
+
* Shared by every passport pattern. Before this existed each country kept its
|
|
36
|
+
* own list, so a passport was only recognised when its shape and its label
|
|
37
|
+
* came from the same country: `Reisepass: CA1234567` was missed because the
|
|
38
|
+
* German pattern rejects that alphabet while the Dutch pattern, whose shape
|
|
39
|
+
* fits, had never heard of "Reisepass" (issue rules-engine#23).
|
|
40
|
+
*
|
|
41
|
+
* The bare Scandinavian "pass", Finnish "passi" and Latvian "pase" are
|
|
42
|
+
* omitted deliberately: context matching is substring, not word-boundary, so
|
|
43
|
+
* they fire inside "password", "passenger", "passive" and "phase". Only the
|
|
44
|
+
* compound forms are listed. Mirrors PASSPORT_CONTEXT in Python.
|
|
45
|
+
*/
|
|
46
|
+
const PASSPORT_CONTEXT = [
|
|
47
|
+
"passport", "passport no", "passport number", "travel document",
|
|
48
|
+
"reisepass", "passnummer", "reisepassnummer", "reisepass-nr", "pass nr",
|
|
49
|
+
"reisedokument",
|
|
50
|
+
"paspoort", "paspoortnummer", "reisdocument", "identiteitsbewijs",
|
|
51
|
+
"passeport", "numéro de passeport", "document de voyage",
|
|
52
|
+
"passaporto", "numero di passaporto", "documento di viaggio",
|
|
53
|
+
"pasaporte", "número de pasaporte", "documento de viaje",
|
|
54
|
+
"passaporte", "número do passaporte",
|
|
55
|
+
"passnr", "passets nummer", "passin numero", "passinumero",
|
|
56
|
+
"passi number", "passinumber",
|
|
57
|
+
"vegabréf", "vegabréfsnúmer",
|
|
58
|
+
"paso numeris", "pases numurs",
|
|
59
|
+
"paszport", "numer paszportu", "cestovní pas", "číslo pasu",
|
|
60
|
+
"cestovný pas", "potni list", "številka potnega lista",
|
|
61
|
+
"putovnica", "broj putovnice",
|
|
62
|
+
"útlevél", "útlevélszám", "pașaport", "numărul pașaportului",
|
|
63
|
+
"паспорт", "номер на паспорт", "διαβατήριο",
|
|
64
|
+
"αριθμός διαβατηρίου", "passaport",
|
|
65
|
+
];
|
|
31
66
|
const SECRET_CONTEXT = [
|
|
32
67
|
// English
|
|
33
68
|
"key", "token", "secret", "password", "credential",
|
|
@@ -118,6 +153,17 @@ const SHARED = {
|
|
|
118
153
|
// "Call (+32 475 12 34 56) today." also masked the ")" (issue
|
|
119
154
|
// rules-engine#3). Mirrors the Python pattern character for character.
|
|
120
155
|
p(EntityType.PHONE, String.raw `\+\d{1,3}(?:[\s\-/]?(?:\(\d{1,4}\)|\d{1,4})){2,7}`, "e164", "International phone number (E.164) — any country"),
|
|
156
|
+
// Passport (any country). The per-country patterns cover BE, DE, FR and NL
|
|
157
|
+
// only; the other 26 had none. Same argument as the E.164 phone rule: one
|
|
158
|
+
// country-independent shape, with the label carrying the precision.
|
|
159
|
+
// Shapes from the project canon (prompts/pii_definitions.md, PASSPORT):
|
|
160
|
+
// EU is 1-2 letters + 7 digits, UK is 9 digits optionally GBR-prefixed.
|
|
161
|
+
// The canon's position is that the EU converged on one shape, so national
|
|
162
|
+
// regexes would contradict it as well as being unverifiable. An earlier
|
|
163
|
+
// draft required a leading letter and missed all-numeric passports, the
|
|
164
|
+
// UK's own format among them; requiring exactly nine digits for the
|
|
165
|
+
// numeric form rejects eight-digit dates on shape (issue rules-engine#23).
|
|
166
|
+
p(EntityType.PASSPORT, String.raw `\b(?:GBR\d{9}|[A-Z]{1,2}\d{7}|\d{9})\b`, null, "Passport number — any country, label-gated", PASSPORT_CONTEXT, true),
|
|
121
167
|
p(EntityType.BIC, String.raw `\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b`, "bic"),
|
|
122
168
|
// Space-separated form: "RZBA AT WW", "NICA BE BB". Written as a separate
|
|
123
169
|
// pattern requiring a space between *every* group, rather than making the
|
|
@@ -211,7 +257,7 @@ const NL = {
|
|
|
211
257
|
p(EntityType.PHONE, String.raw `\b06[\s\-]?\d{2}[\s\-]?\d{2}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
|
|
212
258
|
p(EntityType.PHONE, String.raw `\b0[1-9]\d{1,2}[\s\-]?\d{2,3}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
|
|
213
259
|
p(EntityType.PHONE, String.raw `\+31\s?[1-9]\d{0,2}[\s\-]?\d{3,4}[\s\-]?\d{3,4}\b`),
|
|
214
|
-
p(EntityType.PASSPORT, String.raw `\b[A-Z][A-Z0-9]{8}\b`, null, "",
|
|
260
|
+
p(EntityType.PASSPORT, String.raw `\b[A-Z][A-Z0-9]{8}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
215
261
|
p(EntityType.LICENSE_PLATE, String.raw `\b(?:[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]|\d[\-\s]?[A-Z]{3}[\-\s]?\d{2}|\d{2}[\-\s]?[A-Z]{3}[\-\s]?\d|[A-Z]{2}[\-\s]?\d{2}[\-\s]?[A-Z]{2}|\d{2}[\-\s]?[A-Z]{2}[\-\s]?\d{2}|\d[\-\s]?[A-Z]{2}[\-\s]?\d{3})\b`),
|
|
216
262
|
p(EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\s?[A-Z]{2}\b`),
|
|
217
263
|
],
|
|
@@ -226,7 +272,7 @@ const BE = {
|
|
|
226
272
|
p(EntityType.CHAMBER_OF_COMMERCE, String.raw `\b0\d{3}\.?\d{3}\.?\d{3}\b`, null, "", ["KBO", "BCE", "ondernemingsnummer", "numéro d'entreprise", "enterprise number", "bedrijfsnummer", "Kruispuntbank", "Banque-Carrefour", "Banque Carrefour", "Ondernemingen", "Entreprises", "onder nummer"], true),
|
|
227
273
|
p(EntityType.PHONE, String.raw `\b0[1-9]\d{0,2}[/\s.\-]?\d{2,3}[.\s\-]?\d{2,3}[.\s\-]?\d{2,3}\b`),
|
|
228
274
|
p(EntityType.PHONE, String.raw `\+32\s?\d{1,3}[\s.\-]?\d{2,3}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
|
|
229
|
-
p(EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "",
|
|
275
|
+
p(EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
230
276
|
p(EntityType.DRIVERS_LICENSE, String.raw `\b\d{10}\b`, null, "", ["rijbewijs", "permis de conduire", "driving licence", "driving license", "rijbewijsnummer"], true),
|
|
231
277
|
p(EntityType.LICENSE_PLATE, String.raw `\b[12][\-\s]?[A-Z]{3}[\-\s]?\d{3}\b`),
|
|
232
278
|
p(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),
|
|
@@ -245,7 +291,7 @@ const DE = {
|
|
|
245
291
|
p(EntityType.VAT, String.raw `\bDE\s?\d{9}\b`, "vat_de"),
|
|
246
292
|
p(EntityType.PHONE, String.raw `\b0[1-9]\d{1,4}[\s/\-]?\d{3,8}\b`),
|
|
247
293
|
p(EntityType.PHONE, String.raw `\+49\s?\d{2,5}[\s/\-]?\d{3,8}\b`),
|
|
248
|
-
p(EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "",
|
|
294
|
+
p(EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "", PASSPORT_CONTEXT, true),
|
|
249
295
|
// The separator after the city code is mandatory. When it was optional, a
|
|
250
296
|
// contiguous letter run split across both groups and the hyphen was
|
|
251
297
|
// consumed as the *second* separator, so any "LETTERS-DIGITS" token
|
|
@@ -306,7 +352,7 @@ const FR = {
|
|
|
306
352
|
p(EntityType.VAT, String.raw `\bFR\s?[0-9A-HJ-NP-Z]{2}\s?\d{9}\b`, "vat_fr"),
|
|
307
353
|
p(EntityType.PHONE, String.raw `\b0[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}\b`),
|
|
308
354
|
p(EntityType.PHONE, String.raw `\+33\s?[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
|
|
309
|
-
p(EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "",
|
|
355
|
+
p(EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "", PASSPORT_CONTEXT, true),
|
|
310
356
|
p(EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]{2}\b`),
|
|
311
357
|
p(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),
|
|
312
358
|
p(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),
|
package/dist/esm/rules/cues.d.ts
CHANGED
|
@@ -64,8 +64,20 @@ import { EntityType } from "../types.js";
|
|
|
64
64
|
* How far back a cue may sit. Short on purpose: a cue is only evidence about
|
|
65
65
|
* the value it introduces, which is the lesson of the postal-code year defect,
|
|
66
66
|
* where a cue 150 characters away licensed every number in the window.
|
|
67
|
+
*
|
|
68
|
+
* This bounds how long a *label* may be, not how far it may sit from the
|
|
69
|
+
* value: every pattern is anchored with `$`, so the label still has to reach
|
|
70
|
+
* the span through a run-on or one qualifier word.
|
|
71
|
+
*
|
|
72
|
+
* 32 was too short for German tax labels, the longest the engine carries.
|
|
73
|
+
* "Steuerliche Identifikationsnummer " is 34 characters and
|
|
74
|
+
* "Steueridentifikationsnummer lautet: " is 36, so the label's own start sat
|
|
75
|
+
* outside the window and `(?<![A-Za-z0-9_])` had nothing to anchor against,
|
|
76
|
+
* leaving a critical-tier identifier in the clear (rules-engine#26). 44
|
|
77
|
+
* admits the longest with a qualifier: "Steuerliche Identifikationsnummer
|
|
78
|
+
* lautet: " is 42.
|
|
67
79
|
*/
|
|
68
|
-
export declare const CUE_WINDOW =
|
|
80
|
+
export declare const CUE_WINDOW = 44;
|
|
69
81
|
/**
|
|
70
82
|
* (entity type, label pattern). Ordered: the first match wins, so the narrower
|
|
71
83
|
* label comes first where two could read the same text.
|
package/dist/esm/rules/cues.js
CHANGED
|
@@ -64,8 +64,20 @@ import { EntityType } from "../types.js";
|
|
|
64
64
|
* How far back a cue may sit. Short on purpose: a cue is only evidence about
|
|
65
65
|
* the value it introduces, which is the lesson of the postal-code year defect,
|
|
66
66
|
* where a cue 150 characters away licensed every number in the window.
|
|
67
|
+
*
|
|
68
|
+
* This bounds how long a *label* may be, not how far it may sit from the
|
|
69
|
+
* value: every pattern is anchored with `$`, so the label still has to reach
|
|
70
|
+
* the span through a run-on or one qualifier word.
|
|
71
|
+
*
|
|
72
|
+
* 32 was too short for German tax labels, the longest the engine carries.
|
|
73
|
+
* "Steuerliche Identifikationsnummer " is 34 characters and
|
|
74
|
+
* "Steueridentifikationsnummer lautet: " is 36, so the label's own start sat
|
|
75
|
+
* outside the window and `(?<![A-Za-z0-9_])` had nothing to anchor against,
|
|
76
|
+
* leaving a critical-tier identifier in the clear (rules-engine#26). 44
|
|
77
|
+
* admits the longest with a qualifier: "Steuerliche Identifikationsnummer
|
|
78
|
+
* lautet: " is 42.
|
|
67
79
|
*/
|
|
68
|
-
export const CUE_WINDOW =
|
|
80
|
+
export const CUE_WINDOW = 44;
|
|
69
81
|
/**
|
|
70
82
|
* (entity type, label pattern). Ordered: the first match wins, so the narrower
|
|
71
83
|
* label comes first where two could read the same text.
|
|
@@ -88,8 +100,15 @@ export const CUES = [
|
|
|
88
100
|
/(?<![A-Za-z0-9_])(?:ssn|social\s*security|sozialversicherungsnummer|svnr|sofi)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
89
101
|
// Tax number. Greece's ΑΦΜ is issued by the tax authority; the identity-card
|
|
90
102
|
// number is the ΑΔΤ, above.
|
|
103
|
+
//
|
|
104
|
+
// The German long forms are spelled out rather than left to the run-on after
|
|
105
|
+
// `steuer-?id`. A cue may use its run-on *or* one qualifier word, never both,
|
|
106
|
+
// and "Steuer-IdNr lautet:" needs both: the run-on for "Nr" and the qualifier
|
|
107
|
+
// for "lautet". Naming the whole label leaves the qualifier free.
|
|
108
|
+
// "Steuerliche Identifikationsnummer" matched nothing at all — it is two
|
|
109
|
+
// words, and `steuer-?id` cannot reach across "liche " (rules-engine#26).
|
|
91
110
|
[EntityType.TAX_ID,
|
|
92
|
-
/(?<![A-Za-z0-9_])(?:αφμ|α\.φ\.μ\.|steuer-?id|steuernummer|st\.?-?nr|stnr|tin|finanzamt\s+ist|tax\s*(?:id|no|number)|daňové\s*číslo|numer\s*podatkowy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
111
|
+
/(?<![A-Za-z0-9_])(?:αφμ|α\.φ\.μ\.|steuerliche\s*identifikations-?nummer|steuer-?identifikations-?nummer|steuer-?id-?nr\.?|steuer-?id|steuernummer|st\.?-?nr|stnr|tin|finanzamt\s+ist|tax\s*(?:id|no|number)|daňové\s*číslo|numer\s*podatkowy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
|
93
112
|
// Health insurance: the insured person.
|
|
94
113
|
[EntityType.HEALTH_INSURANCE,
|
|
95
114
|
/(?<![A-Za-z0-9_])(?:versichertennummer|krankenversicherung|kvnr|kv-?nr|mutualité|ziekenfonds|aansluitingsnummer|medical\s*card(?:\s*(?:no|number))?)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "euredact",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "European PII redaction SDK -- fast, zero-dependency, rule-based detection and redaction of personal data across 31 European countries",
|
|
5
5
|
"homepage": "https://euredact.dev",
|
|
6
6
|
"license": "Apache-2.0",
|