@epilot/anonymization
Advanced tools
@@ -27,2 +27,8 @@ import type { AnonymizationStrategy } from './types'; | ||
| /** | ||
| * Per-key rules applied inside an embedded user object (the `user` strategy). | ||
| * Keys not listed are kept (ids, status, language, notification settings, timestamps). | ||
| * Matching is case-insensitive and also applies at any nesting depth. | ||
| */ | ||
| export declare const USER_FIELD_STRATEGY: Record<string, AnonymizationStrategy>; | ||
| /** | ||
| * Additional field-name heuristics used ONLY by the non-schema-aware mode | ||
@@ -29,0 +35,0 @@ * (`anonymizeUnknown`) for arbitrary JSON such as audit logs or inbound integration events. |
+35
-1
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.SYSTEM_KEEP_KEYS = exports.PAYMENT_PSEUDONYMIZE_KEY_PATTERN = exports.ADDRESS_KEEP_KEYS = exports.UNKNOWN_FIELD_HEURISTICS = exports.DEFAULT_PII_FIELDS = exports.STRATEGY_BY_ATTRIBUTE_TYPE = exports.PSEUDONYM_EMAIL_DOMAIN = exports.REDACTED_PLACEHOLDER = void 0; | ||
| exports.SYSTEM_KEEP_KEYS = exports.PAYMENT_PSEUDONYMIZE_KEY_PATTERN = exports.ADDRESS_KEEP_KEYS = exports.UNKNOWN_FIELD_HEURISTICS = exports.USER_FIELD_STRATEGY = exports.DEFAULT_PII_FIELDS = exports.STRATEGY_BY_ATTRIBUTE_TYPE = exports.PSEUDONYM_EMAIL_DOMAIN = exports.REDACTED_PLACEHOLDER = void 0; | ||
| /** | ||
@@ -24,2 +24,5 @@ * Curated classification defaults. | ||
| payment: 'payment', | ||
| // user-relation attributes embed full epilot user objects (name, email, and often an auth | ||
| // `token`) — never pass them through raw | ||
| relation_user: 'user', | ||
| }; | ||
@@ -62,4 +65,35 @@ /** | ||
| 'thread:topic': 'redact', | ||
| // common user-relation attribute names — safety net for when the schema (and thus the | ||
| // `relation_user` type) is unavailable; type-based classification covers custom-named ones | ||
| '*:contact_owner': 'user', | ||
| '*:owner': 'user', | ||
| '*:assigned_to': 'user', | ||
| '*:agent': 'user', | ||
| }; | ||
| /** | ||
| * Per-key rules applied inside an embedded user object (the `user` strategy). | ||
| * Keys not listed are kept (ids, status, language, notification settings, timestamps). | ||
| * Matching is case-insensitive and also applies at any nesting depth. | ||
| */ | ||
| exports.USER_FIELD_STRATEGY = { | ||
| email: 'email', | ||
| name: 'person', | ||
| display_name: 'person', | ||
| full_name: 'person', | ||
| first_name: 'person', | ||
| last_name: 'person', | ||
| given_name: 'person', | ||
| family_name: 'person', | ||
| phone: 'phone', | ||
| // credentials that must never appear in an anonymized response | ||
| token: 'redact', | ||
| access_token: 'redact', | ||
| refresh_token: 'redact', | ||
| api_key: 'redact', | ||
| apikey: 'redact', | ||
| secret: 'redact', | ||
| password: 'redact', | ||
| authorization: 'redact', | ||
| }; | ||
| /** | ||
| * Additional field-name heuristics used ONLY by the non-schema-aware mode | ||
@@ -66,0 +100,0 @@ * (`anonymizeUnknown`) for arbitrary JSON such as audit logs or inbound integration events. |
+33
-0
@@ -94,2 +94,4 @@ "use strict"; | ||
| return mapValue(value, (item) => anonymizePaymentItem(item, pseudonymizer)); | ||
| case 'user': | ||
| return mapValue(value, (item) => anonymizeUserItem(item, pseudonymizer)); | ||
| case 'date_year': | ||
@@ -176,1 +178,32 @@ return (0, exports.mapStringLeaves)(value, (str) => pseudonymizer.dateToYear(str)); | ||
| }; | ||
| /** | ||
| * Anonymize an embedded epilot user object (user-relation attributes such as `contact_owner`). | ||
| * Pseudonymizes name/email fields and REDACTS credentials (`token`, `password`, `secret`, …), | ||
| * while keeping non-identifying operational metadata (ids, status, language, notification | ||
| * settings, timestamps). Rules are applied by key at every nesting depth. | ||
| */ | ||
| const anonymizeUserItem = (item, pseudonymizer) => { | ||
| if (typeof item === 'string') { | ||
| return pseudonymizer.person(item); | ||
| } | ||
| if (Array.isArray(item)) { | ||
| return item.map((i) => anonymizeUserItem(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; | ||
| } | ||
| const strategy = defaults_1.USER_FIELD_STRATEGY[key.toLowerCase()]; | ||
| if (strategy) { | ||
| return (0, exports.applyStrategy)({ value: val, strategy, pseudonymizer }); | ||
| } | ||
| // recurse so a sensitive key nested deeper (e.g. token inside a settings object) is caught | ||
| if ((0, utils_1.isPlainObject)(val) || Array.isArray(val)) { | ||
| return anonymizeUserItem(val, pseudonymizer); | ||
| } | ||
| return val; | ||
| }); | ||
| }; |
+7
-1
@@ -31,3 +31,9 @@ /** | ||
| /** scrub payment details: pseudonymize IBAN/holder identifiers, keep bank metadata */ | ||
| | 'payment'; | ||
| | 'payment' | ||
| /** | ||
| * anonymize an embedded epilot user object (user-relation attributes, e.g. `contact_owner`): | ||
| * pseudonymize name/email, redact credentials (`token`, `password`, `secret`), keep ids and | ||
| * operational metadata (status, language, notification settings) | ||
| */ | ||
| | 'user'; | ||
| /** Data classification set on a schema attribute (`data_classification` in the Entity API) */ | ||
@@ -34,0 +40,0 @@ export type DataClassification = 'public' | 'pii'; |
+2
-2
| { | ||
| "name": "@epilot/anonymization", | ||
| "version": "0.1.0", | ||
| "description": "Deterministic PII pseudonymization and redaction for epilot entity data and arbitrary JSON — data minimization for AI agents, analytics, logs and integrations", | ||
| "version": "0.1.1", | ||
| "description": "Deterministic PII pseudonymization and redaction for epilot entity data and arbitrary JSON \u2014 data minimization for AI agents, analytics, logs and integrations", | ||
| "license": "MIT", | ||
@@ -6,0 +6,0 @@ "author": "epilot GmbH", |
+1
-0
@@ -174,2 +174,3 @@ # @epilot/anonymization | ||
| | Payment items | pseudonymize IBAN/holder; keep bank name / BIC | | ||
| | User-relation attributes (`relation_user`, e.g. `contact_owner`) | pseudonymize name/email, **redact credentials** (`token`, `password`, …), keep ids/status/settings | | ||
| | Curated free-text (note/message content, opportunity description, …) | `[REDACTED]` | | ||
@@ -176,0 +177,0 @@ | `data_classification: 'pii'` fields | pseudonym (single-line) / `[REDACTED]` (multiline) | |
58278
6.27%1052
8.23%195
0.52%