@epilot/anonymization
Advanced tools
+6
-0
@@ -27,2 +27,4 @@ "use strict"; | ||
| relation_user: 'user', | ||
| // consent attributes embed the contact identifier (email/phone) the consent is keyed on | ||
| consent: 'consent', | ||
| }; | ||
@@ -71,2 +73,6 @@ /** | ||
| '*:agent': 'user', | ||
| // common consent attribute names — safety net when the schema is unavailable; type-based | ||
| // classification (`consent`) covers custom-named ones | ||
| '*:consent_email_marketing': 'consent', | ||
| '*:consent_phone_marketing': 'consent', | ||
| }; | ||
@@ -73,0 +79,0 @@ /** |
+47
-0
@@ -96,2 +96,4 @@ "use strict"; | ||
| return mapValue(value, (item) => anonymizeUserItem(item, pseudonymizer)); | ||
| case 'consent': | ||
| return mapValue(value, (item) => anonymizeConsentItem(item, pseudonymizer)); | ||
| case 'date_year': | ||
@@ -209,1 +211,46 @@ return (0, exports.mapStringLeaves)(value, (str) => pseudonymizer.dateToYear(str)); | ||
| }; | ||
| /** keys inside a consent value that hold the contact identifier the consent is keyed on */ | ||
| const CONSENT_IDENTIFIER_KEYS = ['identifier', 'email', 'phone']; | ||
| /** pseudonymize a contact identifier string, preserving whether it reads as email or phone */ | ||
| const pseudonymizeIdentifier = (value, pseudonymizer) => { | ||
| if (value.includes('@')) { | ||
| return pseudonymizer.email(value); | ||
| } | ||
| if (/^[+0-9][\d\s\-/()]*\d$/.test(value)) { | ||
| return pseudonymizer.phone(value); | ||
| } | ||
| return pseudonymizer.value(value); | ||
| }; | ||
| /** | ||
| * Anonymize a consent value (consent attributes such as `consent_email_marketing`). | ||
| * Pseudonymizes the contact identifier(s) the consent is keyed on (email or phone), keeps the | ||
| * consent status and event structure, and scrubs identifiers embedded elsewhere in the value. | ||
| */ | ||
| const anonymizeConsentItem = (item, pseudonymizer) => { | ||
| // consent identifiers can be stored as bare strings (`identifiers: string[]`) | ||
| if (typeof item === 'string') { | ||
| return pseudonymizeIdentifier(item, pseudonymizer); | ||
| } | ||
| if (Array.isArray(item)) { | ||
| return item.map((i) => anonymizeConsentItem(i, pseudonymizer)); | ||
| } | ||
| if (!(0, utils_1.isPlainObject)(item)) { | ||
| return item; | ||
| } | ||
| return (0, utils_1.mapEntries)(item, (val, key) => { | ||
| if (defaults_1.SYSTEM_KEEP_KEYS.includes(key)) { | ||
| return val; | ||
| } | ||
| if (CONSENT_IDENTIFIER_KEYS.includes(key.toLowerCase()) && typeof val === 'string') { | ||
| return pseudonymizeIdentifier(val, pseudonymizer); | ||
| } | ||
| if ((0, utils_1.isPlainObject)(val) || Array.isArray(val)) { | ||
| return anonymizeConsentItem(val, pseudonymizer); | ||
| } | ||
| // scrub any identifier embedded in remaining free text (e.g. event payloads) | ||
| if (typeof val === 'string') { | ||
| return pseudonymizer.scrubText(val); | ||
| } | ||
| return val; | ||
| }); | ||
| }; |
+7
-1
@@ -37,3 +37,9 @@ /** | ||
| */ | ||
| | 'user'; | ||
| | 'user' | ||
| /** | ||
| * anonymize a consent value (`consent` attributes, e.g. `consent_email_marketing`): | ||
| * pseudonymize the contact identifier(s) (email/phone) the consent is keyed on, keep the | ||
| * consent status and event structure | ||
| */ | ||
| | 'consent'; | ||
| /** Data classification set on a schema attribute (`data_classification` in the Entity API) */ | ||
@@ -40,0 +46,0 @@ export type DataClassification = 'public' | 'pii'; |
+1
-1
| { | ||
| "name": "@epilot/anonymization", | ||
| "version": "0.1.2", | ||
| "version": "0.1.4", | ||
| "description": "Deterministic PII pseudonymization and redaction for epilot entity data and arbitrary JSON — data minimization for AI agents, analytics, logs and integrations", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+2
-1
@@ -9,3 +9,3 @@ <h1 align="center"><img alt="epilot" src="https://raw.githubusercontent.com/epilot-dev/sdk-js/main/logo.png" width="200"><br>@epilot/anonymization</h1> | ||
| <p align="center">Deterministic PII pseudonymization and redaction for [epilot](https://www.epilot.cloud) entity | ||
| <p align="center">Deterministic PII pseudonymization and redaction for <a href="https://www.epilot.cloud">epilot</a> entity | ||
| data and arbitrary JSON.</p> | ||
@@ -182,2 +182,3 @@ | ||
| | User-relation attributes (`relation_user`, e.g. `contact_owner`) | pseudonymize name/email, **redact credentials** (`token`, `password`, …), keep ids/status/settings | | ||
| | Consent attributes (`consent`, e.g. `consent_email_marketing`) | pseudonymize the contact identifier (email/phone), keep consent status and events | | ||
| | Curated free-text (note/message content, opportunity description, …) | `[REDACTED]` | | ||
@@ -184,0 +185,0 @@ | `data_classification: 'pii'` fields | pseudonym (single-line) / `[REDACTED]` (multiline) | |
61739
4.71%1111
5.61%202
0.5%