@solongate/proxy 0.6.4 → 0.6.6

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 +59 -20
  2. package/dist/init.js +59 -20
  3. 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
- const API_KEY = process.env.SOLONGATE_API_KEY || '';
964
- const API_URL = process.env.SOLONGATE_API_URL || 'https://api.solongate.com';
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) {
@@ -1135,7 +1153,7 @@ process.stdin.on('end', async () => {
1135
1153
 
1136
1154
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
1137
1155
  const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
1138
- const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
1156
+ const protectedPaths = ['.solongate', '.claude', '.cursor', 'policy.json', '.mcp.json'];
1139
1157
  for (const s of allStrings) {
1140
1158
  for (const p of protectedPaths) {
1141
1159
  if (s.includes(p)) {
@@ -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 ALL tool calls to SolonGate Cloud after execution.
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 API_KEY = process.env.SOLONGATE_API_KEY || '';
1190
- const API_URL = process.env.SOLONGATE_API_URL || 'https://api.solongate.com';
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
- const API_KEY = process.env.SOLONGATE_API_KEY || '';
153
- const API_URL = process.env.SOLONGATE_API_URL || 'https://api.solongate.com';
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) {
@@ -324,7 +342,7 @@ process.stdin.on('end', async () => {
324
342
 
325
343
  // \u2500\u2500 Self-protection: block access to hook files and settings \u2500\u2500
326
344
  const allStrings = scanStrings(args).map(s => s.replace(/\\\\/g, '/').toLowerCase());
327
- const protectedPaths = ['.solongate', '.claude/settings.json', '.cursor/settings.json', 'policy.json'];
345
+ const protectedPaths = ['.solongate', '.claude', '.cursor', 'policy.json', '.mcp.json'];
328
346
  for (const s of allStrings) {
329
347
  for (const p of protectedPaths) {
330
348
  if (s.includes(p)) {
@@ -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 ALL tool calls to SolonGate Cloud after execution.
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 API_KEY = process.env.SOLONGATE_API_KEY || '';
379
- const API_URL = process.env.SOLONGATE_API_URL || 'https://api.solongate.com';
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.4",
3
+ "version": "0.6.6",
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": {