@solongate/proxy 0.15.2 → 0.15.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.
- package/hooks/guard.mjs +71 -2
- package/package.json +1 -1
package/hooks/guard.mjs
CHANGED
|
@@ -290,9 +290,57 @@ process.stdin.on('end', async () => {
|
|
|
290
290
|
const args = data.tool_input || {};
|
|
291
291
|
|
|
292
292
|
// ── Self-protection: block access to hook files and settings ──
|
|
293
|
-
|
|
294
|
-
const protectedPaths = [
|
|
293
|
+
// Hardcoded, no bypass possible — runs before policy/PI config
|
|
294
|
+
const protectedPaths = [
|
|
295
|
+
'.solongate', '.claude', '.cursor', '.gemini', '.antigravity', '.openclaw', '.perplexity',
|
|
296
|
+
'policy.json', '.mcp.json',
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
// Strip shell quotes/escapes: .sol'on'gate → .solongate, .sol"on"gate → .solongate
|
|
300
|
+
function stripShellQuotes(s) {
|
|
301
|
+
return s.replace(/\\(.)/g, '$1').replace(/'/g, '').replace(/"/g, '');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check if a glob/wildcard pattern could match any protected path
|
|
305
|
+
// e.g. ".antig*" matches ".antigravity", "/path/.sol*" matches ".solongate"
|
|
306
|
+
function globMatchesProtected(s) {
|
|
307
|
+
if (!s.includes('*') && !s.includes('?')) return null;
|
|
308
|
+
// Extract all path segments and the full string to test
|
|
309
|
+
const segments = s.split('/').filter(Boolean);
|
|
310
|
+
const candidates = [s, ...segments];
|
|
311
|
+
for (const candidate of candidates) {
|
|
312
|
+
if (!candidate.includes('*') && !candidate.includes('?')) continue;
|
|
313
|
+
for (const p of protectedPaths) {
|
|
314
|
+
// Build regex from glob: * → .*, ? → .
|
|
315
|
+
const escaped = candidate.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
|
|
316
|
+
try {
|
|
317
|
+
if (new RegExp('^' + escaped + '$', 'i').test(p)) return p;
|
|
318
|
+
} catch { /* invalid regex, skip */ }
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Normalize: lowercase, forward slashes, strip shell quotes
|
|
325
|
+
const rawStrings = scanStrings(args).map(s => s.replace(/\\/g, '/').toLowerCase());
|
|
326
|
+
const allStrings = [];
|
|
327
|
+
for (const s of rawStrings) {
|
|
328
|
+
allStrings.push(s);
|
|
329
|
+
// Also add quote-stripped version
|
|
330
|
+
const stripped = stripShellQuotes(s);
|
|
331
|
+
if (stripped !== s) allStrings.push(stripped);
|
|
332
|
+
// Split by spaces (for commands like "rm -rf .sol'on'gate .cl*")
|
|
333
|
+
for (const tok of s.split(/\s+/)) {
|
|
334
|
+
if (tok !== s) {
|
|
335
|
+
allStrings.push(tok);
|
|
336
|
+
const strippedTok = stripShellQuotes(tok);
|
|
337
|
+
if (strippedTok !== tok) allStrings.push(strippedTok);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
295
342
|
for (const s of allStrings) {
|
|
343
|
+
// Direct match
|
|
296
344
|
for (const p of protectedPaths) {
|
|
297
345
|
if (s.includes(p)) {
|
|
298
346
|
const msg = 'SOLONGATE: Access to protected file "' + p + '" is blocked';
|
|
@@ -314,6 +362,27 @@ process.stdin.on('end', async () => {
|
|
|
314
362
|
process.exit(2);
|
|
315
363
|
}
|
|
316
364
|
}
|
|
365
|
+
// Wildcard/glob match
|
|
366
|
+
const globHit = globMatchesProtected(s);
|
|
367
|
+
if (globHit) {
|
|
368
|
+
const msg = 'SOLONGATE: Wildcard pattern "' + s + '" matches protected path "' + globHit + '" — blocked';
|
|
369
|
+
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
370
|
+
try {
|
|
371
|
+
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
372
|
+
method: 'POST',
|
|
373
|
+
headers: { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' },
|
|
374
|
+
body: JSON.stringify({
|
|
375
|
+
tool: data.tool_name || '', arguments: args,
|
|
376
|
+
decision: 'DENY', reason: msg,
|
|
377
|
+
source: 'claude-code-guard',
|
|
378
|
+
}),
|
|
379
|
+
signal: AbortSignal.timeout(3000),
|
|
380
|
+
});
|
|
381
|
+
} catch {}
|
|
382
|
+
}
|
|
383
|
+
process.stderr.write(msg);
|
|
384
|
+
process.exit(2);
|
|
385
|
+
}
|
|
317
386
|
}
|
|
318
387
|
|
|
319
388
|
// ── Fetch PI config from Cloud ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.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": {
|