@abhishekvardanbotta/mask 0.0.0-stage → 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 +21 -0
- package/README.md +142 -3
- package/dist/crypto.d.ts +23 -0
- package/dist/crypto.js +45 -0
- package/dist/detectors/recognizers.d.ts +24 -0
- package/dist/detectors/recognizers.js +274 -0
- package/dist/detectors/validators.d.ts +29 -0
- package/dist/detectors/validators.js +209 -0
- package/dist/fields.d.ts +39 -0
- package/dist/fields.js +219 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +9 -0
- package/dist/policy.d.ts +36 -0
- package/dist/policy.js +117 -0
- package/dist/privacy.d.ts +154 -0
- package/dist/privacy.js +600 -0
- package/dist/tokens.d.ts +20 -0
- package/dist/tokens.js +25 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.js +8 -0
- package/dist/vault.d.ts +30 -0
- package/dist/vault.js +29 -0
- package/package.json +50 -4
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a piece of data is handled.
|
|
3
|
+
*
|
|
4
|
+
* - `PII` — personal data. Replaced with a reversible vault token; restorable by any principal granted `PII`
|
|
5
|
+
* (or `*`, or the entity type itself).
|
|
6
|
+
* - `SENSITIVE` — high-impact data (national IDs, card numbers, health, GDPR Art. 9 categories). Tokenized like PII,
|
|
7
|
+
* but `*` does NOT cover it: a principal needs an explicit `SENSITIVE` or entity-type grant.
|
|
8
|
+
* - `PUBLIC` — left untouched. On a field, it also switches detection off for that subtree.
|
|
9
|
+
* - `BLOCK` — removed irreversibly. Never written to the vault, so nothing can ever restore it.
|
|
10
|
+
*/
|
|
11
|
+
export type Classification = "PII" | "SENSITIVE" | "PUBLIC" | "BLOCK";
|
|
12
|
+
/** Built-in entity types. Any `UPPER_SNAKE` string is also accepted as a custom type. */
|
|
13
|
+
export type BuiltInEntityType = "EMAIL" | "PHONE" | "PERSON" | "ADDRESS" | "LOCATION" | "DATE_OF_BIRTH" | "IP_ADDRESS" | "USERNAME" | "CREDIT_CARD" | "CARD_SECURITY_CODE" | "IBAN" | "BANK_ACCOUNT" | "US_SSN" | "IN_AADHAAR" | "IN_PAN" | "NATIONAL_ID" | "PASSPORT" | "DRIVERS_LICENSE" | "HEALTH" | "SPECIAL_CATEGORY" | "CREDENTIAL";
|
|
14
|
+
export type EntityType = BuiltInEntityType | (string & {});
|
|
15
|
+
/**
|
|
16
|
+
* Who may see a restored value. A grant is a classification (`"PII"`, `"SENSITIVE"`), an entity type (`"EMAIL"`),
|
|
17
|
+
* or `"*"` — every `PII`-classified type, deliberately excluding `SENSITIVE`.
|
|
18
|
+
*/
|
|
19
|
+
export type Grant = "*" | Classification | EntityType;
|
|
20
|
+
/** What a principal sees in place of a value it is not allowed to restore. */
|
|
21
|
+
export type DeniedMode = "redact" | "mask" | "token";
|
|
22
|
+
/** A detector's raw hit, before policy is applied. Offsets are UTF-16 indices into the scanned text. */
|
|
23
|
+
export interface Detection {
|
|
24
|
+
type: EntityType;
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
/** Confidence in [0, 1]. Compared against the policy's `threshold` (default 0.5). */
|
|
28
|
+
score: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A pluggable recognizer. Use it to add an NER model, an LLM-based classifier, a dictionary of customer IDs, etc.
|
|
32
|
+
* Synchronous detectors run everywhere; asynchronous ones run only from `protect()` (not `protectSync()`).
|
|
33
|
+
*/
|
|
34
|
+
export interface Detector {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
detect(text: string): Detection[] | Promise<Detection[]>;
|
|
37
|
+
}
|
|
38
|
+
/** A field-level classification, from config, a schema annotation, or field-name inference. */
|
|
39
|
+
export interface FieldClass {
|
|
40
|
+
classification: Classification;
|
|
41
|
+
type?: EntityType;
|
|
42
|
+
}
|
|
43
|
+
export interface PolicyConfig {
|
|
44
|
+
/** Classification per entity type, overriding the defaults (see `DEFAULT_POLICIES`). */
|
|
45
|
+
policies?: Partial<Record<EntityType, Classification>>;
|
|
46
|
+
/**
|
|
47
|
+
* Classification per field path. Paths are dot-separated keys; array indices are transparent. `*` matches one
|
|
48
|
+
* key, `**` any number. A bare key (`"ssn"`) matches that key at any depth.
|
|
49
|
+
* Example: `{ "customer.email": "PII", "**.diagnosis": { classification: "SENSITIVE", type: "HEALTH" } }`.
|
|
50
|
+
*/
|
|
51
|
+
fields?: Record<string, Classification | FieldClass>;
|
|
52
|
+
/** Extra detectors, run alongside the built-ins. */
|
|
53
|
+
detectors?: Detector[];
|
|
54
|
+
/** Names of built-in recognizers to switch off (see `BUILT_IN_RECOGNIZERS`). */
|
|
55
|
+
disable?: string[];
|
|
56
|
+
/** Minimum detection confidence, 0..1. Default 0.5. */
|
|
57
|
+
threshold?: number;
|
|
58
|
+
/** Classify fields from their key names (`email`, `phoneNumber`, `ssn`, ...). Default true. */
|
|
59
|
+
inferFromFieldNames?: boolean;
|
|
60
|
+
/** Values that are never tokenized, matched exactly (strings) or by test (RegExp). */
|
|
61
|
+
allowlist?: Array<string | RegExp>;
|
|
62
|
+
/** `"redact"` (default) replaces BLOCK values with `[BLOCKED:TYPE]`; `"throw"` rejects the whole operation. */
|
|
63
|
+
onBlock?: "redact" | "throw";
|
|
64
|
+
}
|
|
65
|
+
export interface RestoreOptions {
|
|
66
|
+
/** What the principal may see in the clear. Omit together with `trusted` to restore nothing. */
|
|
67
|
+
allow?: Grant[];
|
|
68
|
+
/** Full restoration of everything restorable — for code inside the trust boundary only. */
|
|
69
|
+
trusted?: boolean;
|
|
70
|
+
/** What denied values become. Default `"redact"`. */
|
|
71
|
+
onDenied?: DeniedMode;
|
|
72
|
+
/** Recorded in the audit log. */
|
|
73
|
+
principal?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface ProtectOptions {
|
|
76
|
+
/** A JSON Schema describing `value`. Its `x-privacy`/`format`/description annotations classify fields. */
|
|
77
|
+
schema?: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface AuditEvent {
|
|
80
|
+
at: string;
|
|
81
|
+
action: "tokenize" | "block" | "restore" | "deny" | "unknown-token";
|
|
82
|
+
type?: EntityType;
|
|
83
|
+
classification?: Classification;
|
|
84
|
+
principal?: string;
|
|
85
|
+
/** The token involved. Never the plaintext. */
|
|
86
|
+
token?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface PrivacySummary {
|
|
89
|
+
tokenized: number;
|
|
90
|
+
blocked: number;
|
|
91
|
+
restored: number;
|
|
92
|
+
denied: number;
|
|
93
|
+
types: EntityType[];
|
|
94
|
+
}
|
|
95
|
+
export declare class PrivacyError extends Error {
|
|
96
|
+
readonly code: "blocked" | "config" | "vault";
|
|
97
|
+
constructor(message: string, code: "blocked" | "config" | "vault");
|
|
98
|
+
}
|
package/dist/types.js
ADDED
package/dist/vault.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Sealed } from "./crypto.js";
|
|
2
|
+
import type { Classification, EntityType } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* One tokenized value as persisted. The plaintext exists only inside `ct` (AES-256-GCM); `fp` is a keyed HMAC, so
|
|
5
|
+
* a store — Redis, SQL, a file — never holds anything readable without the master key.
|
|
6
|
+
*/
|
|
7
|
+
export interface VaultRecord extends Sealed {
|
|
8
|
+
token: string;
|
|
9
|
+
type: EntityType;
|
|
10
|
+
classification: Exclude<Classification, "PUBLIC" | "BLOCK">;
|
|
11
|
+
kind: "string" | "number";
|
|
12
|
+
fp: string;
|
|
13
|
+
createdAt: number;
|
|
14
|
+
expiresAt?: number;
|
|
15
|
+
}
|
|
16
|
+
/** Persistence for vault records, partitioned by scope. Implement it to back the vault with your own database. */
|
|
17
|
+
export interface VaultStore {
|
|
18
|
+
put(scope: string, record: VaultRecord): void | Promise<void>;
|
|
19
|
+
list(scope: string): VaultRecord[] | Promise<VaultRecord[]>;
|
|
20
|
+
deleteScope(scope: string): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Process-lifetime store. Expired records are dropped on read. */
|
|
23
|
+
export declare class InMemoryVaultStore implements VaultStore {
|
|
24
|
+
private readonly scopes;
|
|
25
|
+
put(scope: string, record: VaultRecord): void;
|
|
26
|
+
list(scope: string): VaultRecord[];
|
|
27
|
+
deleteScope(scope: string): void;
|
|
28
|
+
/** Number of scopes held — for tests and diagnostics. */
|
|
29
|
+
get size(): number;
|
|
30
|
+
}
|
package/dist/vault.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Process-lifetime store. Expired records are dropped on read. */
|
|
2
|
+
export class InMemoryVaultStore {
|
|
3
|
+
scopes = new Map();
|
|
4
|
+
put(scope, record) {
|
|
5
|
+
let records = this.scopes.get(scope);
|
|
6
|
+
if (!records) {
|
|
7
|
+
records = new Map();
|
|
8
|
+
this.scopes.set(scope, records);
|
|
9
|
+
}
|
|
10
|
+
records.set(record.token, record);
|
|
11
|
+
}
|
|
12
|
+
list(scope) {
|
|
13
|
+
const records = this.scopes.get(scope);
|
|
14
|
+
if (!records)
|
|
15
|
+
return [];
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
for (const [token, record] of records)
|
|
18
|
+
if (record.expiresAt !== undefined && record.expiresAt <= now)
|
|
19
|
+
records.delete(token);
|
|
20
|
+
return [...records.values()];
|
|
21
|
+
}
|
|
22
|
+
deleteScope(scope) {
|
|
23
|
+
this.scopes.delete(scope);
|
|
24
|
+
}
|
|
25
|
+
/** Number of scopes held — for tests and diagnostics. */
|
|
26
|
+
get size() {
|
|
27
|
+
return this.scopes.size;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abhishekvardanbotta/mask",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Schema-aware PII classification, validated detection, reversible vault tokenization and authorization-checked restoration for LLM applications. Zero runtime dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Abhishek Vardan Botta",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"pii",
|
|
26
|
+
"privacy",
|
|
27
|
+
"tokenization",
|
|
28
|
+
"pseudonymization",
|
|
29
|
+
"redaction",
|
|
30
|
+
"llm",
|
|
31
|
+
"ai",
|
|
32
|
+
"agents",
|
|
33
|
+
"kiln",
|
|
34
|
+
"gdpr",
|
|
35
|
+
"pci"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
42
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
43
|
+
"test": "tsx --test tests/*.test.ts",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"prepublishOnly": "npm run build && npm test"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.13.5",
|
|
49
|
+
"tsx": "^4.19.3",
|
|
50
|
+
"typescript": "^5.7.3"
|
|
51
|
+
}
|
|
52
|
+
}
|