@privacyscrubber/mcp-server 2.4.2 → 2.4.4

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.
@@ -0,0 +1,78 @@
1
+ {
2
+ "serverInfo": {
3
+ "name": "privacyscrubber/pii-masking-mcp",
4
+ "version": "1.6.9"
5
+ },
6
+ "tools": [
7
+ {
8
+ "name": "sanitize_text",
9
+ "description": "Locally scrubs PII, secrets, and credentials (like API keys, passwords, emails, phones, names) from code, logs, or text. Replaces them with safe placeholders (e.g., [EMAIL_1], [API_KEY_1]). Keep your data secure before passing it to any LLM.",
10
+ "inputSchema": {
11
+ "type": "object",
12
+ "properties": {
13
+ "text": {
14
+ "type": "string",
15
+ "description": "The raw text, code, or logs to sanitize."
16
+ },
17
+ "profile": {
18
+ "type": "string",
19
+ "description": "The detection profile to use. Available: 'General' (Free), or PRO profiles: 'Dev' (Engineering/Code), 'Medical', 'Pharma', 'Legal', 'Compliance', 'CCPA', 'Finance', 'Bizops', 'Sales', 'WealthMgmt', 'Insurance', 'Accounting', 'HR', 'Security', 'Marketing', 'Support', 'RealEstate', 'Agents', 'Academic', 'Creative', 'Tech', 'Personal'. Defaults to 'General'."
20
+ }
21
+ },
22
+ "required": ["text"]
23
+ }
24
+ },
25
+ {
26
+ "name": "reveal_text",
27
+ "description": "Replaces masked tokens (e.g., [EMAIL_1], [API_KEY_1]) in the LLM's response back with the original private data from the local volatile RAM-only session map.",
28
+ "inputSchema": {
29
+ "type": "object",
30
+ "properties": {
31
+ "text": {
32
+ "type": "string",
33
+ "description": "The AI generated response containing placeholders to restore."
34
+ }
35
+ },
36
+ "required": ["text"]
37
+ }
38
+ },
39
+ {
40
+ "name": "sanitize_file",
41
+ "description": "Reads a local file, sanitizes its contents using the selected profile, and outputs the safe version for AI analysis. Securely keeps original identifiers in memory.",
42
+ "inputSchema": {
43
+ "type": "object",
44
+ "properties": {
45
+ "file_path": {
46
+ "type": "string",
47
+ "description": "Absolute path to the file to sanitize."
48
+ },
49
+ "profile": {
50
+ "type": "string",
51
+ "description": "The detection profile to use. Available: 'General' (Free), or PRO profiles: 'Dev' (Engineering/Code), 'Medical', 'Pharma', 'Legal', 'Compliance', 'CCPA', 'Finance', 'Bizops', 'Sales', 'WealthMgmt', 'Insurance', 'Accounting', 'HR', 'Security', 'Marketing', 'Support', 'RealEstate', 'Agents', 'Academic', 'Creative', 'Tech', 'Personal'. Defaults to 'General'."
52
+ }
53
+ },
54
+ "required": ["file_path"]
55
+ }
56
+ },
57
+ {
58
+ "name": "create_default_config",
59
+ "description": "Creates a default 'privacyscrubber.json' configuration file in the active workspace root directory if one does not exist. Includes template structures for custom regex rules and exclusion bypass patterns.",
60
+ "inputSchema": {
61
+ "type": "object",
62
+ "properties": {},
63
+ "required": []
64
+ }
65
+ },
66
+ {
67
+ "name": "check_status",
68
+ "description": "Returns the current PrivacyScrubber MCP tier, session usage, available profiles, and PRO upgrade instructions. Call this to see your license status or get setup help.",
69
+ "inputSchema": {
70
+ "type": "object",
71
+ "properties": {},
72
+ "required": []
73
+ }
74
+ }
75
+ ],
76
+ "resources": [],
77
+ "prompts": []
78
+ }
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ # PrivacyScrubber MCP Server License Agreement
2
+
3
+ **Copyright (c) 2026 Ilya Sibiryakov (BrandMeWeb). All Rights Reserved.**
4
+
5
+ ### 1. Grant of License — Community / Development Tier
6
+ Subject to the terms and conditions of this Agreement, BrandMeWeb grants you a personal, worldwide, non-exclusive, non-transferable license to install and run the PrivacyScrubber MCP Server (`@privacyscrubber/mcp-server`) in local IDE environments (such as Cursor, Windsurf, Claude Desktop, and CLI tools) solely for personal evaluation, development, and standard local data sanitization under published Free Tier quotas.
7
+
8
+ ### 2. Commercial, Multi-User & Enterprise Tier
9
+ Use of this software within commercial organizations, production automated pipelines, CI/CD gates, high-volume batch processing, multi-seat corporate teams, or the invocation of the 30 Specialized Industry Profiles (HIPAA PHI, Financial PAN/IBAN, Legal Privilege, DevSecOps Cloud Secrets, Automotive, Telecom, Biotech) requires a valid commercial license key:
10
+ - **PrivacyScrubber PRO**: Individual professional use ($15/mo or $110 Lifetime).
11
+ - **PrivacyScrubber TEAMS**: Team and small enterprise seats ($99/mo flat).
12
+ - **PrivacyScrubber Enterprise / Air-Gapped**: Dedicated corporate VPC clusters, Nitro Enclaves, and custom profile calibration ($12,000/yr).
13
+
14
+ Licensing and key activation: https://privacyscrubber.com/pricing
15
+
16
+ ### 3. Restrictions on Reverse-Engineering & Redistribution
17
+ Except as expressly authorized herein, you may not:
18
+ - Decompile, disassemble, reverse-engineer, or attempt to reconstruct the underlying pattern databases, regular expression taxonomies, or proprietary tokenization finite-state machines.
19
+ - Extract, harvest, or scrape the 30 specialized industry entity taxonomies to train competing machine learning models or build competing redaction products.
20
+ - Distribute, sublicense, lease, or commercially exploit standalone copies of the engine without an active Enterprise OEM agreement.
21
+
22
+ ### 4. Disclaimer of Warranties & Limitation of Liability
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -482,7 +482,7 @@ Returns a visual dashboard showing your current tier, session request count, act
482
482
  * **Response Example (Free Tier):**
