sovr-mcp-proxy 6.0.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5208,6 +5208,973 @@ var init_engine = __esm({
5208
5208
  }
5209
5209
  });
5210
5210
 
5211
+ // src/toolReplacement.ts
5212
+ var toolReplacement_exports = {};
5213
+ __export(toolReplacement_exports, {
5214
+ DEFAULT_TARGET_TOOLS: () => DEFAULT_TARGET_TOOLS,
5215
+ DEFAULT_TOOL_REPLACEMENT_CONFIG: () => DEFAULT_TOOL_REPLACEMENT_CONFIG,
5216
+ describeMode: () => describeMode,
5217
+ parseMode: () => parseMode,
5218
+ shouldIntercept: () => shouldIntercept,
5219
+ transformToolList: () => transformToolList
5220
+ });
5221
+ function transformToolList(upstreamTools, config) {
5222
+ const removedTools = [];
5223
+ const addedTools = [];
5224
+ const wrappedTools = [];
5225
+ let resultTools = [];
5226
+ switch (config.mode) {
5227
+ case "exclusive": {
5228
+ for (const tool of upstreamTools) {
5229
+ if (isTargetTool(tool.name, config.targetTools)) {
5230
+ removedTools.push(tool.name);
5231
+ } else {
5232
+ resultTools.push(tool);
5233
+ }
5234
+ }
5235
+ resultTools.push(SOVR_EXEC_TOOL);
5236
+ addedTools.push(SOVR_EXEC_TOOL.name);
5237
+ break;
5238
+ }
5239
+ case "enforce": {
5240
+ for (const tool of upstreamTools) {
5241
+ if (isTargetTool(tool.name, config.targetTools)) {
5242
+ resultTools.push({
5243
+ ...tool,
5244
+ description: `[SOVR-ENFORCED] ${tool.description || ""} \u2014 All calls are policy-checked by SOVR before execution.`
5245
+ });
5246
+ wrappedTools.push(tool.name);
5247
+ } else {
5248
+ resultTools.push(tool);
5249
+ }
5250
+ }
5251
+ resultTools.push(SOVR_EXEC_TOOL);
5252
+ addedTools.push(SOVR_EXEC_TOOL.name);
5253
+ break;
5254
+ }
5255
+ case "advisory": {
5256
+ resultTools = [...upstreamTools];
5257
+ resultTools.push(SOVR_EXEC_TOOL);
5258
+ addedTools.push(SOVR_EXEC_TOOL.name);
5259
+ break;
5260
+ }
5261
+ case "monitor":
5262
+ default: {
5263
+ resultTools = [...upstreamTools];
5264
+ break;
5265
+ }
5266
+ }
5267
+ if (config.addStatusTool) {
5268
+ resultTools.push(SOVR_STATUS_TOOL);
5269
+ addedTools.push(SOVR_STATUS_TOOL.name);
5270
+ }
5271
+ if (config.addAuditTool) {
5272
+ resultTools.push(SOVR_AUDIT_TOOL);
5273
+ addedTools.push(SOVR_AUDIT_TOOL.name);
5274
+ }
5275
+ return { tools: resultTools, removedTools, addedTools, wrappedTools };
5276
+ }
5277
+ function isTargetTool(toolName, targets) {
5278
+ const lowerName = toolName.toLowerCase();
5279
+ return targets.some((target) => {
5280
+ const lowerTarget = target.toLowerCase();
5281
+ if (lowerName === lowerTarget) return true;
5282
+ if (lowerTarget.includes("*")) {
5283
+ const regex = new RegExp(
5284
+ "^" + lowerTarget.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
5285
+ );
5286
+ return regex.test(lowerName);
5287
+ }
5288
+ return false;
5289
+ });
5290
+ }
5291
+ function shouldIntercept(toolName, config) {
5292
+ switch (config.mode) {
5293
+ case "exclusive":
5294
+ if (toolName === "sovr_exec") {
5295
+ return { intercept: true, reason: "exclusive-mode: all exec calls routed through SOVR" };
5296
+ }
5297
+ if (isTargetTool(toolName, config.targetTools)) {
5298
+ return { intercept: true, reason: "exclusive-mode: native tool blocked, use sovr_exec" };
5299
+ }
5300
+ return { intercept: false, reason: "non-exec tool, passthrough" };
5301
+ case "enforce":
5302
+ if (toolName === "sovr_exec" || isTargetTool(toolName, config.targetTools)) {
5303
+ return { intercept: true, reason: "enforce-mode: policy check required" };
5304
+ }
5305
+ return { intercept: false, reason: "non-exec tool, passthrough" };
5306
+ case "advisory":
5307
+ if (toolName === "sovr_exec") {
5308
+ return { intercept: true, reason: "advisory-mode: SOVR exec call" };
5309
+ }
5310
+ if (isTargetTool(toolName, config.targetTools)) {
5311
+ return { intercept: false, reason: "advisory-mode: native tool allowed with warning" };
5312
+ }
5313
+ return { intercept: false, reason: "non-exec tool, passthrough" };
5314
+ case "monitor":
5315
+ default:
5316
+ return { intercept: false, reason: "monitor-mode: all tools passthrough" };
5317
+ }
5318
+ }
5319
+ function describeMode(config) {
5320
+ switch (config.mode) {
5321
+ case "exclusive":
5322
+ return `EXCLUSIVE MODE: Native execution tools (${config.targetTools.join(", ")}) are REMOVED. The LLM can ONLY execute commands through sovr_exec. Bypass is impossible.`;
5323
+ case "enforce":
5324
+ return `ENFORCE MODE: Native execution tools are wrapped with SOVR policy checks. All command executions are evaluated before running. Dangerous commands are blocked.`;
5325
+ case "advisory":
5326
+ return `ADVISORY MODE: Native tools remain available. SOVR tools are added alongside. Warnings are logged for risky commands but execution is not blocked.`;
5327
+ case "monitor":
5328
+ return `MONITOR MODE: All tools pass through unchanged. SOVR only logs activity. No enforcement or blocking.`;
5329
+ }
5330
+ }
5331
+ function parseMode(input) {
5332
+ const normalized = input.toLowerCase().trim();
5333
+ if (["exclusive", "enforce", "advisory", "monitor"].includes(normalized)) {
5334
+ return normalized;
5335
+ }
5336
+ throw new Error(
5337
+ `Invalid mode: "${input}". Valid modes: exclusive, enforce, advisory, monitor`
5338
+ );
5339
+ }
5340
+ var DEFAULT_TARGET_TOOLS, SOVR_EXEC_TOOL, SOVR_STATUS_TOOL, SOVR_AUDIT_TOOL, DEFAULT_TOOL_REPLACEMENT_CONFIG;
5341
+ var init_toolReplacement = __esm({
5342
+ "src/toolReplacement.ts"() {
5343
+ "use strict";
5344
+ DEFAULT_TARGET_TOOLS = [
5345
+ // Claude Code native tools
5346
+ "Bash",
5347
+ "bash",
5348
+ "shell",
5349
+ "execute_command",
5350
+ "run_command",
5351
+ "terminal",
5352
+ // Cursor / Windsurf / Continue
5353
+ "run_terminal_command",
5354
+ "execute_shell",
5355
+ "shell_exec",
5356
+ // Generic patterns
5357
+ "exec",
5358
+ "system",
5359
+ "subprocess"
5360
+ ];
5361
+ SOVR_EXEC_TOOL = {
5362
+ name: "sovr_exec",
5363
+ description: "Execute a shell command through the SOVR Responsibility Layer. All commands are evaluated against security policies before execution. Dangerous commands (rm -rf, DROP TABLE, etc.) will be blocked. This is the ONLY way to execute commands \u2014 use this for ALL shell operations.",
5364
+ inputSchema: {
5365
+ type: "object",
5366
+ properties: {
5367
+ command: {
5368
+ type: "string",
5369
+ description: "The shell command to execute. Will be policy-checked before running."
5370
+ },
5371
+ workdir: {
5372
+ type: "string",
5373
+ description: "Working directory for the command (optional)."
5374
+ },
5375
+ timeout: {
5376
+ type: "number",
5377
+ description: "Timeout in milliseconds (default: 300000 = 5 min)."
5378
+ }
5379
+ },
5380
+ required: ["command"]
5381
+ }
5382
+ };
5383
+ SOVR_STATUS_TOOL = {
5384
+ name: "sovr_status",
5385
+ description: "Check the current SOVR security status, including active policies, recent decisions, and system health. Use this to understand what commands are allowed or blocked.",
5386
+ inputSchema: {
5387
+ type: "object",
5388
+ properties: {
5389
+ verbose: {
5390
+ type: "boolean",
5391
+ description: "Include detailed policy information (default: false)."
5392
+ }
5393
+ }
5394
+ }
5395
+ };
5396
+ SOVR_AUDIT_TOOL = {
5397
+ name: "sovr_audit",
5398
+ description: "View the SOVR audit log of recent command evaluations. Shows what was allowed, blocked, or escalated.",
5399
+ inputSchema: {
5400
+ type: "object",
5401
+ properties: {
5402
+ limit: {
5403
+ type: "number",
5404
+ description: "Number of recent entries to show (default: 10)."
5405
+ },
5406
+ filter: {
5407
+ type: "string",
5408
+ enum: ["all", "allowed", "blocked", "escalated"],
5409
+ description: "Filter by decision type (default: all)."
5410
+ }
5411
+ }
5412
+ }
5413
+ };
5414
+ DEFAULT_TOOL_REPLACEMENT_CONFIG = {
5415
+ mode: "enforce",
5416
+ targetTools: DEFAULT_TARGET_TOOLS,
5417
+ addStatusTool: true,
5418
+ addAuditTool: true
5419
+ };
5420
+ }
5421
+ });
5422
+
5423
+ // src/commandNormalizer.ts
5424
+ var commandNormalizer_exports = {};
5425
+ __export(commandNormalizer_exports, {
5426
+ getBaseCommand: () => getBaseCommand,
5427
+ hasDestructiveEquivalent: () => hasDestructiveEquivalent,
5428
+ hasEncodingTricks: () => hasEncodingTricks,
5429
+ normalize: () => normalize,
5430
+ summarize: () => summarize
5431
+ });
5432
+ function normalize(command) {
5433
+ const original = command;
5434
+ const segments = [];
5435
+ const suspicion_reasons = [];
5436
+ const trimmed = command.trim();
5437
+ if (!trimmed) {
5438
+ return { segments: [], suspicious: false, suspicion_reasons: [], original };
5439
+ }
5440
+ for (const enc of ENCODING_PATTERNS) {
5441
+ if (enc.pattern.test(trimmed)) {
5442
+ suspicion_reasons.push(`${enc.name}: ${enc.risk}`);
5443
+ }
5444
+ }
5445
+ for (const deq of DESTRUCTIVE_EQUIVALENTS) {
5446
+ if (deq.pattern.test(trimmed)) {
5447
+ suspicion_reasons.push(`Destructive equivalent detected: ${deq.description}`);
5448
+ }
5449
+ }
5450
+ const chainSegments = splitChains(trimmed);
5451
+ for (const chainSeg of chainSegments) {
5452
+ const pipeSegments = splitPipes(chainSeg);
5453
+ for (const pipeSeg of pipeSegments) {
5454
+ const unwrapped = unwrapCommand(pipeSeg.trim());
5455
+ if (unwrapped.unwrapped) {
5456
+ segments.push({
5457
+ raw: pipeSeg.trim(),
5458
+ effective: unwrapped.effective,
5459
+ type: "unwrapped",
5460
+ warnings: unwrapped.warnings
5461
+ });
5462
+ const inner = normalize(unwrapped.effective);
5463
+ for (const innerSeg of inner.segments) {
5464
+ if (innerSeg.effective !== unwrapped.effective) {
5465
+ segments.push(innerSeg);
5466
+ }
5467
+ }
5468
+ suspicion_reasons.push(...inner.suspicion_reasons);
5469
+ } else {
5470
+ const type = pipeSegments.length > 1 ? "pipe-segment" : chainSegments.length > 1 ? "chain-segment" : "direct";
5471
+ segments.push({
5472
+ raw: pipeSeg.trim(),
5473
+ effective: pipeSeg.trim(),
5474
+ type,
5475
+ warnings: []
5476
+ });
5477
+ }
5478
+ }
5479
+ }
5480
+ const subshells = extractSubshells(trimmed);
5481
+ for (const sub of subshells) {
5482
+ segments.push({
5483
+ raw: trimmed,
5484
+ effective: sub,
5485
+ type: "subshell",
5486
+ warnings: ["Subshell command extracted for evaluation"]
5487
+ });
5488
+ }
5489
+ return {
5490
+ segments,
5491
+ suspicious: suspicion_reasons.length > 0,
5492
+ suspicion_reasons,
5493
+ original
5494
+ };
5495
+ }
5496
+ function splitChains(command) {
5497
+ return splitRespectingQuotes(command, /\s*(?:&&|\|\||;)\s*/);
5498
+ }
5499
+ function splitPipes(command) {
5500
+ return splitRespectingQuotes(command, /\s*\|(?!\|)\s*/);
5501
+ }
5502
+ function splitRespectingQuotes(input, delimiter) {
5503
+ const segments = [];
5504
+ let current = "";
5505
+ let inSingleQuote = false;
5506
+ let inDoubleQuote = false;
5507
+ let escaped = false;
5508
+ let i = 0;
5509
+ while (i < input.length) {
5510
+ const char = input[i];
5511
+ if (escaped) {
5512
+ current += char;
5513
+ escaped = false;
5514
+ i++;
5515
+ continue;
5516
+ }
5517
+ if (char === "\\") {
5518
+ escaped = true;
5519
+ current += char;
5520
+ i++;
5521
+ continue;
5522
+ }
5523
+ if (char === "'" && !inDoubleQuote) {
5524
+ inSingleQuote = !inSingleQuote;
5525
+ current += char;
5526
+ i++;
5527
+ continue;
5528
+ }
5529
+ if (char === '"' && !inSingleQuote) {
5530
+ inDoubleQuote = !inDoubleQuote;
5531
+ current += char;
5532
+ i++;
5533
+ continue;
5534
+ }
5535
+ if (!inSingleQuote && !inDoubleQuote) {
5536
+ const remaining = input.slice(i);
5537
+ const match = remaining.match(delimiter);
5538
+ if (match && match.index === 0) {
5539
+ if (current.trim()) {
5540
+ segments.push(current.trim());
5541
+ }
5542
+ current = "";
5543
+ i += match[0].length;
5544
+ continue;
5545
+ }
5546
+ }
5547
+ current += char;
5548
+ i++;
5549
+ }
5550
+ if (current.trim()) {
5551
+ segments.push(current.trim());
5552
+ }
5553
+ return segments.length > 0 ? segments : [input];
5554
+ }
5555
+ function unwrapCommand(command) {
5556
+ for (const wrapper of SHELL_WRAPPERS) {
5557
+ const match = command.match(wrapper.pattern);
5558
+ if (match) {
5559
+ const inner = wrapper.extract(match);
5560
+ if (inner) {
5561
+ return {
5562
+ unwrapped: true,
5563
+ effective: inner.trim(),
5564
+ wrapper: wrapper.name,
5565
+ warnings: [`Unwrapped from ${wrapper.name}: "${command}" \u2192 "${inner.trim()}"`]
5566
+ };
5567
+ }
5568
+ }
5569
+ }
5570
+ return { unwrapped: false, effective: command, warnings: [] };
5571
+ }
5572
+ function extractSubshells(command) {
5573
+ const subshells = [];
5574
+ const dollarParen = /\$\(([^)]+)\)/g;
5575
+ let match;
5576
+ while ((match = dollarParen.exec(command)) !== null) {
5577
+ if (match[1]) subshells.push(match[1].trim());
5578
+ }
5579
+ const backtick = /`([^`]+)`/g;
5580
+ while ((match = backtick.exec(command)) !== null) {
5581
+ if (match[1]) subshells.push(match[1].trim());
5582
+ }
5583
+ return subshells;
5584
+ }
5585
+ function getBaseCommand(command) {
5586
+ const trimmed = command.trim();
5587
+ const firstSpace = trimmed.indexOf(" ");
5588
+ return firstSpace === -1 ? trimmed : trimmed.slice(0, firstSpace);
5589
+ }
5590
+ function hasDestructiveEquivalent(command) {
5591
+ const matches = [];
5592
+ for (const deq of DESTRUCTIVE_EQUIVALENTS) {
5593
+ if (deq.pattern.test(command)) {
5594
+ matches.push({ equivalent: deq.equivalent, description: deq.description });
5595
+ }
5596
+ }
5597
+ return { found: matches.length > 0, matches };
5598
+ }
5599
+ function hasEncodingTricks(command) {
5600
+ const tricks = [];
5601
+ for (const enc of ENCODING_PATTERNS) {
5602
+ if (enc.pattern.test(command)) {
5603
+ tricks.push({ name: enc.name, risk: enc.risk });
5604
+ }
5605
+ }
5606
+ return { found: tricks.length > 0, tricks };
5607
+ }
5608
+ function summarize(result) {
5609
+ const lines = [];
5610
+ lines.push(`Original: ${result.original}`);
5611
+ lines.push(`Segments: ${result.segments.length}`);
5612
+ lines.push(`Suspicious: ${result.suspicious}`);
5613
+ if (result.suspicion_reasons.length > 0) {
5614
+ lines.push(`Suspicion reasons:`);
5615
+ for (const reason of result.suspicion_reasons) {
5616
+ lines.push(` - ${reason}`);
5617
+ }
5618
+ }
5619
+ lines.push(`Segments:`);
5620
+ for (const seg of result.segments) {
5621
+ lines.push(` [${seg.type}] ${seg.effective}`);
5622
+ for (const w of seg.warnings) {
5623
+ lines.push(` \u26A0 ${w}`);
5624
+ }
5625
+ }
5626
+ return lines.join("\n");
5627
+ }
5628
+ var SHELL_WRAPPERS, ENCODING_PATTERNS, DESTRUCTIVE_EQUIVALENTS;
5629
+ var init_commandNormalizer = __esm({
5630
+ "src/commandNormalizer.ts"() {
5631
+ "use strict";
5632
+ SHELL_WRAPPERS = [
5633
+ // bash -c "cmd" / sh -c "cmd"
5634
+ {
5635
+ pattern: /^(?:bash|sh|zsh|dash|ksh)\s+-c\s+(?:"([^"]+)"|'([^']+)'|(\S+))/,
5636
+ extract: (m) => m[1] || m[2] || m[3] || "",
5637
+ name: "shell -c"
5638
+ },
5639
+ // env cmd args...
5640
+ {
5641
+ pattern: /^env\s+(?:-\S+\s+)*(?:\S+=\S+\s+)*(.+)/,
5642
+ extract: (m) => m[1] || "",
5643
+ name: "env"
5644
+ },
5645
+ // xargs cmd
5646
+ {
5647
+ pattern: /^xargs\s+(?:-\S+\s+)*(.+)/,
5648
+ extract: (m) => m[1] || "",
5649
+ name: "xargs"
5650
+ },
5651
+ // sudo cmd
5652
+ {
5653
+ pattern: /^sudo\s+(?:-\S+\s+)*(.+)/,
5654
+ extract: (m) => m[1] || "",
5655
+ name: "sudo"
5656
+ },
5657
+ // nohup cmd
5658
+ {
5659
+ pattern: /^nohup\s+(.+?)(?:\s*&\s*)?$/,
5660
+ extract: (m) => m[1] || "",
5661
+ name: "nohup"
5662
+ },
5663
+ // timeout N cmd
5664
+ {
5665
+ pattern: /^timeout\s+\d+[smhd]?\s+(.+)/,
5666
+ extract: (m) => m[1] || "",
5667
+ name: "timeout"
5668
+ },
5669
+ // nice -n N cmd
5670
+ {
5671
+ pattern: /^nice\s+(?:-n\s+\d+\s+)?(.+)/,
5672
+ extract: (m) => m[1] || "",
5673
+ name: "nice"
5674
+ },
5675
+ // strace / ltrace cmd
5676
+ {
5677
+ pattern: /^(?:strace|ltrace)\s+(?:-\S+\s+)*(.+)/,
5678
+ extract: (m) => m[1] || "",
5679
+ name: "trace"
5680
+ },
5681
+ // watch -n N cmd
5682
+ {
5683
+ pattern: /^watch\s+(?:-n\s+\d+\s+)?(.+)/,
5684
+ extract: (m) => m[1] || "",
5685
+ name: "watch"
5686
+ }
5687
+ ];
5688
+ ENCODING_PATTERNS = [
5689
+ // echo "base64" | base64 -d | bash
5690
+ {
5691
+ pattern: /base64\s+(?:-d|--decode)/,
5692
+ name: "base64-decode",
5693
+ risk: "Command may be hidden via base64 encoding"
5694
+ },
5695
+ // printf '\x72\x6d' (hex encoding)
5696
+ {
5697
+ pattern: /printf\s+.*\\x[0-9a-fA-F]{2}/,
5698
+ name: "hex-printf",
5699
+ risk: "Command may be hidden via hex encoding in printf"
5700
+ },
5701
+ // $'\x72\x6d' (ANSI-C quoting)
5702
+ {
5703
+ pattern: /\$'[^']*\\x[0-9a-fA-F]{2}/,
5704
+ name: "ansi-c-quoting",
5705
+ risk: "Command may be hidden via ANSI-C quoting"
5706
+ },
5707
+ // python -c "import os; os.system('rm -rf /')"
5708
+ {
5709
+ pattern: /python[23]?\s+-c\s+/,
5710
+ name: "python-exec",
5711
+ risk: "Arbitrary code execution via Python"
5712
+ },
5713
+ // perl -e "system('rm -rf /')"
5714
+ {
5715
+ pattern: /perl\s+-e\s+/,
5716
+ name: "perl-exec",
5717
+ risk: "Arbitrary code execution via Perl"
5718
+ },
5719
+ // ruby -e "system('rm -rf /')"
5720
+ {
5721
+ pattern: /ruby\s+-e\s+/,
5722
+ name: "ruby-exec",
5723
+ risk: "Arbitrary code execution via Ruby"
5724
+ },
5725
+ // eval "cmd"
5726
+ {
5727
+ pattern: /\beval\s+/,
5728
+ name: "eval",
5729
+ risk: "Dynamic command evaluation"
5730
+ },
5731
+ // curl ... | bash
5732
+ {
5733
+ pattern: /curl\s+.*\|\s*(?:bash|sh|zsh)/,
5734
+ name: "curl-pipe-shell",
5735
+ risk: "Remote code execution via curl | bash"
5736
+ },
5737
+ // wget ... -O - | bash
5738
+ {
5739
+ pattern: /wget\s+.*\|\s*(?:bash|sh|zsh)/,
5740
+ name: "wget-pipe-shell",
5741
+ risk: "Remote code execution via wget | bash"
5742
+ }
5743
+ ];
5744
+ DESTRUCTIVE_EQUIVALENTS = [
5745
+ // find / -delete (equivalent to rm -rf)
5746
+ { pattern: /find\s+.*-delete/, equivalent: "rm -rf", description: "find -delete is equivalent to rm -rf" },
5747
+ // find / -exec rm {} (equivalent to rm -rf)
5748
+ { pattern: /find\s+.*-exec\s+rm/, equivalent: "rm -rf", description: "find -exec rm is equivalent to rm -rf" },
5749
+ // TRUNCATE TABLE (equivalent to DELETE FROM)
5750
+ { pattern: /TRUNCATE\s+TABLE/i, equivalent: "DELETE FROM", description: "TRUNCATE TABLE is equivalent to DELETE FROM" },
5751
+ // dd if=/dev/zero of=/ (disk wipe)
5752
+ { pattern: /dd\s+.*of=\//, equivalent: "disk-wipe", description: "dd writing to root is destructive" },
5753
+ // mkfs (format disk)
5754
+ { pattern: /mkfs/, equivalent: "disk-format", description: "mkfs formats a disk" },
5755
+ // shred (secure delete)
5756
+ { pattern: /shred\s+/, equivalent: "secure-delete", description: "shred securely deletes files" },
5757
+ // chmod 777 / (open permissions)
5758
+ { pattern: /chmod\s+777\s+\//, equivalent: "open-permissions", description: "chmod 777 / opens all permissions" },
5759
+ // chown root (change ownership)
5760
+ { pattern: /chown\s+.*\//, equivalent: "change-ownership", description: "chown on root paths is dangerous" },
5761
+ // iptables -F (flush firewall)
5762
+ { pattern: /iptables\s+-F/, equivalent: "flush-firewall", description: "iptables -F flushes all firewall rules" }
5763
+ ];
5764
+ }
5765
+ });
5766
+
5767
+ // src/whitelistEngine.ts
5768
+ var whitelistEngine_exports = {};
5769
+ __export(whitelistEngine_exports, {
5770
+ PRESETS: () => PRESETS,
5771
+ PRESET_DEVELOPER: () => PRESET_DEVELOPER,
5772
+ PRESET_PRODUCTION: () => PRESET_PRODUCTION,
5773
+ PRESET_READONLY: () => PRESET_READONLY,
5774
+ WhitelistEngine: () => WhitelistEngine,
5775
+ autoLoadPolicy: () => autoLoadPolicy,
5776
+ generatePolicyFile: () => generatePolicyFile,
5777
+ loadPolicy: () => loadPolicy
5778
+ });
5779
+ function matchPattern(command, pattern) {
5780
+ const normalizedCmd = command.trim().replace(/\s+/g, " ");
5781
+ const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
5782
+ const regexStr = "^" + normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
5783
+ try {
5784
+ return new RegExp(regexStr).test(normalizedCmd);
5785
+ } catch {
5786
+ return false;
5787
+ }
5788
+ }
5789
+ function loadPolicy(filePath) {
5790
+ if (!(0, import_node_fs2.existsSync)(filePath)) {
5791
+ throw new Error(`Policy file not found: ${filePath}`);
5792
+ }
5793
+ const raw = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
5794
+ try {
5795
+ return JSON.parse(raw);
5796
+ } catch {
5797
+ }
5798
+ return parseSimpleYaml(raw);
5799
+ }
5800
+ function autoLoadPolicy(projectDir) {
5801
+ const candidates = [
5802
+ (0, import_node_path2.join)(projectDir, ".sovr", "policy.json"),
5803
+ (0, import_node_path2.join)(projectDir, ".sovr", "policy.yaml"),
5804
+ (0, import_node_path2.join)(projectDir, ".sovr", "policy.yml"),
5805
+ (0, import_node_path2.join)(projectDir, "sovr.policy.json")
5806
+ ];
5807
+ for (const candidate of candidates) {
5808
+ if ((0, import_node_fs2.existsSync)(candidate)) {
5809
+ return loadPolicy(candidate);
5810
+ }
5811
+ }
5812
+ return null;
5813
+ }
5814
+ function parseSimpleYaml(raw) {
5815
+ const lines = raw.split("\n");
5816
+ const policy = {
5817
+ version: "1.0",
5818
+ mode: "whitelist",
5819
+ rules: []
5820
+ };
5821
+ let currentRule = null;
5822
+ let inRules = false;
5823
+ for (const line of lines) {
5824
+ const trimmed = line.trim();
5825
+ if (!trimmed || trimmed.startsWith("#")) continue;
5826
+ if (trimmed.startsWith("version:")) {
5827
+ policy.version = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
5828
+ } else if (trimmed.startsWith("mode:")) {
5829
+ policy.mode = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
5830
+ } else if (trimmed === "rules:") {
5831
+ inRules = true;
5832
+ } else if (inRules && trimmed.startsWith("- pattern:")) {
5833
+ if (currentRule?.pattern) {
5834
+ policy.rules.push(currentRule);
5835
+ }
5836
+ currentRule = {
5837
+ pattern: trimmed.replace("- pattern:", "").trim().replace(/['"]/g, ""),
5838
+ allow: true,
5839
+ enabled: true
5840
+ };
5841
+ } else if (inRules && currentRule) {
5842
+ if (trimmed.startsWith("allow:")) {
5843
+ currentRule.allow = trimmed.includes("true");
5844
+ } else if (trimmed.startsWith("description:")) {
5845
+ currentRule.description = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
5846
+ } else if (trimmed.startsWith("require_approval:")) {
5847
+ currentRule.require_approval = trimmed.includes("true");
5848
+ } else if (trimmed.startsWith("max_args:")) {
5849
+ currentRule.max_args = parseInt(trimmed.split(":")[1].trim());
5850
+ } else if (trimmed.startsWith("priority:")) {
5851
+ currentRule.priority = parseInt(trimmed.split(":")[1].trim());
5852
+ }
5853
+ }
5854
+ }
5855
+ if (currentRule?.pattern) {
5856
+ policy.rules.push(currentRule);
5857
+ }
5858
+ return policy;
5859
+ }
5860
+ function generatePolicyFile(preset, format = "yaml") {
5861
+ const policy = PRESETS[preset];
5862
+ if (!policy) {
5863
+ throw new Error(`Unknown preset: ${preset}. Available: ${Object.keys(PRESETS).join(", ")}`);
5864
+ }
5865
+ if (format === "json") {
5866
+ return JSON.stringify(policy, null, 2);
5867
+ }
5868
+ let yaml = `# SOVR Whitelist Policy
5869
+ `;
5870
+ yaml += `# Preset: ${preset}
5871
+ `;
5872
+ yaml += `# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
5873
+
5874
+ `;
5875
+ yaml += `version: "${policy.version}"
5876
+ `;
5877
+ yaml += `mode: ${policy.mode}
5878
+
5879
+ `;
5880
+ yaml += `rules:
5881
+ `;
5882
+ for (const rule of policy.rules) {
5883
+ yaml += ` - pattern: "${rule.pattern}"
5884
+ `;
5885
+ yaml += ` allow: ${rule.allow}
5886
+ `;
5887
+ if (rule.description) yaml += ` description: "${rule.description}"
5888
+ `;
5889
+ if (rule.require_approval) yaml += ` require_approval: true
5890
+ `;
5891
+ if (rule.max_args !== void 0) yaml += ` max_args: ${rule.max_args}
5892
+ `;
5893
+ yaml += `
5894
+ `;
5895
+ }
5896
+ if (policy.settings) {
5897
+ yaml += `settings:
5898
+ `;
5899
+ for (const [key, value] of Object.entries(policy.settings)) {
5900
+ yaml += ` ${key}: ${value}
5901
+ `;
5902
+ }
5903
+ }
5904
+ return yaml;
5905
+ }
5906
+ var import_node_fs2, import_node_path2, PRESET_READONLY, PRESET_DEVELOPER, PRESET_PRODUCTION, PRESETS, WhitelistEngine;
5907
+ var init_whitelistEngine = __esm({
5908
+ "src/whitelistEngine.ts"() {
5909
+ "use strict";
5910
+ import_node_fs2 = require("fs");
5911
+ import_node_path2 = require("path");
5912
+ PRESET_READONLY = {
5913
+ version: "1.0",
5914
+ mode: "whitelist",
5915
+ rules: [
5916
+ { pattern: "ls *", allow: true, description: "List directory" },
5917
+ { pattern: "cat *", allow: true, description: "Read file" },
5918
+ { pattern: "head *", allow: true, description: "Read file head" },
5919
+ { pattern: "tail *", allow: true, description: "Read file tail" },
5920
+ { pattern: "find *", allow: true, description: "Find files", max_args: 10 },
5921
+ { pattern: "grep *", allow: true, description: "Search in files" },
5922
+ { pattern: "wc *", allow: true, description: "Word count" },
5923
+ { pattern: "file *", allow: true, description: "File type" },
5924
+ { pattern: "pwd", allow: true, description: "Print working directory" },
5925
+ { pattern: "whoami", allow: true, description: "Current user" },
5926
+ { pattern: "date", allow: true, description: "Current date" },
5927
+ { pattern: "echo *", allow: true, description: "Echo text" }
5928
+ ],
5929
+ settings: {
5930
+ max_command_length: 500,
5931
+ allow_env_expansion: false,
5932
+ allow_subshell: false,
5933
+ allow_pipes: true,
5934
+ allow_chains: false,
5935
+ allow_redirects: false
5936
+ }
5937
+ };
5938
+ PRESET_DEVELOPER = {
5939
+ version: "1.0",
5940
+ mode: "whitelist",
5941
+ rules: [
5942
+ // Read operations
5943
+ ...PRESET_READONLY.rules,
5944
+ // Git (safe operations)
5945
+ { pattern: "git status", allow: true, description: "Git status" },
5946
+ { pattern: "git diff *", allow: true, description: "Git diff" },
5947
+ { pattern: "git log *", allow: true, description: "Git log" },
5948
+ { pattern: "git branch *", allow: true, description: "Git branch" },
5949
+ { pattern: "git add *", allow: true, description: "Git add" },
5950
+ { pattern: "git commit *", allow: true, description: "Git commit" },
5951
+ { pattern: "git push *", allow: true, require_approval: true, description: "Git push (requires approval)" },
5952
+ { pattern: "git pull *", allow: true, description: "Git pull" },
5953
+ { pattern: "git checkout *", allow: true, description: "Git checkout" },
5954
+ { pattern: "git stash *", allow: true, description: "Git stash" },
5955
+ // Node.js / npm
5956
+ { pattern: "node *", allow: true, description: "Run Node.js" },
5957
+ { pattern: "npm run *", allow: true, description: "Run npm script" },
5958
+ { pattern: "npm test", allow: true, description: "Run tests" },
5959
+ { pattern: "npm install *", allow: true, description: "Install packages" },
5960
+ { pattern: "npx *", allow: true, description: "Run npx" },
5961
+ { pattern: "pnpm *", allow: true, description: "Run pnpm" },
5962
+ { pattern: "yarn *", allow: true, description: "Run yarn" },
5963
+ // Python
5964
+ { pattern: "python3 *", allow: true, description: "Run Python", max_args: 20 },
5965
+ { pattern: "pip install *", allow: true, description: "Install Python packages" },
5966
+ { pattern: "pip3 install *", allow: true, description: "Install Python packages" },
5967
+ // Build tools
5968
+ { pattern: "make *", allow: true, description: "Run make" },
5969
+ { pattern: "cargo *", allow: true, description: "Run cargo" },
5970
+ // File operations (limited)
5971
+ { pattern: "mkdir *", allow: true, description: "Create directory" },
5972
+ { pattern: "cp *", allow: true, description: "Copy files" },
5973
+ { pattern: "mv *", allow: true, description: "Move files" },
5974
+ { pattern: "touch *", allow: true, description: "Create empty file" },
5975
+ // Dangerous operations — require approval
5976
+ { pattern: "rm *", allow: true, require_approval: true, description: "Delete files (requires approval)" },
5977
+ { pattern: "npm publish *", allow: true, require_approval: true, description: "Publish package (requires approval)" },
5978
+ { pattern: "docker *", allow: true, require_approval: true, description: "Docker operations (requires approval)" }
5979
+ ],
5980
+ settings: {
5981
+ max_command_length: 2e3,
5982
+ allow_env_expansion: true,
5983
+ allow_subshell: false,
5984
+ allow_pipes: true,
5985
+ allow_chains: true,
5986
+ allow_redirects: true
5987
+ }
5988
+ };
5989
+ PRESET_PRODUCTION = {
5990
+ version: "1.0",
5991
+ mode: "whitelist",
5992
+ rules: [
5993
+ { pattern: "echo *", allow: true, description: "Echo" },
5994
+ { pattern: "date", allow: true, description: "Date" },
5995
+ { pattern: "uptime", allow: true, description: "Uptime" },
5996
+ { pattern: "df *", allow: true, description: "Disk usage" },
5997
+ { pattern: "free *", allow: true, description: "Memory usage" },
5998
+ { pattern: "ps *", allow: true, description: "Process list" },
5999
+ { pattern: "curl *", allow: true, require_approval: true, description: "HTTP request (requires approval)" }
6000
+ ],
6001
+ default_action: "deny",
6002
+ settings: {
6003
+ max_command_length: 500,
6004
+ allow_env_expansion: false,
6005
+ allow_subshell: false,
6006
+ allow_pipes: false,
6007
+ allow_chains: false,
6008
+ allow_redirects: false
6009
+ }
6010
+ };
6011
+ PRESETS = {
6012
+ readonly: PRESET_READONLY,
6013
+ developer: PRESET_DEVELOPER,
6014
+ production: PRESET_PRODUCTION
6015
+ };
6016
+ WhitelistEngine = class {
6017
+ policy;
6018
+ constructor(policy) {
6019
+ this.policy = policy;
6020
+ this.policy.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
6021
+ }
6022
+ /**
6023
+ * Evaluate a command against the whitelist policy.
6024
+ */
6025
+ evaluate(command) {
6026
+ const violations = [];
6027
+ const structuralCheck = this.checkStructural(command);
6028
+ violations.push(...structuralCheck.violations);
6029
+ if (structuralCheck.blocked) {
6030
+ return {
6031
+ allowed: false,
6032
+ require_approval: false,
6033
+ reason: `Structural violation: ${violations.join(", ")}`,
6034
+ risk: "critical",
6035
+ violations
6036
+ };
6037
+ }
6038
+ const enabledRules = this.policy.rules.filter((r) => r.enabled !== false);
6039
+ for (const rule of enabledRules) {
6040
+ if (matchPattern(command, rule.pattern)) {
6041
+ if (rule.max_args !== void 0) {
6042
+ const argCount = command.split(/\s+/).length - 1;
6043
+ if (argCount > rule.max_args) {
6044
+ violations.push(`Too many arguments: ${argCount} > ${rule.max_args}`);
6045
+ return {
6046
+ allowed: false,
6047
+ require_approval: false,
6048
+ matched_rule: rule,
6049
+ reason: `Argument limit exceeded for pattern "${rule.pattern}"`,
6050
+ risk: "high",
6051
+ violations
6052
+ };
6053
+ }
6054
+ }
6055
+ if (rule.allow) {
6056
+ return {
6057
+ allowed: true,
6058
+ require_approval: rule.require_approval ?? false,
6059
+ matched_rule: rule,
6060
+ reason: rule.description || `Matched whitelist pattern: ${rule.pattern}`,
6061
+ risk: rule.require_approval ? "medium" : "low",
6062
+ violations
6063
+ };
6064
+ } else {
6065
+ return {
6066
+ allowed: false,
6067
+ require_approval: false,
6068
+ matched_rule: rule,
6069
+ reason: rule.description || `Matched blacklist pattern: ${rule.pattern}`,
6070
+ risk: "high",
6071
+ violations
6072
+ };
6073
+ }
6074
+ }
6075
+ }
6076
+ switch (this.policy.mode) {
6077
+ case "whitelist":
6078
+ return {
6079
+ allowed: false,
6080
+ require_approval: false,
6081
+ reason: `No whitelist rule matches command. In whitelist mode, unlisted commands are DENIED.`,
6082
+ risk: "high",
6083
+ violations
6084
+ };
6085
+ case "blacklist":
6086
+ return {
6087
+ allowed: true,
6088
+ require_approval: false,
6089
+ reason: "No blacklist rule matches. Command allowed by default.",
6090
+ risk: "medium",
6091
+ violations
6092
+ };
6093
+ case "hybrid": {
6094
+ const defaultAction = this.policy.default_action || "deny";
6095
+ return {
6096
+ allowed: defaultAction === "allow",
6097
+ require_approval: defaultAction === "escalate",
6098
+ reason: `No rule matches. Hybrid mode default: ${defaultAction}`,
6099
+ risk: defaultAction === "allow" ? "medium" : "high",
6100
+ violations
6101
+ };
6102
+ }
6103
+ }
6104
+ }
6105
+ /**
6106
+ * Check structural constraints (pipes, chains, subshells, etc.)
6107
+ */
6108
+ checkStructural(command) {
6109
+ const violations = [];
6110
+ const settings = this.policy.settings || {};
6111
+ if (settings.max_command_length && command.length > settings.max_command_length) {
6112
+ violations.push(`Command too long: ${command.length} > ${settings.max_command_length}`);
6113
+ }
6114
+ if (settings.allow_subshell === false) {
6115
+ if (/\$\(/.test(command) || /`[^`]+`/.test(command)) {
6116
+ violations.push("Subshell execution not allowed");
6117
+ }
6118
+ }
6119
+ if (settings.allow_pipes === false) {
6120
+ if (/\|(?!\|)/.test(command)) {
6121
+ violations.push("Pipe chains not allowed");
6122
+ }
6123
+ }
6124
+ if (settings.allow_chains === false) {
6125
+ if (/[;&]|&&|\|\|/.test(command)) {
6126
+ violations.push("Command chaining not allowed");
6127
+ }
6128
+ }
6129
+ if (settings.allow_redirects === false) {
6130
+ if (/[<>]|>>/.test(command)) {
6131
+ violations.push("Output redirection not allowed");
6132
+ }
6133
+ }
6134
+ if (settings.allow_env_expansion === false) {
6135
+ if (/\$[A-Za-z_]/.test(command) || /\$\{/.test(command)) {
6136
+ violations.push("Environment variable expansion not allowed");
6137
+ }
6138
+ }
6139
+ return {
6140
+ blocked: violations.length > 0,
6141
+ violations
6142
+ };
6143
+ }
6144
+ /**
6145
+ * Get the current policy.
6146
+ */
6147
+ getPolicy() {
6148
+ return { ...this.policy };
6149
+ }
6150
+ /**
6151
+ * Add a rule dynamically.
6152
+ */
6153
+ addRule(rule) {
6154
+ this.policy.rules.push(rule);
6155
+ this.policy.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
6156
+ }
6157
+ /**
6158
+ * Remove a rule by pattern.
6159
+ */
6160
+ removeRule(pattern) {
6161
+ const idx = this.policy.rules.findIndex((r) => r.pattern === pattern);
6162
+ if (idx >= 0) {
6163
+ this.policy.rules.splice(idx, 1);
6164
+ return true;
6165
+ }
6166
+ return false;
6167
+ }
6168
+ /**
6169
+ * List all rules.
6170
+ */
6171
+ listRules() {
6172
+ return [...this.policy.rules];
6173
+ }
6174
+ };
6175
+ }
6176
+ });
6177
+
5211
6178
  // src/index.ts
