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.
@@ -0,0 +1,435 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/whitelistEngine.ts
21
+ var whitelistEngine_exports = {};
22
+ __export(whitelistEngine_exports, {
23
+ PRESETS: () => PRESETS,
24
+ PRESET_DEVELOPER: () => PRESET_DEVELOPER,
25
+ PRESET_PRODUCTION: () => PRESET_PRODUCTION,
26
+ PRESET_READONLY: () => PRESET_READONLY,
27
+ WhitelistEngine: () => WhitelistEngine,
28
+ autoLoadPolicy: () => autoLoadPolicy,
29
+ generatePolicyFile: () => generatePolicyFile,
30
+ loadPolicy: () => loadPolicy
31
+ });
32
+ module.exports = __toCommonJS(whitelistEngine_exports);
33
+ var import_node_fs = require("fs");
34
+ var import_node_path = require("path");
35
+ var PRESET_READONLY = {
36
+ version: "1.0",
37
+ mode: "whitelist",
38
+ rules: [
39
+ { pattern: "ls *", allow: true, description: "List directory" },
40
+ { pattern: "cat *", allow: true, description: "Read file" },
41
+ { pattern: "head *", allow: true, description: "Read file head" },
42
+ { pattern: "tail *", allow: true, description: "Read file tail" },
43
+ { pattern: "find *", allow: true, description: "Find files", max_args: 10 },
44
+ { pattern: "grep *", allow: true, description: "Search in files" },
45
+ { pattern: "wc *", allow: true, description: "Word count" },
46
+ { pattern: "file *", allow: true, description: "File type" },
47
+ { pattern: "pwd", allow: true, description: "Print working directory" },
48
+ { pattern: "whoami", allow: true, description: "Current user" },
49
+ { pattern: "date", allow: true, description: "Current date" },
50
+ { pattern: "echo *", allow: true, description: "Echo text" }
51
+ ],
52
+ settings: {
53
+ max_command_length: 500,
54
+ allow_env_expansion: false,
55
+ allow_subshell: false,
56
+ allow_pipes: true,
57
+ allow_chains: false,
58
+ allow_redirects: false
59
+ }
60
+ };
61
+ var PRESET_DEVELOPER = {
62
+ version: "1.0",
63
+ mode: "whitelist",
64
+ rules: [
65
+ // Read operations
66
+ ...PRESET_READONLY.rules,
67
+ // Git (safe operations)
68
+ { pattern: "git status", allow: true, description: "Git status" },
69
+ { pattern: "git diff *", allow: true, description: "Git diff" },
70
+ { pattern: "git log *", allow: true, description: "Git log" },
71
+ { pattern: "git branch *", allow: true, description: "Git branch" },
72
+ { pattern: "git add *", allow: true, description: "Git add" },
73
+ { pattern: "git commit *", allow: true, description: "Git commit" },
74
+ { pattern: "git push *", allow: true, require_approval: true, description: "Git push (requires approval)" },
75
+ { pattern: "git pull *", allow: true, description: "Git pull" },
76
+ { pattern: "git checkout *", allow: true, description: "Git checkout" },
77
+ { pattern: "git stash *", allow: true, description: "Git stash" },
78
+ // Node.js / npm
79
+ { pattern: "node *", allow: true, description: "Run Node.js" },
80
+ { pattern: "npm run *", allow: true, description: "Run npm script" },
81
+ { pattern: "npm test", allow: true, description: "Run tests" },
82
+ { pattern: "npm install *", allow: true, description: "Install packages" },
83
+ { pattern: "npx *", allow: true, description: "Run npx" },
84
+ { pattern: "pnpm *", allow: true, description: "Run pnpm" },
85
+ { pattern: "yarn *", allow: true, description: "Run yarn" },
86
+ // Python
87
+ { pattern: "python3 *", allow: true, description: "Run Python", max_args: 20 },
88
+ { pattern: "pip install *", allow: true, description: "Install Python packages" },
89
+ { pattern: "pip3 install *", allow: true, description: "Install Python packages" },
90
+ // Build tools
91
+ { pattern: "make *", allow: true, description: "Run make" },
92
+ { pattern: "cargo *", allow: true, description: "Run cargo" },
93
+ // File operations (limited)
94
+ { pattern: "mkdir *", allow: true, description: "Create directory" },
95
+ { pattern: "cp *", allow: true, description: "Copy files" },
96
+ { pattern: "mv *", allow: true, description: "Move files" },
97
+ { pattern: "touch *", allow: true, description: "Create empty file" },
98
+ // Dangerous operations — require approval
99
+ { pattern: "rm *", allow: true, require_approval: true, description: "Delete files (requires approval)" },
100
+ { pattern: "npm publish *", allow: true, require_approval: true, description: "Publish package (requires approval)" },
101
+ { pattern: "docker *", allow: true, require_approval: true, description: "Docker operations (requires approval)" }
102
+ ],
103
+ settings: {
104
+ max_command_length: 2e3,
105
+ allow_env_expansion: true,
106
+ allow_subshell: false,
107
+ allow_pipes: true,
108
+ allow_chains: true,
109
+ allow_redirects: true
110
+ }
111
+ };
112
+ var PRESET_PRODUCTION = {
113
+ version: "1.0",
114
+ mode: "whitelist",
115
+ rules: [
116
+ { pattern: "echo *", allow: true, description: "Echo" },
117
+ { pattern: "date", allow: true, description: "Date" },
118
+ { pattern: "uptime", allow: true, description: "Uptime" },
119
+ { pattern: "df *", allow: true, description: "Disk usage" },
120
+ { pattern: "free *", allow: true, description: "Memory usage" },
121
+ { pattern: "ps *", allow: true, description: "Process list" },
122
+ { pattern: "curl *", allow: true, require_approval: true, description: "HTTP request (requires approval)" }
123
+ ],
124
+ default_action: "deny",
125
+ settings: {
126
+ max_command_length: 500,
127
+ allow_env_expansion: false,
128
+ allow_subshell: false,
129
+ allow_pipes: false,
130
+ allow_chains: false,
131
+ allow_redirects: false
132
+ }
133
+ };
134
+ var PRESETS = {
135
+ readonly: PRESET_READONLY,
136
+ developer: PRESET_DEVELOPER,
137
+ production: PRESET_PRODUCTION
138
+ };
139
+ var WhitelistEngine = class {
140
+ policy;
141
+ constructor(policy) {
142
+ this.policy = policy;
143
+ this.policy.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
144
+ }
145
+ /**
146
+ * Evaluate a command against the whitelist policy.
147
+ */
148
+ evaluate(command) {
149
+ const violations = [];
150
+ const structuralCheck = this.checkStructural(command);
151
+ violations.push(...structuralCheck.violations);
152
+ if (structuralCheck.blocked) {
153
+ return {
154
+ allowed: false,
155
+ require_approval: false,
156
+ reason: `Structural violation: ${violations.join(", ")}`,
157
+ risk: "critical",
158
+ violations
159
+ };
160
+ }
161
+ const enabledRules = this.policy.rules.filter((r) => r.enabled !== false);
162
+ for (const rule of enabledRules) {
163
+ if (matchPattern(command, rule.pattern)) {
164
+ if (rule.max_args !== void 0) {
165
+ const argCount = command.split(/\s+/).length - 1;
166
+ if (argCount > rule.max_args) {
167
+ violations.push(`Too many arguments: ${argCount} > ${rule.max_args}`);
168
+ return {
169
+ allowed: false,
170
+ require_approval: false,
171
+ matched_rule: rule,
172
+ reason: `Argument limit exceeded for pattern "${rule.pattern}"`,
173
+ risk: "high",
174
+ violations
175
+ };
176
+ }
177
+ }
178
+ if (rule.allow) {
179
+ return {
180
+ allowed: true,
181
+ require_approval: rule.require_approval ?? false,
182
+ matched_rule: rule,
183
+ reason: rule.description || `Matched whitelist pattern: ${rule.pattern}`,
184
+ risk: rule.require_approval ? "medium" : "low",
185
+ violations
186
+ };
187
+ } else {
188
+ return {
189
+ allowed: false,
190
+ require_approval: false,
191
+ matched_rule: rule,
192
+ reason: rule.description || `Matched blacklist pattern: ${rule.pattern}`,
193
+ risk: "high",
194
+ violations
195
+ };
196
+ }
197
+ }
198
+ }
199
+ switch (this.policy.mode) {
200
+ case "whitelist":
201
+ return {
202
+ allowed: false,
203
+ require_approval: false,
204
+ reason: `No whitelist rule matches command. In whitelist mode, unlisted commands are DENIED.`,
205
+ risk: "high",
206
+ violations
207
+ };
208
+ case "blacklist":
209
+ return {
210
+ allowed: true,
211
+ require_approval: false,
212
+ reason: "No blacklist rule matches. Command allowed by default.",
213
+ risk: "medium",
214
+ violations
215
+ };
216
+ case "hybrid": {
217
+ const defaultAction = this.policy.default_action || "deny";
218
+ return {
219
+ allowed: defaultAction === "allow",
220
+ require_approval: defaultAction === "escalate",
221
+ reason: `No rule matches. Hybrid mode default: ${defaultAction}`,
222
+ risk: defaultAction === "allow" ? "medium" : "high",
223
+ violations
224
+ };
225
+ }
226
+ }
227
+ }
228
+ /**
229
+ * Check structural constraints (pipes, chains, subshells, etc.)
230
+ */
231
+ checkStructural(command) {
232
+ const violations = [];
233
+ const settings = this.policy.settings || {};
234
+ if (settings.max_command_length && command.length > settings.max_command_length) {
235
+ violations.push(`Command too long: ${command.length} > ${settings.max_command_length}`);
236
+ }
237
+ if (settings.allow_subshell === false) {
238
+ if (/\$\(/.test(command) || /`[^`]+`/.test(command)) {
239
+ violations.push("Subshell execution not allowed");
240
+ }
241
+ }
242
+ if (settings.allow_pipes === false) {
243
+ if (/\|(?!\|)/.test(command)) {
244
+ violations.push("Pipe chains not allowed");
245
+ }
246
+ }
247
+ if (settings.allow_chains === false) {
248
+ if (/[;&]|&&|\|\|/.test(command)) {
249
+ violations.push("Command chaining not allowed");
250
+ }
251
+ }
252
+ if (settings.allow_redirects === false) {
253
+ if (/[<>]|>>/.test(command)) {
254
+ violations.push("Output redirection not allowed");
255
+ }
256
+ }
257
+ if (settings.allow_env_expansion === false) {
258
+ if (/\$[A-Za-z_]/.test(command) || /\$\{/.test(command)) {
259
+ violations.push("Environment variable expansion not allowed");
260
+ }
261
+ }
262
+ return {
263
+ blocked: violations.length > 0,
264
+ violations
265
+ };
266
+ }
267
+ /**
268
+ * Get the current policy.
269
+ */
270
+ getPolicy() {
271
+ return { ...this.policy };
272
+ }
273
+ /**
274
+ * Add a rule dynamically.
275
+ */
276
+ addRule(rule) {
277
+ this.policy.rules.push(rule);
278
+ this.policy.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
279
+ }
280
+ /**
281
+ * Remove a rule by pattern.
282
+ */
283
+ removeRule(pattern) {
284
+ const idx = this.policy.rules.findIndex((r) => r.pattern === pattern);
285
+ if (idx >= 0) {
286
+ this.policy.rules.splice(idx, 1);
287
+ return true;
288
+ }
289
+ return false;
290
+ }
291
+ /**
292
+ * List all rules.
293
+ */
294
+ listRules() {
295
+ return [...this.policy.rules];
296
+ }
297
+ };
298
+ function matchPattern(command, pattern) {
299
+ const normalizedCmd = command.trim().replace(/\s+/g, " ");
300
+ const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
301
+ const regexStr = "^" + normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
302
+ try {
303
+ return new RegExp(regexStr).test(normalizedCmd);
304
+ } catch {
305
+ return false;
306
+ }
307
+ }
308
+ function loadPolicy(filePath) {
309
+ if (!(0, import_node_fs.existsSync)(filePath)) {
310
+ throw new Error(`Policy file not found: ${filePath}`);
311
+ }
312
+ const raw = (0, import_node_fs.readFileSync)(filePath, "utf-8");
313
+ try {
314
+ return JSON.parse(raw);
315
+ } catch {
316
+ }
317
+ return parseSimpleYaml(raw);
318
+ }
319
+ function autoLoadPolicy(projectDir) {
320
+ const candidates = [
321
+ (0, import_node_path.join)(projectDir, ".sovr", "policy.json"),
322
+ (0, import_node_path.join)(projectDir, ".sovr", "policy.yaml"),
323
+ (0, import_node_path.join)(projectDir, ".sovr", "policy.yml"),
324
+ (0, import_node_path.join)(projectDir, "sovr.policy.json")
325
+ ];
326
+ for (const candidate of candidates) {
327
+ if ((0, import_node_fs.existsSync)(candidate)) {
328
+ return loadPolicy(candidate);
329
+ }
330
+ }
331
+ return null;
332
+ }
333
+ function parseSimpleYaml(raw) {
334
+ const lines = raw.split("\n");
335
+ const policy = {
336
+ version: "1.0",
337
+ mode: "whitelist",
338
+ rules: []
339
+ };
340
+ let currentRule = null;
341
+ let inRules = false;
342
+ for (const line of lines) {
343
+ const trimmed = line.trim();
344
+ if (!trimmed || trimmed.startsWith("#")) continue;
345
+ if (trimmed.startsWith("version:")) {
346
+ policy.version = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
347
+ } else if (trimmed.startsWith("mode:")) {
348
+ policy.mode = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
349
+ } else if (trimmed === "rules:") {
350
+ inRules = true;
351
+ } else if (inRules && trimmed.startsWith("- pattern:")) {
352
+ if (currentRule?.pattern) {
353
+ policy.rules.push(currentRule);
354
+ }
355
+ currentRule = {
356
+ pattern: trimmed.replace("- pattern:", "").trim().replace(/['"]/g, ""),
357
+ allow: true,
358
+ enabled: true
359
+ };
360
+ } else if (inRules && currentRule) {
361
+ if (trimmed.startsWith("allow:")) {
362
+ currentRule.allow = trimmed.includes("true");
363
+ } else if (trimmed.startsWith("description:")) {
364
+ currentRule.description = trimmed.split(":").slice(1).join(":").trim().replace(/['"]/g, "");
365
+ } else if (trimmed.startsWith("require_approval:")) {
366
+ currentRule.require_approval = trimmed.includes("true");
367
+ } else if (trimmed.startsWith("max_args:")) {
368
+ currentRule.max_args = parseInt(trimmed.split(":")[1].trim());
369
+ } else if (trimmed.startsWith("priority:")) {
370
+ currentRule.priority = parseInt(trimmed.split(":")[1].trim());
371
+ }
372
+ }
373
+ }
374
+ if (currentRule?.pattern) {
375
+ policy.rules.push(currentRule);
376
+ }
377
+ return policy;
378
+ }
379
+ function generatePolicyFile(preset, format = "yaml") {
380
+ const policy = PRESETS[preset];
381
+ if (!policy) {
382
+ throw new Error(`Unknown preset: ${preset}. Available: ${Object.keys(PRESETS).join(", ")}`);
383
+ }
384
+ if (format === "json") {
385
+ return JSON.stringify(policy, null, 2);
386
+ }
387
+ let yaml = `# SOVR Whitelist Policy
388
+ `;
389
+ yaml += `# Preset: ${preset}
390
+ `;
391
+ yaml += `# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
392
+
393
+ `;
394
+ yaml += `version: "${policy.version}"
395
+ `;
396
+ yaml += `mode: ${policy.mode}
397
+
398
+ `;
399
+ yaml += `rules:
400
+ `;
401
+ for (const rule of policy.rules) {
402
+ yaml += ` - pattern: "${rule.pattern}"
403
+ `;
404
+ yaml += ` allow: ${rule.allow}
405
+ `;
406
+ if (rule.description) yaml += ` description: "${rule.description}"
407
+ `;
408
+ if (rule.require_approval) yaml += ` require_approval: true
409
+ `;
410
+ if (rule.max_args !== void 0) yaml += ` max_args: ${rule.max_args}
411
+ `;
412
+ yaml += `
413
+ `;
414
+ }
415
+ if (policy.settings) {
416
+ yaml += `settings:
417
+ `;
418
+ for (const [key, value] of Object.entries(policy.settings)) {
419
+ yaml += ` ${key}: ${value}
420
+ `;
421
+ }
422
+ }
423
+ return yaml;
424
+ }
425
+ // Annotate the CommonJS export names for ESM import in node:
426
+ 0 && (module.exports = {
427
+ PRESETS,
428
+ PRESET_DEVELOPER,
429
+ PRESET_PRODUCTION,
430
+ PRESET_READONLY,
431
+ WhitelistEngine,
432
+ autoLoadPolicy,
433
+ generatePolicyFile,
434
+ loadPolicy
435
+ });