@solongate/proxy 0.6.4 → 0.6.5
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/dist/index.js +58 -19
- package/dist/init.js +58 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -955,13 +955,31 @@ var init_init = __esm({
|
|
|
955
955
|
* SolonGate Policy Guard Hook (PreToolUse)
|
|
956
956
|
* Reads policy.json and blocks tool calls that violate constraints.
|
|
957
957
|
* Exit code 2 = BLOCK, exit code 0 = ALLOW.
|
|
958
|
+
* Logs ALL decisions (ALLOW + DENY) to SolonGate Cloud.
|
|
958
959
|
* Auto-installed by: npx @solongate/proxy init
|
|
959
960
|
*/
|
|
960
|
-
import { readFileSync } from 'node:fs';
|
|
961
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
961
962
|
import { resolve } from 'node:path';
|
|
962
963
|
|
|
963
|
-
|
|
964
|
-
|
|
964
|
+
// \u2500\u2500 Load API key from .env file (Claude Code doesn't load .env into process.env) \u2500\u2500
|
|
965
|
+
function loadEnvKey(dir) {
|
|
966
|
+
try {
|
|
967
|
+
const envPath = resolve(dir, '.env');
|
|
968
|
+
if (!existsSync(envPath)) return {};
|
|
969
|
+
const lines = readFileSync(envPath, 'utf-8').split('\\n');
|
|
970
|
+
const env = {};
|
|
971
|
+
for (const line of lines) {
|
|
972
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
973
|
+
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
974
|
+
}
|
|
975
|
+
return env;
|
|
976
|
+
} catch { return {}; }
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const hookCwdEarly = process.cwd();
|
|
980
|
+
const dotenv = loadEnvKey(hookCwdEarly);
|
|
981
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
982
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
965
983
|
|
|
966
984
|
// \u2500\u2500 Glob Matching \u2500\u2500
|
|
967
985
|
function matchGlob(str, pattern) {
|
|
@@ -1157,21 +1175,25 @@ process.stdin.on('end', async () => {
|
|
|
1157
1175
|
}
|
|
1158
1176
|
|
|
1159
1177
|
const reason = evaluate(policy, args);
|
|
1178
|
+
const decision = reason ? 'DENY' : 'ALLOW';
|
|
1179
|
+
|
|
1180
|
+
// \u2500\u2500 Log ALL decisions to SolonGate Cloud \u2500\u2500
|
|
1181
|
+
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
1182
|
+
try {
|
|
1183
|
+
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
1184
|
+
method: 'POST',
|
|
1185
|
+
headers: { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' },
|
|
1186
|
+
body: JSON.stringify({
|
|
1187
|
+
tool: data.tool_name || '', arguments: args,
|
|
1188
|
+
decision, reason: reason || 'allowed by policy',
|
|
1189
|
+
source: 'claude-code-guard',
|
|
1190
|
+
}),
|
|
1191
|
+
signal: AbortSignal.timeout(3000),
|
|
1192
|
+
});
|
|
1193
|
+
} catch {}
|
|
1194
|
+
}
|
|
1160
1195
|
|
|
1161
1196
|
if (reason) {
|
|
1162
|
-
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
1163
|
-
try {
|
|
1164
|
-
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
1165
|
-
method: 'POST',
|
|
1166
|
-
headers: { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' },
|
|
1167
|
-
body: JSON.stringify({
|
|
1168
|
-
tool: data.tool_name || '', arguments: args,
|
|
1169
|
-
decision: 'DENY', reason, source: 'claude-code-guard',
|
|
1170
|
-
}),
|
|
1171
|
-
signal: AbortSignal.timeout(3000),
|
|
1172
|
-
});
|
|
1173
|
-
} catch {}
|
|
1174
|
-
}
|
|
1175
1197
|
process.stderr.write(reason);
|
|
1176
1198
|
process.exit(2);
|
|
1177
1199
|
}
|
|
@@ -1182,12 +1204,29 @@ process.stdin.on('end', async () => {
|
|
|
1182
1204
|
AUDIT_SCRIPT = `#!/usr/bin/env node
|
|
1183
1205
|
/**
|
|
1184
1206
|
* SolonGate Audit Hook for Claude Code (PostToolUse)
|
|
1185
|
-
* Logs
|
|
1207
|
+
* Logs tool execution results to SolonGate Cloud.
|
|
1186
1208
|
* Auto-installed by: npx @solongate/proxy init
|
|
1187
1209
|
*/
|
|
1210
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
1211
|
+
import { resolve } from 'node:path';
|
|
1212
|
+
|
|
1213
|
+
function loadEnvKey(dir) {
|
|
1214
|
+
try {
|
|
1215
|
+
const envPath = resolve(dir, '.env');
|
|
1216
|
+
if (!existsSync(envPath)) return {};
|
|
1217
|
+
const lines = readFileSync(envPath, 'utf-8').split('\\n');
|
|
1218
|
+
const env = {};
|
|
1219
|
+
for (const line of lines) {
|
|
1220
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
1221
|
+
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
1222
|
+
}
|
|
1223
|
+
return env;
|
|
1224
|
+
} catch { return {}; }
|
|
1225
|
+
}
|
|
1188
1226
|
|
|
1189
|
-
const
|
|
1190
|
-
const
|
|
1227
|
+
const dotenv = loadEnvKey(process.cwd());
|
|
1228
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
1229
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
1191
1230
|
|
|
1192
1231
|
if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
|
|
1193
1232
|
|
package/dist/init.js
CHANGED
|
@@ -144,13 +144,31 @@ var GUARD_SCRIPT = `#!/usr/bin/env node
|
|
|
144
144
|
* SolonGate Policy Guard Hook (PreToolUse)
|
|
145
145
|
* Reads policy.json and blocks tool calls that violate constraints.
|
|
146
146
|
* Exit code 2 = BLOCK, exit code 0 = ALLOW.
|
|
147
|
+
* Logs ALL decisions (ALLOW + DENY) to SolonGate Cloud.
|
|
147
148
|
* Auto-installed by: npx @solongate/proxy init
|
|
148
149
|
*/
|
|
149
|
-
import { readFileSync } from 'node:fs';
|
|
150
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
150
151
|
import { resolve } from 'node:path';
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
// \u2500\u2500 Load API key from .env file (Claude Code doesn't load .env into process.env) \u2500\u2500
|
|
154
|
+
function loadEnvKey(dir) {
|
|
155
|
+
try {
|
|
156
|
+
const envPath = resolve(dir, '.env');
|
|
157
|
+
if (!existsSync(envPath)) return {};
|
|
158
|
+
const lines = readFileSync(envPath, 'utf-8').split('\\n');
|
|
159
|
+
const env = {};
|
|
160
|
+
for (const line of lines) {
|
|
161
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
162
|
+
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
163
|
+
}
|
|
164
|
+
return env;
|
|
165
|
+
} catch { return {}; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const hookCwdEarly = process.cwd();
|
|
169
|
+
const dotenv = loadEnvKey(hookCwdEarly);
|
|
170
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
171
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
154
172
|
|
|
155
173
|
// \u2500\u2500 Glob Matching \u2500\u2500
|
|
156
174
|
function matchGlob(str, pattern) {
|
|
@@ -346,21 +364,25 @@ process.stdin.on('end', async () => {
|
|
|
346
364
|
}
|
|
347
365
|
|
|
348
366
|
const reason = evaluate(policy, args);
|
|
367
|
+
const decision = reason ? 'DENY' : 'ALLOW';
|
|
368
|
+
|
|
369
|
+
// \u2500\u2500 Log ALL decisions to SolonGate Cloud \u2500\u2500
|
|
370
|
+
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
371
|
+
try {
|
|
372
|
+
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
373
|
+
method: 'POST',
|
|
374
|
+
headers: { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' },
|
|
375
|
+
body: JSON.stringify({
|
|
376
|
+
tool: data.tool_name || '', arguments: args,
|
|
377
|
+
decision, reason: reason || 'allowed by policy',
|
|
378
|
+
source: 'claude-code-guard',
|
|
379
|
+
}),
|
|
380
|
+
signal: AbortSignal.timeout(3000),
|
|
381
|
+
});
|
|
382
|
+
} catch {}
|
|
383
|
+
}
|
|
349
384
|
|
|
350
385
|
if (reason) {
|
|
351
|
-
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
352
|
-
try {
|
|
353
|
-
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
354
|
-
method: 'POST',
|
|
355
|
-
headers: { 'Authorization': 'Bearer ' + API_KEY, 'Content-Type': 'application/json' },
|
|
356
|
-
body: JSON.stringify({
|
|
357
|
-
tool: data.tool_name || '', arguments: args,
|
|
358
|
-
decision: 'DENY', reason, source: 'claude-code-guard',
|
|
359
|
-
}),
|
|
360
|
-
signal: AbortSignal.timeout(3000),
|
|
361
|
-
});
|
|
362
|
-
} catch {}
|
|
363
|
-
}
|
|
364
386
|
process.stderr.write(reason);
|
|
365
387
|
process.exit(2);
|
|
366
388
|
}
|
|
@@ -371,12 +393,29 @@ process.stdin.on('end', async () => {
|
|
|
371
393
|
var AUDIT_SCRIPT = `#!/usr/bin/env node
|
|
372
394
|
/**
|
|
373
395
|
* SolonGate Audit Hook for Claude Code (PostToolUse)
|
|
374
|
-
* Logs
|
|
396
|
+
* Logs tool execution results to SolonGate Cloud.
|
|
375
397
|
* Auto-installed by: npx @solongate/proxy init
|
|
376
398
|
*/
|
|
399
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
400
|
+
import { resolve } from 'node:path';
|
|
401
|
+
|
|
402
|
+
function loadEnvKey(dir) {
|
|
403
|
+
try {
|
|
404
|
+
const envPath = resolve(dir, '.env');
|
|
405
|
+
if (!existsSync(envPath)) return {};
|
|
406
|
+
const lines = readFileSync(envPath, 'utf-8').split('\\n');
|
|
407
|
+
const env = {};
|
|
408
|
+
for (const line of lines) {
|
|
409
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
410
|
+
if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '').trim();
|
|
411
|
+
}
|
|
412
|
+
return env;
|
|
413
|
+
} catch { return {}; }
|
|
414
|
+
}
|
|
377
415
|
|
|
378
|
-
const
|
|
379
|
-
const
|
|
416
|
+
const dotenv = loadEnvKey(process.cwd());
|
|
417
|
+
const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
|
|
418
|
+
const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
|
|
380
419
|
|
|
381
420
|
if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
|
|
382
421
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
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": {
|