@solongate/proxy 0.6.1 → 0.6.3

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.
Files changed (3) hide show
  1. package/dist/index.js +31 -27
  2. package/dist/init.js +31 -27
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -664,10 +664,7 @@ function installHooks() {
664
664
  const auditPath = join(hooksDir, "audit.mjs");
665
665
  writeFileSync2(auditPath, AUDIT_SCRIPT);
666
666
  console.log(` Created ${auditPath}`);
667
- const claudeDir = resolve2(".claude");
668
- mkdirSync(claudeDir, { recursive: true });
669
- const settingsPath = join(claudeDir, "settings.json");
670
- const settings = {
667
+ const hookSettings = {
671
668
  hooks: {
672
669
  PreToolUse: [
673
670
  {
@@ -687,19 +684,28 @@ function installHooks() {
687
684
  ]
688
685
  }
689
686
  };
690
- let existing = {};
691
- try {
692
- existing = JSON.parse(readFileSync3(settingsPath, "utf-8"));
693
- } catch {
687
+ const clients = [
688
+ { name: "Claude Code", dir: ".claude" },
689
+ { name: "Cursor", dir: ".cursor" }
690
+ ];
691
+ for (const client of clients) {
692
+ const clientDir = resolve2(client.dir);
693
+ mkdirSync(clientDir, { recursive: true });
694
+ const settingsPath = join(clientDir, "settings.json");
695
+ let existing = {};
696
+ try {
697
+ existing = JSON.parse(readFileSync3(settingsPath, "utf-8"));
698
+ } catch {
699
+ }
700
+ const merged = { ...existing, hooks: hookSettings.hooks };
701
+ writeFileSync2(settingsPath, JSON.stringify(merged, null, 2) + "\n");
702
+ console.log(` Created ${settingsPath}`);
694
703
  }
695
- const merged = { ...existing, hooks: settings.hooks };
696
- writeFileSync2(settingsPath, JSON.stringify(merged, null, 2) + "\n");
697
- console.log(` Created ${settingsPath}`);
698
704
  console.log("");
699
705
  console.log(" Hooks installed:");
700
706
  console.log(" guard.mjs \u2192 blocks policy-violating calls (pre-execution)");
701
707
  console.log(" audit.mjs \u2192 logs all calls to dashboard (post-execution)");
702
- console.log(" .claude/settings.json \u2192 hooks activated for Claude Code");
708
+ console.log(" Activated for: Claude Code, Cursor");
703
709
  }
704
710
  function ensureEnvFile() {
705
711
  const envPath = resolve2(".env");
@@ -979,17 +985,13 @@ function matchGlob(str, pattern) {
979
985
 
980
986
  // \u2500\u2500 Path Glob (supports **) \u2500\u2500
981
987
  function matchPathGlob(path, pattern) {
982
- const p = path.replace(/\\\\\\\\/g, '/').toLowerCase();
983
- const g = pattern.replace(/\\\\\\\\/g, '/').toLowerCase();
988
+ const p = path.replace(/\\\\/g, '/').toLowerCase();
989
+ const g = pattern.replace(/\\\\/g, '/').toLowerCase();
984
990
  if (p === g) return true;
985
991
  if (g.includes('**')) {
986
- const parts = g.split('**');
987
- if (parts.length === 2) {
988
- const [pre, suf] = parts;
989
- const matchPre = !pre || p.startsWith(pre) || p.includes(pre);
990
- const matchSuf = !suf || p.endsWith(suf) || p.includes(suf.slice(1));
991
- return matchPre && matchSuf;
992
- }
992
+ const parts = g.split('**').filter(s => s.length > 0);
993
+ if (parts.length === 0) return true; // just ** or ****
994
+ return parts.every(segment => p.includes(segment));
993
995
  }
994
996
  return matchGlob(p, g);
995
997
  }
@@ -1018,14 +1020,14 @@ function extractFilenames(args) {
1018
1020
  for (const s of scanStrings(args)) {
1019
1021
  if (/^https?:\\/\\//i.test(s)) continue;
1020
1022
  if (s.includes('/') || s.includes('\\\\')) {
1021
- const base = s.replace(/\\\\\\\\/g, '/').split('/').pop();
1023
+ const base = s.replace(/\\\\/g, '/').split('/').pop();
1022
1024
  if (base) names.add(base);
1023
1025
  continue;
1024
1026
  }
1025
1027
  if (s.includes(' ')) {
1026
1028
  for (const tok of s.split(/\\s+/)) {
1027
1029
  if (tok.includes('/') || tok.includes('\\\\')) {
1028
- const b = tok.replace(/\\\\\\\\/g, '/').split('/').pop();
1030
+ const b = tok.replace(/\\\\/g, '/').split('/').pop();
1029
1031
  if (b && looksLikeFilename(b)) names.add(b);
1030
1032
  } else if (looksLikeFilename(tok)) names.add(tok);
1031
1033
  }
@@ -1126,8 +1128,8 @@ process.stdin.on('end', async () => {
1126
1128
  const args = data.tool_input || {};
1127
1129
 
1128
1130
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
1129
- const allStrings = scanStrings(args).map(s => s.replace(/\\\\\\\\/g, '/').toLowerCase());
1130
- const protectedPaths = ['.solongate', '.claude/settings.json', 'policy.json'];
1131
+ const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
1132
+ const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
1131
1133
  for (const s of allStrings) {
1132
1134
  for (const p of protectedPaths) {
1133
1135
  if (s.includes(p)) {
@@ -1138,10 +1140,12 @@ process.stdin.on('end', async () => {
1138
1140
  }
1139
1141
  }
1140
1142
 
1141
- // Load policy
1143
+ // Load policy (use cwd from hook data if available)
1144
+ const hookCwd = data.cwd || process.cwd();
1142
1145
  let policy;
1143
1146
  try {
1144
- policy = JSON.parse(readFileSync(resolve('policy.json'), 'utf-8'));
1147
+ const policyPath = resolve(hookCwd, 'policy.json');
1148
+ policy = JSON.parse(readFileSync(policyPath, 'utf-8'));
1145
1149
  } catch {
1146
1150
  process.exit(0); // No policy = allow all
1147
1151
  }
package/dist/init.js CHANGED
@@ -174,17 +174,13 @@ function matchGlob(str, pattern) {
174
174
 
175
175
  // \u2500\u2500 Path Glob (supports **) \u2500\u2500
176
176
  function matchPathGlob(path, pattern) {
177
- const p = path.replace(/\\\\\\\\/g, '/').toLowerCase();
178
- const g = pattern.replace(/\\\\\\\\/g, '/').toLowerCase();
177
+ const p = path.replace(/\\\\/g, '/').toLowerCase();
178
+ const g = pattern.replace(/\\\\/g, '/').toLowerCase();
179
179
  if (p === g) return true;
180
180
  if (g.includes('**')) {
181
- const parts = g.split('**');
182
- if (parts.length === 2) {
183
- const [pre, suf] = parts;
184
- const matchPre = !pre || p.startsWith(pre) || p.includes(pre);
185
- const matchSuf = !suf || p.endsWith(suf) || p.includes(suf.slice(1));
186
- return matchPre && matchSuf;
187
- }
181
+ const parts = g.split('**').filter(s => s.length > 0);
182
+ if (parts.length === 0) return true; // just ** or ****
183
+ return parts.every(segment => p.includes(segment));
188
184
  }
189
185
  return matchGlob(p, g);
190
186
  }
@@ -213,14 +209,14 @@ function extractFilenames(args) {
213
209
  for (const s of scanStrings(args)) {
214
210
  if (/^https?:\\/\\//i.test(s)) continue;
215
211
  if (s.includes('/') || s.includes('\\\\')) {
216
- const base = s.replace(/\\\\\\\\/g, '/').split('/').pop();
212
+ const base = s.replace(/\\\\/g, '/').split('/').pop();
217
213
  if (base) names.add(base);
218
214
  continue;
219
215
  }
220
216
  if (s.includes(' ')) {
221
217
  for (const tok of s.split(/\\s+/)) {
222
218
  if (tok.includes('/') || tok.includes('\\\\')) {
223
- const b = tok.replace(/\\\\\\\\/g, '/').split('/').pop();
219
+ const b = tok.replace(/\\\\/g, '/').split('/').pop();
224
220
  if (b && looksLikeFilename(b)) names.add(b);
225
221
  } else if (looksLikeFilename(tok)) names.add(tok);
226
222
  }
@@ -321,8 +317,8 @@ process.stdin.on('end', async () => {
321
317
  const args = data.tool_input || {};
322
318
 
323
319
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
324
- const allStrings = scanStrings(args).map(s => s.replace(/\\\\\\\\/g, '/').toLowerCase());
325
- const protectedPaths = ['.solongate', '.claude/settings.json', 'policy.json'];
320
+ const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
321
+ const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
326
322
  for (const s of allStrings) {
327
323
  for (const p of protectedPaths) {
328
324
  if (s.includes(p)) {
@@ -333,10 +329,12 @@ process.stdin.on('end', async () => {
333
329
  }
334
330
  }
335
331
 
336
- // Load policy
332
+ // Load policy (use cwd from hook data if available)
333
+ const hookCwd = data.cwd || process.cwd();
337
334
  let policy;
338
335
  try {
339
- policy = JSON.parse(readFileSync(resolve('policy.json'), 'utf-8'));
336
+ const policyPath = resolve(hookCwd, 'policy.json');
337
+ policy = JSON.parse(readFileSync(policyPath, 'utf-8'));
340
338
  } catch {
341
339
  process.exit(0); // No policy = allow all
342
340
  }
@@ -430,10 +428,7 @@ function installHooks() {
430
428
  const auditPath = join(hooksDir, "audit.mjs");
431
429
  writeFileSync(auditPath, AUDIT_SCRIPT);
432
430
  console.log(` Created ${auditPath}`);
433
- const claudeDir = resolve(".claude");
434
- mkdirSync(claudeDir, { recursive: true });
435
- const settingsPath = join(claudeDir, "settings.json");
436
- const settings = {
431
+ const hookSettings = {
437
432
  hooks: {
438
433
  PreToolUse: [
439
434
  {
@@ -453,19 +448,28 @@ function installHooks() {
453
448
  ]
454
449
  }
455
450
  };
456
- let existing = {};
457
- try {
458
- existing = JSON.parse(readFileSync(settingsPath, "utf-8"));
459
- } catch {
451
+ const clients = [
452
+ { name: "Claude Code", dir: ".claude" },
453
+ { name: "Cursor", dir: ".cursor" }
454
+ ];
455
+ for (const client of clients) {
456
+ const clientDir = resolve(client.dir);
457
+ mkdirSync(clientDir, { recursive: true });
458
+ const settingsPath = join(clientDir, "settings.json");
459
+ let existing = {};
460
+ try {
461
+ existing = JSON.parse(readFileSync(settingsPath, "utf-8"));
462
+ } catch {
463
+ }
464
+ const merged = { ...existing, hooks: hookSettings.hooks };
465
+ writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
466
+ console.log(` Created ${settingsPath}`);
460
467
  }
461
- const merged = { ...existing, hooks: settings.hooks };
462
- writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
463
- console.log(` Created ${settingsPath}`);
464
468
  console.log("");
465
469
  console.log(" Hooks installed:");
466
470
  console.log(" guard.mjs \u2192 blocks policy-violating calls (pre-execution)");
467
471
  console.log(" audit.mjs \u2192 logs all calls to dashboard (post-execution)");
468
- console.log(" .claude/settings.json \u2192 hooks activated for Claude Code");
472
+ console.log(" Activated for: Claude Code, Cursor");
469
473
  }
470
474
  function ensureEnvFile() {
471
475
  const envPath = resolve(".env");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "MCP security proxy — protect any MCP server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {