euredact 0.0.1 → 0.2.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/LICENSE +189 -0
- package/README.md +339 -10
- package/dist/cjs/cache.js +56 -0
- package/dist/cjs/index.js +34 -0
- package/dist/cjs/normalizer.js +45 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rules/countries/index.js +628 -0
- package/dist/cjs/rules/engine.js +129 -0
- package/dist/cjs/rules/matchers.js +56 -0
- package/dist/cjs/rules/structural.js +104 -0
- package/dist/cjs/rules/suppressors.js +211 -0
- package/dist/cjs/rules/validators.js +699 -0
- package/dist/cjs/sdk.js +80 -0
- package/dist/cjs/types.js +42 -0
- package/dist/esm/cache.d.ts +11 -0
- package/dist/esm/cache.js +52 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/normalizer.d.ts +2 -0
- package/dist/esm/normalizer.js +41 -0
- package/dist/esm/rules/countries/index.d.ts +2 -0
- package/dist/esm/rules/countries/index.js +625 -0
- package/dist/esm/rules/engine.d.ts +15 -0
- package/dist/esm/rules/engine.js +125 -0
- package/dist/esm/rules/matchers.d.ts +12 -0
- package/dist/esm/rules/matchers.js +52 -0
- package/dist/esm/rules/structural.d.ts +2 -0
- package/dist/esm/rules/structural.js +101 -0
- package/dist/esm/rules/suppressors.d.ts +9 -0
- package/dist/esm/rules/suppressors.js +208 -0
- package/dist/esm/rules/validators.d.ts +43 -0
- package/dist/esm/rules/validators.js +654 -0
- package/dist/esm/sdk.d.ts +16 -0
- package/dist/esm/sdk.js +76 -0
- package/dist/esm/types.d.ts +65 -0
- package/dist/esm/types.js +39 -0
- package/package.json +55 -9
- package/index.js +0 -3
package/dist/esm/sdk.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ResultCache } from "./cache.js";
|
|
2
|
+
import { normalize, mapOffsetToOriginal } from "./normalizer.js";
|
|
3
|
+
import { RuleEngine } from "./rules/engine.js";
|
|
4
|
+
import { EntityType } from "./types.js";
|
|
5
|
+
const DATE_TYPES = new Set([EntityType.DOB, EntityType.DATE_OF_DEATH]);
|
|
6
|
+
class ReferentialMapper {
|
|
7
|
+
counters = new Map();
|
|
8
|
+
mapping = new Map();
|
|
9
|
+
getLabel(text, entityType) {
|
|
10
|
+
if (!this.mapping.has(text)) {
|
|
11
|
+
const count = (this.counters.get(entityType) ?? 0) + 1;
|
|
12
|
+
this.counters.set(entityType, count);
|
|
13
|
+
this.mapping.set(text, `${entityType}_${count}`);
|
|
14
|
+
}
|
|
15
|
+
return this.mapping.get(text);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class EuRedact {
|
|
19
|
+
engine = new RuleEngine();
|
|
20
|
+
cache = new ResultCache();
|
|
21
|
+
referentialMapper = new ReferentialMapper();
|
|
22
|
+
addCustomPattern(name, pattern) {
|
|
23
|
+
this.engine.addCustomPattern(name, pattern);
|
|
24
|
+
this.cache.clear();
|
|
25
|
+
}
|
|
26
|
+
redact(text, options = {}) {
|
|
27
|
+
const { countries = null, mode = "rules", referentialIntegrity = false, detectDates = false, cache = true, } = options;
|
|
28
|
+
const [normalizedText, offsetMapping] = normalize(text);
|
|
29
|
+
const countriesTuple = countries
|
|
30
|
+
? countries.map(c => c.toUpperCase()).sort()
|
|
31
|
+
: ["ALL"];
|
|
32
|
+
const cacheMode = `${mode}|dates=${detectDates}`;
|
|
33
|
+
let cacheKey;
|
|
34
|
+
if (cache) {
|
|
35
|
+
cacheKey = this.cache.key(normalizedText, countriesTuple, cacheMode);
|
|
36
|
+
const cached = this.cache.get(cacheKey);
|
|
37
|
+
if (cached !== null)
|
|
38
|
+
return cached;
|
|
39
|
+
}
|
|
40
|
+
let detections = this.engine.detect(normalizedText, countries);
|
|
41
|
+
if (offsetMapping !== null) {
|
|
42
|
+
detections = detections.map(d => ({
|
|
43
|
+
...d,
|
|
44
|
+
start: mapOffsetToOriginal(d.start, offsetMapping),
|
|
45
|
+
end: mapOffsetToOriginal(d.end, offsetMapping),
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
if (!detectDates) {
|
|
49
|
+
detections = detections.filter(d => !DATE_TYPES.has(d.entityType));
|
|
50
|
+
}
|
|
51
|
+
detections.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
52
|
+
let redacted = text;
|
|
53
|
+
for (let i = detections.length - 1; i >= 0; i--) {
|
|
54
|
+
const det = detections[i];
|
|
55
|
+
const replacement = referentialIntegrity
|
|
56
|
+
? this.referentialMapper.getLabel(det.text, det.entityType)
|
|
57
|
+
: `[${det.entityType}]`;
|
|
58
|
+
redacted = redacted.slice(0, det.start) + replacement + redacted.slice(det.end);
|
|
59
|
+
}
|
|
60
|
+
const result = {
|
|
61
|
+
redactedText: redacted,
|
|
62
|
+
detections,
|
|
63
|
+
source: "rules",
|
|
64
|
+
degraded: false,
|
|
65
|
+
};
|
|
66
|
+
if (cache && cacheKey) {
|
|
67
|
+
this.cache.put(cacheKey, result);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
redactBatch(texts, options = {}) {
|
|
72
|
+
const countries = options.countries ?? null;
|
|
73
|
+
this.engine.loadCountries(countries ? countries.map(c => c.toUpperCase()) : null);
|
|
74
|
+
return texts.map(text => this.redact(text, options));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare enum EntityType {
|
|
2
|
+
NAME = "NAME",
|
|
3
|
+
ADDRESS = "ADDRESS",
|
|
4
|
+
IBAN = "IBAN",
|
|
5
|
+
BIC = "BIC",
|
|
6
|
+
CREDIT_CARD = "CREDIT_CARD",
|
|
7
|
+
PHONE = "PHONE",
|
|
8
|
+
EMAIL = "EMAIL",
|
|
9
|
+
DOB = "DOB",
|
|
10
|
+
DATE_OF_DEATH = "DATE_OF_DEATH",
|
|
11
|
+
NATIONAL_ID = "NATIONAL_ID",
|
|
12
|
+
SSN = "SSN",
|
|
13
|
+
TAX_ID = "TAX_ID",
|
|
14
|
+
PASSPORT = "PASSPORT",
|
|
15
|
+
DRIVERS_LICENSE = "DRIVERS_LICENSE",
|
|
16
|
+
RESIDENCE_PERMIT = "RESIDENCE_PERMIT",
|
|
17
|
+
LICENSE_PLATE = "LICENSE_PLATE",
|
|
18
|
+
VIN = "VIN",
|
|
19
|
+
VAT = "VAT",
|
|
20
|
+
POSTAL_CODE = "POSTAL_CODE",
|
|
21
|
+
IP_ADDRESS = "IP_ADDRESS",
|
|
22
|
+
IPV6_ADDRESS = "IPV6_ADDRESS",
|
|
23
|
+
MAC_ADDRESS = "MAC_ADDRESS",
|
|
24
|
+
HEALTH_INSURANCE = "HEALTH_INSURANCE",
|
|
25
|
+
HEALTHCARE_PROVIDER = "HEALTHCARE_PROVIDER",
|
|
26
|
+
CHAMBER_OF_COMMERCE = "CHAMBER_OF_COMMERCE",
|
|
27
|
+
IMEI = "IMEI",
|
|
28
|
+
GPS_COORDINATES = "GPS_COORDINATES",
|
|
29
|
+
UUID = "UUID",
|
|
30
|
+
SOCIAL_HANDLE = "SOCIAL_HANDLE",
|
|
31
|
+
SECRET = "SECRET",
|
|
32
|
+
OTHER = "OTHER"
|
|
33
|
+
}
|
|
34
|
+
export declare enum DetectionSource {
|
|
35
|
+
RULES = "rules",
|
|
36
|
+
CLOUD = "cloud"
|
|
37
|
+
}
|
|
38
|
+
export interface Detection {
|
|
39
|
+
entityType: EntityType | string;
|
|
40
|
+
start: number;
|
|
41
|
+
end: number;
|
|
42
|
+
text: string;
|
|
43
|
+
source: DetectionSource;
|
|
44
|
+
country: string | null;
|
|
45
|
+
confidence: string;
|
|
46
|
+
}
|
|
47
|
+
export interface RedactResult {
|
|
48
|
+
redactedText: string;
|
|
49
|
+
detections: Detection[];
|
|
50
|
+
source: string;
|
|
51
|
+
degraded: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface PatternDef {
|
|
54
|
+
entityType: EntityType | string;
|
|
55
|
+
pattern: string;
|
|
56
|
+
validator: string | null;
|
|
57
|
+
description: string;
|
|
58
|
+
contextKeywords: string[];
|
|
59
|
+
requiresContext: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface CountryConfig {
|
|
62
|
+
code: string;
|
|
63
|
+
name: string;
|
|
64
|
+
patterns: PatternDef[];
|
|
65
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export var EntityType;
|
|
2
|
+
(function (EntityType) {
|
|
3
|
+
EntityType["NAME"] = "NAME";
|
|
4
|
+
EntityType["ADDRESS"] = "ADDRESS";
|
|
5
|
+
EntityType["IBAN"] = "IBAN";
|
|
6
|
+
EntityType["BIC"] = "BIC";
|
|
7
|
+
EntityType["CREDIT_CARD"] = "CREDIT_CARD";
|
|
8
|
+
EntityType["PHONE"] = "PHONE";
|
|
9
|
+
EntityType["EMAIL"] = "EMAIL";
|
|
10
|
+
EntityType["DOB"] = "DOB";
|
|
11
|
+
EntityType["DATE_OF_DEATH"] = "DATE_OF_DEATH";
|
|
12
|
+
EntityType["NATIONAL_ID"] = "NATIONAL_ID";
|
|
13
|
+
EntityType["SSN"] = "SSN";
|
|
14
|
+
EntityType["TAX_ID"] = "TAX_ID";
|
|
15
|
+
EntityType["PASSPORT"] = "PASSPORT";
|
|
16
|
+
EntityType["DRIVERS_LICENSE"] = "DRIVERS_LICENSE";
|
|
17
|
+
EntityType["RESIDENCE_PERMIT"] = "RESIDENCE_PERMIT";
|
|
18
|
+
EntityType["LICENSE_PLATE"] = "LICENSE_PLATE";
|
|
19
|
+
EntityType["VIN"] = "VIN";
|
|
20
|
+
EntityType["VAT"] = "VAT";
|
|
21
|
+
EntityType["POSTAL_CODE"] = "POSTAL_CODE";
|
|
22
|
+
EntityType["IP_ADDRESS"] = "IP_ADDRESS";
|
|
23
|
+
EntityType["IPV6_ADDRESS"] = "IPV6_ADDRESS";
|
|
24
|
+
EntityType["MAC_ADDRESS"] = "MAC_ADDRESS";
|
|
25
|
+
EntityType["HEALTH_INSURANCE"] = "HEALTH_INSURANCE";
|
|
26
|
+
EntityType["HEALTHCARE_PROVIDER"] = "HEALTHCARE_PROVIDER";
|
|
27
|
+
EntityType["CHAMBER_OF_COMMERCE"] = "CHAMBER_OF_COMMERCE";
|
|
28
|
+
EntityType["IMEI"] = "IMEI";
|
|
29
|
+
EntityType["GPS_COORDINATES"] = "GPS_COORDINATES";
|
|
30
|
+
EntityType["UUID"] = "UUID";
|
|
31
|
+
EntityType["SOCIAL_HANDLE"] = "SOCIAL_HANDLE";
|
|
32
|
+
EntityType["SECRET"] = "SECRET";
|
|
33
|
+
EntityType["OTHER"] = "OTHER";
|
|
34
|
+
})(EntityType || (EntityType = {}));
|
|
35
|
+
export var DetectionSource;
|
|
36
|
+
(function (DetectionSource) {
|
|
37
|
+
DetectionSource["RULES"] = "rules";
|
|
38
|
+
DetectionSource["CLOUD"] = "cloud";
|
|
39
|
+
})(DetectionSource || (DetectionSource = {}));
|
package/package.json
CHANGED
|
@@ -1,17 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "euredact",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "European PII redaction SDK
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"keywords": ["pii", "redaction", "gdpr", "privacy", "europe", "nlp"],
|
|
7
|
-
"author": "Joren Janssens <joren.janssens@proton.me>",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "European PII redaction SDK -- fast, zero-dependency, rule-based detection and redaction of personal data across 31 European countries",
|
|
8
5
|
"license": "Apache-2.0",
|
|
9
|
-
"
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
|
+
"module": "./dist/esm/index.js",
|
|
9
|
+
"types": "./dist/esm/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/esm/index.d.ts",
|
|
14
|
+
"default": "./dist/esm/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/esm/index.d.ts",
|
|
18
|
+
"default": "./dist/cjs/index.js"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=16.0.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"pii",
|
|
32
|
+
"redaction",
|
|
33
|
+
"gdpr",
|
|
34
|
+
"privacy",
|
|
35
|
+
"europe",
|
|
36
|
+
"eu",
|
|
37
|
+
"iban",
|
|
38
|
+
"bsn",
|
|
39
|
+
"national-id",
|
|
40
|
+
"phone",
|
|
41
|
+
"email",
|
|
42
|
+
"anonymization",
|
|
43
|
+
"data-protection",
|
|
44
|
+
"personal-data",
|
|
45
|
+
"regex"
|
|
46
|
+
],
|
|
10
47
|
"repository": {
|
|
11
48
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/euredact/euredact
|
|
49
|
+
"url": "https://github.com/euredact/euredact"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build:esm": "tsc",
|
|
53
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
54
|
+
"postbuild:cjs": "echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
55
|
+
"build": "rm -rf dist && npm run build:esm && npm run build:cjs",
|
|
56
|
+
"test": "node --import tsx src/__tests__/run.ts",
|
|
57
|
+
"prepublishOnly": "npm run build"
|
|
13
58
|
},
|
|
14
|
-
"
|
|
15
|
-
"
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"typescript": "^5.4.0",
|
|
61
|
+
"tsx": "^4.7.0"
|
|
16
62
|
}
|
|
17
63
|
}
|
package/index.js
DELETED