@privacyscrubber/sdk 2.0.2
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 +258 -0
- package/index.d.ts +217 -0
- package/index.js +581 -0
- package/package.json +51 -0
- package/polyfill.js +33 -0
- package/ps-license-manager.js +257 -0
- package/ps-pii-engine.cjs +1297 -0
- package/ps-pii-engine.js +1297 -0
- package/scrubber-core.cjs +1711 -0
- package/shared-ui.js +533 -0
- package/ui-modals.js +819 -0
|
@@ -0,0 +1,1297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PrivacyScrubber Shared PII Engine
|
|
3
|
+
* Shared between Website Worker, Chrome Extension, and MCP Server.
|
|
4
|
+
* Responsible for running Regex checks, overlap resolution, and reverse scrubbing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
(function() {
|
|
8
|
+
if (typeof window !== 'undefined' && window.PrivacyScrubberEngine) return;
|
|
9
|
+
|
|
10
|
+
// --- START DEFAULT RULES ---
|
|
11
|
+
/**
|
|
12
|
+
* PrivacyScrubber Unified PII Detection Rules
|
|
13
|
+
* Shared between Website Worker, Chrome Extension, and Build Pipeline.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
let DEVOPS_SECRETS = [
|
|
17
|
+
// Secrets & API Keys
|
|
18
|
+
{ name: 'AWS Credentials', type: 'SECRET', regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|AIPA)[A-Z0-9]{16}\b/g },
|
|
19
|
+
{ name: 'JSON Web Token (JWT)', type: 'SECRET', regex: /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
|
|
20
|
+
{ name: 'API Token/Key (GitHub/Slack/NPM)', type: 'SECRET', regex: /\b(?:ghp|gho|ghu|ghs|ghr|glpat|npm|xox[baprs])[-_][A-Za-z0-9_-]{10,}\b/g },
|
|
21
|
+
{ name: 'Stripe API Key', type: 'SECRET', regex: /\b(?:[rs]k)_(?:test|live)_[a-zA-Z0-9]{14,}\b/g },
|
|
22
|
+
{ name: 'OpenAI Project API Key', type: 'SECRET', regex: /\b(?:sk|pk)-(?:proj-)?[a-zA-Z0-9_-]{16,}\b/gi },
|
|
23
|
+
{ name: 'Database Connection URI', type: 'SECRET', regex: /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^\s"']+/gi },
|
|
24
|
+
{ name: 'Generic Secret/Key', type: 'SECRET', regex: /\b(?:sk|pk|secret|key|token|auth)(?:[-_][a-zA-Z0-9_-]{3,}|(?=[a-zA-Z0-9_-]{5,}\b)(?=[a-zA-Z_-]*[0-9])[a-zA-Z0-9_-]{5,})\b/gi },
|
|
25
|
+
{ name: 'Hash / Hex Key (32-64 chars)', type: 'SECRET', regex: /\b[a-fA-F0-9]{32,64}\b/g },
|
|
26
|
+
{ name: 'CVE Identifier', type: 'SECRET', regex: /\bCVE-\d{4}-\d{4,}\b/gi },
|
|
27
|
+
{ name: 'Cryptographic Hash', type: 'SECRET', regex: /\b(?:MD5|SHA1|SHA256)[:\s][a-f0-9]{32,64}\b/gi },
|
|
28
|
+
{ name: 'Database/API Secret', type: 'SECRET', regex: /\b(?:DB|POSTGRES|REDIS|MYSQL|AWS|SECRET|PASSWORD|TOKEN|API)[A-Z0-9_]*\s*[:=]\s*[^\s"']+\b|\b(?:API_KEY|SECRET_KEY|PRIVATE_KEY|ACCESS_KEY|AUTH_KEY|ENCRYPTION_KEY)\s*[:=]\s*[^\s"']+\b/gi },
|
|
29
|
+
{ name: 'Proprietary IP / Confidential', type: 'SECRET', regex: /\b(?:CONFIDENTIAL|PROPRIETARY|TRADE SECRET|DO NOT DISTRIBUTE|INTERNAL USE ONLY)\b/gi },
|
|
30
|
+
{ name: 'Private Cryptographic Key', type: 'SECRET', regex: /-----BEGIN (?:RSA |EC |PGP |DSA )?PRIVATE KEY-----/g }
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
let REGEX_RULES = [
|
|
34
|
+
...DEVOPS_SECRETS,
|
|
35
|
+
// Emails
|
|
36
|
+
{ type: 'EMAIL', regex: /\b[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]{1,255}\.[a-zA-Z]{2,}\b/g },
|
|
37
|
+
|
|
38
|
+
// Financial Data (PCI-DSS, Bank Accounts, Direct Deposits, Cards, IBAN, SWIFT, Routing Numbers)
|
|
39
|
+
{ type: 'FINANCIAL', regex: /\b[A-Z]{4}(?:AD|AE|AF|AG|AI|AL|AM|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BL|BM|BN|BO|BQ|BR|BS|BT|BV|BW|BY|BZ|CA|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|CR|CU|CV|CW|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EE|EG|EH|ER|ES|ET|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MF|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MX|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|SS|ST|SV|SX|SY|SZ|TC|TD|TF|TG|TJ|TK|TL|TM|TN|TO|TR|TT|TV|TW|TZ|UA|UG|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|YE|YT|ZA|ZM|ZW)[A-Z2-9][A-NP-Z0-9](?:[A-Z0-9]{3})?\b/g },
|
|
40
|
+
{ type: 'FINANCIAL', regex: /\b[A-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}[a-zA-Z0-9]{0,16}\b/g },
|
|
41
|
+
{ type: 'FINANCIAL', regex: /\bPORTFOLIO[-_][A-Z0-9]{5,}\b/gi },
|
|
42
|
+
{ type: 'FINANCIAL', regex: /\b(?:\d[ -]?){13,19}\b/g },
|
|
43
|
+
{ type: 'FINANCIAL', regex: /\b(?:1|3|bc1)[a-zA-HJ-NP-Z0-9]{25,39}\b/g },
|
|
44
|
+
{ type: 'FINANCIAL', regex: /\b0x[a-fA-F0-9]{40}\b/g },
|
|
45
|
+
// Masked / Direct Deposit Bank Account Numbers & Routing Numbers
|
|
46
|
+
{ type: 'FINANCIAL', regex: /\b(?:Account|Acct|Checking|Savings|Direct\s+Deposit)\s*(?:#|ID|No\.?|Number)?[:\s#]*(?:[\*xX•.-]{3,}\d{2,6}|\d{4}[-\s]?\d{4}[-\s]?\d{2,6})\b/gi },
|
|
47
|
+
{ type: 'FINANCIAL', regex: /\b(?:ABA|Routing|RTN)\s*(?:#|ID|No\.?|Number)?[:\s#]*\d{9}\b/gi },
|
|
48
|
+
|
|
49
|
+
// Legal & Court
|
|
50
|
+
{ type: 'LEGAL', regex: /\bCASE[-_][A-Z0-9_-]{4,}\b/gi },
|
|
51
|
+
{ type: 'LEGAL', regex: /\bMATTER[-_][A-Z0-9_-]{4,}\b/gi },
|
|
52
|
+
{ type: 'PRIVILEGE', regex: /ATTORNEY[- ]CLIENT[- ]PRIVILEGE/gi },
|
|
53
|
+
|
|
54
|
+
// Professional IDs & Organizations
|
|
55
|
+
{ type: 'NAME', isContextName: true, regex: /\b(?:[A-Z][A-Za-z0-9&.,'-]*[ \t\xA0]+){1,5}(?:Inc\.?|LLC|Corp\.?|Corporation|Ltd\.?|Limited|Co\.?|Company|Group|Holdings|Solutions|Services|Technologies|Logistics|Industries|Capital|Bank|Partners|LLP|PLLC)(?:\s+(?:LLC|Inc\.?|Corp\.?|Ltd\.?|USA|Group))?\b/g },
|
|
56
|
+
{ type: 'ID', regex: /\b(?:Employee|Emp|EE|Worker|Staff|File|Badge|Member|Advisor|Producer|Agent|Borrower)\s*(?:#|ID|No\.?|Number)[:\s#]*[A-Z0-9-]{3,15}\b/gi },
|
|
57
|
+
{ type: 'ID', regex: /\b(?:Pay\s+Group|Cost\s+Center|Dept|Department)(?:[:#][\s#]*[A-Za-z0-9_-]{2,30}|\s+[A-Za-z0-9_-]*\d[A-Za-z0-9_-]*)\b/gi },
|
|
58
|
+
{ type: 'ID', regex: /(?:\b(?:Box\s+d\b|d\.\s*(?:Control|#)?|d\s+Control)\s*(?:number|no\.?|#|num)?[:\s#]*|\bControl\s*(?:number|no\.?|#|num)[:\s#]*|\bControl[:#]\s*)([A-Za-z0-9-]{3,30})/gi },
|
|
59
|
+
{ type: 'ID', regex: /\bEEID[ -]?\d{4,}\b/gi },
|
|
60
|
+
{ type: 'ID', regex: /\bRESUME[-_]?[A-Z0-9]{4,}\b/gi },
|
|
61
|
+
{ type: 'ID', regex: /\bLEAD[-_][A-Z0-9]{5,}\b/gi },
|
|
62
|
+
{ type: 'ID', regex: /\bCAMPAIGN[-_][A-Z0-9]{4,}\b/gi },
|
|
63
|
+
{ type: 'ID', regex: /\bDEAL[-_][A-Z0-9]{4,}\b/gi },
|
|
64
|
+
{ type: 'ID', regex: /\bENTITY[-_][0-9]{4,}\b/gi },
|
|
65
|
+
{ type: 'ID', regex: /\bOPPORTUNITY[-_][A-Z0-9]{5,}\b/gi },
|
|
66
|
+
{ type: 'ID', regex: /\bPROSPECT[-_][A-Z0-9]{5,}\b/gi },
|
|
67
|
+
{ type: 'ID', regex: /\bTICKET[-_][A-Z0-9]{5,}\b/gi },
|
|
68
|
+
{ type: 'ID', regex: /\bZENDESK[-_][0-9]{4,}\b/gi },
|
|
69
|
+
{ type: 'ID', regex: /\bEMP[-_]\d{3,}\b/gi },
|
|
70
|
+
{ type: 'ID', regex: /\bLIS[-]?\d{6,}\b/gi },
|
|
71
|
+
{ type: 'ID', regex: /\bPARCEL[-]?\d{5,}\b/gi },
|
|
72
|
+
{ type: 'ID', regex: /\bAGENT[-_][A-Z0-9]{4,}\b/gi },
|
|
73
|
+
{ type: 'ID', regex: /\bTASK[-_][A-Z0-9]{5,}\b/gi },
|
|
74
|
+
{ type: 'ID', regex: /\bSTUDENT[-_][0-9]{5,}\b/gi },
|
|
75
|
+
{ type: 'ID', regex: /\bCOURSE[-_][A-Z0-9]{4,}\b/gi },
|
|
76
|
+
{ type: 'ID', regex: /\bINSTANCE[-_]ID[-_][a-z0-9-]{10,}\b/gi },
|
|
77
|
+
{ type: 'ID', regex: /\bENV[-_][A-Z0-9]{3,}\b/gi },
|
|
78
|
+
{ type: 'ID', regex: /\bTENANT[-_]ID[-_][0-9]{4,}\b/gi },
|
|
79
|
+
|
|
80
|
+
// Insurance & Health Plan IDs
|
|
81
|
+
{ type: 'ID', regex: /\b(?:BCB|BCBS|AETNA|CIGNA|UHC|HUMANA|MEDICARE|MEDICAID)[-_A-Za-z0-9]+\b/gi },
|
|
82
|
+
{ type: 'ID', regex: /\b(?:Insurance\s+(?:ID|No\.?|Number|#)|Policy(?:\s*(?:ID|No\.?|Number|#)|[:#])|Member\s*(?:ID|No\.?|Number|#|[:#])|Subscriber\s*(?:ID|No\.?|Number|#|[:#])|Group\s*(?:ID|No\.?|Number|#|[:#])|Plan\s*(?:ID|No\.?|Number|#|[:#])|Health(?:\s+Plan)?\s*(?:ID|No\.?|Number|#)|Rx\s*(?:ID|No\.?|Number|Group|BIN|PCN|#))[:\s#]+([A-Za-z0-9-]{3,30})\b/gi },
|
|
83
|
+
{ type: 'ID', regex: /\b(?:Health\s+Plan(?:\s+Beneficiary)?|Beneficiary(?:\s+(?:No\.?|Number|ID|Num|#))?|HPN)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
84
|
+
{ type: 'ID', regex: /\bHPN[-_][A-Za-z0-9-]+\b/gi },
|
|
85
|
+
{ type: 'ADDRESS', isContextAddress: true, regex: /(?:(?:\bBox\s+f\b|\bf\.\s*|\bf\s+(?=Employee))\s*(?:Employee(?:'s)?\s*)?(?:address[,\s]+and\s+ZIP\s+code|address)?|(?:Employee(?:'s)?\s+address[,\s]+and\s+ZIP\s+code))[\s:#]*([A-Za-z0-9#.,\s-]{4,55}?)(?=\r?\n|$|\s{3,}|\t|Box|\d+\b|1\b|2\b|Wages|Federal|Social|Medicare)/gi },
|
|
86
|
+
{ type: 'ADDRESS', isContextAddress: true, regex: /(?:(?:Borrower(?:'s)?|Co-Borrower(?:'s)?|Employee(?:'s)?|Employer(?:'s)?|Home|Mailing|Property|Physical)\s+address)[\s:#]+([A-Za-z0-9#.,\s-]{4,55}?)(?=\r?\n|$|\s{3,}|\t|City|State|ZIP|SSN|EIN|Phone|Box|\d+\b)/gi },
|
|
87
|
+
{ type: 'ADDRESS', regex: /\b\d{1,6}[ \t\xA0]+(?:[A-Za-z0-9.-]+[ \t\xA0]+){0,4}(?:St|Street|Ave|Avenue|Blvd|Boulevard|Rd|Road|Ln|Lane|Dr|Drive|Way|Ct|Court|Pl|Place|Terrace|Pkwy|Parkway|Sq|Square|Hwy|Highway|Cir|Circle|Trl|Trail|Loop|Row|Pike|Bishopsgate|Gate|Walk|Close|Hill|Crescent|Gardens|Grove|Mews|Yard|Box|PO Box|P\.O\.[ \t\xA0]*Box)\b(?:[ \t\xA0]*,?[ \t\xA0]*(?:Apt|Apartment|Suite|Ste|Unit|#|Fl|Floor|Bldg|Building)\.?[ \t\xA0]*[A-Za-z0-9-]+)?/gi },
|
|
88
|
+
{ type: 'ADDRESS', regex: /\b(?:P\.?O\.?[ \t\xA0]*Box|PO[ \t\xA0]*Box)[ \t\xA0]+\d{1,6}\b/gi },
|
|
89
|
+
{ type: 'ADDRESS', regex: /\b[A-Za-z][a-zA-Z\s.-]{1,25},?\s+(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)\s+\d{5}(?:-\d{4})?\b/g },
|
|
90
|
+
{ type: 'ADDRESS', regex: /\b(?:ZIP|Postal|Code)?\s*(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)\s+\d{5}(?:-\d{4})?\b/g },
|
|
91
|
+
{ type: 'ADDRESS', regex: /\b\d{5}-\d{4}\b/g },
|
|
92
|
+
// SSN / National IDs
|
|
93
|
+
{ type: 'ID', regex: /\b\d{3}-\d{2}-\d{4}\b/g },
|
|
94
|
+
{ type: 'ID', regex: /(?<=^|[^\w*])(?:\*{3}|X{3}|x{3}|\*{2}|X{2}|x{2})[ -]?(?:\*{2}|X{2}|x{2})\b/gi },
|
|
95
|
+
{ type: 'ID', regex: /(?<=^|[^\w*])(?:\*{3}|X{3}|x{3}|\*{2}|X{2}|x{2})[ -]?(?:\*{2}|X{2}|x{2})[ -]?\d{4}\b/g },
|
|
96
|
+
{ type: 'ID', regex: /\b\d{2}-\d{7}\b/g },
|
|
97
|
+
{ type: 'ID', regex: /\b(?:HICN|MBI)[:\s#]*([0-9][A-Za-z][0-9A-Za-z][0-9]-[A-Za-z][0-9A-Za-z][0-9]-[A-Za-z0-9]{4})\b/gi },
|
|
98
|
+
{ type: 'ID', regex: /\b[1-9][A-Za-z][0-9A-Za-z][0-9]-[A-Za-z][0-9A-Za-z][0-9]-[A-Za-z0-9]{4}\b/gi },
|
|
99
|
+
{ type: 'LOCATION', regex: /\b[A-Za-z][a-zA-Z .'-]{1,25},\s*(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)\b/g },
|
|
100
|
+
|
|
101
|
+
// PHI & Medical
|
|
102
|
+
{ type: 'PHI', regex: /\b(?:MRN|Patient ID|Medical Record (?:No\.?|Num(?:ber)?|#)|Patient (?:No\.?|Num(?:ber)?|#))[\s:#]+([A-Za-z0-9-]+)/gi },
|
|
103
|
+
{ type: 'PHI', regex: /\bMRN[-_ ]*[A-Za-z0-9-]{4,}\b/gi },
|
|
104
|
+
{ type: 'ID', regex: /\b(?:NPI|National Provider Identifier)[:\s#]*(\d{10})\b/gi },
|
|
105
|
+
{ type: 'ID', regex: /\b(?:Device\s+(?:Identifier|ID|Serial|No\.?|Number)|UDI)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
106
|
+
{ type: 'ID', regex: /\bUDI[-_][A-Za-z0-9-]+\b/gi },
|
|
107
|
+
{ type: 'ID', regex: /\b(?:Vehicle\s+(?:Serial|ID|Identification(?:\s+Number)?|No\.?|Number)|VIN)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
108
|
+
{ type: 'PHI', regex: /\b(?!NCT|INC|SEC|ISO|DOC|SOC|CPT)[A-Z]{2,3}\d{6,8}\b/g },
|
|
109
|
+
{ type: 'PHI', regex: /\bNHS[ -]?\d{3}[ -]?\d{3}[ -]?\d{4}\b/gi },
|
|
110
|
+
|
|
111
|
+
// Copyrights
|
|
112
|
+
{ type: 'COPYRIGHT', regex: /\bPROJECT[-_][A-Z0-9]{5,}\b/gi },
|
|
113
|
+
{ type: 'COPYRIGHT', regex: /\b(DRAFT|ASSET|SCRIPT)[-_][0-9]{4,}\b/gi },
|
|
114
|
+
|
|
115
|
+
// General Privacy, Dates & Secrets
|
|
116
|
+
{ type: 'ID', regex: /\b(?:GDPR|HIPAA|CCPA|SOC2)[-_]AUDIT[-_]\d{4}\b/gi },
|
|
117
|
+
{ type: 'ID', regex: /\bPOLICY[-_][A-Z0-9]{5,}\b/gi },
|
|
118
|
+
{ type: 'ID', regex: /\bGRADE[S]?\s*:\s*[A-DF][+-]?\b/gi },
|
|
119
|
+
{ type: 'DATE', regex: /\b(?:DOB|BIRTHDAY|Date of Birth)[\s:]+([0-9./-]{6,10})\b/gi },
|
|
120
|
+
{ type: 'SECRET', regex: /\b(?:PASSWORD|PWD|SECRET)\s*[:=]\s*["']?[\S]{4,}["']?/gi },
|
|
121
|
+
|
|
122
|
+
// Standard IDs (SSN, EIN, Passport, VAT)
|
|
123
|
+
{ type: 'ID', regex: /\b\d{3}-\d{2}-\d{4}\b/g },
|
|
124
|
+
{ type: 'ID', regex: /\b(?:XXX|xxx|\*\*\*)[ -]?(?:XX|xx|\*\*)[ -]?\d{4}\b/g },
|
|
125
|
+
{ type: 'ID', regex: /\b\d{2}-\d{7}\b/g },
|
|
126
|
+
{ type: 'ID', regex: /(?:(?:[A-Za-z]:\\|\/(?:usr|var|etc|home|root|Users|private|tmp|opt|bin|sbin|dev|Applications|Library)\/)[a-zA-Z0-9_.-]+(?:[\/\\][a-zA-Z0-9_.-]+)*|\/(?:[a-zA-Z0-9_.-]+\/)+[a-zA-Z0-9_.-]+\.(?:txt|pdf|docx|xlsx|csv|js|ts|json|env|log|key|pem|crt|conf|yaml|yml|xml|html|sql|py|go|rs|c|cpp|h|sh|bin|zip|tar|gz|png|jpg|jpeg|svg|webp|wasm)\b)/g },
|
|
127
|
+
{ type: 'ID', regex: /\b[A-CEGHJ-PR-TW-Z]{1}[A-CEGHJ-NPR-TW-Z]{1}[0-9]{6}[A-DFM]{1}\b/gi },
|
|
128
|
+
{ type: 'ID', regex: /\b[A-Z]{2}[0-9]{6,12}\b/gi },
|
|
129
|
+
{ type: 'ID', regex: /[A-Z0-9<]{30,44}/g },
|
|
130
|
+
|
|
131
|
+
// IT / Technical IDs
|
|
132
|
+
{ type: 'IP', regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g },
|
|
133
|
+
{ type: 'IP', regex: /\b(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}\b/g },
|
|
134
|
+
{ type: 'ID', regex: /\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g },
|
|
135
|
+
{ type: 'ID', regex: /(?<![:/A-Za-z0-9])(?:\/[a-zA-Z0-9_.-]+(?:[\/\\][a-zA-Z0-9_.-]+)+|\b[a-zA-Z]:\\[\w.-]+(?:\\[\w.-]+)*)/g },
|
|
136
|
+
{ type: 'ID', regex: /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?\b/g },
|
|
137
|
+
{ type: 'ID', regex: /\b\d{4}-\d{2}-\d{2}\b/g },
|
|
138
|
+
{ type: 'ID', regex: /\b\d{2}\/\d{2}\/\d{4}\b/g },
|
|
139
|
+
|
|
140
|
+
// Phone Numbers
|
|
141
|
+
{ type: 'PHONE', regex: /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g },
|
|
142
|
+
{ type: 'PHONE', regex: /\+?[1-9]\d{1,3}[\s.-]\(?\d{1,4}\)?[\s.-]\d{2,4}[\s.-]\d{4}/g },
|
|
143
|
+
{ type: 'PHONE', regex: /(?:\+44\s?7\d{3}|\(?07\d{3}\)?)\s?\d{3}\s?\d{3}\b/g },
|
|
144
|
+
{ type: 'PHONE', regex: /\b(?:\d{3}[-.\s]\d{4}|\(\d{3}\)\s??\d{3}[-.\s]??\d{4}|\d{3}[-.\s]??\d{3}[-.\s]??\d{4})\b/g },
|
|
145
|
+
|
|
146
|
+
// Geolocation (Lat/Long)
|
|
147
|
+
{ type: 'LOCATION', regex: /\b-?\d{1,3}\.\d{4,6}[° ]?[NSns],\s*-?\d{1,3}\.\d{4,6}[° ]?[EWew]\b/g },
|
|
148
|
+
{ type: 'LOCATION', regex: /\b(LATITUDE|LONGITUDE)[:\s]-?\d{1,3}\.\d{4,10}\b/gi },
|
|
149
|
+
|
|
150
|
+
// Driver Licenses & State IDs
|
|
151
|
+
{ type: 'ID', regex: /\bDL[ -]?\d{6,12}\b/gi },
|
|
152
|
+
{ type: 'ID', regex: /\bDRIVER[S]?\s+LICENSE[ -]?\d{6,15}\b/gi },
|
|
153
|
+
|
|
154
|
+
// Generalized Name Detection (First [Middle] Last) — max 1 middle word to avoid grabbing job titles
|
|
155
|
+
// Middle word must be: a particle (van/de/etc.), a single initial (A. or A), or a capitalized word of ≥2 lowercase letters
|
|
156
|
+
{ type: 'NAME', isAggressiveName: true, regex: /(?<=^|[^\p{L}\p{N}_])(?:\p{Lu}[\p{Ll}'-]*\p{Ll}|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*)(?:[ \t\xA0]+(?:\p{Lu}[\p{Ll}'-]*\p{Ll}|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*|\p{Lu}\.?|van|von|de|di|da|la|le|del|du|der|van[ \t\xA0]+de|van[ \t\xA0]+der)){0,1}[ \t\xA0]+(?:\p{Lu}[\p{Ll}'-]*\p{Ll}|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*)(?:'s)?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
157
|
+
// Payroll Format Names (e.g. "BARKER, KELLY", "BARKER, KELLY M", "DOE, JOHN M.")
|
|
158
|
+
{ type: 'NAME', regex: /\b[A-Z]{2,25},\s+[A-Z]{2,25}(?:\s+[A-Z]\.?|\s+[A-Z]{2,25})*\b/g },
|
|
159
|
+
// All-Caps Names (2–3 words, supporting single-letter middle initial e.g. "KELLY M BARKER", "JOHN M BARKER")
|
|
160
|
+
{ type: 'NAME', regex: /(?<=^|[^\p{L}\p{N}_])\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:[ \t\xA0]+(?:\p{Lu}\.?|[A-Z][a-z]+))?[ \t\xA0]+\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:'s)?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
161
|
+
// ALL-CAPS first + middle initial(s) with optional spaces + ALL-CAPS last name
|
|
162
|
+
{ type: 'NAME', regex: /(?<=^|[^\p{L}\p{N}_])\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:[ \t\xA0]*\p{Lu}\.)+[ \t\xA0]*\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:'s)?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
163
|
+
// ALL-CAPS first name + optional middle initial(s) with optional spaces + Mixed-Case last name
|
|
164
|
+
{ type: 'NAME', regex: /(?<=^|[^\p{L}\p{N}_])\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:(?:[ \t\xA0]*\p{Lu}\.)+[ \t\xA0]*|[ \t\xA0]+)(?:\p{Lu}\p{Ll}[\p{Ll}'-]*|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*)(?:'s)?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
165
|
+
// Names with Honorifics (with or without period, supporting single or multi-word full names) (Unicode-safe)
|
|
166
|
+
{ type: 'NAME', regex: /(?<=^|[^\p{L}\p{N}_])(?:Mr|Mrs|Ms|Dr|Prof|Hon|Mr\.|Mrs\.|Ms\.|Dr\.|Prof\.|Hon\.)[ \t\xA0]+(?:\p{Lu}[\p{Ll}'-]*\p{Ll}|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*)(?:[ \t\xA0]+(?:\p{Lu}[\p{Ll}'-]*\p{Ll}|\p{Lu}[\p{Ll}'-]*[\p{Lu}'-][\p{Ll}'-]*))?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
167
|
+
|
|
168
|
+
// W-2 Box c Employer Block (Name, Address, and Zip Code)
|
|
169
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:\bBox\s+c\b|\bc\.\s*|\bc\s+(?=Employer))\s*(?:Employer(?:'s)?\s*)?(?:name[,\s]+address[,\s]+and\s+ZIP\s+code|name)?|(?:Employer(?:'s)?\s+name[,\s]+address[,\s]+and\s+ZIP\s+code))[\s:#]*([A-Za-z0-9&., \t\xA0'-]{2,45}?)(?=\r?\n|$|\s{3,}|\t|EIN|FEIN|Box|\d+\b|Wages|Federal|Social|Medicare)/gi },
|
|
170
|
+
// W-2 Box e Employee Name & Initial
|
|
171
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:\bBox\s+e\b|\be\.\s*|\be\s+(?=Employee))\s*(?:Employee(?:'s)?\s*)?(?:first\s+name(?:\s+(?:and|&)\s+initial)?|name)?[:\s#]*|(?:Employee(?:'s)?\s+first\s+name(?:\s+(?:and|&)\s+initial)?))[\s:#]*([A-Za-z0-9.\s'-]{2,35}?)(?=\r?\n|$|\s{3,}|\t|Last|Surname|Suff|Box|\d+\b|1\b)/gi },
|
|
172
|
+
// Contextual First Names (Employee's first name, First name, Given name)
|
|
173
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:Employee(?:'s)?|Borrower(?:'s)?|Co-Borrower(?:'s)?|Applicant(?:'s)?|Candidate(?:'s)?|Worker(?:'s)?|Taxpayer(?:'s)?|Spouse(?:'s)?|Person(?:'s)?)\s+)?(?:First\s+name(?:\s+(?:and|&)\s+initial)?|Given\s+name)[\s:#]+(?:\b|\b\s*)([A-Za-z0-9.\s'-]{2,30}?)(?=\r?\n|$|\s{3,}|\t|Last|Surname|Family|Suff|Box|Address|SSN|EIN)/gi },
|
|
174
|
+
// Contextual Last Names (Last name, Surname, Family name)
|
|
175
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:Employee(?:'s)?|Borrower(?:'s)?|Co-Borrower(?:'s)?|Applicant(?:'s)?|Candidate(?:'s)?|Worker(?:'s)?|Taxpayer(?:'s)?|Spouse(?:'s)?|Person(?:'s)?)\s+)?(?:Last\s+name|Surname|Family\s+name)[\s:#]+(?:\b|\b\s*)([A-Za-z'-]{2,30})/gi },
|
|
176
|
+
// Contextual General Names (Employee, Principal Investigator, Physician, Doctor, etc.)
|
|
177
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:Employee|Employer|Borrower|Co-Borrower|Applicant|Candidate|Worker|Taxpayer|Spouse|Client|Customer|Patient|Claimant|Insured|Tenant|Landlord|Buyer|Seller|Plaintiff|Defendant|Testator|Author)\s+(?:Name|Full\s+Name)|(?:Principal\s+Investigator|Investigator|Physician|Doctor|Supervisor|Reporting\s+To|Prepared\s+By|Attention|Attn|Contact(?:\s+Person)?|Author)(?:\s+Name)?|(?:Employee|Employer|Borrower|Client|Customer|Patient|Claimant|Insured|Doctor|Attn|Signatory|Witness|Tenant|Landlord|Buyer|Seller|Plaintiff|Defendant|Author)\s*[:=#])[:\s#]*(?:\b|\b\s*)([A-Z\p{Lu}][A-Za-z0-9&.,\s'-]{1,35}?)(?=\r?\n|$|\s{3,}|\t|Employee|Employer|Address|Phone|SSN|EIN|FEIN|Date|Pay|Rate|Tax|W-2|OMB|Copy|Box|Status)/giu },
|
|
178
|
+
// Box e shorthand
|
|
179
|
+
{ type: 'NAME', isContextName: true, regex: /\b(?:Box\s+e)\s*[:#-]\s*([A-Za-z0-9&.,\s'-]{2,40})/gi }
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
let PROFILE_RULES = {
|
|
183
|
+
general: [],
|
|
184
|
+
legal: [
|
|
185
|
+
{ type: 'LEGAL', regex: /\bCASE[-_][A-Z0-9_-]{4,}\b/gi },
|
|
186
|
+
{ type: 'LEGAL', regex: /\bMATTER[-_][A-Z0-9_-]{4,}\b/gi },
|
|
187
|
+
{ type: 'LEGAL', regex: /\b[A-Z]{2,4}[- ]?\d{2}[- ]?\d{4,}\b/g },
|
|
188
|
+
{ type: 'PRIVILEGE', regex: /ATTORNEY[- ]CLIENT[- ]PRIVILEGE/gi }
|
|
189
|
+
],
|
|
190
|
+
hr: [
|
|
191
|
+
{ type: 'NAME', isContextName: true, regex: /(?:Candidate|Applicant|Employee|Reporting To|Manager|Mentored by|Direct Report)[\s:]+([A-Z][a-z]*(?:\s+[A-Z][a-z]*)?)/g },
|
|
192
|
+
{ type: 'ID', regex: /\bEEID[ -]?\d{4,}\b/gi },
|
|
193
|
+
{ type: 'ID', regex: /\bEMP[-_]\d{3,}\b/gi },
|
|
194
|
+
{ type: 'ID', regex: /\bRESUME[-_]?[A-Z0-9]{4,}\b/gi },
|
|
195
|
+
{ type: 'DATE', regex: /\b(?:DOB|BIRTHDAY|Date of Birth)[\s:]+([0-9./-]{6,10})\b/gi },
|
|
196
|
+
{ type: 'DATE', regex: /\b(?:Graduated|Graduation|Class of)[:\s]+(?:(?:Spring|Summer|Fall|Winter)\s+)?(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|January|February|March|April|June|July|August|September|October|November|December)\s+)?\d{4}\b/gi },
|
|
197
|
+
{ type: 'ID', regex: /(?:https?:\/\/)?(?:www\.)?linkedin\.com\/in\/[A-Za-z0-9_-]+/gi },
|
|
198
|
+
{ type: 'ID', regex: /(?:https?:\/\/)?(?:www\.)?github\.com\/[A-Za-z0-9_-]+/gi },
|
|
199
|
+
{ type: 'ADDRESS', regex: /\b\d{1,6}\s+(?:[A-Z0-9][a-zA-Z0-9-]*\s+){1,3}(?:St|Street|Ave|Avenue|Blvd|Boulevard|Rd|Road|Ln|Lane|Drive|Way|Ct|Court|Pl|Place|Terrace|Pkwy|Parkway|Sq|Square|Highway|Hwy|Circle|Cir|Trail|Trl|Dr(?!\.?\s+[A-Z][a-z]+))\b/g }
|
|
200
|
+
],
|
|
201
|
+
finance: [
|
|
202
|
+
{ type: 'FINANCIAL', regex: /\b[A-Z]{4}(?:AD|AE|AF|AG|AI|AL|AM|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BL|BM|BN|BO|BQ|BR|BS|BT|BV|BW|BY|BZ|CA|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|CR|CU|CV|CW|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EE|EG|EH|ER|ES|ET|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MF|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MX|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|SS|ST|SV|SX|SY|SZ|TC|TD|TF|TG|TJ|TK|TL|TM|TN|TO|TR|TT|TV|TW|TZ|UA|UG|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|YE|YT|ZA|ZM|ZW)[A-Z2-9][A-NP-Z0-9](?:[A-Z0-9]{3})?\b/g },
|
|
203
|
+
{ type: 'FINANCIAL', regex: /\b[A-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}[a-zA-Z0-9]{0,16}\b/g },
|
|
204
|
+
{ type: 'FINANCIAL', regex: /\bPORTFOLIO[-_][A-Z0-9]{5,}\b/gi }
|
|
205
|
+
],
|
|
206
|
+
medical: [
|
|
207
|
+
{ type: 'PHI', regex: /\b(?:MRN|Patient ID|Medical Record (?:No\.?|Num(?:ber)?|#)|Patient (?:No\.?|Num(?:ber)?|#))[\s:#]+([A-Za-z0-9-]+)/gi },
|
|
208
|
+
{ type: 'DATE', regex: /\b(?:DOB|Date of Birth|BIRTHDAY)[\s:]+([0-9./-]{6,10})\b/gi },
|
|
209
|
+
{ type: 'PHI', regex: /\bMRN[-_ ]*[A-Za-z0-9-]{4,}\b/gi },
|
|
210
|
+
{ type: 'ID', regex: /\b(?:Insurance\s+(?:ID|No\.?|Number|#)|Policy(?:\s*(?:ID|No\.?|Number|#)|[:#])|Member\s*(?:ID|No\.?|Number|#|[:#])|Subscriber\s*(?:ID|No\.?|Number|#|[:#])|Group\s*(?:ID|No\.?|Number|#|[:#])|Plan\s*(?:ID|No\.?|Number|#|[:#])|Health(?:\s+Plan)?\s*(?:ID|No\.?|Number|#)|Rx\s*(?:ID|No\.?|Number|Group|BIN|PCN|#))[:\s#]+([A-Za-z0-9-]{3,30})\b/gi },
|
|
211
|
+
{ type: 'ID', regex: /\b(?:Health\s+Plan(?:\s+Beneficiary)?|Beneficiary(?:\s+No\.?|\s+Number)?|HPN)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
212
|
+
{ type: 'ID', regex: /\bHPN[-_][A-Za-z0-9-]+\b/gi },
|
|
213
|
+
{ type: 'ID', regex: /\b(?:BCB|BCBS|AETNA|CIGNA|UHC|HUMANA|MEDICARE|MEDICAID)[-_A-Za-z0-9]+\b/gi },
|
|
214
|
+
{ type: 'ID', regex: /\b(?:NPI|National Provider Identifier)[:\s#]*(\d{10})\b/gi },
|
|
215
|
+
{ type: 'ID', regex: /\b(?:Device\s+(?:Identifier|ID|Serial|No\.?|Number)|UDI)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
216
|
+
{ type: 'ID', regex: /\bUDI[-_][A-Za-z0-9-]+\b/gi },
|
|
217
|
+
{ type: 'ID', regex: /\b(?:Vehicle\s+(?:Serial|ID|Identification(?:\s+Number)?|No\.?|Number)|VIN)[:\s#]+([A-Za-z0-9-]+)/gi },
|
|
218
|
+
{ type: 'ID', regex: /\bVIN[-_][A-Za-z0-9-]+\b/gi },
|
|
219
|
+
{ type: 'PHI', regex: /\b(?!NCT|INC|SEC|ISO|DOC|SOC|CPT)[A-Z]{2,3}\d{6,8}\b/g },
|
|
220
|
+
{ type: 'PHI', regex: /\bNHS[ -]?\d{3}[ -]?\d{3}[ -]?\d{4}\b/gi }
|
|
221
|
+
],
|
|
222
|
+
security: [
|
|
223
|
+
{ type: 'SECRET', regex: /\b(?:INCIDENT|BREACH)[-_: ]?ID[-_: ]?[0-9]{4,10}\b/gi }
|
|
224
|
+
],
|
|
225
|
+
marketing: [
|
|
226
|
+
{ type: 'ID', regex: /\b(?:LEAD|PROSPECT)[-_: ]*[A-Z0-9_-]*[0-9][A-Z0-9_-]*\b/gi },
|
|
227
|
+
{ type: 'ID', regex: /\b(?:CAMPAIGN|CLID|GCLID|FBCLID)[-_:=]*[A-Za-z0-9_-]{10,}\b/gi },
|
|
228
|
+
{ type: 'FINANCIAL', regex: /(?<=\b(?:LTV|CAC)[\s:]*)(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[0-9,.]+\b/gi },
|
|
229
|
+
{ type: 'ID', regex: /\b(?:SEGMENT|COHORT)[-_: ]*[0-9]{4,8}\b/gi }
|
|
230
|
+
],
|
|
231
|
+
bizops: [
|
|
232
|
+
{ type: 'ID', regex: /\b(?:DEAL|KPI|METRIC)[-_: ]?[A-Z0-9]{4,}\b/gi },
|
|
233
|
+
{ type: 'ID', regex: /\b(?:ENTITY|VENDOR|PARTNER)[-_: ]?[0-9]{4,10}\b/gi },
|
|
234
|
+
{ type: 'FINANCIAL', regex: /\b(?:REVENUE|EBITDA|PROFIT|MARGIN)[\s:_-]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)?[0-9,.]+[KM]?\b/gi },
|
|
235
|
+
{ type: 'SECRET', regex: /\b(?:NDA|M&A|MERGER)[-_: ]?[A-Z0-9]{4,}\b/gi }
|
|
236
|
+
],
|
|
237
|
+
sales: [
|
|
238
|
+
{ type: 'ID', regex: /\bOPPORTUNITY[-_: ]?[A-Z0-9]{5,}\b/gi },
|
|
239
|
+
{ type: 'ID', regex: /\b(?:DOCUSIGN|CONTRACT)[-_: ]?[0-9A-F]{8,32}\b/gi },
|
|
240
|
+
{ type: 'FINANCIAL', regex: /\b(?:ARR|MRR|QUOTA)[\s:]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)?[0-9,.]+[KM]?\b/gi },
|
|
241
|
+
{ type: 'ID', regex: /\b(?:SFDC|HUBSPOT)[-_: ]?[0-9A-Z]{15,18}\b/gi }
|
|
242
|
+
],
|
|
243
|
+
support: [
|
|
244
|
+
{ type: 'ID', regex: /\bTICKET[-_:# ]?[A-Z0-9]{5,10}\b/gi },
|
|
245
|
+
{ type: 'ID', regex: /\b(?:ZENDESK|INTERCOM|JIRA)[-_:# ]?[0-9]{4,10}\b/gi },
|
|
246
|
+
{ type: 'ID', regex: /\b(?:REFUND|RETURN|RMA)[-_:# ]?[A-Z0-9]{6,12}\b/gi },
|
|
247
|
+
{ type: 'ID', regex: /\b(?:LOYALTY|REWARDS)[-_:# ]?\d{8,12}\b/gi }
|
|
248
|
+
],
|
|
249
|
+
realestate: [
|
|
250
|
+
{ type: 'ID', regex: /\b(?:MLS|LIS)[- ]?\d{6,10}\b/gi },
|
|
251
|
+
{ type: 'ID', regex: /\bPARCEL[- ]?\d{5,15}\b/gi },
|
|
252
|
+
{ type: 'ID', regex: /\bTENANT[-_]ID[-_][0-9]{4,}\b/gi },
|
|
253
|
+
{ type: 'FINANCIAL', regex: /(?<=\b(?:RENT|LEASE|ESCROW)[\s:]+)(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[0-9,]{3,}(?:\.\d{2})?\b/gi },
|
|
254
|
+
{ type: 'SECRET', regex: /\b(?:GATE|DOOR|LOBBY)[-_ ](?:CODE|PIN)[\s:]*\d{4,6}\b/gi }
|
|
255
|
+
],
|
|
256
|
+
compliance: [
|
|
257
|
+
{ type: 'SECRET', regex: /\b(?:GDPR|HIPAA|CCPA|SOC2|ISO27001)[-_: ]?AUDIT[-_: ]?\d{4}\b/gi },
|
|
258
|
+
{ type: 'SECRET', regex: /\b(?:DPA|POLICY)[-_: ]?[A-Z0-9]{5,15}\b/gi },
|
|
259
|
+
{ type: 'ID', regex: /\b(?:SAR|DSAR)(?:[-_\/: ]+[A-Z0-9/_-]+|[0-9][A-Z0-9/_-]*)\b/gi }
|
|
260
|
+
],
|
|
261
|
+
ccpa: [
|
|
262
|
+
{ type: 'ID', regex: /\b(?:DL|DRIVER['’]?S?\s+LICENSE)[:\s#-]*[A-Z0-9]{6,12}\b/gi },
|
|
263
|
+
{ type: 'LOCATION', regex: /\b-?\d{1,3}\.\d{4,6}[° ]?[NSns],\s*-?\d{1,3}\.\d{4,6}[° ]?[EWew]\b/g },
|
|
264
|
+
{ type: 'ID', regex: /\b(?:CCPA|CPRA)[-_: ]?OPT[-_ ]OUT\b/gi },
|
|
265
|
+
{ type: 'ID', regex: /\bACCOUNT[ -]?(?:ID|NUM|NUMBER)[:\s]+[A-Z0-9]{6,20}\b/gi }
|
|
266
|
+
],
|
|
267
|
+
engineering: [
|
|
268
|
+
{ type: 'SECRET', regex: /(?<=\b(?:DB|POSTGRES|REDIS|MYSQL|AWS|SECRET|PASSWORD|TOKEN|API|KEY)[A-Z0-9_]*\s*[:=]\s*["']?)[A-Za-z0-9_-]{10,}/gi },
|
|
269
|
+
{ type: 'ID', regex: /\b[a-z0-9._-]+\/[a-z0-9._-]+:[a-z0-9._-]+\b/g },
|
|
270
|
+
{ type: 'ID', regex: /\b[a-fA-F0-9]{40}\b/g },
|
|
271
|
+
{ type: 'ID', regex: /\b[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.svc\.cluster\.local\b/g }
|
|
272
|
+
],
|
|
273
|
+
agents: [
|
|
274
|
+
{ type: 'ID', regex: /\b(?:AGENT|VECTOR|EMBEDDING)[-_: ]?(?:ID[-_: ]?)?[A-Z0-9]{8,}\b/gi },
|
|
275
|
+
{ type: 'ID', regex: /\bTASK[-_: ]?[A-Z0-9]{5,15}\b/gi },
|
|
276
|
+
{ type: 'SECRET', regex: /\b(?:SYS_PROMPT|SYSTEM_PROMPT|OPENAI_API_KEY)[-_: ]?[A-Za-z0-9_-]{10,}\b/gi }
|
|
277
|
+
],
|
|
278
|
+
academic: [
|
|
279
|
+
{ type: 'ID', regex: /\b(?:STUDENT|ALUMNI)[-_:# ]?[0-9]{5,10}\b/gi },
|
|
280
|
+
{ type: 'ID', regex: /\bCOURSE[-_:# ]?[A-Z]{3,4}[ ]?[0-9]{3,4}\b/gi },
|
|
281
|
+
{ type: 'ID', regex: /\b(?:FERPA|IRB)[-_:# ]*[A-Z0-9_-]*\d[A-Z0-9_-]*\b/gi },
|
|
282
|
+
{ type: 'ID', regex: /\bGRADE[S]?[\s:][A-DF][+-]?\b/gi }
|
|
283
|
+
],
|
|
284
|
+
creative: [
|
|
285
|
+
{ type: 'SECRET', regex: /\b(?:PROJECT|DRAFT|ASSET|SCRIPT|IP)[-_: ]?[0-9]{4,10}\b/gi },
|
|
286
|
+
{ type: 'SECRET', regex: /\b(?:SPOILER|UNRELEASED|EMBARGOED)\b/gi },
|
|
287
|
+
{ type: 'NAME', regex: /\b(?:GHOSTWRITER|SOURCE)[:\s][A-Z][a-z]+ [A-Z][a-z]+\b/g }
|
|
288
|
+
],
|
|
289
|
+
tech: [
|
|
290
|
+
{ type: 'ID', regex: /\b(?:INSTANCE|NODE|CLUSTER)[-_]ID[-_][a-z0-9-]{10,}\b/gi },
|
|
291
|
+
{ type: 'ID', regex: /\bENV[-_: ]?(?:PROD|STAGING|DEV|TEST|QA)\b/gi },
|
|
292
|
+
{ type: 'SECRET', regex: /\b(?:CONFIG|KUBECONFIG|TFSTATE)[-_: ]?[A-Z0-9]{6,15}\b/gi }
|
|
293
|
+
],
|
|
294
|
+
personal: [
|
|
295
|
+
{ type: 'DATE', regex: /\b(?:DOB|BIRTHDAY|Date of Birth)[\s:]+([0-9./-]{6,10})\b/gi },
|
|
296
|
+
{ type: 'SECRET', regex: /\b(?:PASSWORD|PWD|SECRET|PIN)[\s:]*[\S]{4,20}\b/gi },
|
|
297
|
+
{ type: 'PHONE', regex: /\b(?:WIFE|HUSBAND|PARTNER|MOM|DAD)[\s:]+(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b/gi }
|
|
298
|
+
],
|
|
299
|
+
wealthmgmt: [
|
|
300
|
+
{ type: 'LEGAL', regex: /\b(?:THE\s+)?[A-Z][A-Z\s.'-]{4,}\s+(?:(?:REVOCABLE|IRREVOCABLE|LIVING|FAMILY|TESTAMENTARY|CHARITABLE|GENERATION-SKIPPING)\s+)*TRUST\b/g },
|
|
301
|
+
// Mixed-Caps name: All-Caps first name (3+ chars) + optional initials + Mixed-Case last name
|
|
302
|
+
// Handles PDF-rendered trust names where OCR/extraction produces e.g. "KKKKK I. Mmmmmmm"
|
|
303
|
+
{ type: 'NAME', regex: /(?<=^|[^\p{L}\p{N}_])\p{Lu}{3,}(?:[\p{Lu}'-]*\p{Lu})?(?:[ \t\xA0]+\p{Lu}\.)*[ \t\xA0]+\p{Lu}\p{Ll}[\p{Ll}'-]*(?:\p{Lu}[\p{Ll}'-]*)?\p{L}(?:'s)?(?=[^\p{L}\p{N}_]|$)/gu },
|
|
304
|
+
{ type: 'FINANCIAL', regex: /\b(?:ABA|Routing(?:\s+No)?|RTN)[:\s#]*\d{9}\b/gi },
|
|
305
|
+
{ type: 'FINANCIAL', regex: /\b(?:Account|Acct\.?)[:\s#]*\d{4}[-\s]?\d{4}[-\s]?\d{2,6}\b/gi },
|
|
306
|
+
{ type: 'FINANCIAL', regex: /\b(?:Policy|Contract)\s*(?:No\.?|Number|#)[:\s]+[A-Z0-9][A-Z0-9\-]{3,14}\b/gi },
|
|
307
|
+
{ type: 'ID', regex: /\bCRD\s*#?\s*\d{4,8}\b/gi },
|
|
308
|
+
{ type: 'FINANCIAL', regex: /\b(?:Annual\s+(?:Distribution|Withdrawal)|RMD|Required\s+Minimum\s+Distribution)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.][\d]{2})?\b/gi },
|
|
309
|
+
{ type: 'FINANCIAL', regex: /\b(?:Portfolio|Market\s+Value|Net\s+Worth|AUM|Total\s+Assets)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,.]+[KMB]?\b/gi },
|
|
310
|
+
{ type: 'FINANCIAL', regex: /\b(?:Roth\s+)?(?:IRA|401k|401\(k\)|403b|403\(b\)|SEP|SIMPLE)\s*(?:Account|Acct|Plan)?\s*(?:No|Number|#)?[:\s#]*[A-Z0-9]{4,15}\b/gi }
|
|
311
|
+
],
|
|
312
|
+
insurance: [
|
|
313
|
+
{ type: 'ID', regex: /\b(?:Claim|CLM)[:\s#-]*[A-Z0-9]{6,15}\b/gi },
|
|
314
|
+
{ type: 'ID', regex: /\b(?:Policy|POL)[:\s#-]*[A-Z]{0,4}[-]?\d{6,12}\b/gi },
|
|
315
|
+
{ type: 'FINANCIAL', regex: /\b(?:Loss|Claim\s+Amount|Settlement|Reserve|Indemnity)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
316
|
+
{ type: 'ID', regex: /\b(?:Adjuster\s+(?:ID|No)|Claim\s+Rep(?:\.|resentative)?)[:\s#]*[A-Z0-9]{4,12}\b/gi },
|
|
317
|
+
{ type: 'ID', regex: /\bNAIC[:\s#]*\d{5}\b/gi },
|
|
318
|
+
{ type: 'FINANCIAL', regex: /\b(?:Deductible|Premium|Coverage\s+Amount)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
319
|
+
{ type: 'ID', regex: /\b[A-HJ-NPR-Z0-9]{17}\b/g },
|
|
320
|
+
{ type: 'ID', regex: /\b(?:Insured|Named\s+Insured)[:\s]+[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+/g },
|
|
321
|
+
{ type: 'ID', regex: /\b(?:Agent|Producer)\s*(?:Code|No|ID)[:\s#]*[A-Z0-9]{4,12}\b/gi }
|
|
322
|
+
],
|
|
323
|
+
accounting: [
|
|
324
|
+
{ type: 'ID', regex: /\b(?:EIN|FEIN|Tax\s+ID)[:\s#]*\d{2}-\d{7}\b/gi },
|
|
325
|
+
{ type: 'FINANCIAL', regex: /\b(?:AGI|Adjusted\s+Gross\s+Income|Taxable\s+Income|Total\s+Income)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
326
|
+
{ type: 'FINANCIAL', regex: /\bBox\s+\d{1,2}[a-z]?[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
327
|
+
{ type: 'ID', regex: /\b(?:Form|Schedule)\s+(?:1040|1040-SR|W-2|W-4|1099-[A-Z]{1,4}|K-1|941|990|4562)\b/gi },
|
|
328
|
+
{ type: 'FINANCIAL', regex: /\b(?:Federal|State)\s+(?:Tax\s+)?(?:Refund|Amount\s+Owed|Balance\s+Due)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
329
|
+
{ type: 'ID', regex: /\b(?:State\s+Tax\s+ID|SUI|UI\s+Account\s+No)[:\s#]*[A-Z]{0,3}[-]?\d{4,15}\b/gi },
|
|
330
|
+
{ type: 'FINANCIAL', regex: /\b(?:Net\s+Pay|Gross\s+Pay|Taxable\s+Wages)[:\s]+(?:[$€£¥₪₽₹]|(?:USD|EUR|GBP|CHF|ILS|RUB)\s?)[\d,]+(?:[.]\d{2})?\b/gi },
|
|
331
|
+
{ type: 'ID', regex: /\b(?:CAF|Practitioner\s+(?:PIN|ID)|PTIN)[:\s#]*[A-Z]?\d{6,9}\b/gi }
|
|
332
|
+
],
|
|
333
|
+
pharma: [
|
|
334
|
+
{ type: 'ID', regex: /\b(?:Subject|Patient|Participant)\s+(?:ID|No)[:\s#]*[A-Z0-9]{3,12}\b/gi },
|
|
335
|
+
{ type: 'ID', regex: /\b(?:Protocol|Study)\s*(?:No|Number|ID)[:\s#]*[A-Z0-9][-A-Z0-9]{3,15}\b/gi },
|
|
336
|
+
{ type: 'ID', regex: /\b(?:IND|NDA|BLA|ANDA)\s*(?:No|Number|#)?[:\s]*\d{3,6}\b/gi },
|
|
337
|
+
{ type: 'ID', regex: /\bIRB[:\s#]*[A-Z0-9]{4,12}\b/gi },
|
|
338
|
+
{ type: 'ID', regex: /\b(?:Site\s+(?:No|ID)|Investigator\s+Site)[:\s#]*\d{3,6}\b/gi },
|
|
339
|
+
{ type: 'ID', regex: /\b(?:Batch|Lot|Serial)\s*(?:No[.]?|Number|#)?[:\s#]*[A-Z0-9][A-Z0-9\-]{3,14}\b/gi },
|
|
340
|
+
{ type: 'PHI', regex: /\b(?:Dose|Dosage)[:\s]+\d+(?:\.\d+)?\s*(?:mg|mcg|mL|IU|units?)\b/gi },
|
|
341
|
+
{ type: 'ID', regex: /\b(?:CRF|eCRF|Case\s+Report\s+Form)\s*(?:No|Page|ID)?[:\s#]*[A-Z0-9]{2,10}\b/gi }
|
|
342
|
+
],
|
|
343
|
+
underwriting: [
|
|
344
|
+
// Employer Corporate / Business Names
|
|
345
|
+
{ type: 'NAME', isContextName: true, regex: /\b(?:[A-Z][A-Za-z0-9&.,'-]*[ \t\xA0]+){1,5}(?:Inc\.?|LLC|Corp\.?|Corporation|Ltd\.?|Limited|Co\.?|Company|Group|Holdings|Solutions|Services|Technologies|Logistics|Industries|Capital|Bank|Partners|LLP|PLLC)(?:\s+(?:LLC|Inc\.?|Corp\.?|Ltd\.?|USA|Group))?\b/g },
|
|
346
|
+
{ type: 'NAME', isContextName: true, regex: /(?:Employer|Company|Organization|Business)\s*(?:Name)?[\s:#]+([A-Za-z0-9&.,\s'-]{2,40}?)(?=\r?\n|$|\s{3,}|\t|Address|EIN|FEIN|Phone|W-2|Rate|Pay|Wage)/gi },
|
|
347
|
+
|
|
348
|
+
// W-2 & Tax Identifiers
|
|
349
|
+
{ type: 'ID', regex: /(?:\b(?:Box\s+d\b|d\.\s*(?:Control|#)?|d\s+Control)\s*(?:number|no\.?|#|num)?[:\s#]*|\bControl\s*(?:number|no\.?|#|num)[:\s#]*|\bControl[:#]\s*)([A-Za-z0-9-]{3,30})/gi },
|
|
350
|
+
{ type: 'ID', regex: /\b(?:EIN|FEIN|Tax\s+ID)[:\s#]*\d{2}-\d{7}\b/gi },
|
|
351
|
+
{ type: 'ID', regex: /\b\d{2}-\d{7}\b/g },
|
|
352
|
+
{ type: 'ID', regex: /\b\d{3}-\d{2}-\d{4}\b/g },
|
|
353
|
+
{ type: 'ID', regex: /\b(?:XXX|xxx|\*\*\*)[ -]?(?:XX|xx|\*\*)[ -]?\d{4}\b/g },
|
|
354
|
+
|
|
355
|
+
// Employee, Loan & Payroll IDs
|
|
356
|
+
{ type: 'ID', regex: /\b(?:Employee|Emp|EE|Worker|Borrower|Badge|Advisor|Producer|Agent|Applicant|File)\s*(?:#|ID|No\.?|Number)[:\s#]*[A-Z0-9-]{3,20}\b/gi },
|
|
357
|
+
{ type: 'ID', regex: /\b(?:Pay\s+Group|Cost\s+Center|Dept|Department)(?:[:#][\s#]*[A-Za-z0-9_-]{2,30}|\s+[A-Za-z0-9_-]*\d[A-Za-z0-9_-]*)\b/gi },
|
|
358
|
+
{ type: 'ID', regex: /\b(?:Loan|Application|Deal|Borrower|File)\s*(?:#|ID|No\.?|Number)[:\s#]*[A-Za-z0-9-]{4,25}\b/gi },
|
|
359
|
+
|
|
360
|
+
// Direct Deposit, Bank Accounts & Routing Numbers (Masked & Unmasked)
|
|
361
|
+
{ type: 'FINANCIAL', regex: /\b(?:Account|Acct|Checking|Savings|Direct\s+Deposit)\s*(?:#|ID|No\.?|Number)?[:\s#]*(?:[\*xX•.-]{3,}\d{2,6}|\d{4}[-\s]?\d{4}[-\s]?\d{2,6})\b/gi },
|
|
362
|
+
{ type: 'FINANCIAL', regex: /\b(?:ABA|Routing|RTN)\s*(?:#|ID|No\.?|Number)?[:\s#]*\d{9}\b/gi },
|
|
363
|
+
|
|
364
|
+
// Borrower & Co-Borrower Names (ALL-CAPS, Payroll, Title Case)
|
|
365
|
+
{ type: 'NAME', regex: /\b[A-Z]{2,25},\s+[A-Z]{2,25}(?:\s+[A-Z]\.?|\s+[A-Z]{2,25})*\b/g },
|
|
366
|
+
{ type: 'NAME', isAggressiveName: true, regex: /(?<=^|[^\p{L}\p{N}_])\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:[ \t\xA0]+(?:\p{Lu}\.?|[A-Z][a-z]+))?[ \t\xA0]+\p{Lu}{2,}(?:[\p{Lu}'-]*\p{Lu})?(?:'s)?(?=[^\p{L}\p{N}_]|$)(?![ \t\xA0]*:)/gu },
|
|
367
|
+
{ type: 'NAME', isContextName: true, regex: /(?:(?:Borrower|Co-Borrower|Applicant|Co-Applicant|Employee|Worker|Taxpayer|Candidate|Primary\s+Borrower|Joint\s+Borrower|Account\s+Holder|Insured|Client)\s*(?:Name)?|First\s+name|Given\s+name)[\s:#]+(?:\b|\b\s*)([A-Za-z0-9&.,\s'-]{2,35}?)(?=\r?\n|$|\s{3,}|\t|SSN|EIN|DOB|Address|Phone|Rate|Pay|Wage|Date|Box|Last|Surname)/gi },
|
|
368
|
+
{ type: 'NAME', isContextName: true, regex: /(?:Last\s+name|Surname|Family\s+name)[\s:#]+(?:\b|\b\s*)([A-Za-z'-]{2,30})/gi },
|
|
369
|
+
|
|
370
|
+
// Addresses & Locations
|
|
371
|
+
{ type: 'ADDRESS', isContextAddress: true, regex: /(?:(?:Borrower(?:'s)?|Co-Borrower(?:'s)?|Employee(?:'s)?|Employer(?:'s)?|Home|Mailing|Property|Physical)\s+address|(?:(?:\bBox\s+f\b|\bf\.\s*|\bf\s+(?=Employee))\s*(?:Employee(?:'s)?\s*)?address))[\s:#]+([A-Za-z0-9#.,\s-]{4,55}?)(?=\r?\n|$|\s{3,}|\t|City|State|ZIP|SSN|EIN|Phone|Box|\d+\b)/gi },
|
|
372
|
+
{ type: 'ADDRESS', regex: /\b\d{1,6}[ \t\xA0]+(?:[A-Za-z0-9.-]+[ \t\xA0]+){1,4}(?:St|Street|Ave|Avenue|Blvd|Boulevard|Rd|Road|Ln|Lane|Dr|Drive|Way|Ct|Court|Pl|Place|Terrace|Pkwy|Parkway|Sq|Square|Hwy|Highway|Cir|Circle|Trl|Trail|Loop|Row|Pike|Box|PO Box|P\.O\.[ \t\xA0]*Box)\b(?:[ \t\xA0]*,?[ \t\xA0]*(?:Apt|Apartment|Suite|Ste|Unit|#|Fl|Floor|Bldg|Building)\.?[ \t\xA0]*[A-Za-z0-9-]+)?/gi },
|
|
373
|
+
{ type: 'ADDRESS', regex: /\b[A-Za-z][a-zA-Z\s.-]{1,25},?\s+(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)\s+\d{5}(?:-\d{4})?\b/g },
|
|
374
|
+
{ type: 'ADDRESS', regex: /\b\d{5}-\d{4}\b/g },
|
|
375
|
+
{ type: 'LOCATION', regex: /\b[A-Za-z][a-zA-Z .'-]{1,25}(?:,\s*(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR)|\s+(?:AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA|HI|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY|DC|PR))\b/g }
|
|
376
|
+
]
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
let NAME_STOP_LIST = new Set([
|
|
380
|
+
'clinical note', 'case note', 'prod log', 'siem alert', 'critical security incident', 'security incident', 'hr review', 'crm export', 'bank statement', 'file export', 'database row', 'lease application', 'strategy export', 'action log', 'glossary query', 'tool comparison', 'call transcript', 'zendesk ticket', 'board minutes', 'agent context', 'config dump', 'database dump', 'patient note', 'medical record', 'admission note', 'discharge summary', 'progress note', 'hiring review', 'security audit', 'incident response', 'server log', 'system log', 'api response', 'error log', 'audit log', 'debug log',
|
|
381
|
+
'tax statement', 'wage and tax statement', 'wage and tax', 'wage statement', 'earning statement', 'earnings statement', 'pay statement', 'pay stub', 'paystub', 'withholding statement',
|
|
382
|
+
'case no', 'account no', 'client no', 'ref no', 'matter no',
|
|
383
|
+
'affected user', 'incident date', 'incident type', 'incident report',
|
|
384
|
+
'review period', 'review date', 'salary band', 'salary range',
|
|
385
|
+
'personal email', 'work email', 'business email',
|
|
386
|
+
'lead source', 'account exec', 'account executive',
|
|
387
|
+
'next appt', 'next appointment', 'last appt', 'chief complaint',
|
|
388
|
+
'portfolio value', 'portfolio manager',
|
|
389
|
+
'tax id', 'account number', 'employee id', 'social security',
|
|
390
|
+
'date of', 'date of birth', 'place of birth',
|
|
391
|
+
'hiring manager', 'direct report', 'team lead', 'team leader',
|
|
392
|
+
'job title', 'job function', 'pay grade',
|
|
393
|
+
'review type', 'performance review', 'annual review',
|
|
394
|
+
'invoice number', 'purchase order', 'order number',
|
|
395
|
+
'oak street', 'main street', 'high street', 'park avenue',
|
|
396
|
+
'first street', 'second street', 'third street',
|
|
397
|
+
'north avenue', 'south avenue', 'east side', 'west side',
|
|
398
|
+
'soc team', 'hr team', 'it team', 'qa team', 'ux team',
|
|
399
|
+
'new york', 'los angeles', 'san francisco', 'las vegas',
|
|
400
|
+
'united states', 'united kingdom', 'north america', 'south america',
|
|
401
|
+
'senior analyst', 'senior engineer', 'senior manager', 'senior consultant',
|
|
402
|
+
'junior analyst', 'junior engineer', 'junior developer',
|
|
403
|
+
'vice president', 'chief executive', 'chief officer',
|
|
404
|
+
'account manager', 'project manager', 'product manager',
|
|
405
|
+
'security officer', 'compliance officer',
|
|
406
|
+
'general hospital', 'medical center', 'urgent care', 'primary care',
|
|
407
|
+
'read more', 'learn more', 'click here', 'sign up', 'log in',
|
|
408
|
+
'privacy policy', 'terms of', 'terms of service',
|
|
409
|
+
'true positive', 'false positive', 'open source',
|
|
410
|
+
'private identifiers', 'data privacy', 'system instruction', 'system instructions', 'critical', 'instruction',
|
|
411
|
+
'user data', 'protected user', 'privacy scrubber', 'end of',
|
|
412
|
+
'thank you', 'best regards', 'kind regards', 'warm regards', 'yours truly', 'sincerely yours', 'good morning', 'good afternoon', 'good evening', 'hello world', 'hello there',
|
|
413
|
+
'supreme court', 'high court', 'district court', 'civil court', 'non disclosure', 'data protection', 'intellectual property', 'trade secret', 'force majeure', 'habeas corpus', 'amicus curiae', 'board member', 'board meeting', 'general assembly',
|
|
414
|
+
'first name', 'last name', 'middle name', 'full name', 'email address', 'phone number', 'cell phone', 'home phone', 'zip code', 'postal code', 'page number', 'section one', 'table contents', 'table of', 'figure one',
|
|
415
|
+
'marketing department', 'sales department', 'engineering team', 'product team', 'customer support', 'human resources', 'public relations',
|
|
416
|
+
'artificial intelligence', 'machine learning', 'deep learning', 'large language', 'operating system', 'source code', 'user interface', 'web browser', 'pull request', 'merge request', 'commit message', 'code review', 'cloud computing', 'database schema',
|
|
417
|
+
'blood pressure', 'heart rate', 'chief physician', 'treating physician', 'health care', 'healthcare provider', 'medical record', 'medical record number', 'acute bronchitis', 'vital signs', 'vital sign', 'health plan', 'health plan beneficiary', 'device identifier', 'vehicle serial',
|
|
418
|
+
'grade a', 'grade b', 'grade c', 'grade d', 'grade f',
|
|
419
|
+
'version 1', 'version 2', 'version 3', 'version 4', 'version 5',
|
|
420
|
+
'step 1', 'step 2', 'step 3', 'step 4', 'step 5',
|
|
421
|
+
'page 1', 'page 2', 'page 3', 'page 4', 'page 5',
|
|
422
|
+
'cs101', 'course cs101',
|
|
423
|
+
'docket number', 'docket numbers', 'dockets section', 'case name', 'case names', 'case number', 'case numbers', 'law firm', 'law firms', 'counsel stack', 'counselstack', 'counselstack connector', 'tier 0', 'tier 1', 'tier 2', 'tier 3', 'tier 4', 'do not', 'do not write', 'specific permission', 'write again', 'without permission', 'without specific permission', 'on screen', 'in report', 'own line', 'connector access', 'prompt instruction', 'prompt instructions', 'finding report', 'findings report', 'wage and tax statement', 'form w-2', 'wage and tax', 'tax statement', 'u.s. individual', 'income tax return', 'earnings statement', 'adp totalsource', 'paychex flex', 'clinical progress note', 'inpatient discharge summary', 'mri brain', 'closing disclosure', 'settlement statement', 'clinical protocol', 'research strategy', 'deposit in escrow', 'incident record', 'institutional review board', 'review board', 'national institutes', 'institutes health', 'dana-farber', 'cancer institute', 'harvard medical school', 'medical school', 'department homeland security', 'homeland security', 'control sc-28', 'sc-28 attestation', 'system security plan', 'morgan stanley', 'blackstone capital', 'communicating pi',
|
|
424
|
+
'white bishop', 'black bishop', 'white knight', 'black knight', 'white king', 'black king', 'white queen', 'black queen', 'white rook', 'black rook', 'white pawn', 'black pawn', 'chess piece', 'chess pieces', 'chess game', 'chess match', 'disney-pixar', 'disney pixar', 'pixar animation', 'close-up', 'close up',
|
|
425
|
+
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december',
|
|
426
|
+
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
|
|
427
|
+
'yesterday', 'tomorrow', 'today', 'last week', 'next month', 'early morning', 'late night',
|
|
428
|
+
'microsoft office', 'google workspace', 'slack channel', 'zoom meeting', 'teams call',
|
|
429
|
+
'amazon prime', 'netflix show', 'youtube video', 'twitter post', 'linkedin profile',
|
|
430
|
+
'central park', 'white house', 'grand canyon', 'mount everest', 'pacific ocean', 'atlantic ocean',
|
|
431
|
+
'silicon valley', 'wall street', 'fifth avenue', 'times square', 'golden gate', 'empire state',
|
|
432
|
+
'statue liberty', 'tower bridge', 'big ben', 'eiffel tower', 'great wall',
|
|
433
|
+
'london bridge', 'san jose', 'las vegas', 'mexico city', 'hong kong', 'san diego',
|
|
434
|
+
'software development', 'user experience', 'front end', 'back end', 'full stack',
|
|
435
|
+
'web application', 'mobile app', 'desktop app', 'cloud services', 'data analytics',
|
|
436
|
+
'cyber security', 'network security', 'information security', 'incident response',
|
|
437
|
+
'disaster recovery', 'business continuity', 'risk management', 'quality assurance',
|
|
438
|
+
'user acceptance', 'beta test', 'release candidate', 'version control', 'continuous integration',
|
|
439
|
+
'continuous deployment', 'agile scrum', 'kanban board', 'sprint planning', 'daily standup',
|
|
440
|
+
'product backlog', 'user story', 'acceptance criteria', 'technical debt', 'codebase',
|
|
441
|
+
'repository', 'branch name', 'tag version', 'hot fix', 'patch note',
|
|
442
|
+
'legal counsel', 'general counsel', 'human capital', 'talent acquisition',
|
|
443
|
+
'employee benefits', 'payroll services', 'stock options', 'health insurance',
|
|
444
|
+
'retirement plan', 'vacation time', 'sick leave', 'performance bonus',
|
|
445
|
+
'commission structure', 'sales target', 'market share', 'brand identity',
|
|
446
|
+
'marketing campaign', 'ad spend', 'click rate', 'conversion rate',
|
|
447
|
+
'customer lifetime', 'churn rate', 'user engagement', 'social media',
|
|
448
|
+
'content strategy', 'search engine', 'local search', 'organic traffic',
|
|
449
|
+
'paid search', 'email marketing', 'press release', 'case study',
|
|
450
|
+
'white paper', 'user guide', 'help center', 'frequently asked',
|
|
451
|
+
'support ticket', 'service level', 'response time', 'resolution time',
|
|
452
|
+
'customer satisfaction', 'net promoter', 'user feedback', 'product feature',
|
|
453
|
+
'roadmap item', 'beta program', 'early access', 'invite only',
|
|
454
|
+
'project status', 'meeting notes', 'call transcript', 'action items', 'follow up', 'best practices', 'success stories',
|
|
455
|
+
'class name', 'function name', 'variable name', 'database table', 'schema name', 'index name', 'query result', 'error message', 'warning message', 'log entry', 'debug log', 'stack trace',
|
|
456
|
+
'staff member', 'team member', 'board member', 'board meeting', 'committee member', 'executive board',
|
|
457
|
+
'email us', 'contact us', 'about us', 'sign in', 'sign out',
|
|
458
|
+
'driver license', 'drivers license', 'opt out', 'opt-out', 'ccpa opt', 'cpra opt', 'spoiler unreleased', 'unreleased draft',
|
|
459
|
+
'lighting', 'keyboard', 'creating', 'building', 'training', 'planning', 'starting', 'painting', 'printing', 'returned', 'released', 'required', 'accepted', 'imported', 'services', 'products', 'accounts', 'settings', 'partners', 'keywords', 'keystone', 'keyspace', 'keynotes', 'keychain'
|
|
460
|
+
, 'quarterly results', 'strategic planning', 'market research', 'customer base', 'privacy settings', 'account settings', 'security settings', 'download now', 'free trial', 'limited time', 'copyright protected', 'all rights', 'rights reserved', 'credit score', 'monthly rent', 'lease application', 'property address', 'reference number', 'additional identifier', 'lease agreement', 'hiring review', 'candidate name', 'privacy policy', 'terms of service', 'machine learning', 'artificial intelligence', 'generative ai', 'silicon valley', 'google cloud', 'amazon web', 'data science', 'operating system', 'software engineer', 'product manager', 'project manager', 'data analyst', 'gross margin', 'revenue growth', 'source code', 'version control', 'large language model',
|
|
461
|
+
'wages', 'wage', 'tips', 'compensation', 'withheld', 'withholding', 'medicare', 'deductions', 'deduction',
|
|
462
|
+
'regular', 'hours', 'holiday', 'overtime', 'commission', 'bonus', 'bonuses', 'records', 'record', 'statement', 'statements', 'rate', 'rates', 'current', 'ytd', 'benefits', 'taxable', 'pre-tax', 'post-tax', 'reimbursements', 'reimbursement', 'fica', 'oasdi', 'disability', 'unemployment', 'sui', 'sdi', 'std', 'ltd', 'exemptions', 'exemption', 'allowances', 'allowance', 'filing', 'status', 'single', 'married', 'head', 'household', 'advice', 'frequency', 'bi-weekly', 'biweekly', 'weekly', 'monthly', 'semi-monthly', 'direct', 'deposit', 'routing', 'box', 'boxes', 'code', 'control', 'omb', 'copy', 'instructions', 'information', 'deferred', 'adoption', 'statutory', 'third-party', 'sick', 'form', 'schedule', 'w-2', 'w2', 'w-4', 'w4', '1099', 'k-1', '1040', 'fed', 'med', 'fwt', 'swt', 'fed w/h', 'fed med', 'locality', 'state wages', 'state tax', 'local wages', 'local tax', 'allocated', 'nonqualified', 'suff', 'suffix', 'allocated tips', 'advance eic', 'advance eic payment', 'dependent care', 'dependent care benefits', 'nonqualified plans', 'statutory employee', 'retirement plan', 'third-party sick pay',
|
|
463
|
+
'uk', 'eu', 'us', 'usa', 'dpa', 'ico', 'dpo', 'ciso', 'ssrn', 'elsevier', 'gdpr', 'ccpa', 'cpra', 'hipaa', 'soc2', 'iso27001', 'governance', 'regulatory', 'framework', 'mandate', 'mandates', 'guidance', 'statute', 'statutes', 'jurisdiction', 'jurisdictions', 'recital', 'article', 'articles', 'treatise', 'workstation', 'device', 'endpoint', 'blueprint', 'personal', 'cross-border', 'cross', 'border', 'transfer', 'retrieval', 'augmented', 'generation', 'limitation', 'discrimination', 'imposes', 'heavy', 'identifiable', 'bypasses', 'author', 'contact', 'keywords', 'keyword', 'keys', 'key', 'separation', 'pre-print', 'preprint']);
|
|
464
|
+
|
|
465
|
+
let JARGON_WORDS = new Set(['step', 'page', 'grade', 'version', 'course', 'class', 'follow', 'chapter', 'lesson', 'unit', 'marketing', 'manager', 'specialist', 'science', 'administration', 'university', 'skills', 'leadership', 'communication', 'working', 'proficiency', 'decision', 'driven', 'experience', 'summary', 'bachelor', 'ads', 'solutions', 'positioning', 'acquisition', 'strategy', 'research', 'database', 'forecast', 'interest', 'prepared', 'merchant', 'document', 'feedback', 'template', 'campaign', 'partners', 'settings', 'keystone', 'llm', 'gpt', 'chatgpt', 'openai', 'anthropic', 'claude', 'gemini', 'api', 'json', 'xml', 'html', 'css', 'javascript', 'python', 'golang', 'typescript', 'rust', 'fastapi', 'snowflake', 'kubernetes', 'terraform', 'docker', 'redis', 'kafka', 'pytorch', 'policy', 'terms', 'conditions', 'release', 'sprint', 'deployment', 'cluster', 'instance', 'package', 'module', 'revenue', 'margin', 'gross', 'quarter', 'system', 'code', 'data', 'cloud', 'server', 'database', 'artificial', 'intelligence', 'learning', 'generative', 'regular', 'hours', 'holiday', 'earnings', 'deductions', 'withheld', 'withholding', 'taxes', 'medicare', 'benefits', 'reimbursements', 'compensation', 'wages', 'code', 'uk', 'eu', 'us', 'usa', 'dpa', 'ico', 'dpo', 'ciso', 'ssrn', 'elsevier', 'gdpr', 'ccpa', 'cpra', 'hipaa', 'soc2', 'iso27001', 'governance', 'regulatory', 'framework', 'mandate', 'mandates', 'guidance', 'statute', 'statutes', 'jurisdiction', 'jurisdictions', 'recital', 'article', 'articles', 'treatise', 'workstation', 'device', 'endpoint', 'blueprint', 'personal', 'cross-border', 'cross', 'border', 'transfer', 'retrieval', 'augmented', 'generation', 'limitation', 'discrimination', 'imposes', 'heavy', 'identifiable', 'bypasses', 'author', 'contact', 'keywords', 'keyword', 'keys', 'key', 'separation', 'pre-print', 'preprint']);
|
|
466
|
+
|
|
467
|
+
let NOT_NAME_WORDS = new Set([
|
|
468
|
+
'uk', 'eu', 'us', 'usa', 'dpa', 'ico', 'dpo', 'ciso', 'ssrn', 'elsevier', 'gdpr', 'ccpa', 'cpra', 'hipaa', 'soc2', 'iso27001', 'governance', 'regulatory', 'framework', 'mandate', 'mandates', 'guidance', 'statute', 'statutes', 'jurisdiction', 'jurisdictions', 'recital', 'article', 'articles', 'treatise', 'workstation', 'device', 'endpoint', 'blueprint', 'personal', 'cross-border', 'cross', 'border', 'transfer', 'retrieval', 'augmented', 'generation', 'limitation', 'discrimination', 'imposes', 'heavy', 'identifiable', 'bypasses', 'author', 'contact', 'keywords', 'keyword', 'keys', 'key', 'separation', 'pre-print', 'preprint',
|
|
469
|
+
// Grammatical & Sentence Starters
|
|
470
|
+
'the', 'a', 'an', 'this', 'that', 'these', 'those', 'my', 'your', 'his', 'her', 'their', 'our', 'its', 'it', 'he', 'she', 'they', 'we', 'i', 'you', 'who', 'whom', 'which', 'what', 'whose', 'why', 'how', 'when', 'where', 'with', 'for', 'from', 'by', 'to', 'at', 'in', 'on', 'of', 'about', 'as', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'and', 'but', 'or', 'so', 'yet', 'im', "i'm", "you're", "they're", "we're", "it's", "he's", "she's", "that's", "there's", "what's", "who's", "i've", "you've", "we've", "they've", "i'll", "you'll", "we'll", "they'll", "i'd", "you'd", "we'd", "they'd",
|
|
471
|
+
// Verbs, Auxiliaries, Commands & Imperatives
|
|
472
|
+
'do', 'does', 'did', 'done', 'doing', 'dont', "don't", 'doesnt', "doesn't", 'didnt', "didn't", 'not', 'no', 'never', 'always',
|
|
473
|
+
'be', 'is', 'am', 'are', 'was', 'were', 'been', 'being',
|
|
474
|
+
'have', 'has', 'had', 'having',
|
|
475
|
+
'can', 'could', 'may', 'might', 'must', 'shall', 'should', 'will', 'would', 'wont', "won't", 'wouldnt', "wouldn't", 'shouldnt', "shouldn't", 'couldnt', "couldn't", 'cant', "can't", 'cannot',
|
|
476
|
+
'write', 'writing', 'written', 'writes', 'read', 'reading', 'reads',
|
|
477
|
+
'wait', 'waiting', 'waited', 'waits', 'place', 'placing', 'placed', 'places',
|
|
478
|
+
'display', 'displaying', 'displayed', 'displays',
|
|
479
|
+
'provide', 'providing', 'provided', 'provides',
|
|
480
|
+
'show', 'showing', 'shown', 'shows',
|
|
481
|
+
'tell', 'telling', 'told', 'tells',
|
|
482
|
+
'ask', 'asking', 'asked', 'asks',
|
|
483
|
+
'use', 'using', 'used', 'uses',
|
|
484
|
+
'select', 'selecting', 'selected', 'selects',
|
|
485
|
+
'find', 'finding', 'findings', 'found', 'finds',
|
|
486
|
+
'reference', 'referencing', 'referenced', 'references',
|
|
487
|
+
'access', 'accessing', 'accessed', 'accesses',
|
|
488
|
+
'note', 'noting', 'noted', 'notes',
|
|
489
|
+
'get', 'getting', 'got', 'gotten', 'gets',
|
|
490
|
+
'make', 'making', 'made', 'makes',
|
|
491
|
+
'give', 'giving', 'given', 'gives',
|
|
492
|
+
'take', 'taking', 'took', 'taken', 'takes',
|
|
493
|
+
'put', 'putting', 'puts',
|
|
494
|
+
'set', 'setting', 'sets',
|
|
495
|
+
'keep', 'keeping', 'kept', 'keeps',
|
|
496
|
+
'let', 'letting', 'lets',
|
|
497
|
+
'leave', 'leaving', 'left', 'leaves',
|
|
498
|
+
'run', 'running', 'ran', 'runs',
|
|
499
|
+
'stop', 'stopping', 'stopped', 'stops',
|
|
500
|
+
'start', 'starting', 'started', 'starts',
|
|
501
|
+
'check', 'checking', 'checked', 'checks',
|
|
502
|
+
'print', 'printing', 'printed', 'prints',
|
|
503
|
+
'generate', 'generating', 'generated', 'generates',
|
|
504
|
+
'create', 'creating', 'created', 'creates',
|
|
505
|
+
'build', 'building', 'built', 'builds',
|
|
506
|
+
'include', 'including', 'included', 'includes',
|
|
507
|
+
'exclude', 'excluding', 'excluded', 'excludes',
|
|
508
|
+
'format', 'formatting', 'formatted', 'formats',
|
|
509
|
+
'change', 'changing', 'changed', 'changes',
|
|
510
|
+
'send', 'sending', 'sent', 'sends',
|
|
511
|
+
'receive', 'receiving', 'received', 'receives',
|
|
512
|
+
'delete', 'deleting', 'deleted', 'deletes',
|
|
513
|
+
'remove', 'removing', 'removed', 'removes',
|
|
514
|
+
'insert', 'inserting', 'inserted', 'inserts',
|
|
515
|
+
'update', 'updating', 'updated', 'updates',
|
|
516
|
+
'review', 'reviewing', 'reviewed', 'reviews',
|
|
517
|
+
'allow', 'allowing', 'allowed', 'allows',
|
|
518
|
+
'deny', 'denying', 'denied', 'denies',
|
|
519
|
+
'require', 'requiring', 'required', 'requires',
|
|
520
|
+
'turn', 'turning', 'turned', 'turns',
|
|
521
|
+
'switch', 'switching', 'switched', 'switches',
|
|
522
|
+
'enable', 'enabling', 'enabled', 'enables',
|
|
523
|
+
'disable', 'disabling', 'disabled', 'disables',
|
|
524
|
+
'ensure', 'ensuring', 'ensured', 'ensures',
|
|
525
|
+
'verify', 'verifying', 'verified', 'verifies',
|
|
526
|
+
'execute', 'executing', 'executed', 'executes',
|
|
527
|
+
'test', 'testing', 'tested', 'tests',
|
|
528
|
+
'install', 'installing', 'installed', 'installs',
|
|
529
|
+
'uninstall', 'uninstalling', 'uninstalled', 'uninstalls',
|
|
530
|
+
'suppose', 'supposed', 'supposing', 'supposes',
|
|
531
|
+
'respond', 'responding', 'responded', 'responds',
|
|
532
|
+
'preserve', 'preserving', 'preserved', 'preserves',
|
|
533
|
+
'replace', 'replacing', 'replaced', 'replaces',
|
|
534
|
+
'present', 'presented', 'presenting', 'presents',
|
|
535
|
+
'admit', 'admitted', 'admitting', 'admits',
|
|
536
|
+
'complain', 'complained', 'complaining', 'complains',
|
|
537
|
+
'prescribe', 'prescribed', 'prescribing', 'prescribes',
|
|
538
|
+
'diagnose', 'diagnosed', 'diagnosing', 'diagnoses',
|
|
539
|
+
'report', 'reported', 'reporting', 'reports',
|
|
540
|
+
'state', 'stated', 'stating', 'states',
|
|
541
|
+
'undergo', 'underwent', 'undergoing', 'undergoes',
|
|
542
|
+
'experience', 'experienced', 'experiencing', 'experiences',
|
|
543
|
+
'arrive', 'arrived', 'arriving', 'arrives',
|
|
544
|
+
'order', 'ordered', 'ordering', 'orders',
|
|
545
|
+
// Adverbs, Prepositions, Conjunctions & Modifiers
|
|
546
|
+
'again', 'without', 'with', 'within', 'specific', 'specifically', 'permission', 'permissions',
|
|
547
|
+
'underneath', 'above', 'below', 'between', 'among', 'together', 'separately', 'instead',
|
|
548
|
+
'also', 'too', 'either', 'neither', 'both', 'each', 'every', 'all', 'some', 'any', 'none',
|
|
549
|
+
'only', 'just', 'already', 'currently', 'more', 'most', 'less', 'least',
|
|
550
|
+
'very', 'quite', 'rather', 'such', 'same', 'different', 'other', 'others', 'another',
|
|
551
|
+
'like', 'unlike', 'similar', 'complete', 'completely', 'entire', 'entirely',
|
|
552
|
+
'exact', 'exactly', 'approximate', 'approximately', 'general', 'generally',
|
|
553
|
+
'direct', 'directly', 'indirect', 'indirectly', 'total', 'totally', 'full', 'fully',
|
|
554
|
+
'partial', 'partially', 'own', 'proper', 'properly',
|
|
555
|
+
'now', 'then', 'soon', 'later', 'here', 'there', 'everywhere', 'nowhere', 'somewhere', 'anywhere',
|
|
556
|
+
'inside', 'outside', 'before', 'after', 'since', 'until', 'till',
|
|
557
|
+
'while', 'whereas', 'unless', 'although', 'though', 'even', 'because',
|
|
558
|
+
'therefore', 'however', 'furthermore', 'moreover', 'meanwhile', 'otherwise', 'besides', 'further',
|
|
559
|
+
// Greetings & Salutations
|
|
560
|
+
'hello', 'hi', 'hey', 'dear', 'greetings',
|
|
561
|
+
// Document & Resume Structure
|
|
562
|
+
'summary', 'experience', 'education', 'skills', 'languages', 'project', 'history', 'background', 'objective', 'profile', 'awards', 'honors', 'certifications', 'publications', 'interests', 'references', 'statement', 'statements', 'form', 'forms',
|
|
563
|
+
// Business & Job Roles
|
|
564
|
+
'manager', 'director', 'specialist', 'analyst', 'engineer', 'developer', 'consultant', 'officer', 'representative', 'agent', 'lead', 'leader', 'president', 'coordinator', 'admin', 'administrator', 'executive', 'founder', 'partner', 'intern', 'trainee', 'advisor', 'head', 'vp', 'chief',
|
|
565
|
+
// Departments & Fields
|
|
566
|
+
'marketing', 'sales', 'engineering', 'finance', 'accounting', 'legal', 'operations', 'support', 'recruiting', 'talent', 'acquisition', 'compliance', 'security', 'technical', 'development', 'product', 'design', 'creative', 'strategy', 'planning', 'analytics', 'science', 'business', 'administration',
|
|
567
|
+
// Tools & Tech Concepts
|
|
568
|
+
'google', 'ads', 'analytics', 'meta', 'hubspot', 'crm', 'salesforce', 'wordpress', 'mailchimp', 'adobe', 'figma', 'canva', 'slack', 'zoom', 'teams', 'microsoft', 'office', 'excel', 'word', 'powerpoint', 'notion', 'jira', 'confluence', 'github', 'gitlab', 'aws', 'gcp', 'azure', 'cloud', 'database', 'sql', 'python', 'golang', 'typescript', 'rust', 'fastapi', 'snowflake', 'kubernetes', 'terraform', 'docker', 'redis', 'kafka', 'pytorch', 'java', 'javascript', 'html', 'css', 'react', 'node', 'api', 'saas', 'b2b', 'b2c', 'url', 'domain', 'website', 'app', 'application', 'software', 'email', 'phone', 'contact', 'address',
|
|
569
|
+
|
|
570
|
+
// General Academic & Professional vocabulary
|
|
571
|
+
'bachelor', 'master', 'doctor', 'associate', 'degree', 'university', 'college', 'school', 'institute', 'academy', 'graduated', 'major', 'minor', 'gpa', 'cum', 'laude', 'honors', 'deans', 'list', 'scholarship',
|
|
572
|
+
// Quality & Adjectives
|
|
573
|
+
'results', 'driven', 'oriented', 'expert', 'professional', 'proven', 'track', 'record', 'creative', 'excellent', 'communication', 'verbal', 'written', 'native', 'fluent', 'bilingual', 'working', 'proficiency', 'strategic', 'interpersonal', 'teamwork', 'organizational', 'detail', 'analytical',
|
|
574
|
+
// Common Resume / Business Phrases
|
|
575
|
+
'results-driven', 'data-driven', 'customer-centric', 'detail-oriented', 'cross-functional', 'self-motivated', 'time-management', 'problem-solving', 'fast-paced', 'year-over-year',
|
|
576
|
+
// Legal & Trust terms
|
|
577
|
+
'trust', 'trustee', 'co-trustee', 'settlor', 'grantor', 'beneficiary', 'agreement', 'will', 'estate', 'witness', 'declaration', 'signatory', 'testator', 'notary', 'commission', 'county', 'state', 'court', 'article', 'section', 'paragraph', 'schedule', 'exhibit', 'amendment', 'addendum', 'power', 'attorney', 'guardian', 'executor', 'administrator', 'survivor', 'predecessor', 'successor', 'whereof', 'hereby', 'thereby', 'herein', 'therein', 'witnesseth', 'whereas', 'therefore', 'now', 'dated', 'effective', 'matter', 'case', 'cases', 'docket', 'dockets', 'number', 'numbers', 'firm', 'firms', 'lawyer', 'lawyers', 'counsel', 'counsels', 'counselstack', 'tier', 'tiers', 'finding', 'findings', 'connector', 'connectors', 'platform', 'platforms',
|
|
578
|
+
// Medical & Clinical terms
|
|
579
|
+
'clinical', 'note', 'notes', 'dx', 'rx', 'tx', 'hx', 'px', 'sx', 'type', 'diabetes', 'referred', 'referral', 'diagnosed', 'diagnosis', 'patient', 'insurance', 'bcbs', 'mrn', 'dob',
|
|
580
|
+
// Tax & Payroll terms
|
|
581
|
+
'wages', 'wage', 'tips', 'compensation', 'withheld', 'withholding', 'medicare', 'deductions', 'deduction', 'earning', 'earnings', 'gross', 'net', 'pay', 'payroll', 'paystub', 'taxable', 'exempt', 'allowance', 'allowances', 'regular', 'hours', 'holiday', 'overtime', 'commission', 'bonus', 'bonuses', 'records', 'record', 'statement', 'statements', 'rate', 'rates', 'current', 'ytd', 'benefits', 'taxable', 'pre-tax', 'post-tax', 'reimbursements', 'reimbursement', 'fica', 'oasdi', 'disability', 'unemployment', 'sui', 'sdi', 'std', 'ltd', 'exemptions', 'exemption', 'allowances', 'allowance', 'filing', 'status', 'single', 'married', 'head', 'household', 'advice', 'frequency', 'bi-weekly', 'biweekly', 'weekly', 'monthly', 'semi-monthly', 'direct', 'deposit', 'routing', 'box', 'boxes', 'code', 'control', 'omb', 'copy', 'instructions', 'information', 'deferred', 'adoption', 'statutory', 'third-party', 'sick', 'form', 'schedule', 'w-2', 'w2', 'w-4', 'w4', '1099', 'k-1', '1040', 'fed', 'med', 'fwt', 'swt', 'fed w/h', 'fed med', 'locality', 'state wages', 'state tax', 'local wages', 'local tax', 'allocated', 'nonqualified',
|
|
582
|
+
'incident', 'incidents', 'critical', 'production', 'impacted', 'reported', 'details', 'vulnerability', 'vulnerabilities', 'host', 'types', 'type', 'leaked', 'leak', 'leaks', 'masked', 'mask', 'masking', 'leave', 'screen', 'screens', 'risk', 'risks', 'cluster', 'clusters', 'parameter', 'parameters', 'processing', 'process', 'processed', 'verified', 'verify', 'verification', 'playground', 'guide', 'guides', 'protection', 'protect', 'corporate', 'enterprise', 'log', 'logs', 'airplane', 'mode', 'zero', 'trust', 'top', 'data', 'live', 'scrubber', 'scrub', 'scrubbed', 'note', 'notes', 'secret', 'secrets', 'card', 'cards', 'raw', 'input', 'output', 'contains', 'contain', 'contained', 'platform', 'solutions', 'pricing', 'company', 'news', 'dashboard', 'add', 'chrome', 'sample', 'samples', 'try', 'terms', 'privacy', 'policy', 'policies', 'home', 'compliance', 'framework', 'frameworks', 'audit', 'audits', 'receipt', 'receipts', 'overview', 'explore', 'vectors', 'vector', 'standard', 'standards', 'status', 'preview', 'view', 'actions', 'action', 'button', 'buttons', 'option', 'options', 'general', 'specialized', 'custom', 'rule', 'rules', 'token', 'tokens', 'value', 'values', 'session', 'sessions', 'local', 'server', 'servers', 'cloud', 'ram', 'memory', 'offline', 'online', 'client', 'browser', 'extension', 'workspace', 'workplace', 'pan', 'phi', 'pii', 'soc', 'soc2', 'gdpr', 'hipaa', 'ccpa', 'iso27001', 'pci', 'dss', 'nist', 'chatgpt', 'claude', 'gemini', 'copilot', 'perplexity', 'deepseek', 'qwen', 'grok', 'llama', 'mistral', 'ai', 'llm', 'prompt', 'prompts', 'transmission', 'transit', 'egress', 'neutralized', 'stripped', 'isolated', 'isolation', 'unlocked', 'locked', 'unlock', 'download', 'copy', 'dismiss', 'close', 'save', 'settings', 'protect', 'reveal', 'unmask', 'restore', 'restored', 'export', 'import', 'sanitization', 'sanitizer', 'sensitive', 'entities', 'entity', 'breakdown', 'metrics', 'exposure', 'high', 'low', 'medium', 'cryptographic', 'separation', 'pseudonymization', 'minimization', 'transparency', 'forensic', 'extraction', 'conduit', 'liability', 'processor', 'binding', 'bindings', 'signature', 'certified', 'certificate', 'payload', 'transmitted', 'evaluation', 'air-gapped', 'client-side', 'zero-trust', 'iso', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', 'statutory', 'declaration', 'pass', 'passed', 'fail', 'failed', 'side', 'privacyscrubber', 'ztds', 'safe harbor', 'harbor', 'safe', 'ciso', 'united', 'states', 'district', 'court', 'northern', 'southern', 'eastern', 'western', 'division', 'in re', 'litigation', 'complaint', 'violation', 'defend', 'trade', 'secrets', 'demand', 'jury', 'trial', 'federal', 'question', 'diversity', 'citizenship', 'mutual', 'non-disclosure', 'confidentiality', 'recitals', 'standard', 'governing', 'jpmorgan', 'chase', 'bank', 'beginning', 'ending', 'balance', 'deposits', 'additions', 'withdrawals', 'electronic', 'transfers', 'subscription', 'billed', 'lifetime', 'amount', 'due', 'subtotal', 'residential', 'commercial', 'lease', 'premises', 'deposit', 'utilities', 'closing', 'disclosure', 'settlement', 'trid', 'hud-1', 'purchase', 'price', 'escrow', 'funds', 'institutional', 'review', 'board', 'irb', 'national', 'institutes', 'health', 'nih', 'research', 'strategy', 'grant', 'adenocarcinoma', 'pancreatic', 'ductal', 'dana-farber', 'cancer', 'institute', 'harvard', 'department', 'homeland', 'cisa', 'controlled', 'unclassified', 'cui', 'foia', 'responsive', 'attestation', 'fedramp', 'ssp', 'package', 'moderate', 'impact', 'sshd', 'publickey', 'cron', 'systemd', 'failed', 'accepted', 'password', 'individual', 'income', 'totalsource', 'paychex', 'flex', 'lisinopril', 'metformin', 'atorvastatin', 'serum', 'creatinine', 'egfr', 'hba1c', 'vitals', 'copd', 'obstructive', 'pulmonary', 'disease', 'chronic', 'mri', 'brain', 'contrast', 'parenchyma', 'ventricles', 'fazekas', 'systems', 'distributed', 'event-driven', 'cfa', 'blackstone', 'morgan', 'chartered', 'confidential', 'information', 'cisa', 'attestation', 'sc-28', 'sp', '800-53', 'fedramp', 'clinicaltrials', 'nct', 'protocol', 'grant', 'r01', 'pancreatic', 'adenocarcinoma', 'dana-farber', 'harvard', 'cardiology', 'diverticulitis', 'resection', 'superbill', 'hcpcs', 'icd-10', 'progress', 'name', 'names', 'partner', 'partners', 'capital', 'adp', 'tax', 'taxes', 'inpatient', 'discharge', 'clinicaltrials.gov', 'id', 'ids', 'phd', 'md', 'do', 'jd', 'mba', 'cpa', 'pi', 'in', 'at', 'on', 'of', 'to', 'by', 'or', 'as', 'if', 'an', 'is', 'it', 'be', 'we', 'us', 'up', 'so', 'no', 'do', 'go', 'he', 'me', 'my',
|
|
583
|
+
// Games, Chess, and Playing Pieces
|
|
584
|
+
'bishop', 'bishops', 'knight', 'knights', 'rook', 'rooks', 'pawn', 'pawns', 'king', 'kings', 'queen', 'queens', 'chessboard', 'checkmate', 'stalemate', 'castling', 'en passant', 'chess',
|
|
585
|
+
// Colors & Visual Descriptors
|
|
586
|
+
'white', 'black', 'red', 'blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'brown', 'gray', 'grey', 'dark', 'light', 'gold', 'silver', 'bronze',
|
|
587
|
+
// Animation, 3D Rendering & Prompt Terminology
|
|
588
|
+
'pixar', 'disney', 'animation', 'render', 'rendering', 'composition', 'cinematic', 'smooth', 'glides', 'glide', 'gliding', 'capture', 'captures', 'capturing', 'camera', 'orbit', 'orbits', 'orbiting', 'trapped', 'trap', 'trapping', 'square', 'squares', 'character', 'characters', 'expressive', 'living', 'texture', 'textures', 'reflection', 'reflections', 'grain', 'candlelight', 'wooden', 'polished', 'vertical', 'horizontal', 'macro', 'closeup', 'close-up', 'scene', 'scenes', 'shot', 'shots', 'shadow', 'shadows',
|
|
589
|
+
// Email, Outreach, Guest Posting & Agency Business Vocabulary
|
|
590
|
+
'guest', 'post', 'posts', 'posting', 'attached', 'attach', 'attachment', 'attachments', 'updated', 'update', 'updates', 'list', 'lists', 'line', 'lines', 'rate', 'rates', 'affordable', 'services', 'service', 'infotech', 'technologies', 'technology', 'agency', 'agencies', 'digital', 'marketing', 'traffic', 'smart', 'design', 'seo', 'per', 'host', 'hosting', 'sites', 'site', 'inbox', 'starred', 'snoozed', 'important', 'sent', 'drafts', 'draft', 'spam', 'bin', 'trash', 'purchases', 'travel', 'social', 'forums', 'promotions', 'promotion', 'reply', 'forward', 'labels', 'label', 'compose', 'message', 'messages', 'mailer', 'outreach', 'backlink', 'backlinks', 'domain', 'authority', 'da', 'dr', 'founder', 'ceo', 'cto', 'cfo', 'coo', 'vp', 'head', 'lead',
|
|
591
|
+
// US States
|
|
592
|
+
'california', 'texas', 'florida', 'york', 'illinois', 'pennsylvania', 'ohio', 'georgia', 'michigan', 'carolina', 'virginia', 'washington', 'arizona', 'massachusetts', 'tennessee', 'indiana', 'maryland', 'missouri', 'wisconsin', 'colorado', 'minnesota', 'alabama', 'louisiana', 'kentucky', 'oregon', 'oklahoma', 'connecticut', 'utah', 'iowa', 'nevada', 'arkansas', 'mississippi', 'kansas', 'new mexico', 'nebraska', 'idaho', 'hawaii', 'maine', 'new hampshire', 'rhode island', 'montana', 'delaware', 'south dakota', 'north dakota', 'alaska', 'vermont', 'wyoming',
|
|
593
|
+
'f.3d', 'f.supp', 'u.s.c.', 'v.', 'plaintiff', 'defendant', 'v', 'u.s.', 'court', 'app.', 'reporter', 'cir.']);
|
|
594
|
+
|
|
595
|
+
const PROFILE_JARGON = {
|
|
596
|
+
medical: [
|
|
597
|
+
'sleep', 'apnea', 'symptom', 'symptoms', 'trauma', 'hypertension', 'health', 'disease', 'condition',
|
|
598
|
+
'diagnosis', 'treatment', 'medication', 'dose', 'patient', 'clinic', 'surgery', 'therapy', 'alcohol',
|
|
599
|
+
'cannabis', 'blood', 'pressure', 'heart', 'rate', 'emergency', 'contact', 'relationship', 'type',
|
|
600
|
+
'diabetes', 'cancer', 'asthma', 'copd', 'covid', 'infection', 'syndrome', 'disorder', 'chronic', 'acute',
|
|
601
|
+
'illness', 'fever', 'allergy', 'pain', 'referral', 'referred', 'prescription', 'prescribed', 'doctor',
|
|
602
|
+
'physician', 'nurse', 'hospital', 'clinical', 'note', 'notes', 'dx', 'rx', 'tx', 'hx', 'px', 'sx',
|
|
603
|
+
'insurance', 'bcbs', 'bronchitis', 'amoxicillin', 'penicillin', 'antibiotic', 'antibiotics', 'vital',
|
|
604
|
+
'vitals', 'bp', 'hr', 'bpm', 'mmhg', 'allergies', 'dosage', 'parenchyma', 'ventricles', 'fazekas',
|
|
605
|
+
'ischemia', 'lesion', 'cardiology', 'atherosclerotic', 'aortocoronary', 'bypass', 'graft', 'hyperlipidemia',
|
|
606
|
+
'diverticulitis', 'resection', 'colon', 'superbill', 'encounter', 'cpt', 'hcpcs', 'icd-10'
|
|
607
|
+
],
|
|
608
|
+
realestate: [
|
|
609
|
+
'escrow', 'tenant', 'landlord', 'lease', 'mortgage', 'appraisal', 'broker', 'property', 'zoning',
|
|
610
|
+
'parcel', 'rent', 'buyer', 'seller', 'agent', 'listing', 'residential', 'commercial', 'premises',
|
|
611
|
+
'deposit', 'utilities', 'closing', 'disclosure', 'settlement', 'trid', 'hud-1', 'purchase', 'price',
|
|
612
|
+
'funds', 'lender', 'deed', 'title', 'disbursement'
|
|
613
|
+
],
|
|
614
|
+
legal: [
|
|
615
|
+
'testator', 'notary', 'commission', 'county', 'court', 'affidavit', 'plaintiff', 'defendant',
|
|
616
|
+
'litigation', 'jurisdiction', 'agreement', 'contract', 'settlement', 'clause', 'article', 'section',
|
|
617
|
+
'matter', 'case', 'united', 'states', 'district', 'northern', 'southern', 'eastern', 'western',
|
|
618
|
+
'division', 'complaint', 'violation', 'defend', 'trade', 'secrets', 'act', 'demand', 'jury', 'trial',
|
|
619
|
+
'federal', 'question', 'diversity', 'citizenship', 'mutual', 'non-disclosure', 'confidentiality',
|
|
620
|
+
'recitals', 'whereas', 'confidential', 'information', 'care', 'term', 'exhibit', 'party', 'parties',
|
|
621
|
+
'disclosing', 'receiving', 'witnesseth', 'standard', 'indemnification', 'liability', 'governing',
|
|
622
|
+
'law', 'docket', 'deponent', 'reporter', 'appearances', 'examination', 'services', 'scope', 'work',
|
|
623
|
+
'master', 'msa'
|
|
624
|
+
],
|
|
625
|
+
academic: [
|
|
626
|
+
'institutional', 'review', 'board', 'irb', 'protocol', 'national', 'institutes', 'health', 'nih',
|
|
627
|
+
'research', 'strategy', 'grant', 'adenocarcinoma', 'pancreatic', 'ductal', 'dana-farber', 'cancer',
|
|
628
|
+
'institute', 'harvard', 'medical', 'school', 'application', 'study', 'title', 'identifier',
|
|
629
|
+
'investigator', 'principal', 'investigators', 'sponsor', 'protection', 'confidentiality', 'harbor',
|
|
630
|
+
'standards', 'clinical', 'trials', 'de-identification', 'aggregation', 'identifiers', 'aims',
|
|
631
|
+
'sequencing', 'biomarkers', 'methylation'
|
|
632
|
+
],
|
|
633
|
+
finance: [
|
|
634
|
+
'jpmorgan', 'chase', 'bank', 'beginning', 'ending', 'balance', 'deposits', 'additions', 'withdrawals',
|
|
635
|
+
'electronic', 'transfers', 'statement', 'customer', 'subscription', 'plan', 'billed', 'lifetime',
|
|
636
|
+
'amount', 'due', 'subtotal', 'sales', 'tax', 'regular', 'pay', 'bonus', 'gross', 'net', 'earnings',
|
|
637
|
+
'withholding', 'withheld', 'social', 'security', 'medicare', 'fica', 'oasdi', 'swt', 'fwt', 'direct',
|
|
638
|
+
'deposit', 'checking', 'savings', 'account', 'invoice', 'currency', 'status', 'routing', 'check',
|
|
639
|
+
'advice', 'summary', 'credit', 'debit'
|
|
640
|
+
],
|
|
641
|
+
devops: [
|
|
642
|
+
'failed', 'accepted', 'publickey', 'password', 'cron', 'systemd', 'sshd', 'authentication',
|
|
643
|
+
'connection', 'timeout', 'cluster', 'postgres', 'postgresql', 'root', 'admin', 'syslog', 'daemon',
|
|
644
|
+
'service', 'kubernetes', 'k8s', 'docker', 'container', 'pod', 'namespace', 'ingress', 'egress'
|
|
645
|
+
],
|
|
646
|
+
compliance: [
|
|
647
|
+
'department', 'homeland', 'security', 'cisa', 'controlled', 'unclassified', 'information', 'cui',
|
|
648
|
+
'foia', 'responsive', 'record', 'ciso', 'attestation', 'fedramp', 'ssp', 'package', 'moderate',
|
|
649
|
+
'impact', 'level', 'nist', 'sp', '800-53', 'sc-28', 'fips', '140-3', 'dhs', 'exemption', 'privacy',
|
|
650
|
+
'mandate', 'statutory', 'declaration'
|
|
651
|
+
],
|
|
652
|
+
support: [
|
|
653
|
+
'ticket', 'status', 'priority', 'requester', 'assignee', 'organization', 'solved', 'open', 'pending',
|
|
654
|
+
'incident', 'record', 'servicenow', 'zendesk', 'jira', 'rate', 'limit', 'quota', 'concurrency',
|
|
655
|
+
'settings', 'access', 'connection', 'pool', 'exhaustion'
|
|
656
|
+
],
|
|
657
|
+
hr: [
|
|
658
|
+
'candidate', 'employee', 'payroll', 'benefits', 'salary', 'vacation', 'supervisor', 'subordinate',
|
|
659
|
+
'performance', 'appraisal', 'interview', 'resume', 'applicant', 'engineer', 'developer', 'analyst',
|
|
660
|
+
'specialist', 'director', 'manager', 'lead', 'staff', 'senior', 'junior', 'experience', 'education',
|
|
661
|
+
'skills', 'certifications', 'summary', 'responsibilities'
|
|
662
|
+
],
|
|
663
|
+
sales: [
|
|
664
|
+
'prospect', 'opportunity', 'quota', 'pipeline', 'deal', 'revenue', 'forecast', 'lead', 'churn',
|
|
665
|
+
'client', 'customer'
|
|
666
|
+
]
|
|
667
|
+
};
|
|
668
|
+
// --- END DEFAULT RULES ---
|
|
669
|
+
|
|
670
|
+
function hydrateRegex(r) {
|
|
671
|
+
if (r && typeof r.regex === 'string') {
|
|
672
|
+
if (r.regex.startsWith('/')) {
|
|
673
|
+
try {
|
|
674
|
+
const lastSlash = r.regex.lastIndexOf('/');
|
|
675
|
+
const pattern = r.regex.substring(1, lastSlash);
|
|
676
|
+
const flags = r.regex.substring(lastSlash + 1);
|
|
677
|
+
return { ...r, regex: new RegExp(pattern, flags) };
|
|
678
|
+
} catch (e) {
|
|
679
|
+
console.error('Failed to hydrate regex:', r.regex, e);
|
|
680
|
+
return r;
|
|
681
|
+
}
|
|
682
|
+
} else {
|
|
683
|
+
return { ...r, regex: new RegExp(r.regex, 'gi') };
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return r;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function init(config) {
|
|
690
|
+
if (!config) return;
|
|
691
|
+
if (config.regexes) {
|
|
692
|
+
REGEX_RULES = config.regexes.map(hydrateRegex);
|
|
693
|
+
}
|
|
694
|
+
if (config.profiles) {
|
|
695
|
+
PROFILE_RULES = {};
|
|
696
|
+
for (const [profile, rules] of Object.entries(config.profiles)) {
|
|
697
|
+
PROFILE_RULES[profile] = rules.map(hydrateRegex);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (config.names) NAME_STOP_LIST = new Set(config.names);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const PROFILE_ALIAS_MAP = {
|
|
704
|
+
'general': 'general',
|
|
705
|
+
'underwriting': 'underwriting', 'lending': 'underwriting', 'mortgage': 'underwriting', 'loan': 'underwriting', 'income': 'underwriting', 'income_verification': 'underwriting', 'payroll': 'underwriting', 'w2': 'underwriting', 'paystub': 'underwriting',
|
|
706
|
+
'medical': 'medical', 'healthcare': 'medical', 'health': 'medical', 'pharma': 'pharma', 'hipaa': 'medical',
|
|
707
|
+
'engineering': 'engineering', 'dev': 'engineering', 'devops': 'engineering', 'tech': 'tech',
|
|
708
|
+
'finance': 'finance', 'bizops': 'bizops', 'sales': 'sales', 'wealthmgmt': 'wealthmgmt', 'wealth': 'wealthmgmt', 'insurance': 'insurance', 'accounting': 'accounting', 'pci': 'finance',
|
|
709
|
+
'legal': 'legal', 'compliance': 'compliance', 'ccpa': 'ccpa', 'gdpr': 'compliance', 'soc2': 'compliance', 'iso27001': 'compliance', 'nist': 'compliance', 'dpo': 'compliance', 'grc': 'compliance',
|
|
710
|
+
'hr': 'hr', 'security': 'security', 'marketing': 'marketing', 'support': 'support',
|
|
711
|
+
'realestate': 'realestate', 'academic': 'academic', 'agents': 'agents', 'ai_agents': 'agents', 'creative': 'creative', 'personal': 'personal'
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
function getActiveRules(activeProfile) {
|
|
715
|
+
let activeRules = [...REGEX_RULES];
|
|
716
|
+
if (activeProfile && activeProfile.toLowerCase() !== 'general') {
|
|
717
|
+
const canonicalProfile = PROFILE_ALIAS_MAP[activeProfile.toLowerCase()] || 'general';
|
|
718
|
+
if (canonicalProfile !== 'general' && PROFILE_RULES[canonicalProfile]) {
|
|
719
|
+
activeRules = PROFILE_RULES[canonicalProfile].concat(activeRules);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return activeRules;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function stitchOrphanedNameLines(text, profile) {
|
|
726
|
+
if (profile === 'medical') {
|
|
727
|
+
text = text.replace(/Patient Name:\s*\n+([A-Z][a-zA-Z]+\s[A-Z][a-zA-Z]+)/g, 'Patient Name: $1');
|
|
728
|
+
} else if (profile === 'legal') {
|
|
729
|
+
text = text.replace(/Defendant:\s*\n+([A-Z][a-zA-Z]+\s[A-Z][a-zA-Z]+)/g, 'Defendant: $1');
|
|
730
|
+
}
|
|
731
|
+
return text;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function detectMatches(text, activeProfile = 'general', customRules = [], nlpNames = [], enabledEntities = null, mlEntities = []) {
|
|
735
|
+
let textToProcess = text.replace(/[\u200b\u200c\u200d\ufeff]/g, '');
|
|
736
|
+
textToProcess = textToProcess.replace(/([a-zA-Z])(Email|Phone|Mobile|Tel|Address|IP|ID|URL|SSN|Date):/g, '$1 $2:');
|
|
737
|
+
|
|
738
|
+
let matches = [];
|
|
739
|
+
|
|
740
|
+
if (customRules && customRules.length > 0) {
|
|
741
|
+
const sorted = [...customRules].sort((a, b) => {
|
|
742
|
+
const patternA = typeof a === 'string' ? a : (a.pattern || (a.regex ? a.regex.source : '') || '');
|
|
743
|
+
const patternB = typeof b === 'string' ? b : (b.pattern || (b.regex ? b.regex.source : '') || '');
|
|
744
|
+
return patternB.length - patternA.length;
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
sorted.forEach(cr => {
|
|
748
|
+
const pattern = typeof cr === 'string' ? cr : (cr.pattern || (cr.regex ? cr.regex.source : ''));
|
|
749
|
+
if (!pattern) return;
|
|
750
|
+
const label = typeof cr === 'string' ? 'CUSTOM' : (cr.label || cr.mask || cr.name || 'CUSTOM');
|
|
751
|
+
|
|
752
|
+
let rx;
|
|
753
|
+
try {
|
|
754
|
+
const isExact = !/(\^|\$|\\[bBdDwWsS]|\[|\(|\{|\*|\+|\|)/.test(pattern);
|
|
755
|
+
if (isExact) {
|
|
756
|
+
const safe = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
757
|
+
rx = new RegExp(`(?:^|\\b)${safe}(?:\\b|$)`, 'gi');
|
|
758
|
+
} else {
|
|
759
|
+
rx = new RegExp(pattern, 'gi');
|
|
760
|
+
}
|
|
761
|
+
} catch (e) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
if (rx.test('')) return;
|
|
765
|
+
rx.lastIndex = 0;
|
|
766
|
+
let m;
|
|
767
|
+
while ((m = rx.exec(textToProcess)) !== null) {
|
|
768
|
+
matches.push({ start: m.index, end: m.index + m[0].length, value: m[0], type: 'CUSTOM', customLabel: label });
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const activeRules = getActiveRules(activeProfile);
|
|
774
|
+
const currentJargon = PROFILE_JARGON[PROFILE_ALIAS_MAP[activeProfile?.toLowerCase()]] ? new Set([...JARGON_WORDS, ...PROFILE_JARGON[PROFILE_ALIAS_MAP[activeProfile?.toLowerCase()]]]) : JARGON_WORDS;
|
|
775
|
+
|
|
776
|
+
activeRules.forEach(rule => {
|
|
777
|
+
if (enabledEntities && enabledEntities.indexOf(rule.type) === -1) return;
|
|
778
|
+
if (!rule.regex) return;
|
|
779
|
+
|
|
780
|
+
rule.regex.lastIndex = 0;
|
|
781
|
+
let m;
|
|
782
|
+
while ((m = rule.regex.exec(textToProcess)) !== null) {
|
|
783
|
+
let matchedText = m[0];
|
|
784
|
+
let start = m.index;
|
|
785
|
+
|
|
786
|
+
if (m.length > 1 && m[1] !== undefined && m[1] !== '') {
|
|
787
|
+
matchedText = m[1];
|
|
788
|
+
const relOffset = m[0].indexOf(m[1]);
|
|
789
|
+
if (relOffset !== -1) {
|
|
790
|
+
start = m.index + relOffset;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (matchedText.trim().length < 2) continue;
|
|
794
|
+
const end = start + matchedText.length;
|
|
795
|
+
|
|
796
|
+
if (rule.type !== 'NAME' && rule.type !== 'ADDRESS') {
|
|
797
|
+
const val = matchedText.toLowerCase().trim();
|
|
798
|
+
if (NAME_STOP_LIST.has(val) || NOT_NAME_WORDS.has(val) || currentJargon.has(val)) {
|
|
799
|
+
// Skip if generic English dictionary term matched by greedy regex (e.g. SWIFT matching SCRUBBER or CONTAINS)
|
|
800
|
+
if (rule.type === 'FINANCIAL' || rule.type === 'ID' || rule.type === 'PRIVACY' || rule.type === 'SECRET' || rule.type === 'LEGAL' || rule.type === 'PHI') {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
matches.push({ start, end, value: matchedText, type: rule.type });
|
|
805
|
+
} else {
|
|
806
|
+
if (rule.type === 'NAME') {
|
|
807
|
+
matchedText = matchedText.replace(/[.,;:]+$/, '').trim();
|
|
808
|
+
}
|
|
809
|
+
let val = matchedText.toLowerCase().trim();
|
|
810
|
+
if (!val || NAME_STOP_LIST.has(val) || currentJargon.has(val) || NOT_NAME_WORDS.has(val)) continue;
|
|
811
|
+
|
|
812
|
+
if (rule.type === 'NAME') {
|
|
813
|
+
let words = val.split(/[ \t\xA0]+/);
|
|
814
|
+
if (!rule.isContextName) {
|
|
815
|
+
let origWords = matchedText.split(/[ \t\xA0]+/);
|
|
816
|
+
while (words.length > 2 && (currentJargon.has(words[0]) || (words[0].length > 1 && NOT_NAME_WORDS.has(words[0])) || (words[0].replace(/[^\p{L}]/gu, '').length > 1 && NOT_NAME_WORDS.has(words[0].replace(/[^\p{L}]/gu, ''))))) {
|
|
817
|
+
origWords.shift();
|
|
818
|
+
words.shift();
|
|
819
|
+
const nextStart = matchedText.indexOf(origWords[0]);
|
|
820
|
+
if (nextStart !== -1) {
|
|
821
|
+
start += nextStart;
|
|
822
|
+
matchedText = matchedText.substring(nextStart);
|
|
823
|
+
val = matchedText.toLowerCase().trim();
|
|
824
|
+
} else {
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (!rule.isContextName && (
|
|
830
|
+
words.some(w => {
|
|
831
|
+
const cleanW = w.replace(/[^\p{L}]/gu, '');
|
|
832
|
+
if (cleanW.length <= 1) return false;
|
|
833
|
+
return currentJargon.has(w) || NOT_NAME_WORDS.has(w) || NOT_NAME_WORDS.has(cleanW) || NAME_STOP_LIST.has(w) || NAME_STOP_LIST.has(cleanW);
|
|
834
|
+
}) ||
|
|
835
|
+
val.split(/[ \t\xA0-]+/).some(sw => {
|
|
836
|
+
const cleanSW = sw.replace(/[^\p{L}]/gu, '');
|
|
837
|
+
if (cleanSW.length <= 1) return false;
|
|
838
|
+
return currentJargon.has(sw) || NOT_NAME_WORDS.has(sw) || NOT_NAME_WORDS.has(cleanSW) || NAME_STOP_LIST.has(sw) || NAME_STOP_LIST.has(cleanSW);
|
|
839
|
+
})
|
|
840
|
+
)) continue;
|
|
841
|
+
|
|
842
|
+
if (rule.isContextName && words.length > 0 && words.every(w => {
|
|
843
|
+
const cleanW = w.replace(/[^\p{L}]/gu, '');
|
|
844
|
+
return !cleanW || currentJargon.has(w) || NOT_NAME_WORDS.has(w) || NOT_NAME_WORDS.has(cleanW) || NAME_STOP_LIST.has(w) || NAME_STOP_LIST.has(cleanW);
|
|
845
|
+
})) {
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
} else if (rule.type === 'ADDRESS') {
|
|
849
|
+
const cleanVal = val.replace(/[.,;!?]/g, ' ').trim();
|
|
850
|
+
if (NAME_STOP_LIST.has(cleanVal) || currentJargon.has(cleanVal) || NOT_NAME_WORDS.has(cleanVal)) continue;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
matches.push({ start, end: start + matchedText.length, value: matchedText, type: rule.type });
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
if (nlpNames && nlpNames.length > 0) {
|
|
859
|
+
nlpNames.forEach(name => {
|
|
860
|
+
if (!name || typeof name !== 'string' || name.trim().length < 3) return;
|
|
861
|
+
const val = name.toLowerCase().trim();
|
|
862
|
+
if (NAME_STOP_LIST.has(val) || NOT_NAME_WORDS.has(val) || currentJargon.has(val)) return;
|
|
863
|
+
const words = val.split(/\s+/);
|
|
864
|
+
if (words.every(w => NOT_NAME_WORDS.has(w) || currentJargon.has(w) || NAME_STOP_LIST.has(w))) return;
|
|
865
|
+
const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
866
|
+
const rx = new RegExp(`(?<=^|[^\\p{L}\\p{N}_])${safeName}(?=[^\\p{L}\\p{N}_]|$)`, 'gu');
|
|
867
|
+
let m;
|
|
868
|
+
while ((m = rx.exec(textToProcess)) !== null) {
|
|
869
|
+
matches.push({ start: m.index, end: m.index + m[0].length, value: m[0], type: 'NAME' });
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (mlEntities && mlEntities.length > 0) {
|
|
875
|
+
mlEntities.forEach(item => {
|
|
876
|
+
const safeName = item.word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
877
|
+
const rx = new RegExp(`(?<=^|[^\\p{L}\\p{N}_])${safeName}(?=[^\\p{L}\\p{N}_]|$)`, 'giu');
|
|
878
|
+
let m;
|
|
879
|
+
while ((m = rx.exec(textToProcess)) !== null) {
|
|
880
|
+
matches.push({ start: m.index, end: m.index + m[0].length, value: m[0], type: item.type });
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const learnedNames = new Set();
|
|
886
|
+
matches.forEach(m => {
|
|
887
|
+
if (m.type === 'NAME') {
|
|
888
|
+
const nameWords = m.value.split(/[^\p{L}'-]+/u);
|
|
889
|
+
nameWords.forEach(w => {
|
|
890
|
+
if (w && w.length >= 2 && /^\p{Lu}/u.test(w)) {
|
|
891
|
+
const wl = w.toLowerCase();
|
|
892
|
+
if (!NOT_NAME_WORDS.has(wl) && !JARGON_WORDS.has(wl) && !NAME_STOP_LIST.has(wl) && !currentJargon.has(wl)) {
|
|
893
|
+
learnedNames.add(w);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
if (learnedNames.size > 0) {
|
|
901
|
+
learnedNames.forEach(name => {
|
|
902
|
+
const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
903
|
+
const rx = new RegExp(`(?<=^|[^\\p{L}\\p{N}_])${safeName}(?=[^\\p{L}\\p{N}_]|$)`, 'gu');
|
|
904
|
+
let m;
|
|
905
|
+
while ((m = rx.exec(textToProcess)) !== null) {
|
|
906
|
+
matches.push({ start: m.index, end: m.index + m[0].length, value: m[0], type: 'NAME' });
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
matches.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
912
|
+
let filtered = [];
|
|
913
|
+
let lastEnd = 0;
|
|
914
|
+
matches.forEach(m => {
|
|
915
|
+
if (m.start >= lastEnd) { filtered.push(m); lastEnd = m.end; }
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
return { filteredMatches: filtered, processedText: textToProcess };
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const LABEL_ALIASES = {
|
|
922
|
+
NAME: ['NAME', 'NAMES', 'USERNAME', 'USER_NAME', 'CLIENTNAME', 'CLIENT_NAME', 'CANDIDATE_NAME', 'FULL_NAME', 'FIRSTNAME', 'FIRST_NAME', 'LASTNAME', 'LAST_NAME', 'SURNAME', 'ИМЯ', 'ИМЕНА', 'ПОЛЬЗОВАТЕЛЬ', 'ФИО', 'КЛИЕНТ', 'NOMBRE', 'NOMBRES', 'USUARIO', 'CLIENTE', 'NOM', 'NOMS', 'UTILISATEUR', 'NAME', 'NAMEN', 'BENUTZER', 'KUNDE', 'NOME', 'COGNOME', 'UTENTE', 'NAAM', 'GEBRUIKER', 'KLANT'],
|
|
923
|
+
EMAIL: ['EMAIL', 'EMAILS', 'EMAILADDR', 'EMAIL_ADDR', 'EMAILADDRESS', 'EMAIL_ADDRESS', 'EMAIL_ADR', 'MAIL', 'MAILS', 'ПОЧТА', 'ЭЛ_ПОЧТА', 'АДРЕС_ПОЧТЫ', 'МЕЙЛ', 'МАЙЛ', 'CORREO', 'COURRIEL', 'CORREO_ELECTRONICO', 'MEL'],
|
|
924
|
+
PHONE: ['PHONE', 'PHONES', 'PHONENUM', 'PHONE_NUM', 'PHONENUMBER', 'PHONE_NUMBER', 'TEL', 'TELS', 'TELEPHONE', 'TELEPHONES', 'MOBILE', 'CELL', 'ТЕЛЕФОН', 'ТЕЛЕФОНЫ', 'НОМЕР_ТЕЛЕФОНА', 'НОМЕР', 'MOVIL', 'PORTABLE', 'HANDY', 'TELEFONI', 'CELLULARE'],
|
|
925
|
+
ID: ['ID', 'IDS', 'IDNUM', 'ID_NUM', 'IDNUMBER', 'ID_NUMBER', 'IDENTIFIER', 'IDENTIFIERS', 'PASSPORT', 'SSN', 'EIN', 'TAXID', 'TAX_ID', 'LICENSE', 'LICENSE_PLATE', 'ИД', 'ИДЕНТИФИКАТОР', 'ПАСПОРТ', 'СНИЛС', 'ИНН', 'IDENTIFICADOR', 'PASAPORTE', 'IDENTIFIANT', 'PASSEPORT', 'IDENTIFIKATOR', 'PASS', 'IDENTIFICATORE', 'PASSAPORTO'],
|
|
926
|
+
FINANCIAL: ['FINANCIAL', 'FINANCIALS', 'MONEY', 'AMOUNT', 'PRICE', 'COST', 'CARD', 'CREDITCARD', 'DEBITCARD', 'ACCOUNT', 'IBAN', 'BIC', 'ДЕНЬГИ', 'СУММА', 'КАРТА', 'СЧЕТ', 'БАНК', 'DINERO', 'CANTIDAD', 'TARJETA', 'CUENTA', 'ARGENT', 'MONTANT', 'COMPTE', 'GELD', 'BETRAG', 'KONTO'],
|
|
927
|
+
ADDRESS: ['ADDRESS', 'ADDRESSES', 'STREET', 'STREET_ADDRESS', 'CITY', 'STATE', 'ZIP', 'ZIPCODE', 'ZIP_CODE', 'COUNTRY', 'LOCATION', 'АДРЕС', 'АДРЕСА', 'УЛИЦА', 'ГОРОД', 'СТРАНА', 'DIRECCION', 'DIRECCIONES', 'CALLE', 'CIUDAD', 'PAIS', 'ADRESSE', 'ADRESSES', 'RUE', 'VILLE', 'STRASSE', 'STADT', 'LAND'],
|
|
928
|
+
DATE: ['DATE', 'DATES', 'BIRTHDAY', 'DOB', 'ДАТА', 'ДАТЫ', 'ДЕНЬ_РОЖДЕНИЯ', 'FECHA', 'FECHAS', 'CUMPLEANOS', 'ANNIVERSAIRE', 'DATUM', 'DATEN', 'GEBURTSTAG'],
|
|
929
|
+
PHI: ['PHI', 'MRN', 'NHS', 'HEALTH', 'MEDICAL', 'PATIENT', 'МЕД', 'ПАЦИЕНТ', 'PACIENTE'],
|
|
930
|
+
SECRET: ['SECRET', 'SECRETS', 'KEY', 'KEYS', 'TOKEN', 'TOKENS', 'PASSWORD', 'PASSWORDS', 'AUTH', 'APIKEY', 'API_KEY', 'КЛЮЧ', 'КЛЮЧИ', 'ПАРОЛЬ', 'ПАРОЛИ', 'ТОКЕН', 'CLAVE', 'CONTRASENA', 'CLE', 'MOT_DE_PASSE', 'SCHLUESSEL', 'PASSWORT'],
|
|
931
|
+
CUSTOM: ['CUSTOM', 'CUSTOMS', 'RULE', 'RULES', 'КАСТОМ', 'ПРАВИЛО']
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
function getLabelAliases(label) {
|
|
935
|
+
const upper = label.toUpperCase();
|
|
936
|
+
if (LABEL_ALIASES[upper]) return LABEL_ALIASES[upper];
|
|
937
|
+
const aliases = new Set([label, upper, label.toLowerCase()]);
|
|
938
|
+
aliases.add(label.replace(/_/g, ' '));
|
|
939
|
+
aliases.add(label.replace(/_/g, '-'));
|
|
940
|
+
aliases.add(label.replace(/ /g, '_'));
|
|
941
|
+
aliases.add(label.replace(/-/g, '_'));
|
|
942
|
+
return Array.from(aliases);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function formatToken(label, index, format = 'brackets') {
|
|
946
|
+
const cleanLabel = String(label || 'PII').replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();
|
|
947
|
+
switch(format) {
|
|
948
|
+
case 'xml': return `<${cleanLabel}_${index}>`;
|
|
949
|
+
case 'mustache': return `{{${cleanLabel}_${index}}}`;
|
|
950
|
+
case 'underscores': return `__${cleanLabel}_${index}__`;
|
|
951
|
+
case 'brackets':
|
|
952
|
+
default: return `[${cleanLabel}_${index}]`;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function buildRestorationRegexAndRules(tokenMap) {
|
|
957
|
+
const ObjectKeys = Object.keys(tokenMap);
|
|
958
|
+
if (ObjectKeys.length === 0) return { compositeRegex: null, looseRules: [] };
|
|
959
|
+
|
|
960
|
+
const sortedKeys = [...ObjectKeys].sort((a, b) => {
|
|
961
|
+
const innerA = a.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '');
|
|
962
|
+
const innerB = b.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '');
|
|
963
|
+
const matchA = innerA.match(/^([A-Za-z_0-9]+?)[-_]?(\d+)$/);
|
|
964
|
+
const matchB = innerB.match(/^([A-Za-z_0-9]+?)[-_]?(\d+)$/);
|
|
965
|
+
if (matchA && matchB) {
|
|
966
|
+
const idxA = parseInt(matchA[2], 10);
|
|
967
|
+
const idxB = parseInt(matchB[2], 10);
|
|
968
|
+
const labelA = matchA[1];
|
|
969
|
+
const labelB = matchB[1];
|
|
970
|
+
if (idxA !== idxB) return idxB - idxA;
|
|
971
|
+
if (labelA.length !== labelB.length) return labelB.length - labelA.length;
|
|
972
|
+
}
|
|
973
|
+
return b.length - a.length;
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
const looseRules = [];
|
|
977
|
+
const regexParts = [];
|
|
978
|
+
|
|
979
|
+
sortedKeys.forEach(k => {
|
|
980
|
+
const inner = k.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '');
|
|
981
|
+
const match = inner.match(/^([A-Za-z_0-9]+?)[-_]?(\d+)$/);
|
|
982
|
+
if (match) {
|
|
983
|
+
const label = match[1];
|
|
984
|
+
const baseIndex = parseInt(match[2], 10);
|
|
985
|
+
const aliases = getLabelAliases(label);
|
|
986
|
+
|
|
987
|
+
const escapedAliases = aliases.map(a => a.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
988
|
+
const aliasesGroup = `(?:${escapedAliases.join('|')})`;
|
|
989
|
+
const loosePattern = `(?:\\[\\s*${aliasesGroup}[-_\\s]*0*${baseIndex}\\s*\\]|<\\s*${aliasesGroup}[-_\\s]*0*${baseIndex}\\s*>|\\{\\{\\s*${aliasesGroup}[-_\\s]*0*${baseIndex}\\s*\\}\\}|__\\s*${aliasesGroup}[-_\\s]*0*${baseIndex}\\s*__|(?<![A-Za-z0-9\\u0400-\\u04FF_])${aliasesGroup}[-_\\s]*0*${baseIndex}(?![A-Za-z0-9\\u0400-\\u04FF_]))(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})?`;
|
|
990
|
+
looseRules.push({ token: k, pattern: loosePattern });
|
|
991
|
+
}
|
|
992
|
+
regexParts.push(k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
let compositeRegex = null;
|
|
996
|
+
if (regexParts.length > 0) {
|
|
997
|
+
compositeRegex = new RegExp(`(?:\\b|\\[|<|\\{\\{|__)?(?:(?:(?<!\\w)|(?<=\\s))(?:${regexParts.join('|')})(?:(?!\\w)|(?=\\s)))(?:\\b|\\]|>|\\}\\}|__)?(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})?`, 'g');
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
return { compositeRegex, looseRules };
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function isJsonPayload(str) {
|
|
1004
|
+
if (!str || typeof str !== "string") return false;
|
|
1005
|
+
const trimmed = str.trim();
|
|
1006
|
+
if (!((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]")))) {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
try {
|
|
1010
|
+
JSON.parse(trimmed);
|
|
1011
|
+
return true;
|
|
1012
|
+
} catch (_) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function cleanAIPromptPrefix(text) {
|
|
1018
|
+
if (!text) return "";
|
|
1019
|
+
let cleaned = text;
|
|
1020
|
+
// If text is a full valid JSON object or array, preserve structure
|
|
1021
|
+
if (!isJsonPayload(cleaned)) {
|
|
1022
|
+
// 1. Strip raw CSS / style blocks leaked from ChatGPT Canvas, web components or stylesheets (handles multi-line, unclosed and variable definitions)
|
|
1023
|
+
cleaned = cleaned.replace(/^\s*(?:[.#][a-zA-Z0-9_-]+|\[[a-zA-Z0-9_#.:\-*>=,'"\s]+\]|:is\([^)]+\)|[a-zA-Z0-9_-]+)?\s*\{[^}]*?(?:\}\s*|\n\n+|$)/gi, "");
|
|
1024
|
+
cleaned = cleaned.replace(/^[;{} \t\r\n]+/, "");
|
|
1025
|
+
cleaned = cleaned.replace(/(?:^|\n)[a-zA-Z0-9_#.:\-*>[\]=\s,'"]+\{[^}]*(--[a-zA-Z0-9_-]+:|color-mix\(|var\()[^}]*\}/g, "");
|
|
1026
|
+
}
|
|
1027
|
+
// 2. Strip AI author prefixes and platform artifacts
|
|
1028
|
+
cleaned = cleaned.replace(/^\s*(?:Claude responded|Claude|ChatGPT|Gemini|Grok|DeepSeek|Kimi|Copilot|Assistant|User)\s*(?::|\bsaid\b|\bresponded\b|(?=\s))\s*/i, "");
|
|
1029
|
+
cleaned = cleaned.replace(/^(?:Here (?:is|are) (?:the )?(?:redacted|scrubbed|sanitized|processed|clean|updated|modified) (?:text|output|version|data).*?[:\n]+|\*\*Scrubbed Text\*\*[:\n]+|### Scrubbed Text[:\n]+)/i, '');
|
|
1030
|
+
cleaned = cleaned.replace(/^\s*Edit\s*\n+/i, "");
|
|
1031
|
+
cleaned = cleaned.replace(/\s*\bEdit\s+in\s+a\s+page\b\s*$/i, "");
|
|
1032
|
+
// 3. Strip stray leading colons, semicolons, or separators left by stripped icons/artifact headers
|
|
1033
|
+
cleaned = cleaned.replace(/^[:;|\-\—\–]+(?=\n|$)/, "");
|
|
1034
|
+
cleaned = cleaned.replace(/^[:;]+\s*/, "");
|
|
1035
|
+
return cleaned.trim();
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
function buildFastTokenLookup(sessionMap) {
|
|
1039
|
+
const lookup = new Map();
|
|
1040
|
+
const customRegexParts = [];
|
|
1041
|
+
const keys = Object.keys(sessionMap || {});
|
|
1042
|
+
|
|
1043
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1044
|
+
const k = keys[i];
|
|
1045
|
+
const v = sessionMap[k];
|
|
1046
|
+
lookup.set(k, v);
|
|
1047
|
+
lookup.set(k.toUpperCase(), v);
|
|
1048
|
+
|
|
1049
|
+
const inner = k.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '');
|
|
1050
|
+
const match = inner.match(/^([A-Za-z_0-9]+?)[-_]?(\d+)$/);
|
|
1051
|
+
if (match) {
|
|
1052
|
+
const label = match[1];
|
|
1053
|
+
const baseIndex = parseInt(match[2], 10);
|
|
1054
|
+
const aliases = getLabelAliases(label);
|
|
1055
|
+
for (let a = 0; a < aliases.length; a++) {
|
|
1056
|
+
const u = aliases[a].toUpperCase();
|
|
1057
|
+
lookup.set(u + '_' + baseIndex, v);
|
|
1058
|
+
lookup.set(u + '-' + baseIndex, v);
|
|
1059
|
+
lookup.set(u + ' ' + baseIndex, v);
|
|
1060
|
+
lookup.set(u + baseIndex, v);
|
|
1061
|
+
lookup.set('[' + u + '_' + baseIndex + ']', v);
|
|
1062
|
+
lookup.set('<' + u + '_' + baseIndex + '>', v);
|
|
1063
|
+
lookup.set('{{' + u + '_' + baseIndex + '}}', v);
|
|
1064
|
+
lookup.set('__' + u + '_' + baseIndex + '__', v);
|
|
1065
|
+
lookup.set('[' + u + ' ' + baseIndex + ']', v);
|
|
1066
|
+
lookup.set('[' + u + '-' + baseIndex + ']', v);
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
customRegexParts.push(k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
let regexStr = '(?:\\[\\s*[A-Za-z0-9_\\-А-Яа-яЁё ]+\\s*\\]|<\\s*[A-Za-z0-9_\\-А-Яа-яЁё ]+\\s*>|\\{\\{\\s*[A-Za-z0-9_\\-А-Яа-яЁё ]+\\s*\\}\\}|__\\s*[A-Za-z0-9_\\-А-Яа-яЁё ]+\\s*__|(?<=^|[^a-zA-Z0-9_А-Яа-яЁё])[A-Za-z_А-Яа-яЁё]+[-_\\s]*\\d+)';
|
|
1074
|
+
if (customRegexParts.length > 0) {
|
|
1075
|
+
regexStr = '(?:' + regexStr + '|' + customRegexParts.join('|') + ')';
|
|
1076
|
+
}
|
|
1077
|
+
const tokenRegex = new RegExp(regexStr + '(?:\'s|’s|s|[а-яёА-ЯЁ]{1,3})?', 'gi');
|
|
1078
|
+
|
|
1079
|
+
return { lookup, tokenRegex };
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function resolveTokenValue(rawMatch, targetTokenKey, sessionMap) {
|
|
1083
|
+
if (!sessionMap) return undefined;
|
|
1084
|
+
if (sessionMap[targetTokenKey] !== undefined) return sessionMap[targetTokenKey];
|
|
1085
|
+
if (sessionMap[rawMatch] !== undefined) return sessionMap[rawMatch];
|
|
1086
|
+
const cleanRaw = rawMatch.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '').trim();
|
|
1087
|
+
for (const k of Object.keys(sessionMap)) {
|
|
1088
|
+
const cleanK = k.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '').trim();
|
|
1089
|
+
if (cleanK.toLowerCase() === cleanRaw.toLowerCase()) {
|
|
1090
|
+
return sessionMap[k];
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return undefined;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function unscrubText(text, sessionMap) {
|
|
1097
|
+
let restoredCount = 0;
|
|
1098
|
+
let result = text;
|
|
1099
|
+
const tokens = Object.keys(sessionMap || {});
|
|
1100
|
+
if (tokens.length === 0) return { text: result, count: 0 };
|
|
1101
|
+
|
|
1102
|
+
result = cleanAIPromptPrefix(result);
|
|
1103
|
+
|
|
1104
|
+
if (tokens.length > 50) {
|
|
1105
|
+
const { lookup, tokenRegex } = buildFastTokenLookup(sessionMap);
|
|
1106
|
+
result = result.replace(tokenRegex, (match) => {
|
|
1107
|
+
if (lookup.has(match)) {
|
|
1108
|
+
restoredCount++;
|
|
1109
|
+
return lookup.get(match);
|
|
1110
|
+
}
|
|
1111
|
+
const upper = match.toUpperCase();
|
|
1112
|
+
if (lookup.has(upper)) {
|
|
1113
|
+
restoredCount++;
|
|
1114
|
+
return lookup.get(upper);
|
|
1115
|
+
}
|
|
1116
|
+
const possMatch = match.match(/^([\s\S]+?)('s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1117
|
+
if (possMatch) {
|
|
1118
|
+
const base = possMatch[1];
|
|
1119
|
+
const suffix = possMatch[2];
|
|
1120
|
+
if (lookup.has(base)) {
|
|
1121
|
+
restoredCount++;
|
|
1122
|
+
return lookup.get(base) + suffix;
|
|
1123
|
+
}
|
|
1124
|
+
if (lookup.has(base.toUpperCase())) {
|
|
1125
|
+
restoredCount++;
|
|
1126
|
+
return lookup.get(base.toUpperCase()) + suffix;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return match;
|
|
1130
|
+
});
|
|
1131
|
+
return { text: result, count: restoredCount };
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const { compositeRegex, looseRules } = buildRestorationRegexAndRules(sessionMap);
|
|
1135
|
+
|
|
1136
|
+
if (compositeRegex) {
|
|
1137
|
+
result = result.replace(compositeRegex, (match) => {
|
|
1138
|
+
const suffixMatch = match.match(/(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1139
|
+
const suffix = suffixMatch ? suffixMatch[0] : '';
|
|
1140
|
+
const baseMatch = suffix ? match.slice(0, -suffix.length) : match;
|
|
1141
|
+
const cleanMatch = baseMatch.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '').trim();
|
|
1142
|
+
const token = `[${cleanMatch}]`;
|
|
1143
|
+
|
|
1144
|
+
const val = resolveTokenValue(baseMatch, token, sessionMap) ?? resolveTokenValue(baseMatch, cleanMatch, sessionMap);
|
|
1145
|
+
if (val !== undefined) {
|
|
1146
|
+
restoredCount++;
|
|
1147
|
+
return val + suffix;
|
|
1148
|
+
}
|
|
1149
|
+
return match;
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
looseRules.forEach(rule => {
|
|
1154
|
+
const rx = new RegExp(rule.pattern, 'gi');
|
|
1155
|
+
result = result.replace(rx, (match) => {
|
|
1156
|
+
const suffixMatch = match.match(/(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1157
|
+
const suffix = suffixMatch ? suffixMatch[0] : '';
|
|
1158
|
+
const baseMatch = suffix ? match.slice(0, -suffix.length) : match;
|
|
1159
|
+
|
|
1160
|
+
const val = sessionMap[rule.token] ?? resolveTokenValue(baseMatch, rule.token, sessionMap);
|
|
1161
|
+
if (val !== undefined) {
|
|
1162
|
+
restoredCount++;
|
|
1163
|
+
return val + suffix;
|
|
1164
|
+
}
|
|
1165
|
+
return match;
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
return { text: result, count: restoredCount };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function escapeHTML(str) {
|
|
1173
|
+
if (!str) return '';
|
|
1174
|
+
return String(str)
|
|
1175
|
+
.replace(/&/g, '&')
|
|
1176
|
+
.replace(/</g, '<')
|
|
1177
|
+
.replace(/>/g, '>')
|
|
1178
|
+
.replace(/"/g, '"')
|
|
1179
|
+
.replace(/'/g, ''');
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function unscrubTextAsHTML(text, sessionMap) {
|
|
1183
|
+
let restoredCount = 0;
|
|
1184
|
+
let cleaned = cleanAIPromptPrefix(text || '');
|
|
1185
|
+
let result = escapeHTML(cleaned);
|
|
1186
|
+
const tokens = Object.keys(sessionMap || {});
|
|
1187
|
+
if (tokens.length === 0) return { text: result, count: 0 };
|
|
1188
|
+
|
|
1189
|
+
if (tokens.length > 50) {
|
|
1190
|
+
const { lookup, tokenRegex } = buildFastTokenLookup(sessionMap);
|
|
1191
|
+
result = result.replace(tokenRegex, (match) => {
|
|
1192
|
+
let rawVal = null;
|
|
1193
|
+
let suffix = '';
|
|
1194
|
+
if (lookup.has(match)) {
|
|
1195
|
+
rawVal = lookup.get(match);
|
|
1196
|
+
} else if (lookup.has(match.toUpperCase())) {
|
|
1197
|
+
rawVal = lookup.get(match.toUpperCase());
|
|
1198
|
+
} else {
|
|
1199
|
+
const possMatch = match.match(/^([\s\S]+?)('s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1200
|
+
if (possMatch) {
|
|
1201
|
+
const base = possMatch[1];
|
|
1202
|
+
suffix = possMatch[2];
|
|
1203
|
+
if (lookup.has(base)) rawVal = lookup.get(base);
|
|
1204
|
+
else if (lookup.has(base.toUpperCase())) rawVal = lookup.get(base.toUpperCase());
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
if (rawVal !== null) {
|
|
1208
|
+
restoredCount++;
|
|
1209
|
+
const escapedVal = escapeHTML(rawVal);
|
|
1210
|
+
const cleanMatch = match.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '').trim();
|
|
1211
|
+
return `<span class="ps-restored-data entity-tag entity-revealed" title="Original Token: ${escapeHTML(cleanMatch)}">${escapedVal}</span>${escapeHTML(suffix)}`;
|
|
1212
|
+
}
|
|
1213
|
+
return match;
|
|
1214
|
+
});
|
|
1215
|
+
return { text: result, count: restoredCount };
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const { compositeRegex, looseRules } = buildRestorationRegexAndRules(sessionMap);
|
|
1219
|
+
|
|
1220
|
+
if (compositeRegex) {
|
|
1221
|
+
result = result.replace(compositeRegex, (match) => {
|
|
1222
|
+
const suffixMatch = match.match(/(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1223
|
+
const suffix = suffixMatch ? suffixMatch[0] : '';
|
|
1224
|
+
const baseMatch = suffix ? match.slice(0, -suffix.length) : match;
|
|
1225
|
+
const cleanMatch = baseMatch.replace(/^\[|<|\{\{|__|\]|>|\}\}|__/g, '').trim();
|
|
1226
|
+
const token = `[${cleanMatch}]`;
|
|
1227
|
+
|
|
1228
|
+
const val = resolveTokenValue(baseMatch, token, sessionMap) ?? resolveTokenValue(baseMatch, cleanMatch, sessionMap);
|
|
1229
|
+
if (val !== undefined) {
|
|
1230
|
+
restoredCount++;
|
|
1231
|
+
const escapedVal = escapeHTML(val);
|
|
1232
|
+
return `<span class="ps-restored-data entity-tag entity-revealed" title="Original Token: ${escapeHTML(token)}">${escapedVal}</span>${escapeHTML(suffix)}`;
|
|
1233
|
+
}
|
|
1234
|
+
return match;
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
looseRules.forEach(rule => {
|
|
1239
|
+
const rx = new RegExp(rule.pattern, 'gi');
|
|
1240
|
+
result = result.replace(rx, (match, offset, fullStr) => {
|
|
1241
|
+
const before = fullStr.substring(0, offset);
|
|
1242
|
+
const lastOpen = before.lastIndexOf('<');
|
|
1243
|
+
const lastClose = before.lastIndexOf('>');
|
|
1244
|
+
if (lastOpen > lastClose) return match;
|
|
1245
|
+
|
|
1246
|
+
const suffixMatch = match.match(/(?:'s|’s|s|[а-яёА-ЯЁ]{1,3})$/);
|
|
1247
|
+
const suffix = suffixMatch ? suffixMatch[0] : '';
|
|
1248
|
+
const baseMatch = suffix ? match.slice(0, -suffix.length) : match;
|
|
1249
|
+
|
|
1250
|
+
const val = sessionMap[rule.token] ?? resolveTokenValue(baseMatch, rule.token, sessionMap);
|
|
1251
|
+
if (val !== undefined) {
|
|
1252
|
+
restoredCount++;
|
|
1253
|
+
const escapedVal = escapeHTML(val);
|
|
1254
|
+
return `<span class="ps-restored-data entity-tag entity-revealed" title="Original Token: ${escapeHTML(rule.token)} (Fuzzy Match)">${escapedVal}</span>${escapeHTML(suffix)}`;
|
|
1255
|
+
}
|
|
1256
|
+
return match;
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
return { text: result, count: restoredCount };
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const PrivacyScrubberEngine = {
|
|
1264
|
+
init,
|
|
1265
|
+
hydrateRegex,
|
|
1266
|
+
getActiveRules,
|
|
1267
|
+
stitchOrphanedNameLines,
|
|
1268
|
+
detectMatches,
|
|
1269
|
+
getRulesMap: () => ({ REGEX_RULES, PROFILE_RULES }),
|
|
1270
|
+
getDevopsRules: () => DEVOPS_SECRETS,
|
|
1271
|
+
LABEL_ALIASES,
|
|
1272
|
+
getLabelAliases,
|
|
1273
|
+
PROFILE_ALIAS_MAP,
|
|
1274
|
+
formatToken,
|
|
1275
|
+
buildRestorationRegexAndRules,
|
|
1276
|
+
unscrubText,
|
|
1277
|
+
unscrubTextAsHTML,
|
|
1278
|
+
cleanAIPromptPrefix,
|
|
1279
|
+
// Expose underlying constants for Node scripts & build pipeline proxy
|
|
1280
|
+
REGEX_RULES,
|
|
1281
|
+
PROFILE_RULES,
|
|
1282
|
+
NAME_STOP_LIST,
|
|
1283
|
+
JARGON_WORDS,
|
|
1284
|
+
NOT_NAME_WORDS,
|
|
1285
|
+
PROFILE_JARGON,
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
if (typeof exports !== 'undefined') {
|
|
1289
|
+
module.exports = PrivacyScrubberEngine;
|
|
1290
|
+
}
|
|
1291
|
+
if (typeof self !== 'undefined') {
|
|
1292
|
+
self.PrivacyScrubberEngine = PrivacyScrubberEngine;
|
|
1293
|
+
}
|
|
1294
|
+
if (typeof window !== 'undefined') {
|
|
1295
|
+
window.PrivacyScrubberEngine = PrivacyScrubberEngine;
|
|
1296
|
+
}
|
|
1297
|
+
})();
|