euredact 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -1
- package/dist/cjs/cache.js +2 -2
- package/dist/cjs/index.js +10 -1
- package/dist/cjs/normalizer.js +16 -18
- package/dist/cjs/rules/bicRegistry.js +239 -0
- package/dist/cjs/rules/countries/index.js +102 -29
- package/dist/cjs/rules/deDistricts.js +92 -0
- package/dist/cjs/rules/engine.js +131 -19
- package/dist/cjs/rules/matchers.js +3 -1
- package/dist/cjs/rules/suppressors.js +286 -10
- package/dist/cjs/rules/validators.js +62 -12
- package/dist/cjs/sdk.js +20 -0
- package/dist/cjs/types.js +18 -2
- package/dist/esm/cache.js +2 -2
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/normalizer.js +16 -18
- package/dist/esm/rules/bicRegistry.d.ts +50 -0
- package/dist/esm/rules/bicRegistry.js +233 -0
- package/dist/esm/rules/countries/index.js +102 -29
- package/dist/esm/rules/deDistricts.d.ts +16 -0
- package/dist/esm/rules/deDistricts.js +89 -0
- package/dist/esm/rules/engine.d.ts +8 -0
- package/dist/esm/rules/engine.js +130 -19
- package/dist/esm/rules/matchers.d.ts +1 -1
- package/dist/esm/rules/matchers.js +3 -1
- package/dist/esm/rules/suppressors.js +286 -10
- package/dist/esm/rules/validators.d.ts +25 -0
- package/dist/esm/rules/validators.js +60 -11
- package/dist/esm/sdk.d.ts +7 -0
- package/dist/esm/sdk.js +20 -0
- package/dist/esm/types.d.ts +15 -1
- package/dist/esm/types.js +17 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -166,7 +166,7 @@ interface Detection {
|
|
|
166
166
|
String enum with all supported PII categories:
|
|
167
167
|
|
|
168
168
|
```
|
|
169
|
-
NAME ADDRESS
|
|
169
|
+
NAME ADDRESS BANK_ACCOUNT BIC
|
|
170
170
|
CREDIT_CARD PHONE EMAIL DOB
|
|
171
171
|
DATE_OF_DEATH NATIONAL_ID SSN TAX_ID
|
|
172
172
|
PASSPORT DRIVERS_LICENSE RESIDENCE_PERMIT LICENSE_PLATE
|
|
@@ -178,6 +178,76 @@ SOCIAL_HANDLE SECRET OTHER
|
|
|
178
178
|
|
|
179
179
|
For custom patterns, `entityType` is a plain string (e.g. `"EMPLOYEE_ID"`).
|
|
180
180
|
|
|
181
|
+
## Country codes
|
|
182
|
+
|
|
183
|
+
`countries` accepts **ISO 3166-1 alpha-2** codes. The two EU/VAT spellings are
|
|
184
|
+
accepted as equivalents: `GB`/`UK` and `GR`/`EL`. Codes are case-insensitive
|
|
185
|
+
and whitespace-tolerant.
|
|
186
|
+
|
|
187
|
+
An **unrecognised** code does not throw — it logs an `[euredact]` warning and
|
|
188
|
+
detection continues with the shared, country-independent patterns. Throwing on
|
|
189
|
+
an unknown locale invites callers to wrap the call in `try/catch` and skip
|
|
190
|
+
redaction entirely, failing open with unredacted PII.
|
|
191
|
+
|
|
192
|
+
## Country-independent detection
|
|
193
|
+
|
|
194
|
+
`countries` narrows which country-specific patterns run. It does **not** gate
|
|
195
|
+
identifiers that carry their own evidence:
|
|
196
|
+
|
|
197
|
+
| Type | Why it ignores `countries` |
|
|
198
|
+
|---|---|
|
|
199
|
+
| `BANK_ACCOUNT` (IBAN) | carries an ISO 3166 country code and a mod-97 checksum |
|
|
200
|
+
| `PHONE` (with `+` prefix) | an E.164 country prefix is self-identifying |
|
|
201
|
+
| `EMAIL`, `CREDIT_CARD`, `VIN`, `IMEI`, `IP_ADDRESS`, `UUID`, `SECRET` | no national form to gate on |
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
redact("Rekening: BE68 5390 0754 7034", { countries: ["AT"] });
|
|
205
|
+
// -> 'Rekening: [BANK_ACCOUNT]'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## BIC detection
|
|
209
|
+
|
|
210
|
+
BIC is the only bank identifier here with **no check digit**, and characters
|
|
211
|
+
5-6 of ordinary uppercase words are frequently valid ISO 3166 country codes
|
|
212
|
+
(`DRINGEND` -> `GE`, `HOSPITAL` -> `IT`). Detection is therefore gated:
|
|
213
|
+
|
|
214
|
+
| Stage | Condition | Result |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| Gate 0 | the token also occurs as an ordinary lowercase word in the same document | never emitted |
|
|
217
|
+
| Tier 1 | registry hit on the BIC6 institution+country prefix | emitted |
|
|
218
|
+
| Gate 2 | heading / shouted-word shape | never emitted |
|
|
219
|
+
| Tier 2 | `BIC`/`SWIFT` keyword, an IBAN, or a bank block in the enclosing line, record or paragraph | emitted |
|
|
220
|
+
| — | none of the above | never emitted |
|
|
221
|
+
|
|
222
|
+
The package bundles **no licensed BIC data** — only a small seed list of BIC6
|
|
223
|
+
prefixes compiled from publicly published bank data. Deployments holding a
|
|
224
|
+
licensed directory install it at startup:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { setBicRegistry } from "euredact";
|
|
228
|
+
|
|
229
|
+
setBicRegistry(["ABNANL2A", "INGBNL2A", "BBRUBE"]); // iterable
|
|
230
|
+
setBicRegistry(bic => myDirectory.has(bic)); // membership callable
|
|
231
|
+
setBicRegistry(null); // remove
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The registry is an **accept** signal, never a filter: a code missing from it
|
|
235
|
+
falls through to the context gate and is still detected when banking context is
|
|
236
|
+
present. A stale list costs a little recall on bare, contextless BICs — it
|
|
237
|
+
never causes a leak. Annual review is sufficient.
|
|
238
|
+
|
|
239
|
+
## `IBAN` is now `BANK_ACCOUNT`
|
|
240
|
+
|
|
241
|
+
The canonical type name is `BANK_ACCOUNT`; `IBAN` was a legacy alias.
|
|
242
|
+
`detection.entityType` is now `"BANK_ACCOUNT"` and the placeholder written into
|
|
243
|
+
redacted text is `[BANK_ACCOUNT]`.
|
|
244
|
+
|
|
245
|
+
`EntityType.IBAN` is kept as an alias with the same value, so
|
|
246
|
+
`EntityType.IBAN === EntityType.BANK_ACCOUNT` and code referring to the member
|
|
247
|
+
keeps working. Code matching the *string* `"IBAN"` — or the `[IBAN]`
|
|
248
|
+
placeholder — must be updated. `LEGACY_TYPE_ALIASES` publishes the mapping.
|
|
249
|
+
|
|
250
|
+
|
|
181
251
|
## Custom Patterns
|
|
182
252
|
|
|
183
253
|
Register domain-specific PII patterns at runtime:
|
package/dist/cjs/cache.js
CHANGED
|
@@ -21,10 +21,10 @@ class ResultCache {
|
|
|
21
21
|
key(text, countries, mode) {
|
|
22
22
|
const sorted = [...countries].sort();
|
|
23
23
|
const raw = `${text}|${sorted.join("|")}|${mode}`;
|
|
24
|
-
//
|
|
24
|
+
// Include raw length to reduce collision risk alongside dual FNV-1a hashes
|
|
25
25
|
const h1 = fnv1aHash(raw);
|
|
26
26
|
const h2 = fnv1aHash(raw.slice(Math.floor(raw.length / 2)) + raw.slice(0, Math.floor(raw.length / 2)));
|
|
27
|
-
return h1
|
|
27
|
+
return `${raw.length}:${h1}${h2}`;
|
|
28
28
|
}
|
|
29
29
|
get(key) {
|
|
30
30
|
if (!this.enabled)
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.COUNTRY_CONFIGS = exports.EuRedact = exports.DetectionSource = exports.EntityType = void 0;
|
|
3
|
+
exports.SEED_BIC6_PREFIXES = exports.getBicRegistry = exports.setBicRegistry = exports.COUNTRY_CONFIGS = exports.EuRedact = exports.LEGACY_TYPE_ALIASES = exports.DetectionSource = exports.EntityType = void 0;
|
|
4
4
|
exports.availableCountries = availableCountries;
|
|
5
5
|
exports.redact = redact;
|
|
6
6
|
exports.addCustomPattern = addCustomPattern;
|
|
7
|
+
exports.clear = clear;
|
|
7
8
|
exports.redactBatch = redactBatch;
|
|
8
9
|
var types_js_1 = require("./types.js");
|
|
9
10
|
Object.defineProperty(exports, "EntityType", { enumerable: true, get: function () { return types_js_1.EntityType; } });
|
|
10
11
|
Object.defineProperty(exports, "DetectionSource", { enumerable: true, get: function () { return types_js_1.DetectionSource; } });
|
|
12
|
+
Object.defineProperty(exports, "LEGACY_TYPE_ALIASES", { enumerable: true, get: function () { return types_js_1.LEGACY_TYPE_ALIASES; } });
|
|
11
13
|
var sdk_js_1 = require("./sdk.js");
|
|
12
14
|
Object.defineProperty(exports, "EuRedact", { enumerable: true, get: function () { return sdk_js_1.EuRedact; } });
|
|
13
15
|
var index_js_1 = require("./rules/countries/index.js");
|
|
14
16
|
Object.defineProperty(exports, "COUNTRY_CONFIGS", { enumerable: true, get: function () { return index_js_1.COUNTRY_CONFIGS; } });
|
|
17
|
+
var bicRegistry_js_1 = require("./rules/bicRegistry.js");
|
|
18
|
+
Object.defineProperty(exports, "setBicRegistry", { enumerable: true, get: function () { return bicRegistry_js_1.setBicRegistry; } });
|
|
19
|
+
Object.defineProperty(exports, "getBicRegistry", { enumerable: true, get: function () { return bicRegistry_js_1.getBicRegistry; } });
|
|
20
|
+
Object.defineProperty(exports, "SEED_BIC6_PREFIXES", { enumerable: true, get: function () { return bicRegistry_js_1.SEED_BIC6_PREFIXES; } });
|
|
15
21
|
const sdk_js_2 = require("./sdk.js");
|
|
16
22
|
const index_js_2 = require("./rules/countries/index.js");
|
|
17
23
|
let _instance = null;
|
|
@@ -29,6 +35,9 @@ function redact(text, options) {
|
|
|
29
35
|
function addCustomPattern(name, pattern) {
|
|
30
36
|
getInstance().addCustomPattern(name, pattern);
|
|
31
37
|
}
|
|
38
|
+
function clear() {
|
|
39
|
+
getInstance().clear();
|
|
40
|
+
}
|
|
32
41
|
function redactBatch(texts, options) {
|
|
33
42
|
return getInstance().redactBatch(texts, options);
|
|
34
43
|
}
|
package/dist/cjs/normalizer.js
CHANGED
|
@@ -9,27 +9,25 @@ function normalize(text) {
|
|
|
9
9
|
return [normalized, buildOffsetMapping(text, normalized)];
|
|
10
10
|
}
|
|
11
11
|
function buildOffsetMapping(original, normalized) {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
12
|
+
// Precompute cumulative NFD lengths for normalized characters
|
|
13
|
+
const normNfdCum = [0];
|
|
14
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
15
|
+
normNfdCum.push(normNfdCum[i] + normalized[i].normalize("NFD").length);
|
|
16
|
+
}
|
|
17
|
+
// Precompute cumulative NFD lengths for original characters
|
|
18
|
+
const origNfdCum = [0];
|
|
19
|
+
for (let i = 0; i < original.length; i++) {
|
|
20
|
+
origNfdCum.push(origNfdCum[i] + original[i].normalize("NFD").length);
|
|
21
|
+
}
|
|
22
|
+
// Two-pointer scan: map each normalized position to its original position
|
|
15
23
|
const normToOrig = [];
|
|
24
|
+
let origPtr = 0;
|
|
16
25
|
for (let normIdx = 0; normIdx < normalized.length; normIdx++) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
nfdStart += normalized[i].normalize("NFD").length;
|
|
21
|
-
}
|
|
22
|
-
let targetOrig = 0;
|
|
23
|
-
let running = 0;
|
|
24
|
-
for (let oi = 0; oi < original.length; oi++) {
|
|
25
|
-
const nfdOc = original[oi].normalize("NFD");
|
|
26
|
-
if (running + nfdOc.length > nfdStart) {
|
|
27
|
-
targetOrig = oi;
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
running += nfdOc.length;
|
|
26
|
+
const nfdStart = normNfdCum[normIdx];
|
|
27
|
+
while (origPtr + 1 < original.length && origNfdCum[origPtr + 1] <= nfdStart) {
|
|
28
|
+
origPtr++;
|
|
31
29
|
}
|
|
32
|
-
normToOrig.push(
|
|
30
|
+
normToOrig.push(origPtr);
|
|
33
31
|
}
|
|
34
32
|
return normToOrig;
|
|
35
33
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BIC registry — tier-1 membership testing for ISO 9362 bank identifier codes.
|
|
4
|
+
*
|
|
5
|
+
* BIC is the only bank identifier in the supported set with **no check digit**
|
|
6
|
+
* (IBAN has mod-97, VAT has country-specific checksums). Structure alone cannot
|
|
7
|
+
* separate a genuine BIC from an ordinary 8- or 11-character uppercase word,
|
|
8
|
+
* because characters 5-6 of everyday words are frequently valid ISO 3166
|
|
9
|
+
* country codes (`DRINGEND` -> `GE`, `HOSPITAL` -> `IT`). Registry membership
|
|
10
|
+
* is therefore the strongest precision signal available.
|
|
11
|
+
*
|
|
12
|
+
* Two sources are consulted, in order:
|
|
13
|
+
*
|
|
14
|
+
* 1. A deployment-supplied registry, installed via {@link setBicRegistry}.
|
|
15
|
+
* This is the path for anyone holding a licensed directory (SWIFTRef).
|
|
16
|
+
* 2. {@link SEED_BIC6_PREFIXES} — a small bundled set of institution+country
|
|
17
|
+
* prefixes for major European banks, compiled from publicly published bank
|
|
18
|
+
* data. No licensed directory is bundled.
|
|
19
|
+
*
|
|
20
|
+
* Matching is on the **BIC6 prefix** (4-letter institution + 2-letter country),
|
|
21
|
+
* so any location and branch code on a known institution is accepted. The
|
|
22
|
+
* prefix is the stable part of a BIC; branch codes churn as branches open and
|
|
23
|
+
* close.
|
|
24
|
+
*
|
|
25
|
+
* The registry is used as an *accept* signal, never as a filter: a candidate
|
|
26
|
+
* that misses it falls through to the context gate in `suppressors.ts` and is
|
|
27
|
+
* still detected when banking context is present. A stale list therefore costs
|
|
28
|
+
* a little recall on bare, contextless BICs — it never causes a leak. Annual
|
|
29
|
+
* review is sufficient.
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.SEED_BIC6_PREFIXES = void 0;
|
|
33
|
+
exports.setBicRegistry = setBicRegistry;
|
|
34
|
+
exports.getBicRegistry = getBicRegistry;
|
|
35
|
+
exports.isRegisteredBic = isRegisteredBic;
|
|
36
|
+
// BIC6 prefixes (institution + country) of major European institutions,
|
|
37
|
+
// compiled from publicly published bank data — not from a licensed directory.
|
|
38
|
+
// Kept deliberately small: this covers the banks that appear bare and
|
|
39
|
+
// contextless in real documents. Anything outside this list is still detected
|
|
40
|
+
// via the context gate.
|
|
41
|
+
exports.SEED_BIC6_PREFIXES = new Set([
|
|
42
|
+
// --- Netherlands ---
|
|
43
|
+
"ABNANL", // ABN AMRO
|
|
44
|
+
"INGBNL", // ING
|
|
45
|
+
"RABONL", // Rabobank
|
|
46
|
+
"SNSBNL", // SNS Bank
|
|
47
|
+
"TRIONL", // Triodos
|
|
48
|
+
"BUNQNL", // bunq
|
|
49
|
+
"KNABNL", // Knab
|
|
50
|
+
"ASNBNL", // ASN Bank
|
|
51
|
+
"RBRBNL", // RegioBank
|
|
52
|
+
"FVLBNL", // Van Lanschot
|
|
53
|
+
"BITSNL", // BinckBank
|
|
54
|
+
"DEUTNL", // Deutsche Bank NL
|
|
55
|
+
// --- Belgium ---
|
|
56
|
+
"BBRUBE", // BNP Paribas Fortis
|
|
57
|
+
"GEBABE", // BNP Paribas Fortis (legacy)
|
|
58
|
+
"KREDBE", // KBC
|
|
59
|
+
"GKCCBE", // Belfius
|
|
60
|
+
"BPOTBE", // bpost bank
|
|
61
|
+
"ARSPBE", // Argenta
|
|
62
|
+
"AXABBE", // AXA Bank Belgium
|
|
63
|
+
"CREGBE", // Belfius (legacy)
|
|
64
|
+
"NICABE", // Crelan
|
|
65
|
+
"TRIOBE", // Triodos Belgium
|
|
66
|
+
"CTBKBE", // Banca Monte Paschi Belgio
|
|
67
|
+
"JVBABE", // Van Breda
|
|
68
|
+
// --- Germany ---
|
|
69
|
+
"DEUTDE", // Deutsche Bank
|
|
70
|
+
"COBADE", // Commerzbank
|
|
71
|
+
"PBNKDE", // Postbank
|
|
72
|
+
"GENODE", // Volksbanken / Raiffeisen
|
|
73
|
+
"MALADE", // Sparkassen
|
|
74
|
+
"HELADE", // Helaba / Sparkassen
|
|
75
|
+
"BYLADE", // BayernLB / Sparkassen
|
|
76
|
+
"SOLADE", // Sparkassen (Baden-Wurttemberg)
|
|
77
|
+
"WELADE", // Sparkassen (Westfalen)
|
|
78
|
+
"NOLADE", // Norddeutsche Landesbank
|
|
79
|
+
"SPKKDE", // Sparkasse
|
|
80
|
+
"HYVEDE", // UniCredit / HypoVereinsbank
|
|
81
|
+
"DRESDE", // Dresdner (legacy Commerzbank)
|
|
82
|
+
"INGDDE", // ING-DiBa
|
|
83
|
+
"NTSBDE", // N26
|
|
84
|
+
"SFRTDE", // Sutor / fintech BaaS
|
|
85
|
+
"TEDADE", // Telda / BaaS
|
|
86
|
+
// --- France ---
|
|
87
|
+
"BNPAFR", // BNP Paribas
|
|
88
|
+
"SOGEFR", // Societe Generale
|
|
89
|
+
"AGRIFR", // Credit Agricole
|
|
90
|
+
"CCBPFR", // Banque Populaire
|
|
91
|
+
"CEPAFR", // Caisse d'Epargne
|
|
92
|
+
"CMCIFR", // Credit Mutuel / CIC
|
|
93
|
+
"PSSTFR", // La Banque Postale
|
|
94
|
+
"BREDFR", // BRED Banque Populaire
|
|
95
|
+
"CRLYFR", // LCL
|
|
96
|
+
"HSBCFR", // HSBC France
|
|
97
|
+
"TRZOFR", // Treezor
|
|
98
|
+
"QNTOFR", // Qonto
|
|
99
|
+
// --- Austria ---
|
|
100
|
+
"GIBAAT", // Erste Bank / Sparkassen
|
|
101
|
+
"BKAUAT", // UniCredit Bank Austria
|
|
102
|
+
"RZBAAT", // Raiffeisen Bank International
|
|
103
|
+
"RLNWAT", // Raiffeisen NO-Wien
|
|
104
|
+
"OBKLAT", // Oberbank
|
|
105
|
+
"BAWAAT", // BAWAG P.S.K.
|
|
106
|
+
"VBOEAT", // Volksbank
|
|
107
|
+
"HYPTAT", // Hypo Tirol
|
|
108
|
+
"SPIHAT", // Sparkasse
|
|
109
|
+
// --- Luxembourg ---
|
|
110
|
+
"BCEELU", // Banque et Caisse d'Epargne de l'Etat
|
|
111
|
+
"BILLLU", // Banque Internationale a Luxembourg
|
|
112
|
+
"BGLLLU", // BGL BNP Paribas
|
|
113
|
+
"CCRALU", // Banque Raiffeisen
|
|
114
|
+
"BLUXLU", // Banque de Luxembourg
|
|
115
|
+
// --- Ireland / UK ---
|
|
116
|
+
"AIBKIE", // Allied Irish Banks
|
|
117
|
+
"BOFIIE", // Bank of Ireland
|
|
118
|
+
"ULSBIE", // Ulster Bank
|
|
119
|
+
"CITIIE", // Citibank Ireland
|
|
120
|
+
"BARCGB", // Barclays
|
|
121
|
+
"HBUKGB", // HSBC UK
|
|
122
|
+
"MIDLGB", // HSBC (legacy Midland)
|
|
123
|
+
"LOYDGB", // Lloyds
|
|
124
|
+
"NWBKGB", // NatWest
|
|
125
|
+
"RBOSGB", // Royal Bank of Scotland
|
|
126
|
+
"TSBSGB", // TSB
|
|
127
|
+
"REVOGB", // Revolut
|
|
128
|
+
"MONZGB", // Monzo
|
|
129
|
+
"STARGB", // Starling
|
|
130
|
+
"CHASGB", // JP Morgan Chase UK
|
|
131
|
+
// --- Nordics ---
|
|
132
|
+
"NDEADK", // Nordea Denmark
|
|
133
|
+
"DABADK", // Danske Bank
|
|
134
|
+
"JYBADK", // Jyske Bank
|
|
135
|
+
"SYBKDK", // Sydbank
|
|
136
|
+
"NDEASE", // Nordea Sweden
|
|
137
|
+
"ESSESE", // SEB
|
|
138
|
+
"HANDSE", // Handelsbanken
|
|
139
|
+
"SWEDSE", // Swedbank
|
|
140
|
+
"DNBANO", // DNB
|
|
141
|
+
"NDEANO", // Nordea Norway
|
|
142
|
+
"NDEAFI", // Nordea Finland
|
|
143
|
+
"OKOYFI", // OP Corporate Bank
|
|
144
|
+
"HELSFI", // Aktia
|
|
145
|
+
"NBIIIS", // Landsbankinn (NBIIISRE)
|
|
146
|
+
"ESJAIS", // Arion Bank (ESJAISRE)
|
|
147
|
+
"GLITIS", // Islandsbanki (GLITISRE)
|
|
148
|
+
"MPBAIS", // Kvika banki (MPBAISRE)
|
|
149
|
+
"KBNONO", // BNbank ASA (KBNONO22)
|
|
150
|
+
"PRBONO", // Boligbanken ASA (PRBONO22)
|
|
151
|
+
"AARBDE", // Aareal Bank AG (AARBDE5W)
|
|
152
|
+
// --- Southern / Central Europe ---
|
|
153
|
+
"UNCRIT", // UniCredit Italy
|
|
154
|
+
"BCITIT", // Intesa Sanpaolo
|
|
155
|
+
"PASCIT", // Monte dei Paschi
|
|
156
|
+
"BPMOIT", // Banco BPM
|
|
157
|
+
"CRPPIT", // Cassa di Risparmio
|
|
158
|
+
"BSCHES", // Banco Santander
|
|
159
|
+
"BBVAES", // BBVA
|
|
160
|
+
"CAIXES", // CaixaBank
|
|
161
|
+
"SABBES", // Banco Sabadell
|
|
162
|
+
"BKBKES", // Bankinter
|
|
163
|
+
"BCOMPT", // Millennium BCP
|
|
164
|
+
"CGDIPT", // Caixa Geral de Depositos
|
|
165
|
+
"BPIPPT", // Banco BPI
|
|
166
|
+
"NOVBPT", // Novo Banco
|
|
167
|
+
"BPKOPL", // PKO Bank Polski
|
|
168
|
+
"BREXPL", // mBank
|
|
169
|
+
"INGBPL", // ING Bank Slaski
|
|
170
|
+
"WBKPPL", // Santander Bank Polska
|
|
171
|
+
"KOMBCZ", // Komercni banka
|
|
172
|
+
"CEKOCZ", // CSOB
|
|
173
|
+
"GIBACZ", // Ceska sporitelna
|
|
174
|
+
"TATRSK", // Tatra banka
|
|
175
|
+
"SUBASK", // Vseobecna uverova banka
|
|
176
|
+
"GIBASK", // Slovenska sporitelna
|
|
177
|
+
"OTPVHU", // OTP Bank
|
|
178
|
+
"BUDAHU", // Budapest Bank
|
|
179
|
+
"MKKBHU", // MKB Bank
|
|
180
|
+
// --- Switzerland ---
|
|
181
|
+
"UBSWCH", // UBS
|
|
182
|
+
"CRESCH", // Credit Suisse
|
|
183
|
+
"POFICH", // PostFinance
|
|
184
|
+
"ZKBKCH", // Zurcher Kantonalbank
|
|
185
|
+
"RAIFCH", // Raiffeisen Switzerland
|
|
186
|
+
"MIGRCH", // Migros Bank
|
|
187
|
+
]);
|
|
188
|
+
let provider = null;
|
|
189
|
+
/**
|
|
190
|
+
* Install a deployment-supplied BIC registry, consulted ahead of the seed list.
|
|
191
|
+
*
|
|
192
|
+
* Accepts a membership callable, any iterable of BIC strings, or `null` to
|
|
193
|
+
* remove a previously installed registry. Entries may be full BIC8/BIC11 codes
|
|
194
|
+
* or bare BIC6 prefixes; both are matched, case-insensitively and ignoring
|
|
195
|
+
* spaces.
|
|
196
|
+
*/
|
|
197
|
+
function setBicRegistry(registry) {
|
|
198
|
+
if (registry === null) {
|
|
199
|
+
provider = null;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (typeof registry === "function") {
|
|
203
|
+
provider = registry;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const known = new Set();
|
|
207
|
+
for (const entry of registry) {
|
|
208
|
+
const e = String(entry).trim();
|
|
209
|
+
if (e && !e.startsWith("#"))
|
|
210
|
+
known.add(e.replace(/ /g, "").toUpperCase());
|
|
211
|
+
}
|
|
212
|
+
provider = (bic) => known.has(bic);
|
|
213
|
+
}
|
|
214
|
+
/** Return the installed registry provider, or null if none is installed. */
|
|
215
|
+
function getBicRegistry() {
|
|
216
|
+
return provider;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Tier 1: is `candidate` a BIC of a known institution?
|
|
220
|
+
*
|
|
221
|
+
* Checks the deployment-supplied registry first (full code, then BIC6 prefix),
|
|
222
|
+
* then the bundled seed prefixes.
|
|
223
|
+
*/
|
|
224
|
+
function isRegisteredBic(candidate) {
|
|
225
|
+
const c = candidate.replace(/ /g, "").toUpperCase();
|
|
226
|
+
if (c.length !== 8 && c.length !== 11)
|
|
227
|
+
return false;
|
|
228
|
+
const prefix = c.slice(0, 6);
|
|
229
|
+
if (provider !== null) {
|
|
230
|
+
try {
|
|
231
|
+
if (provider(c) || provider(prefix))
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// a broken provider must not break detection
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return exports.SEED_BIC6_PREFIXES.has(prefix);
|
|
239
|
+
}
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.COUNTRY_CONFIGS = void 0;
|
|
4
4
|
const types_js_1 = require("../../types.js");
|
|
5
|
+
const validators_js_1 = require("../validators.js");
|
|
6
|
+
/**
|
|
7
|
+
* Build a country-independent IBAN pattern from the ISO 13616 length table.
|
|
8
|
+
*
|
|
9
|
+
* An IBAN carries its own country code and a mod-97 checksum, so it is
|
|
10
|
+
* self-identifying and must never depend on the caller's `countries` setting —
|
|
11
|
+
* a Belgian account in an Austrian file is still an account.
|
|
12
|
+
*
|
|
13
|
+
* Each alternative pins the *exact* character count for its country, which
|
|
14
|
+
* stops the match absorbing a following word: without it, greedy matching
|
|
15
|
+
* turns `BE68 5390 0754 7034 KBC` into one over-long candidate that fails
|
|
16
|
+
* mod-97 and is lost entirely.
|
|
17
|
+
*/
|
|
18
|
+
function buildIbanPattern() {
|
|
19
|
+
const alternatives = Object.entries(validators_js_1.IBAN_LENGTHS)
|
|
20
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
21
|
+
.map(([code, length]) => `${code}\\d{2}(?:[ \\-]?[A-Z0-9]){${length - 4}}`);
|
|
22
|
+
return String.raw `\b(?:` + alternatives.join("|") + String.raw `)\b`;
|
|
23
|
+
}
|
|
24
|
+
const GENERIC_IBAN = buildIbanPattern();
|
|
5
25
|
/**
|
|
6
26
|
* Helper to create a PatternDef with sensible defaults.
|
|
7
27
|
*/
|
|
@@ -9,6 +29,45 @@ function p(entityType, pattern, validator = null, description = "", contextKeywo
|
|
|
9
29
|
return { entityType, pattern, validator, description, contextKeywords, requiresContext };
|
|
10
30
|
}
|
|
11
31
|
// ---------------------------------------------------------------------------
|
|
32
|
+
// Secret context keywords (consolidated, reused across SECRET patterns)
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
const SECRET_CONTEXT = [
|
|
35
|
+
// English
|
|
36
|
+
"key", "token", "secret", "password", "credential",
|
|
37
|
+
"api_key", "apikey", "api-key", "auth", "bearer",
|
|
38
|
+
"aws_secret_access_key", "access_key", "private_key",
|
|
39
|
+
"AES_KEY", "ENCRYPTION_KEY", "SIGNING_KEY",
|
|
40
|
+
"ACCOUNT_SID", "AUTH_TOKEN", "TWILIO",
|
|
41
|
+
"RAILS_SECRET", "SECRET_KEY_BASE", "DATABASE_URL",
|
|
42
|
+
"NPM_TOKEN", "DIGITALOCEAN", "GOOGLE_API",
|
|
43
|
+
"DB_PASS", "DB_PASSWORD", "DATASOURCE_PASSWORD",
|
|
44
|
+
"JWT_SIGNING", "passwd", "DISCORD_TOKEN",
|
|
45
|
+
// Dutch
|
|
46
|
+
"wachtwoord", "sleutel", "geheim",
|
|
47
|
+
// French
|
|
48
|
+
"mot de passe", "mot_de_passe", "clé", "jeton",
|
|
49
|
+
// German
|
|
50
|
+
"Passwort", "Kennwort", "Schlüssel", "Geheimnis",
|
|
51
|
+
// Italian
|
|
52
|
+
"chiave", "segreto",
|
|
53
|
+
// Spanish
|
|
54
|
+
"contraseña", "clave",
|
|
55
|
+
// Portuguese
|
|
56
|
+
"senha", "chave",
|
|
57
|
+
// Polish
|
|
58
|
+
"hasło", "klucz",
|
|
59
|
+
// Czech/Slovak
|
|
60
|
+
"heslo", "kľúč",
|
|
61
|
+
// Hungarian
|
|
62
|
+
"jelszó", "kulcs",
|
|
63
|
+
// Romanian
|
|
64
|
+
"parola",
|
|
65
|
+
// Nordic
|
|
66
|
+
"lösenord", "nøkkel", "nyckel", "avain", "lykill",
|
|
67
|
+
// Greek
|
|
68
|
+
"κωδικός",
|
|
69
|
+
];
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
12
71
|
// DOB context keywords (reused across SHARED patterns)
|
|
13
72
|
// ---------------------------------------------------------------------------
|
|
14
73
|
const DOB_CONTEXT = [
|
|
@@ -36,8 +95,28 @@ const SHARED = {
|
|
|
36
95
|
code: "SHARED",
|
|
37
96
|
name: "Shared EU Patterns",
|
|
38
97
|
patterns: [
|
|
39
|
-
|
|
98
|
+
// JavaScript's \w and \b are ASCII-only, so the Python engine's \w (which
|
|
99
|
+
// is Unicode-aware) matched local parts like "petras_ž@..." while this one
|
|
100
|
+
// did not. \p{L}\p{N} with the `u` flag restores parity; the leading guard
|
|
101
|
+
// is a Unicode-safe word boundary.
|
|
102
|
+
p(types_js_1.EntityType.EMAIL, String.raw `(?<![\p{L}\p{N}._%+\-])[\p{L}\p{N}._%+\-]+@(?:[\p{L}\p{N}\-]+\.)+\p{L}{2,}\b`),
|
|
103
|
+
// IBAN (any country) — structure + mod-97 only. Country-specific IBAN
|
|
104
|
+
// patterns still exist and win on identical spans; this one guarantees an
|
|
105
|
+
// IBAN is never missed just because its country was not requested.
|
|
106
|
+
p(types_js_1.EntityType.BANK_ACCOUNT, GENERIC_IBAN, "iban", "IBAN — any country (ISO 13616, mod-97 validated)"),
|
|
107
|
+
// International phone (E.164, any country). Runs alongside the per-country
|
|
108
|
+
// patterns, which each hard-code a single grouping and miss the rest:
|
|
109
|
+
// "+43 664 8213 907" was invisible to the Austrian pattern, which expects
|
|
110
|
+
// an unbroken subscriber number. A leading "+" is self-identifying, so
|
|
111
|
+
// this is deliberately not country-gated.
|
|
112
|
+
p(types_js_1.EntityType.PHONE, String.raw `\+\d{1,3}(?:[\s\-/]?\(?\d{1,4}\)?){2,7}`, "e164", "International phone number (E.164) — any country"),
|
|
40
113
|
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"),
|
|
114
|
+
// Space-separated form: "RZBA AT WW", "NICA BE BB". Written as a separate
|
|
115
|
+
// pattern requiring a space between *every* group, rather than making the
|
|
116
|
+
// spaces optional above — optional spaces would let "GEBABEBB KBC" match
|
|
117
|
+
// as one 11-character code and claim the following word. Only a literal
|
|
118
|
+
// space is allowed, so a match cannot span a line break.
|
|
119
|
+
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", "BIC/SWIFT code (space-separated groups)"),
|
|
41
120
|
p(types_js_1.EntityType.CREDIT_CARD, String.raw `\b(?:4[0-9]{3}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}|5[1-5][0-9]{2}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}|3[47][0-9]{2}[\s\-]?[0-9]{6}[\s\-]?[0-9]{5})\b`, "luhn"),
|
|
42
121
|
p(types_js_1.EntityType.VIN, String.raw `\b[A-HJ-NPR-Z0-9]{17}\b`, "vin"),
|
|
43
122
|
p(types_js_1.EntityType.IP_ADDRESS, String.raw `\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b`),
|
|
@@ -48,7 +127,7 @@ const SHARED = {
|
|
|
48
127
|
p(types_js_1.EntityType.IMEI, String.raw `\b\d{2}[\-\s]\d{6}[\-\s]\d{6}[\-\s]\d\b`, "imei", "formatted"),
|
|
49
128
|
p(types_js_1.EntityType.GPS_COORDINATES, String.raw `-?(?:[1-8]?\d(?:\.\d{4,})|90(?:\.0{4,}))\s*[,;/]\s*-?(?:1[0-7]\d(?:\.\d{4,})|180(?:\.0{4,})|\d{1,2}(?:\.\d{4,}))`),
|
|
50
129
|
p(types_js_1.EntityType.UUID, String.raw `\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\b`),
|
|
51
|
-
p(types_js_1.EntityType.SOCIAL_HANDLE, String.raw `(
|
|
130
|
+
p(types_js_1.EntityType.SOCIAL_HANDLE, String.raw `(?<![\p{L}\p{N}_])@\p{L}[\p{L}\p{N}_.]{1,29}\b`),
|
|
52
131
|
// --- Secret / API Key (known prefixes — always active) ---
|
|
53
132
|
p(types_js_1.EntityType.SECRET, String.raw `\bAKIA[A-Z0-9]{16}\b`, null, "AWS Access Key ID"),
|
|
54
133
|
p(types_js_1.EntityType.SECRET, String.raw `\bgh[poas]_[a-zA-Z0-9]{36,}\b`, null, "GitHub token"),
|
|
@@ -70,35 +149,18 @@ const SHARED = {
|
|
|
70
149
|
p(types_js_1.EntityType.SECRET, String.raw `\bxapp-\d[\d\-]{20,}`, null, "Slack app numeric token"),
|
|
71
150
|
p(types_js_1.EntityType.SECRET, String.raw `\bxapp-[a-zA-Z0-9\-]{20,}`, null, "Slack app token"),
|
|
72
151
|
p(types_js_1.EntityType.SECRET, String.raw `\bdapi[a-f0-9]{32,}\b`, null, "Databricks PAT"),
|
|
152
|
+
// Discord bot token (3-segment base64 with numeric first segment)
|
|
153
|
+
p(types_js_1.EntityType.SECRET, String.raw `\b[MN][A-Za-z0-9]{23,}\.[A-Za-z0-9_\-]{6}\.[A-Za-z0-9_\-]{27,}\b`, null, "Discord bot token"),
|
|
154
|
+
// PEM private key headers
|
|
155
|
+
p(types_js_1.EntityType.SECRET, String.raw `-----BEGIN\s(?:RSA\s)?PRIVATE\sKEY-----[\s\S]{0,16384}?-----END\s(?:RSA\s)?PRIVATE\sKEY-----`, null, "PEM private key"),
|
|
156
|
+
// AWS Secret Access Key (40 chars, base64 with +/ after an AKIA line)
|
|
157
|
+
p(types_js_1.EntityType.SECRET, String.raw `(?<=aws_secret_access_key\s{0,4}[:=]\s{0,4})[A-Za-z0-9+/]{40}`, null, "AWS Secret Access Key"),
|
|
73
158
|
// --- Secret / API Key (connection strings with embedded credentials) ---
|
|
74
|
-
p(types_js_1.EntityType.SECRET, String.raw `(?:mongodb|mysql|postgres(?:ql)?|redis|amqp|rabbitmq):\/\/[^\s:]
|
|
159
|
+
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"),
|
|
75
160
|
// --- Secret / API Key (assignment-based: KEY=value or KEY: value) ---
|
|
76
|
-
p(types_js_1.EntityType.SECRET, String.raw `(?<=[:=] )\S{8,}|(?<=[:=])\S{8,}`, "high_entropy", "Assigned secret value",
|
|
77
|
-
"key", "token", "secret", "password", "credential",
|
|
78
|
-
"api_key", "apikey", "api-key", "auth", "bearer",
|
|
79
|
-
"aws_secret_access_key", "access_key", "private_key",
|
|
80
|
-
"wachtwoord", "mot de passe", "Passwort", "Schlüssel",
|
|
81
|
-
"clé", "sleutel", "lösenord", "hasło", "heslo",
|
|
82
|
-
"jelszó", "contraseña", "senha", "parola",
|
|
83
|
-
"AES_KEY", "ENCRYPTION_KEY", "SIGNING_KEY",
|
|
84
|
-
"ACCOUNT_SID", "AUTH_TOKEN", "TWILIO",
|
|
85
|
-
"RAILS_SECRET", "SECRET_KEY_BASE", "DATABASE_URL",
|
|
86
|
-
"NPM_TOKEN", "DIGITALOCEAN", "GOOGLE_API",
|
|
87
|
-
"kľúč", "nøkkel", "nyckel", "avain", "lykill",
|
|
88
|
-
"DB_PASS", "DB_PASSWORD", "DATASOURCE_PASSWORD",
|
|
89
|
-
"JWT_SIGNING", "SIGNING_KEY",
|
|
90
|
-
"passwd", "Kennwort",
|
|
91
|
-
], true),
|
|
161
|
+
p(types_js_1.EntityType.SECRET, String.raw `(?<=[:=] )\S{8,}|(?<=[:=])\S{8,}`, "high_entropy", "Assigned secret value", SECRET_CONTEXT, true),
|
|
92
162
|
// --- Secret / API Key (entropy-based fallback for longer tokens) ---
|
|
93
|
-
p(types_js_1.EntityType.SECRET, String.raw `\b[A-Za-z0-9_\-+/]{24,}[A-Za-z0-9_\-+/=]*\b`, "high_entropy", "High-entropy token",
|
|
94
|
-
"key", "token", "secret", "password", "credential",
|
|
95
|
-
"api_key", "apikey", "api-key", "auth", "bearer",
|
|
96
|
-
"aws_secret_access_key", "access_key", "private_key",
|
|
97
|
-
"wachtwoord", "mot de passe", "Passwort", "Schlüssel",
|
|
98
|
-
"clé", "sleutel", "lösenord", "hasło", "heslo",
|
|
99
|
-
"jelszó", "contraseña", "senha", "parola",
|
|
100
|
-
"kľúč", "nøkkel", "nyckel", "avain", "lykill",
|
|
101
|
-
], true),
|
|
163
|
+
p(types_js_1.EntityType.SECRET, String.raw `\b[A-Za-z0-9_\-+/]{24,}[A-Za-z0-9_\-+/=]*\b`, "high_entropy", "High-entropy token", SECRET_CONTEXT, true),
|
|
102
164
|
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),
|
|
103
165
|
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),
|
|
104
166
|
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_ISO_CONTEXT, true),
|
|
@@ -155,7 +217,13 @@ const DE = {
|
|
|
155
217
|
p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{1,4}[\s/\-]?\d{3,8}\b`),
|
|
156
218
|
p(types_js_1.EntityType.PHONE, String.raw `\+49\s?\d{2,5}[\s/\-]?\d{3,8}\b`),
|
|
157
219
|
p(types_js_1.EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9A-Z]{8,9}\b`, null, "", ["Reisepass", "passport", "Passnummer", "Reisepassnummer", "Reisepass Nummer", "Reisepass-Nr", "Pass Nr"], true),
|
|
158
|
-
|
|
220
|
+
// The separator after the city code is mandatory. When it was optional, a
|
|
221
|
+
// contiguous letter run split across both groups and the hyphen was
|
|
222
|
+
// consumed as the *second* separator, so any "LETTERS-DIGITS" token
|
|
223
|
+
// matched: ICD-10 parsed as city "IC" + letters "D" + "-" + "10", and
|
|
224
|
+
// ISO-9001, COVID-19, RFC-2822, POL-2025 likewise. A real plate always
|
|
225
|
+
// separates the city code: "B-AB 1234", "M-XY 99".
|
|
226
|
+
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`),
|
|
159
227
|
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),
|
|
160
228
|
p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bHR[AB]\s?\d{4,6}\b`),
|
|
161
229
|
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"], true),
|
|
@@ -172,6 +240,11 @@ const AT = {
|
|
|
172
240
|
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"),
|
|
173
241
|
p(types_js_1.EntityType.VAT, String.raw `\bATU\d{8}\b`),
|
|
174
242
|
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{3,4}[\s\-]?\d{5,8}\b`),
|
|
243
|
+
// Short area codes (Vienna is "01") split across three groups:
|
|
244
|
+
// "01 53460 2215". The pattern above requires 3-4 digits after the trunk
|
|
245
|
+
// 0, so it never matched these. Both separators are mandatory here, which
|
|
246
|
+
// keeps "01 2025" (a date fragment) out.
|
|
247
|
+
p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,4}[\s\-/]\d{3,7}[\s\-/]\d{2,6}\b`, null, "Austrian national phone — short area code, grouped"),
|
|
175
248
|
p(types_js_1.EntityType.PHONE, String.raw `\+43\s?\d{3,4}[\s\-]?\d{5,8}`),
|
|
176
249
|
p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-ZÄÖÜ]{1,2}\s?\d{1,5}\s?[A-Z]{1,2}\b`, null, "", ["Kennzeichen", "Nummernschild", "Kfz-Kennzeichen"], true),
|
|
177
250
|
p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["PLZ", "Postleitzahl", "Adresse", "Anschrift", "Straße", "Str.", "Gasse", "Weg", "Platz", "Postal:", "Wohnort"], true),
|