@solongate/proxy 0.25.7 → 0.26.1
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 +21 -29
- package/hooks/guard.mjs +126 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6135,45 +6135,37 @@ var PolicySyncManager = class {
|
|
|
6135
6135
|
};
|
|
6136
6136
|
|
|
6137
6137
|
// src/ai-judge.ts
|
|
6138
|
-
var SYSTEM_PROMPT = `You are a security judge for an
|
|
6138
|
+
var SYSTEM_PROMPT = `You are a security judge for an AI coding tool. You evaluate tool calls and decide if they should be ALLOWED or DENIED.
|
|
6139
6139
|
|
|
6140
6140
|
You will receive a JSON object with:
|
|
6141
|
-
- "tool": the
|
|
6141
|
+
- "tool": the tool name being called
|
|
6142
6142
|
- "arguments": the tool's arguments
|
|
6143
|
-
- "protected_files": list of files that must NEVER be
|
|
6144
|
-
- "protected_paths": list of directories
|
|
6143
|
+
- "protected_files": EXACT list of files that must NEVER be accessed. ONLY these specific files are protected \u2014 nothing else.
|
|
6144
|
+
- "protected_paths": EXACT list of directories that must NEVER be accessed. ONLY these specific paths are protected \u2014 nothing else.
|
|
6145
6145
|
- "denied_actions": list of actions that are never allowed
|
|
6146
6146
|
|
|
6147
|
-
|
|
6148
|
-
- Read, display, copy, move, or exfiltrate any protected file \u2014 even through:
|
|
6149
|
-
- Shell glob patterns (e.g., "cred*" could match "credentials.json")
|
|
6150
|
-
- Command substitution ($(...), backticks)
|
|
6151
|
-
- Process substitution (<(cat file), <(command file)) \u2014 ALWAYS check inside <(...) for protected files
|
|
6152
|
-
- Variable interpolation or string concatenation (e.g., f=".en"; cat \${f}v builds ".env")
|
|
6153
|
-
- Partial variable construction \u2014 if shell variables are concatenated to form a filename, analyze the RESULT
|
|
6154
|
-
- Input redirection (< file)
|
|
6155
|
-
- Script files that might read protected files
|
|
6156
|
-
- Find/exec patterns
|
|
6157
|
-
- Multi-stage operations: if command A archives/copies a protected file and command B reads the copy, DENY the ENTIRE chain (e.g., "tar czf /tmp/x.tgz .env && tar xzf /tmp/x.tgz -O")
|
|
6158
|
-
- Any Unix/Windows utility that reads file content (cat, head, tail, less, more, type, perl, awk, sed, sort, diff, comm, paste, tee, xxd, od, strings, dd, tr, tar, zip, etc.)
|
|
6159
|
-
- Delete, modify, or overwrite any protected file or path
|
|
6160
|
-
- Send data to external URLs, webhooks, or attacker-controlled endpoints
|
|
6161
|
-
- Execute remotely downloaded code (curl|bash, wget|sh, etc.)
|
|
6162
|
-
- Leak environment variables (printenv, env, /proc/self/environ, npm run env, process.env)
|
|
6163
|
-
- Create scripts that bypass security controls
|
|
6147
|
+
IMPORTANT: You must ONLY protect files and paths that are EXPLICITLY listed in protected_files and protected_paths. If a file is NOT in the list, it is NOT protected and access should be ALLOWED. Do NOT invent or assume additional protected files.
|
|
6164
6148
|
|
|
6165
|
-
|
|
6166
|
-
-
|
|
6167
|
-
-
|
|
6168
|
-
-
|
|
6169
|
-
-
|
|
6149
|
+
DENY if the tool call could, directly or indirectly, access a file from the protected_files list \u2014 even through:
|
|
6150
|
+
- Shell glob patterns (e.g., "cred*" could match "credentials.json" IF credentials.json is in protected_files)
|
|
6151
|
+
- Command substitution ($(...), backticks)
|
|
6152
|
+
- Process substitution (<(cat file)) \u2014 check inside <(...) for protected files
|
|
6153
|
+
- Variable interpolation (e.g., f=".en"; cat \${f}v builds ".env" \u2014 DENY only if .env is in protected_files)
|
|
6154
|
+
- Input redirection (< file)
|
|
6155
|
+
- Multi-stage operations: tar/cp a protected file then read the copy \u2014 DENY the entire chain
|
|
6156
|
+
- Any utility that reads file content (cat, head, tail, less, perl, awk, sed, xxd, od, strings, dd, etc.)
|
|
6157
|
+
|
|
6158
|
+
Also DENY if:
|
|
6159
|
+
- The command sends data to external URLs (curl -d, wget --post)
|
|
6160
|
+
- The command leaks environment variables (printenv, env, process.env)
|
|
6161
|
+
- The command executes remotely downloaded code (curl|bash)
|
|
6170
6162
|
|
|
6171
6163
|
ALLOW if:
|
|
6172
|
-
- The
|
|
6164
|
+
- The file is NOT in protected_files \u2014 even if cat, head, etc. is used. Reading non-protected files is normal.
|
|
6165
|
+
- The action is a normal development operation (ls, git status, npm build, cat app.js, etc.)
|
|
6173
6166
|
- The action does not touch any protected file or path
|
|
6174
|
-
- The action is clearly benign (creating hello.js, reading public docs, etc.)
|
|
6175
6167
|
|
|
6176
|
-
|
|
6168
|
+
CRITICAL: Only DENY access to files EXPLICITLY in the protected_files list. "cat app.js" is ALLOWED if app.js is not in protected_files. Do NOT over-block.
|
|
6177
6169
|
|
|
6178
6170
|
Respond with ONLY valid JSON, no markdown, no explanation outside the JSON:
|
|
6179
6171
|
{"decision": "ALLOW" or "DENY", "reason": "brief one-line explanation", "confidence": 0.0 to 1.0}`;
|
package/hooks/guard.mjs
CHANGED
|
@@ -1013,7 +1013,132 @@ process.stdin.on('end', async () => {
|
|
|
1013
1013
|
process.exit(0); // No policy = allow all
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
|
-
|
|
1016
|
+
let reason = evaluate(policy, args);
|
|
1017
|
+
|
|
1018
|
+
// ── AI Judge: semantic intent analysis (runs when policy ALLOWs) ──
|
|
1019
|
+
if (!reason) {
|
|
1020
|
+
const GROQ_KEY = process.env.GROQ_API_KEY || dotenv.GROQ_API_KEY || '';
|
|
1021
|
+
let aiJudgeEnabled = false;
|
|
1022
|
+
let aiJudgeModel = 'llama-3.1-8b-instant';
|
|
1023
|
+
let aiJudgeEndpoint = 'https://api.groq.com/openai';
|
|
1024
|
+
let aiJudgeTimeout = 5000;
|
|
1025
|
+
|
|
1026
|
+
// Check cloud config for AI Judge settings
|
|
1027
|
+
if (API_KEY && API_KEY.startsWith('sg_live_')) {
|
|
1028
|
+
try {
|
|
1029
|
+
const cfgRes = await fetch(API_URL + '/api/v1/project-config/ai-judge', {
|
|
1030
|
+
headers: { 'Authorization': 'Bearer ' + API_KEY },
|
|
1031
|
+
signal: AbortSignal.timeout(3000),
|
|
1032
|
+
});
|
|
1033
|
+
if (cfgRes.ok) {
|
|
1034
|
+
const cfg = await cfgRes.json();
|
|
1035
|
+
aiJudgeEnabled = Boolean(cfg.enabled);
|
|
1036
|
+
if (cfg.model) aiJudgeModel = cfg.model;
|
|
1037
|
+
if (cfg.endpoint) aiJudgeEndpoint = cfg.endpoint;
|
|
1038
|
+
if (cfg.timeoutMs) aiJudgeTimeout = cfg.timeoutMs;
|
|
1039
|
+
}
|
|
1040
|
+
} catch {}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (aiJudgeEnabled && GROQ_KEY) {
|
|
1044
|
+
try {
|
|
1045
|
+
// Extract protected files/paths from policy
|
|
1046
|
+
const protectedFiles = [];
|
|
1047
|
+
const protectedPathsList = [];
|
|
1048
|
+
if (policy && policy.rules) {
|
|
1049
|
+
for (const rule of policy.rules) {
|
|
1050
|
+
if (rule.effect === 'DENY' && rule.enabled !== false) {
|
|
1051
|
+
if (rule.filenameConstraints && rule.filenameConstraints.denied) {
|
|
1052
|
+
for (const f of rule.filenameConstraints.denied) protectedFiles.push(f);
|
|
1053
|
+
}
|
|
1054
|
+
if (rule.pathConstraints && rule.pathConstraints.denied) {
|
|
1055
|
+
for (const p of rule.pathConstraints.denied) protectedPathsList.push(p);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const judgePayload = JSON.stringify({
|
|
1062
|
+
tool: toolName,
|
|
1063
|
+
arguments: args,
|
|
1064
|
+
protected_files: protectedFiles,
|
|
1065
|
+
protected_paths: protectedPathsList,
|
|
1066
|
+
denied_actions: ['file deletion', 'data exfiltration', 'remote code execution', 'environment variable leak', 'security control bypass'],
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
const systemPrompt = `You are a security judge for an AI coding tool. You evaluate tool calls and decide if they should be ALLOWED or DENIED.
|
|
1070
|
+
|
|
1071
|
+
You will receive a JSON object with:
|
|
1072
|
+
- "tool": the tool name being called (e.g., Bash, Write, Read, Edit)
|
|
1073
|
+
- "arguments": the tool's arguments
|
|
1074
|
+
- "protected_files": EXACT list of files that must NEVER be accessed. ONLY these specific files are protected — nothing else.
|
|
1075
|
+
- "protected_paths": EXACT list of directories that must NEVER be accessed. ONLY these specific paths are protected — nothing else.
|
|
1076
|
+
- "denied_actions": list of actions that are never allowed
|
|
1077
|
+
|
|
1078
|
+
IMPORTANT: You must ONLY protect files and paths that are EXPLICITLY listed in protected_files and protected_paths. If a file is NOT in the list, it is NOT protected and access should be ALLOWED. Do NOT invent or assume additional protected files.
|
|
1079
|
+
|
|
1080
|
+
DENY if the tool call could, directly or indirectly, access a file from the protected_files list — even through:
|
|
1081
|
+
- Shell glob patterns (e.g., "cred*" could match "credentials.json" IF credentials.json is in protected_files)
|
|
1082
|
+
- Command substitution ($(...), backticks)
|
|
1083
|
+
- Process substitution (<(cat file)) — check inside <(...) for protected files
|
|
1084
|
+
- Variable interpolation (e.g., f=".en"; cat \${f}v builds ".env" — DENY only if .env is in protected_files)
|
|
1085
|
+
- Input redirection (< file)
|
|
1086
|
+
- Multi-stage operations: tar/cp a protected file then read the copy — DENY the entire chain
|
|
1087
|
+
- Any utility that reads file content (cat, head, tail, less, perl, awk, sed, xxd, od, strings, dd, etc.)
|
|
1088
|
+
|
|
1089
|
+
Also DENY if:
|
|
1090
|
+
- The command sends data to external URLs (curl -d, wget --post)
|
|
1091
|
+
- The command leaks environment variables (printenv, env, process.env)
|
|
1092
|
+
- The command executes remotely downloaded code (curl|bash)
|
|
1093
|
+
|
|
1094
|
+
ALLOW if:
|
|
1095
|
+
- The file is NOT in protected_files — even if cat, head, etc. is used. Reading non-protected files is normal.
|
|
1096
|
+
- The action is a normal development operation (ls, git status, npm build, cat app.js, etc.)
|
|
1097
|
+
- The action does not touch any protected file or path
|
|
1098
|
+
|
|
1099
|
+
CRITICAL: Only DENY access to files EXPLICITLY in the protected_files list. "cat app.js" is ALLOWED if app.js is not in protected_files. "cat package.json" is ALLOWED if package.json is not in protected_files. Do NOT over-block.
|
|
1100
|
+
|
|
1101
|
+
Respond with ONLY valid JSON: {"decision": "ALLOW" or "DENY", "reason": "brief explanation", "confidence": 0.0 to 1.0}`;
|
|
1102
|
+
|
|
1103
|
+
const llmRes = await fetch(aiJudgeEndpoint + '/v1/chat/completions', {
|
|
1104
|
+
method: 'POST',
|
|
1105
|
+
headers: {
|
|
1106
|
+
'Content-Type': 'application/json',
|
|
1107
|
+
'Authorization': 'Bearer ' + GROQ_KEY,
|
|
1108
|
+
},
|
|
1109
|
+
body: JSON.stringify({
|
|
1110
|
+
model: aiJudgeModel,
|
|
1111
|
+
messages: [
|
|
1112
|
+
{ role: 'system', content: systemPrompt },
|
|
1113
|
+
{ role: 'user', content: judgePayload },
|
|
1114
|
+
],
|
|
1115
|
+
temperature: 0,
|
|
1116
|
+
max_tokens: 200,
|
|
1117
|
+
}),
|
|
1118
|
+
signal: AbortSignal.timeout(aiJudgeTimeout),
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
if (llmRes.ok) {
|
|
1122
|
+
const llmData = await llmRes.json();
|
|
1123
|
+
const content = llmData.choices?.[0]?.message?.content || '';
|
|
1124
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
1125
|
+
if (jsonMatch) {
|
|
1126
|
+
const verdict = JSON.parse(jsonMatch[0]);
|
|
1127
|
+
if (verdict.decision === 'DENY') {
|
|
1128
|
+
reason = '[AI Judge] ' + (verdict.reason || 'Blocked by semantic analysis');
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
} else {
|
|
1132
|
+
// Fail-closed: LLM error → DENY
|
|
1133
|
+
reason = '[AI Judge] LLM endpoint error (fail-closed)';
|
|
1134
|
+
}
|
|
1135
|
+
} catch (err) {
|
|
1136
|
+
// Fail-closed: timeout or parse error → DENY
|
|
1137
|
+
reason = '[AI Judge] ' + (err.message || 'error') + ' (fail-closed)';
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1017
1142
|
const decision = reason ? 'DENY' : 'ALLOW';
|
|
1018
1143
|
|
|
1019
1144
|
// ── Log ALL decisions to SolonGate Cloud ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
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": {
|