483
483
  ```
484
484
  ╔══════════════════════════════════════════════════╗
485
- ║ PrivacyScrubber MCP Server v2.4.2 ║
485
+ ║ PrivacyScrubber MCP Server v2.4.4 ║
486
486
  ╠══════════════════════════════════════════════════╣
487
487
  ║ 🔓 Tier: FREE ║
488
488
  ║ 📊 Session requests: 5 ║
package/index.js CHANGED
@@ -352,7 +352,7 @@ export class StreamingDetokenizer {
352
352
  const tailLen = 160;
353
353
  const tail = this.pendingBuffer.length > tailLen ? this.pendingBuffer.slice(-tailLen) : this.pendingBuffer;
354
354
  const offset = this.pendingBuffer.length > tailLen ? this.pendingBuffer.length - tailLen : 0;
355
- const m = tail.match(/(?:(?:\[|【|[|〔|「|『|<|\{\{?|__?|%5[Bb]|\\u005[Bb]|&(?:amp;)?(?:#0*91;|#[xX]0*5[Bb];|lbrack;))[A-Za-z0-9_\-А-Яа-яЁё *`~.\\]{0,128}(?:%5?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?3?|[xX]0*5?[Dd]?)?|r(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)?|%[0-9A-Fa-f]?|\\(?:u(?:0(?:0(?:5)?)?)?)?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?1?|[xX]0*5?)?|l(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)$/);
355
+ const m = tail.match(/(?:(?:\[|【|[|〔|「|『|<|\{\{?|__?|%5[Bb]|\\u005[Bb]|&(?:amp;)?(?:#0*91;|#[xX]0*5[Bb];|lbrack;))[A-Za-z0-9_\-\u0410-\u044F\u0401\u0451 *`~.\\]{0,128}(?:%5?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?3?|[xX]0*5?[Dd]?)?|r(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)?|%[0-9A-Fa-f]?|\\(?:u(?:0(?:0(?:5)?)?)?)?|&(?:a(?:m(?:p(?:;)?)?)?)?(?:#(?:0*9?1?|[xX]0*5?)?|l(?:b(?:r(?:a(?:c(?:k)?)?)?)?)?)?)$/);
356
356
 
357
357
  if (m) {
358
358
  const candidate = m[0];
@@ -814,7 +814,7 @@ function buildPsr2Receipt(tokenMap, timestamp = new Date().toISOString()) {
814
814
  const labels = (tokenMap && typeof tokenMap === 'object' ? Object.keys(tokenMap) : []).slice().sort();
815
815
  const counts = {};
816
816
  for (const t of labels) {
817
- const m = t.match(/\[([A-Z_]+)_\d+\]/);
817
+ const m = String(t).trim().match(/^(?:\[|<|\{\{|__)([A-Z0-9_]+)_\d+(?:\]|>|\}\}|__)$/);
818
818
  const type = m ? m[1] : 'CUSTOM';
819
819
  counts[type] = (counts[type] || 0) + 1;
820
820
  }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": "0.3",
3
3
  "name": "privacyscrubber/pii-masking-mcp",
4
- "version": "2.4.2",
4
+ "version": "2.4.4",
5
5
  "description": "CISO-Approved Zero-Trust PII & Secrets Redaction MCP Server for Cursor, Windsurf, and Claude Desktop. Prevent API leaks and comply with HIPAA/SOC 2 locally.",
6
6
  "author": {
7
7
  "name": "PrivacyScrubber",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privacyscrubber/mcp-server",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "mcpName": "io.github.moxno/privacyscrubber-mcp",
5
5
  "description": "CISO-Approved Zero-Trust PII & Secrets Redaction MCP Server for Cursor, Windsurf, and Claude Desktop. Prevent API leaks and comply with HIPAA/SOC 2 locally.",
6
6
  "main": "index.js",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "scripts": {
36
36
  "start": "node index.js",
37
- "build": "node -e \"const fs=require('fs'); if(fs.existsSync('../scripts/build-mcp.js')) require('child_process').execSync('node ../scripts/build-mcp.js', {stdio:'inherit'}); else console.log('MCP assets are pre-built.');\"",
37
+ "build": "node -e \"const fs=require('fs'); if(fs.existsSync('../scripts/build-mcp.js')) require('child_process').execSync('node ../scripts/build-mcp.js --keep-obfuscated', {stdio:'inherit'}); else console.log('MCP assets are pre-built.');\"",
38
38
  "test": "node test-mcp.js && node test-mcp-deep.js && node test-mcp-streaming.js",
39
39
  "test:stream": "node test-mcp-streaming.js",
40
- "prepublishOnly": "npm test"
40
+ "prepublishOnly": "npm run build && node -e \"const fs=require('fs'); for(const f of ['ps-license-manager.js','ps-license-manager.cjs','ps-pii-engine.js','ps-pii-engine.cjs','scrubber-core.cjs']){const c=fs.readFileSync(f,'utf8'); if(c.includes('OFFLINE_KEY_SALT')||c.includes('computeV2Signature')||!c.includes('_0x')){console.error('FATAL: Unobfuscated '+f+' detected!'); process.exit(1);}}\" && npm test"
41
41
  },
42
42
  "dependencies": {
43
43
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -27,7 +27,7 @@ const cmdIndex = args.indexOf("--");
27
27
 
28
28
  if (args.includes("--version") || args.includes("-v")) {
29
29
  const pkgPath = path.resolve(__dirname, './package.json');
30
- let version = '2.4.2';
30
+ let version = '2.4.4';
31
31
  try { version = JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version; } catch (_) {}
32
32
  console.log(`ps-guard / pii-masking-run v${version} (PrivacyScrubber MCP)`);
33
33
  process.exit(0);
@@ -250,18 +250,23 @@ function performSanitization(text) {
250
250
  if (!license.isPro && result.tokenMap) {
251
251
  // Free tier: check for secrets and warn
252
252
  let hasSecrets = false;
253
+ const SECRET_TOKEN_RE = /^\[(?:SECRET|API_TOKEN|AWS_KEY|JWT_TOKEN|STRIPE_KEY|OPENAI_KEY|DB_URI)_\d+\]$/;
253
254
  Object.entries(result.tokenMap).forEach(([token, original]) => {
254
- let isSecret = false;
255
- for (const detector of DEVOPS_SECRETS_DETECTOR) {
256
- detector.regex.lastIndex = 0;
257
- if (detector.regex.test(original)) {
258
- isSecret = true;
259
- break;
255
+ let isSecret = SECRET_TOKEN_RE.test(token);
256
+ if (!isSecret) {
257
+ for (const detector of DEVOPS_SECRETS_DETECTOR) {
258
+ detector.regex.lastIndex = 0;
259
+ if (detector.regex.test(original)) {
260
+ isSecret = true;
261
+ break;
262
+ }
260
263
  }
261
264
  }
262
265
  if (isSecret) {
263
266
  hasSecrets = true;
264
267
  result.scrubbedText = result.scrubbedText.replaceAll(token, '[SECRET_BLOCKED_UPGRADE_TO_PRO]');
268
+ delete result.tokenMap[token];
269
+ delete localSessionMap[token];
265
270
  }
266
271
  });
267
272
 
@@ -280,7 +285,7 @@ function buildCisoAuditTelemetry(currentTokenMap = {}) {
280
285
  const keys = Array.isArray(currentTokenMap) ? currentTokenMap : Object.keys(currentTokenMap);
281
286
  for (const t of keys) {
282
287
  const tokenStr = typeof t === 'string' ? t : (t.token || t.mask || '');
283
- const match = tokenStr.match(/\[([A-Z_]+)_\d+\]/);
288
+ const match = tokenStr.match(/\[([A-Z0-9_]+)_\d+\]/);
284
289
  const baseType = match ? match[1] : (tokenStr.replace(/\[|\]/g, '').replace(/_[0-9]+$/, '') || 'CUSTOM');
285
290
  entities[baseType] = (entities[baseType] || 0) + 1;
286
291
  totalCount++;
@@ -289,7 +294,7 @@ function buildCisoAuditTelemetry(currentTokenMap = {}) {
289
294
  const types = Object.keys(entities);
290
295
  let riskLevel = 'LOW EXPOSURE';
291
296
  if (totalCount > 0) {
292
- const hasHighRisk = types.some(t => ['ID', 'SSN', 'CREDIT_CARD', 'PASSPORT', 'BANK', 'API_KEY', 'SECRET', 'PASSWORD', 'MRN', 'KEY'].includes(t));
297
+ const hasHighRisk = types.some(t => ['ID', 'SSN', 'CREDIT_CARD', 'PASSPORT', 'BANK', 'API_KEY', 'SECRET', 'PASSWORD', 'MRN', 'KEY', 'AWS_KEY', 'STRIPE_KEY', 'JWT_TOKEN', 'API_TOKEN'].includes(t));
293
298
  if (hasHighRisk || types.length >= 3 || totalCount >= 10) {
294
299
  riskLevel = 'CRITICAL (HIGH EXPOSURE)';
295
300
  } else if (types.length >= 2 || totalCount >= 3) {
@@ -399,7 +404,7 @@ function buildPsr2Receipt(tokenMap, timestamp = new Date().toISOString()) {
399
404
  const labels = (tokenMap && typeof tokenMap === 'object' ? Object.keys(tokenMap) : []).slice().sort();
400
405
  const counts = {};
401
406
  for (const t of labels) {
402
- const m = t.match(/\[([A-Z_]+)_\d+\]/);
407
+ const m = String(t).trim().match(/^(?:\[|<|\{\{|__)([A-Z0-9_]+)_\d+(?:\]|>|\}\}|__)$/);
403
408
  const type = m ? m[1] : 'CUSTOM';
404
409
  counts[type] = (counts[type] || 0) + 1;
405
410
  }