5212
6179
  var index_exports = {};
5213
6180
  __export(index_exports, {
@@ -5529,7 +6496,7 @@ var UsageTracker = class {
5529
6496
  };
5530
6497
 
5531
6498
  // src/index.ts
5532
- var PROXY_VERSION = "5.2.0";
6499
+ var PROXY_VERSION = "7.0.0";
5533
6500
  var SOVR_MIN_VERSION_URL = "https://api.sovr.inc/api/sovr/v1/version/check";
5534
6501
  var SOVR_DASHBOARD_URL = "https://sovr.inc/dashboard/api-keys";
5535
6502
  function validateApiKey(key) {
@@ -6347,11 +7314,15 @@ var McpProxy = class extends import_node_events.EventEmitter {
6347
7314
  };
6348
7315
  async function cli(args) {
6349
7316
  const { PolicyEngine: PolicyEngine2, DEFAULT_RULES: DEFAULT_RULES2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
7317
+ const { transformToolList: transformToolList2, shouldIntercept: shouldIntercept2, parseMode: parseMode2 } = await Promise.resolve().then(() => (init_toolReplacement(), toolReplacement_exports));
7318
+ const { normalize: normalizeCommand, summarize: summarizeNormalization } = await Promise.resolve().then(() => (init_commandNormalizer(), commandNormalizer_exports));
6350
7319
  let upstreamCmd = "";
6351
7320
  let upstreamArgs = [];
6352
7321
  let rulesFile = null;
6353
7322
  let verbose = false;
6354
7323
  let startupTimeoutMs = 3e4;
7324
+ let mode = "enforce";
7325
+ let whitelistPreset = null;
6355
7326
  for (let i = 0; i < args.length; i++) {
6356
7327
  switch (args[i]) {
6357
7328
  case "--upstream":
@@ -6373,14 +7344,39 @@ async function cli(args) {
6373
7344
  case "-t":
6374
7345
  startupTimeoutMs = parseInt(args[++i] ?? "30000", 10);
6375
7346
  break;
7347
+ case "--mode":
7348
+ case "-m":
7349
+ mode = args[++i] ?? "enforce";
7350
+ break;
7351
+ case "--whitelist":
7352
+ case "-w":
7353
+ whitelistPreset = args[++i] ?? null;
7354
+ break;
7355
+ default: {
7356
+ const modeMatch = args[i]?.match(/^--mode=(.+)$/);
7357
+ if (modeMatch) {
7358
+ mode = modeMatch[1];
7359
+ break;
7360
+ }
7361
+ const wlMatch = args[i]?.match(/^--whitelist=(.+)$/);
7362
+ if (wlMatch) {
7363
+ whitelistPreset = wlMatch[1];
7364
+ break;
7365
+ }
7366
+ }
6376
7367
  }
6377
7368
  }
6378
7369
  if (!upstreamCmd) {
6379
7370
  process.stderr.write(
6380
- 'Usage: sovr-mcp-proxy --upstream "command args..." [--rules policy.json] [--timeout 30000] [--verbose]\n'
7371
+ 'Usage: sovr-mcp-proxy --upstream "command args..." [--mode=exclusive|enforce|advisory|monitor] [--whitelist=preset|path] [--verbose]\n'
6381
7372
  );
6382
7373
  process.exit(1);
6383
7374
  }
7375
+ if (!["exclusive", "enforce", "advisory", "monitor"].includes(mode)) {
7376
+ process.stderr.write(`[SOVR] Invalid mode: ${mode}. Must be: exclusive|enforce|advisory|monitor
7377
+ `);
7378
+ process.exit(1);
7379
+ }
6384
7380
  let rules = DEFAULT_RULES2;
6385
7381
  if (rulesFile) {
6386
7382
  const fs = await import("fs");
@@ -6388,6 +7384,26 @@ async function cli(args) {
6388
7384
  const parsed = JSON.parse(content);
6389
7385
  rules = parsed.rules ?? parsed;
6390
7386
  }
7387
+ const { WhitelistEngine: WhitelistEngine2, PRESETS: WL_PRESETS } = await Promise.resolve().then(() => (init_whitelistEngine(), whitelistEngine_exports));
7388
+ let whitelist = null;
7389
+ if (whitelistPreset) {
7390
+ const presetNames = Object.keys(WL_PRESETS);
7391
+ if (presetNames.includes(whitelistPreset)) {
7392
+ whitelist = new WhitelistEngine2(WL_PRESETS[whitelistPreset]);
7393
+ } else {
7394
+ try {
7395
+ const fs = await import("fs");
7396
+ const content = fs.readFileSync(whitelistPreset, "utf-8");
7397
+ const parsed = JSON.parse(content);
7398
+ whitelist = new WhitelistEngine2(parsed);
7399
+ } catch (err) {
7400
+ process.stderr.write(`[SOVR] Failed to load whitelist from ${whitelistPreset}: ${err.message}
7401
+ `);
7402
+ process.exit(1);
7403
+ }
7404
+ }
7405
+ }
7406
+ const validatedMode = parseMode2(mode);
6391
7407
  const engine = new PolicyEngine2({
6392
7408
  rules,
6393
7409
  audit_log: true,
@@ -6400,6 +7416,24 @@ async function cli(args) {
6400
7416
  }
6401
7417
  }
6402
7418
  });
7419
+ process.stderr.write(`
7420
+ `);
7421
+ process.stderr.write(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
7422
+ `);
7423
+ process.stderr.write(` \u2551 SOVR MCP Proxy v${PROXY_VERSION} \u2551
7424
+ `);
7425
+ process.stderr.write(` \u2551 Mode: ${mode.toUpperCase().padEnd(40)}\u2551
7426
+ `);
7427
+ if (whitelist) {
7428
+ process.stderr.write(` \u2551 Whitelist: ${(whitelistPreset ?? "custom").padEnd(35)}\u2551
7429
+ `);
7430
+ }
7431
+ process.stderr.write(` \u2551 Upstream: ${upstreamCmd.substring(0, 36).padEnd(36)}\u2551
7432
+ `);
7433
+ process.stderr.write(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
7434
+ `);
7435
+ process.stderr.write(`
7436
+ `);
6403
7437
  const proxy = new McpProxy({
6404
7438
  engine,
6405
7439
  upstream: { command: upstreamCmd, args: upstreamArgs },
@@ -6416,8 +7450,37 @@ async function cli(args) {
6416
7450
  `[ESCALATED] ${info.toolName}: ${info.decision.reason}
6417
7451
  `
6418
7452
  );
7453
+ },
7454
+ onIntercept: (info) => {
7455
+ if (info.arguments?.command && typeof info.arguments.command === "string") {
7456
+ const normalized = normalizeCommand(info.arguments.command);
7457
+ if (verbose) {
7458
+ process.stderr.write(`[SOVR] Normalized: ${summarizeNormalization(normalized)}
7459
+ `);
7460
+ if (normalized.suspicious) {
7461
+ process.stderr.write(`[SOVR] \u26A0 Suspicious: ${normalized.suspicion_reasons.join(", ")}
7462
+ `);
7463
+ }
7464
+ }
7465
+ }
7466
+ if (whitelist && info.arguments?.command && typeof info.arguments.command === "string") {
7467
+ const wlResult = whitelist.evaluate(info.arguments.command);
7468
+ if (!wlResult.allowed && (validatedMode === "enforce" || validatedMode === "exclusive")) {
7469
+ if (verbose) {
7470
+ process.stderr.write(`[SOVR] Whitelist DENIED: ${wlResult.reason}
7471
+ `);
7472
+ }
7473
+ }
7474
+ }
6419
7475
  }
6420
7476
  });
7477
+ if (validatedMode === "exclusive") {
7478
+ proxy.on("intercept", () => {
7479
+ if (verbose) {
7480
+ process.stderr.write("[SOVR] Exclusive mode: all tool calls routed through SOVR\n");
7481
+ }
7482
+ });
7483
+ }
6421
7484
  await proxy.start();
6422
7485
  }
6423
7486
  var index_default = McpProxy;