@solongate/proxy 0.49.0 → 0.50.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/README.md +72 -72
- package/dist/audit/index.js +0 -0
- package/dist/index.js +79 -782
- package/dist/login.js +2 -3
- package/hooks/audit.mjs +379 -293
- package/hooks/guard.bundled.mjs +7740 -7738
- package/hooks/guard.mjs +3 -1
- package/hooks/stop.mjs +75 -75
- package/package.json +76 -80
- package/dist/init.d.ts +0 -18
- package/dist/init.js +0 -910
package/hooks/guard.mjs
CHANGED
|
@@ -31,7 +31,7 @@ import { createHash } from 'node:crypto';
|
|
|
31
31
|
// the installed hook self-updates when the cloud version is higher (see
|
|
32
32
|
// maybeSelfUpdate). This is what makes guard fixes propagate without a manual
|
|
33
33
|
// reinstall — the same trust model as the OPA WASM this hook already runs.
|
|
34
|
-
const HOOK_VERSION =
|
|
34
|
+
const HOOK_VERSION = 18;
|
|
35
35
|
|
|
36
36
|
// Safe file read with size limit (1MB max) to prevent DoS via large files
|
|
37
37
|
const MAX_FILE_READ = 1024 * 1024; // 1MB
|
|
@@ -1419,6 +1419,7 @@ process.stdin.on('end', async () => {
|
|
|
1419
1419
|
reason: 'ghost path (hidden from agent)',
|
|
1420
1420
|
permission: guessPermission(toolName),
|
|
1421
1421
|
source: `${AGENT_TYPE}-guard`, agent_id: AGENT_TYPE, agent_name: AGENT_NAME,
|
|
1422
|
+
session_id: data.session_id || '',
|
|
1422
1423
|
evaluation_time_ms: Date.now() - _evalStart,
|
|
1423
1424
|
}),
|
|
1424
1425
|
signal: AbortSignal.timeout(3000),
|
|
@@ -1497,6 +1498,7 @@ process.stdin.on('end', async () => {
|
|
|
1497
1498
|
permission: guessPermission(toolName),
|
|
1498
1499
|
source: `${AGENT_TYPE}-guard`,
|
|
1499
1500
|
agent_id: AGENT_TYPE, agent_name: AGENT_NAME,
|
|
1501
|
+
session_id: data.session_id || '',
|
|
1500
1502
|
evaluation_time_ms: Date.now() - _evalStart,
|
|
1501
1503
|
};
|
|
1502
1504
|
// PI hook layer removed — piResult fields no longer attached.
|
package/hooks/stop.mjs
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* SolonGate Stop Hook (Stop event)
|
|
4
|
-
* Logs a single ALLOW when Claude responds WITHOUT any tool calls.
|
|
5
|
-
* If tool calls were made, audit.mjs already logged them — this hook skips.
|
|
6
|
-
* Auto-installed by: npx @solongate/proxy
|
|
7
|
-
*/
|
|
8
|
-
import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
9
|
-
import { resolve, join } from 'node:path';
|
|
10
|
-
|
|
11
|
-
function loadEnvKey(dir) {
|
|
12
|
-
try {
|
|
13
|
-
const envPath = resolve(dir, '.env');
|
|
14
|
-
if (!existsSync(envPath)) return {};
|
|
15
|
-
const lines = readFileSync(envPath, 'utf-8').split('\n');
|
|
16
|
-
const env = {};
|
|
17
|
-
for (const line of lines) {
|
|
18
|
-
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
19
|
-
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
20
|
-
}
|
|
21
|
-
return env;
|
|
22
|
-
} catch { return {}; }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const dotenv = loadEnvKey(process.cwd());
|
|
26
|
-
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
27
|
-
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
28
|
-
|
|
29
|
-
// Agent identity from CLI args: node stop.mjs <agent_id> <agent_name>
|
|
30
|
-
const AGENT_ID = process.argv[2] || 'claude-code';
|
|
31
|
-
const AGENT_NAME = process.argv[3] || 'Claude Code';
|
|
32
|
-
|
|
33
|
-
if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
|
|
34
|
-
|
|
35
|
-
// Flag file: audit.mjs writes this when a tool call was logged in this turn.
|
|
36
|
-
// If the flag exists, tool calls were made → skip (already logged).
|
|
37
|
-
// If the flag doesn't exist, no tool calls → log 1 ALLOW for the text response.
|
|
38
|
-
const flagDir = resolve('.solongate');
|
|
39
|
-
const flagFile = join(flagDir, '.last-tool-call');
|
|
40
|
-
|
|
41
|
-
let input = '';
|
|
42
|
-
process.stdin.on('data', c => input += c);
|
|
43
|
-
process.stdin.on('end', async () => {
|
|
44
|
-
try {
|
|
45
|
-
// Check if tool calls were made in this turn
|
|
46
|
-
if (existsSync(flagFile)) {
|
|
47
|
-
// Tool calls happened → audit.mjs already logged them. Clean up flag.
|
|
48
|
-
try { unlinkSync(flagFile); } catch {}
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// No tool calls → log 1 ALLOW for the text-only response (fire-and-forget)
|
|
53
|
-
fetch(`${API_URL}/api/v1/audit-logs`, {
|
|
54
|
-
method: 'POST',
|
|
55
|
-
headers: {
|
|
56
|
-
'Authorization': `Bearer ${API_KEY}`,
|
|
57
|
-
'Content-Type': 'application/json',
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify({
|
|
60
|
-
tool: '_response',
|
|
61
|
-
arguments: {},
|
|
62
|
-
decision: 'ALLOW',
|
|
63
|
-
reason: 'text response (no tool calls)',
|
|
64
|
-
source: `${AGENT_ID}-hook`,
|
|
65
|
-
evaluationTimeMs: 0,
|
|
66
|
-
agent_id: AGENT_ID,
|
|
67
|
-
agent_name: AGENT_NAME,
|
|
68
|
-
}),
|
|
69
|
-
signal: AbortSignal.timeout(5000),
|
|
70
|
-
}).catch(() => {}).finally(() => process.exit(0));
|
|
71
|
-
setTimeout(() => process.exit(0), 3000);
|
|
72
|
-
} catch {
|
|
73
|
-
process.exit(0);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SolonGate Stop Hook (Stop event)
|
|
4
|
+
* Logs a single ALLOW when Claude responds WITHOUT any tool calls.
|
|
5
|
+
* If tool calls were made, audit.mjs already logged them — this hook skips.
|
|
6
|
+
* Auto-installed by: npx @solongate/proxy login
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { resolve, join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
function loadEnvKey(dir) {
|
|
12
|
+
try {
|
|
13
|
+
const envPath = resolve(dir, '.env');
|
|
14
|
+
if (!existsSync(envPath)) return {};
|
|
15
|
+
const lines = readFileSync(envPath, 'utf-8').split('\n');
|
|
16
|
+
const env = {};
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
19
|
+
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
20
|
+
}
|
|
21
|
+
return env;
|
|
22
|
+
} catch { return {}; }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const dotenv = loadEnvKey(process.cwd());
|
|
26
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
27
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
28
|
+
|
|
29
|
+
// Agent identity from CLI args: node stop.mjs <agent_id> <agent_name>
|
|
30
|
+
const AGENT_ID = process.argv[2] || 'claude-code';
|
|
31
|
+
const AGENT_NAME = process.argv[3] || 'Claude Code';
|
|
32
|
+
|
|
33
|
+
if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
|
|
34
|
+
|
|
35
|
+
// Flag file: audit.mjs writes this when a tool call was logged in this turn.
|
|
36
|
+
// If the flag exists, tool calls were made → skip (already logged).
|
|
37
|
+
// If the flag doesn't exist, no tool calls → log 1 ALLOW for the text response.
|
|
38
|
+
const flagDir = resolve('.solongate');
|
|
39
|
+
const flagFile = join(flagDir, '.last-tool-call');
|
|
40
|
+
|
|
41
|
+
let input = '';
|
|
42
|
+
process.stdin.on('data', c => input += c);
|
|
43
|
+
process.stdin.on('end', async () => {
|
|
44
|
+
try {
|
|
45
|
+
// Check if tool calls were made in this turn
|
|
46
|
+
if (existsSync(flagFile)) {
|
|
47
|
+
// Tool calls happened → audit.mjs already logged them. Clean up flag.
|
|
48
|
+
try { unlinkSync(flagFile); } catch {}
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// No tool calls → log 1 ALLOW for the text-only response (fire-and-forget)
|
|
53
|
+
fetch(`${API_URL}/api/v1/audit-logs`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
tool: '_response',
|
|
61
|
+
arguments: {},
|
|
62
|
+
decision: 'ALLOW',
|
|
63
|
+
reason: 'text response (no tool calls)',
|
|
64
|
+
source: `${AGENT_ID}-hook`,
|
|
65
|
+
evaluationTimeMs: 0,
|
|
66
|
+
agent_id: AGENT_ID,
|
|
67
|
+
agent_name: AGENT_NAME,
|
|
68
|
+
}),
|
|
69
|
+
signal: AbortSignal.timeout(5000),
|
|
70
|
+
}).catch(() => {}).finally(() => process.exit(0));
|
|
71
|
+
setTimeout(() => process.exit(0), 3000);
|
|
72
|
+
} catch {
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
});
|
package/package.json
CHANGED
|
@@ -1,80 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"solongate": "./dist/index.js",
|
|
8
|
-
"solongate-proxy": "./dist/index.js",
|
|
9
|
-
"solongate-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"tool-security",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
},
|
|
63
|
-
"
|
|
64
|
-
"@
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"@solongate/tsconfig": "
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"typecheck": "tsc --noEmit",
|
|
78
|
-
"clean": "rm -rf dist .turbo"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@solongate/proxy",
|
|
3
|
+
"version": "0.50.0",
|
|
4
|
+
"description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"solongate": "./dist/index.js",
|
|
8
|
+
"solongate-proxy": "./dist/index.js",
|
|
9
|
+
"solongate-audit": "./dist/audit/index.js",
|
|
10
|
+
"proxy": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/lib.js",
|
|
13
|
+
"types": "./dist/lib.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/lib.d.ts",
|
|
17
|
+
"import": "./dist/lib.js"
|
|
18
|
+
},
|
|
19
|
+
"./cli": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"hooks",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup && pnpm build:types && pnpm build:hooks",
|
|
31
|
+
"build:types": "node scripts/build-types.mjs",
|
|
32
|
+
"build:hooks": "node scripts/bundle-hooks.mjs",
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"clean": "rm -rf dist .turbo"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"ai-tool-security",
|
|
39
|
+
"ai-tool-proxy",
|
|
40
|
+
"security",
|
|
41
|
+
"proxy",
|
|
42
|
+
"gateway",
|
|
43
|
+
"firewall",
|
|
44
|
+
"ai-security",
|
|
45
|
+
"tool-security",
|
|
46
|
+
"claude",
|
|
47
|
+
"openclaw",
|
|
48
|
+
"solongate",
|
|
49
|
+
"prompt-injection",
|
|
50
|
+
"path-traversal",
|
|
51
|
+
"rate-limiting"
|
|
52
|
+
],
|
|
53
|
+
"author": "SolonGate <hello@solongate.com>",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"homepage": "https://solongate.com",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "https://github.com/solongate/solongate"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18.0.0"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
65
|
+
"zod": "^3.25.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@open-policy-agent/opa-wasm": "^1.10.0",
|
|
69
|
+
"chalk": "^5.3.0",
|
|
70
|
+
"@solongate/tsconfig": "workspace:*",
|
|
71
|
+
"esbuild": "^0.19.12",
|
|
72
|
+
"tsup": "^8.3.0",
|
|
73
|
+
"tsx": "^4.19.0",
|
|
74
|
+
"typescript": "^5.7.0"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/init.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* solongate init — Automatically wrap MCP servers with SolonGate protection.
|
|
4
|
-
*
|
|
5
|
-
* Reads your existing MCP configuration, wraps each server with the
|
|
6
|
-
* SolonGate proxy, and writes the protected configuration back.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* solongate-init [options]
|
|
10
|
-
*
|
|
11
|
-
* Options:
|
|
12
|
-
* --config <path> Path to MCP config file (default: auto-detect)
|
|
13
|
-
* --policy <file> Custom policy JSON file (auto-detects policy.json)
|
|
14
|
-
* --all Protect all servers without prompting
|
|
15
|
-
* --dry-run Show what would change without writing
|
|
16
|
-
* --restore Restore original config from backup
|
|
17
|
-
*/
|
|
18
|
-
export {};
|