@solongate/proxy 0.6.2 → 0.6.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.
Files changed (3) hide show
  1. package/dist/index.js +19 -15
  2. package/dist/init.js +19 -15
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -985,17 +985,13 @@ function matchGlob(str, pattern) {
985
985
 
986
986
  // \u2500\u2500 Path Glob (supports **) \u2500\u2500
987
987
  function matchPathGlob(path, pattern) {
988
- const p = path.replace(/\\\\\\\\/g, '/').toLowerCase();
989
- const g = pattern.replace(/\\\\\\\\/g, '/').toLowerCase();
988
+ const p = path.replace(/\\\\/g, '/').toLowerCase();
989
+ const g = pattern.replace(/\\\\/g, '/').toLowerCase();
990
990
  if (p === g) return true;
991
991
  if (g.includes('**')) {
992
- const parts = g.split('**');
993
- if (parts.length === 2) {
994
- const [pre, suf] = parts;
995
- const matchPre = !pre || p.startsWith(pre) || p.includes(pre);
996
- const matchSuf = !suf || p.endsWith(suf) || p.includes(suf.slice(1));
997
- return matchPre && matchSuf;
998
- }
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));
999
995
  }
1000
996
  return matchGlob(p, g);
1001
997
  }
@@ -1024,14 +1020,14 @@ function extractFilenames(args) {
1024
1020
  for (const s of scanStrings(args)) {
1025
1021
  if (/^https?:\\/\\//i.test(s)) continue;
1026
1022
  if (s.includes('/') || s.includes('\\\\')) {
1027
- const base = s.replace(/\\\\\\\\/g, '/').split('/').pop();
1023
+ const base = s.replace(/\\\\/g, '/').split('/').pop();
1028
1024
  if (base) names.add(base);
1029
1025
  continue;
1030
1026
  }
1031
1027
  if (s.includes(' ')) {
1032
1028
  for (const tok of s.split(/\\s+/)) {
1033
1029
  if (tok.includes('/') || tok.includes('\\\\')) {
1034
- const b = tok.replace(/\\\\\\\\/g, '/').split('/').pop();
1030
+ const b = tok.replace(/\\\\/g, '/').split('/').pop();
1035
1031
  if (b && looksLikeFilename(b)) names.add(b);
1036
1032
  } else if (looksLikeFilename(tok)) names.add(tok);
1037
1033
  }
@@ -1060,7 +1056,13 @@ function extractCommands(args) {
1060
1056
  const fields = ['command', 'cmd', 'function', 'script', 'shell'];
1061
1057
  if (typeof args === 'object' && args) {
1062
1058
  for (const [k, v] of Object.entries(args)) {
1063
- if (fields.includes(k.toLowerCase()) && typeof v === 'string') cmds.push(v);
1059
+ if (fields.includes(k.toLowerCase()) && typeof v === 'string') {
1060
+ // Split chained commands: cd /path && npm install \u2192 [cd /path, npm install]
1061
+ for (const part of v.split(/\\s*(?:&&|\\|\\||;|\\|)\\s*/)) {
1062
+ const trimmed = part.trim();
1063
+ if (trimmed) cmds.push(trimmed);
1064
+ }
1065
+ }
1064
1066
  }
1065
1067
  }
1066
1068
  return cmds;
@@ -1132,7 +1134,7 @@ process.stdin.on('end', async () => {
1132
1134
  const args = data.tool_input || {};
1133
1135
 
1134
1136
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
1135
- const allStrings = scanStrings(args).map(s => s.replace(/\\\\\\\\/g, '/').toLowerCase());
1137
+ const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
1136
1138
  const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
1137
1139
  for (const s of allStrings) {
1138
1140
  for (const p of protectedPaths) {
@@ -1144,10 +1146,12 @@ process.stdin.on('end', async () => {
1144
1146
  }
1145
1147
  }
1146
1148
 
1147
- // Load policy
1149
+ // Load policy (use cwd from hook data if available)
1150
+ const hookCwd = data.cwd || process.cwd();
1148
1151
  let policy;
1149
1152
  try {
1150
- policy = JSON.parse(readFileSync(resolve('policy.json'), 'utf-8'));
1153
+ const policyPath = resolve(hookCwd, 'policy.json');
1154
+ policy = JSON.parse(readFileSync(policyPath, 'utf-8'));
1151
1155
  } catch {
1152
1156
  process.exit(0); // No policy = allow all
1153
1157
  }
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
  }
@@ -249,7 +245,13 @@ function extractCommands(args) {
249
245
  const fields = ['command', 'cmd', 'function', 'script', 'shell'];
250
246
  if (typeof args === 'object' && args) {
251
247
  for (const [k, v] of Object.entries(args)) {
252
- if (fields.includes(k.toLowerCase()) && typeof v === 'string') cmds.push(v);
248
+ if (fields.includes(k.toLowerCase()) && typeof v === 'string') {
249
+ // Split chained commands: cd /path && npm install \u2192 [cd /path, npm install]
250
+ for (const part of v.split(/\\s*(?:&&|\\|\\||;|\\|)\\s*/)) {
251
+ const trimmed = part.trim();
252
+ if (trimmed) cmds.push(trimmed);
253
+ }
254
+ }
253
255
  }
254
256
  }
255
257
  return cmds;
@@ -321,7 +323,7 @@ process.stdin.on('end', async () => {
321
323
  const args = data.tool_input || {};
322
324
 
323
325
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
324
- const allStrings = scanStrings(args).map(s => s.replace(/\\\\\\\\/g, '/').toLowerCase());
326
+ const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
325
327
  const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
326
328
  for (const s of allStrings) {
327
329
  for (const p of protectedPaths) {
@@ -333,10 +335,12 @@ process.stdin.on('end', async () => {
333
335
  }
334
336
  }
335
337
 
336
- // Load policy
338
+ // Load policy (use cwd from hook data if available)
339
+ const hookCwd = data.cwd || process.cwd();
337
340
  let policy;
338
341
  try {
339
- policy = JSON.parse(readFileSync(resolve('policy.json'), 'utf-8'));
342
+ const policyPath = resolve(hookCwd, 'policy.json');
343
+ policy = JSON.parse(readFileSync(policyPath, 'utf-8'));
340
344
  } catch {
341
345
  process.exit(0); // No policy = allow all
342
346
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
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": {