@privacyscrubber/mcp-server 1.7.2 → 1.7.6
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/config-schema.json +10 -0
- package/index.js +222 -41
- package/package.json +5 -1
- package/ps-license-manager.js +263 -0
- package/ps-pii-engine.cjs +823 -0
- package/ps-pii-engine.js +823 -0
- package/scrubber-core.cjs +1419 -11
package/index.js
CHANGED
|
@@ -37,7 +37,10 @@ const MCP_VERSION = require('./package.json').version;
|
|
|
37
37
|
// Import the production core engine with 100% parity
|
|
38
38
|
const scrubberCorePath = path.resolve(__dirname, './scrubber-core.cjs');
|
|
39
39
|
const PrivacyScrubberCore = require(scrubberCorePath);
|
|
40
|
-
|
|
40
|
+
let LicenseManager = require('./ps-license-manager.js');
|
|
41
|
+
if (!LicenseManager || typeof LicenseManager.validate !== 'function') {
|
|
42
|
+
LicenseManager = global.LicenseManager;
|
|
43
|
+
}
|
|
41
44
|
|
|
42
45
|
// Initialize core engine
|
|
43
46
|
PrivacyScrubberCore.init();
|
|
@@ -83,15 +86,144 @@ function checkLicenseStatus() {
|
|
|
83
86
|
const result = LicenseManager.validate(key);
|
|
84
87
|
|
|
85
88
|
if (!result.valid) {
|
|
89
|
+
mcpLog(`DEBUG: License invalid because: ${result.reason || "Unknown"}`);
|
|
86
90
|
return { isPro: false, type: null, error: result.reason || "Invalid license format or signature." };
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
return { isPro: true, type: result.tier, error: null };
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
// ──
|
|
93
|
-
|
|
94
|
-
const
|
|
96
|
+
// ── Free Tier Usage Tracking (Persistent) ─────────────────────────────
|
|
97
|
+
const FREE_TIER_DAILY_LIMIT = 10;
|
|
98
|
+
const STAR_PROMPT_THRESHOLD = 5;
|
|
99
|
+
|
|
100
|
+
function getUsageFilePath() {
|
|
101
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
102
|
+
return path.resolve(homeDir, '.privacyscrubber-usage.json');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getDailyUsage() {
|
|
106
|
+
const file = getUsageFilePath();
|
|
107
|
+
const today = new Date().toISOString().split('T')[0];
|
|
108
|
+
if (!fs.existsSync(file)) return 0;
|
|
109
|
+
try {
|
|
110
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
111
|
+
if (data.date === today) {
|
|
112
|
+
return data.count || 0;
|
|
113
|
+
}
|
|
114
|
+
} catch (e) {}
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function incrementDailyUsage() {
|
|
119
|
+
const file = getUsageFilePath();
|
|
120
|
+
const today = new Date().toISOString().split('T')[0];
|
|
121
|
+
let count = 0;
|
|
122
|
+
if (fs.existsSync(file)) {
|
|
123
|
+
try {
|
|
124
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
125
|
+
if (data.date === today) count = data.count || 0;
|
|
126
|
+
} catch (e) {}
|
|
127
|
+
}
|
|
128
|
+
count++;
|
|
129
|
+
try {
|
|
130
|
+
fs.writeFileSync(file, JSON.stringify({ date: today, count }), 'utf8');
|
|
131
|
+
} catch (e) {}
|
|
132
|
+
return count;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function checkFreeTierLimit(isPro) {
|
|
136
|
+
if (isPro) return { blocked: false, count: 0 };
|
|
137
|
+
|
|
138
|
+
const count = incrementDailyUsage();
|
|
139
|
+
// Feedback / Growth loop triggers
|
|
140
|
+
if (count === 3) {
|
|
141
|
+
mcpLog(`${colors.cyan}💬 We are building the ultimate privacy tool for developers. What feature should we add next? Let us know: https://privacyscrubber.com/feedback${colors.reset}\n`);
|
|
142
|
+
}
|
|
143
|
+
if (count === STAR_PROMPT_THRESHOLD) {
|
|
144
|
+
mcpLog(`${colors.yellowBold}🔒 PrivacyScrubber is running securely. If this tool saved your PII today, please drop a star on GitHub: https://github.com/moxno/privacyscrubber-mcp${colors.reset}\n`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (count >= FREE_TIER_DAILY_LIMIT) {
|
|
148
|
+
mcpLog(`${colors.redBold}🚫 [PrivacyScrubber] Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Request BLOCKED.${colors.reset}\n${colors.cyan}👉 Get a PRO key for unlimited use: https://privacyscrubber.com/pricing${colors.reset}\n`);
|
|
149
|
+
return { blocked: true, count };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { blocked: false, count };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildCisoAuditTelemetry(currentTokenMap = {}) {
|
|
156
|
+
const entities = {};
|
|
157
|
+
let totalCount = 0;
|
|
158
|
+
|
|
159
|
+
if (currentTokenMap && typeof currentTokenMap === 'object') {
|
|
160
|
+
const keys = Array.isArray(currentTokenMap) ? currentTokenMap : Object.keys(currentTokenMap);
|
|
161
|
+
for (const t of keys) {
|
|
162
|
+
const tokenStr = typeof t === 'string' ? t : (t.token || t.mask || '');
|
|
163
|
+
const match = tokenStr.match(/\[([A-Z_]+)_\d+\]/);
|
|
164
|
+
const baseType = match ? match[1] : (tokenStr.replace(/\[|\]/g, '').replace(/_[0-9]+$/, '') || 'CUSTOM');
|
|
165
|
+
entities[baseType] = (entities[baseType] || 0) + 1;
|
|
166
|
+
totalCount++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const types = Object.keys(entities);
|
|
171
|
+
let riskLevel = 'LOW EXPOSURE';
|
|
172
|
+
|
|
173
|
+
if (totalCount > 0) {
|
|
174
|
+
const hasHighRiskEntities = types.some(t => ['ID', 'SSN', 'CREDIT_CARD', 'PASSPORT', 'BANK', 'API_KEY', 'SECRET', 'PASSWORD', 'MRN', 'KEY'].includes(t));
|
|
175
|
+
if (hasHighRiskEntities || types.length >= 3 || totalCount >= 10) {
|
|
176
|
+
riskLevel = 'CRITICAL (HIGH EXPOSURE)';
|
|
177
|
+
} else if (types.length >= 2 || totalCount >= 3) {
|
|
178
|
+
riskLevel = 'MODERATE EXPOSURE';
|
|
179
|
+
} else {
|
|
180
|
+
riskLevel = 'LOW EXPOSURE';
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
riskLevel = 'CLEAN (ZERO PII)';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const frameworksSet = new Set(['ZTDS Standard']);
|
|
187
|
+
if (types.includes('NAME') || types.includes('EMAIL') || types.includes('PHONE')) {
|
|
188
|
+
frameworksSet.add('GDPR (Art. 4)');
|
|
189
|
+
frameworksSet.add('CCPA/CPRA');
|
|
190
|
+
}
|
|
191
|
+
if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT')) {
|
|
192
|
+
frameworksSet.add('SOC 2 Type II');
|
|
193
|
+
frameworksSet.add('ISO 27001 (A.8.11)');
|
|
194
|
+
}
|
|
195
|
+
if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD'].includes(t))) {
|
|
196
|
+
frameworksSet.add('PCI DSS v4.0');
|
|
197
|
+
}
|
|
198
|
+
if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT'].includes(t))) {
|
|
199
|
+
frameworksSet.add('HIPAA §164.514');
|
|
200
|
+
}
|
|
201
|
+
if (types.some(t => ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'KEY'].includes(t))) {
|
|
202
|
+
frameworksSet.add('NIST SP 800-53');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
totalCount,
|
|
207
|
+
entities,
|
|
208
|
+
types,
|
|
209
|
+
riskLevel,
|
|
210
|
+
frameworksList: Array.from(frameworksSet)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function formatAuditReceipt(telemetry) {
|
|
215
|
+
if (telemetry.totalCount === 0) {
|
|
216
|
+
return "\\n\\n> 🛡️ **PrivacyScrubber Audit Receipt**: CLEAN (ZERO PII DETECTED)\\n";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const entitiesList = Object.entries(telemetry.entities)
|
|
220
|
+
.map(([type, count]) => `${count} ${type}`)
|
|
221
|
+
.join(', ');
|
|
222
|
+
|
|
223
|
+
const icon = telemetry.riskLevel.includes('CRITICAL') ? '🔴' : (telemetry.riskLevel.includes('MODERATE') ? '🟠' : '🟢');
|
|
224
|
+
|
|
225
|
+
return `\\n\\n> 🛡️ **PrivacyScrubber Audit Receipt**\\n> * **Risk Level:** ${icon} ${telemetry.riskLevel}\\n> * **Compliance Enforced:** ${telemetry.frameworksList.join(', ')}\\n> * **Tokens Masked:** ${telemetry.totalCount} (${entitiesList})\\n`;
|
|
226
|
+
}
|
|
95
227
|
|
|
96
228
|
// Build a clean upsell content block (Disabled to prevent agent-visible marketing content)
|
|
97
229
|
function buildUpsellBlock(reason) {
|
|
@@ -385,7 +517,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
385
517
|
let finalProfile = targetProfile;
|
|
386
518
|
const extraBlocks = [];
|
|
387
519
|
|
|
388
|
-
|
|
520
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
521
|
+
if (limitStatus.blocked) {
|
|
522
|
+
return {
|
|
523
|
+
isError: true,
|
|
524
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
525
|
+
};
|
|
526
|
+
}
|
|
389
527
|
|
|
390
528
|
const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
|
|
391
529
|
if (isAdvanced && !license.isPro) {
|
|
@@ -409,16 +547,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
409
547
|
}
|
|
410
548
|
}
|
|
411
549
|
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
extraBlocks.push(buildUpsellBlock(null));
|
|
417
|
-
}
|
|
550
|
+
const { scrubbedText, newTokens } = performSanitization(processedText, finalProfile);
|
|
551
|
+
|
|
552
|
+
const telemetry = buildCisoAuditTelemetry(newTokens);
|
|
553
|
+
const receiptMd = formatAuditReceipt(telemetry);
|
|
418
554
|
|
|
419
555
|
return {
|
|
420
556
|
content: [
|
|
421
|
-
{ type: "text", text:
|
|
557
|
+
{ type: "text", text: scrubbedText + receiptMd },
|
|
422
558
|
...extraBlocks.filter(Boolean)
|
|
423
559
|
]
|
|
424
560
|
};
|
|
@@ -532,7 +668,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
532
668
|
let finalProfile = targetProfile;
|
|
533
669
|
const extraBlocks = [];
|
|
534
670
|
|
|
535
|
-
|
|
671
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
672
|
+
if (limitStatus.blocked) {
|
|
673
|
+
return {
|
|
674
|
+
isError: true,
|
|
675
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
536
678
|
|
|
537
679
|
const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
|
|
538
680
|
if (isAdvanced && !license.isPro) {
|
|
@@ -555,14 +697,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
555
697
|
}
|
|
556
698
|
|
|
557
699
|
const sanitized = performSanitization(processedContent, finalProfile);
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
700
|
+
const { scrubbedText, newTokens } = sanitized;
|
|
701
|
+
const telemetry = buildCisoAuditTelemetry(newTokens);
|
|
702
|
+
const receiptMarkdown = formatAuditReceipt(telemetry);
|
|
703
|
+
const combinedOutput = `${scrubbedText}\n\n${receiptMarkdown}`;
|
|
562
704
|
|
|
563
705
|
return {
|
|
564
706
|
content: [
|
|
565
|
-
{ type: "text", text:
|
|
707
|
+
{ type: "text", text: combinedOutput },
|
|
566
708
|
...extraBlocks.filter(Boolean)
|
|
567
709
|
]
|
|
568
710
|
};
|
|
@@ -604,7 +746,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
604
746
|
let finalProfile = targetProfile;
|
|
605
747
|
const extraBlocks = [];
|
|
606
748
|
|
|
607
|
-
|
|
749
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
750
|
+
if (limitStatus.blocked) {
|
|
751
|
+
return {
|
|
752
|
+
isError: true,
|
|
753
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
754
|
+
};
|
|
755
|
+
}
|
|
608
756
|
|
|
609
757
|
const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
|
|
610
758
|
if (isAdvanced && !license.isPro) {
|
|
@@ -627,14 +775,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
627
775
|
}
|
|
628
776
|
|
|
629
777
|
const sanitized = performSanitization(processedContent, finalProfile);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
778
|
+
const { scrubbedText, newTokens } = sanitized;
|
|
779
|
+
const telemetry = buildCisoAuditTelemetry(newTokens);
|
|
780
|
+
const receiptMarkdown = formatAuditReceipt(telemetry);
|
|
781
|
+
const combinedOutput = `${scrubbedText}\n\n${receiptMarkdown}`;
|
|
634
782
|
|
|
635
783
|
return {
|
|
636
784
|
content: [
|
|
637
|
-
{ type: "text", text:
|
|
785
|
+
{ type: "text", text: combinedOutput },
|
|
638
786
|
...extraBlocks.filter(Boolean)
|
|
639
787
|
]
|
|
640
788
|
};
|
|
@@ -663,7 +811,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
663
811
|
let finalProfile = targetProfile;
|
|
664
812
|
const extraBlocks = [];
|
|
665
813
|
|
|
666
|
-
|
|
814
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
815
|
+
if (limitStatus.blocked) {
|
|
816
|
+
return {
|
|
817
|
+
isError: true,
|
|
818
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
819
|
+
};
|
|
820
|
+
}
|
|
667
821
|
|
|
668
822
|
const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
|
|
669
823
|
if (isAdvanced && !license.isPro) {
|
|
@@ -686,14 +840,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
686
840
|
}
|
|
687
841
|
|
|
688
842
|
const sanitized = performSanitization(processedContent, finalProfile);
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}
|
|
843
|
+
const { scrubbedText, newTokens } = sanitized;
|
|
844
|
+
const telemetry = buildCisoAuditTelemetry(newTokens);
|
|
845
|
+
const receiptMarkdown = formatAuditReceipt(telemetry);
|
|
846
|
+
const combinedOutput = `${scrubbedText}\n\n${receiptMarkdown}`;
|
|
693
847
|
|
|
694
848
|
return {
|
|
695
849
|
content: [
|
|
696
|
-
{ type: "text", text:
|
|
850
|
+
{ type: "text", text: combinedOutput },
|
|
697
851
|
...extraBlocks.filter(Boolean)
|
|
698
852
|
]
|
|
699
853
|
};
|
|
@@ -736,7 +890,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
736
890
|
let finalProfile = targetProfile;
|
|
737
891
|
const extraBlocks = [];
|
|
738
892
|
|
|
739
|
-
|
|
893
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
894
|
+
if (limitStatus.blocked) {
|
|
895
|
+
return {
|
|
896
|
+
isError: true,
|
|
897
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
898
|
+
};
|
|
899
|
+
}
|
|
740
900
|
|
|
741
901
|
const charLimit = (isAdvanced && !license.isPro) ? 5000 : 15000;
|
|
742
902
|
if (isAdvanced && !license.isPro) {
|
|
@@ -758,15 +918,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
758
918
|
}
|
|
759
919
|
}
|
|
760
920
|
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
extraBlocks.push(buildUpsellBlock(null));
|
|
765
|
-
}
|
|
921
|
+
const { scrubbedText, newTokens } = performSanitization(processedContent2, finalProfile);
|
|
922
|
+
const telemetry = buildCisoAuditTelemetry(newTokens);
|
|
923
|
+
const receiptMd = formatAuditReceipt(telemetry);
|
|
766
924
|
|
|
767
925
|
return {
|
|
768
926
|
content: [
|
|
769
|
-
{ type: "text", text:
|
|
927
|
+
{ type: "text", text: scrubbedText + receiptMd },
|
|
770
928
|
...extraBlocks.filter(Boolean)
|
|
771
929
|
]
|
|
772
930
|
};
|
|
@@ -793,6 +951,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
793
951
|
const customRules = loadCustomRules();
|
|
794
952
|
const normalizedProfile = (profile || "general").trim().toLowerCase();
|
|
795
953
|
const license = checkLicenseStatus();
|
|
954
|
+
|
|
955
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
956
|
+
if (limitStatus.blocked) {
|
|
957
|
+
return {
|
|
958
|
+
isError: true,
|
|
959
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
960
|
+
};
|
|
961
|
+
}
|
|
796
962
|
const localSessionMap = {};
|
|
797
963
|
|
|
798
964
|
const result = PrivacyScrubberCore.scrubText(content, customRules, {}, normalizedProfile, localSessionMap, license.isPro);
|
|
@@ -943,7 +1109,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
943
1109
|
`║ PrivacyScrubber MCP Server v${MCP_VERSION.padEnd(10)} ║`,
|
|
944
1110
|
'╠══════════════════════════════════════════════════╣',
|
|
945
1111
|
`║ ${tierIcon} Tier: ${tier.padEnd(43)}║`,
|
|
946
|
-
`║ 📊 Session requests: ${String(
|
|
1112
|
+
`║ 📊 Session requests: ${String(getDailyUsage()).padEnd(27)}║`,
|
|
947
1113
|
`║ 📁 Input size limit: ${sizeLimit.padEnd(27)}║`,
|
|
948
1114
|
'╠══════════════════════════════════════════════════╣',
|
|
949
1115
|
`║ 🏷️ Profiles: ${profileList.substring(0,35).padEnd(35)}║`,
|
|
@@ -1013,20 +1179,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1013
1179
|
const customRules = loadCustomRules();
|
|
1014
1180
|
const normalizedProfile = (profile || "general").trim().toLowerCase();
|
|
1015
1181
|
const license = checkLicenseStatus();
|
|
1182
|
+
|
|
1183
|
+
const limitStatus = checkFreeTierLimit(license.isPro);
|
|
1184
|
+
if (limitStatus.blocked) {
|
|
1185
|
+
return {
|
|
1186
|
+
isError: true,
|
|
1187
|
+
content: [{ type: "text", text: `Error: Free tier daily limit exhausted (${FREE_TIER_DAILY_LIMIT} requests). Please set your PRO license key to continue. Get a key at: https://privacyscrubber.com/pricing` }]
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1016
1190
|
|
|
1191
|
+
let totalTokensForDirectory = {};
|
|
1017
1192
|
for (const file of filesToScan) {
|
|
1018
1193
|
try {
|
|
1019
1194
|
const stat = await fs.promises.stat(file);
|
|
1020
1195
|
if (stat.size > 10 * 1024 * 1024) continue; // skip >10MB
|
|
1021
1196
|
|
|
1022
1197
|
const buffer = fs.readFileSync(file);
|
|
1023
|
-
// basic binary check
|
|
1024
1198
|
if (buffer.indexOf(0) !== -1) continue;
|
|
1025
1199
|
const content = buffer.toString('utf8');
|
|
1026
1200
|
|
|
1027
1201
|
const localSessionMap = {};
|
|
1028
1202
|
const result = PrivacyScrubberCore.scrubText(content, customRules, {}, normalizedProfile, localSessionMap, license.isPro);
|
|
1029
1203
|
|
|
1204
|
+
Object.assign(totalTokensForDirectory, localSessionMap);
|
|
1205
|
+
|
|
1030
1206
|
let isSecretLeak = false;
|
|
1031
1207
|
if (!license.isPro && result.tokenMap) {
|
|
1032
1208
|
Object.entries(result.tokenMap).forEach(([token, original]) => {
|
|
@@ -1062,8 +1238,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1062
1238
|
reportLines.push("✅ Clean. No PII or secrets detected in the scanned files.");
|
|
1063
1239
|
}
|
|
1064
1240
|
|
|
1241
|
+
const telemetry = buildCisoAuditTelemetry(totalTokensForDirectory);
|
|
1242
|
+
const receiptMd = formatAuditReceipt(telemetry);
|
|
1243
|
+
|
|
1065
1244
|
return {
|
|
1066
|
-
content: [{ type: "text", text: reportLines.join('\n') }]
|
|
1245
|
+
content: [{ type: "text", text: reportLines.join('\n') + receiptMd }]
|
|
1067
1246
|
};
|
|
1068
1247
|
}
|
|
1069
1248
|
|
|
@@ -1166,14 +1345,16 @@ function performSanitization(text, profile) {
|
|
|
1166
1345
|
const license = checkLicenseStatus();
|
|
1167
1346
|
const result = PrivacyScrubberCore.scrubText(text, customRules, {}, normalizedProfile, sessionMap, license.isPro);
|
|
1168
1347
|
|
|
1348
|
+
const newTokens = {};
|
|
1169
1349
|
// Update our volatile map with new matches
|
|
1170
1350
|
if (result.tokenMap) {
|
|
1171
1351
|
Object.entries(result.tokenMap).forEach(([token, original]) => {
|
|
1172
1352
|
sessionMap[token] = original;
|
|
1353
|
+
newTokens[token] = original;
|
|
1173
1354
|
});
|
|
1174
1355
|
}
|
|
1175
1356
|
|
|
1176
|
-
return result.scrubbedText;
|
|
1357
|
+
return { scrubbedText: result.scrubbedText, newTokens };
|
|
1177
1358
|
}
|
|
1178
1359
|
|
|
1179
1360
|
function truncateIfFree(text, isPro, charLimit = 15000) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@privacyscrubber/mcp-server",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.6",
|
|
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",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"install.js",
|
|
14
14
|
"pii-masking-run.js",
|
|
15
15
|
"scrubber-core.cjs",
|
|
16
|
+
"ps-license-manager.js",
|
|
17
|
+
"ps-pii-engine.cjs",
|
|
18
|
+
"ps-pii-engine.js",
|
|
19
|
+
"config-schema.json",
|
|
16
20
|
"smithery.yaml",
|
|
17
21
|
"server.json",
|
|
18
22
|
"manifest.json",
|