@solongate/proxy 0.37.0 → 0.39.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/dist/index.js +21 -0
- package/dist/lib.js +21 -0
- package/hooks/guard.mjs +58 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5707,6 +5707,27 @@ var AiJudge = class _AiJudge {
|
|
|
5707
5707
|
}
|
|
5708
5708
|
this.consecutiveFailures = 0;
|
|
5709
5709
|
}
|
|
5710
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
5711
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
5712
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
5713
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
5714
|
+
if (!couldMatchProtected) {
|
|
5715
|
+
const allProtected = [...this.protectedFiles, ...this.protectedPaths];
|
|
5716
|
+
for (const p of allProtected) {
|
|
5717
|
+
const core = p.replace(/[*?[\]{}]/g, "").replace(/^\.+/, "").toLowerCase();
|
|
5718
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
5719
|
+
couldMatchProtected = true;
|
|
5720
|
+
break;
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
if (!couldMatchProtected) {
|
|
5725
|
+
return {
|
|
5726
|
+
decision: "ALLOW",
|
|
5727
|
+
reason: "No protected file/path referenced in arguments",
|
|
5728
|
+
confidence: 1
|
|
5729
|
+
};
|
|
5730
|
+
}
|
|
5710
5731
|
const sanitizedArgs = this.sanitizeArgs(args);
|
|
5711
5732
|
const userMessage = JSON.stringify({
|
|
5712
5733
|
tool: toolName,
|
package/dist/lib.js
CHANGED
|
@@ -4006,6 +4006,27 @@ var AiJudge = class _AiJudge {
|
|
|
4006
4006
|
}
|
|
4007
4007
|
this.consecutiveFailures = 0;
|
|
4008
4008
|
}
|
|
4009
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
4010
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
4011
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
4012
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
4013
|
+
if (!couldMatchProtected) {
|
|
4014
|
+
const allProtected = [...this.protectedFiles, ...this.protectedPaths];
|
|
4015
|
+
for (const p of allProtected) {
|
|
4016
|
+
const core = p.replace(/[*?[\]{}]/g, "").replace(/^\.+/, "").toLowerCase();
|
|
4017
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
4018
|
+
couldMatchProtected = true;
|
|
4019
|
+
break;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
if (!couldMatchProtected) {
|
|
4024
|
+
return {
|
|
4025
|
+
decision: "ALLOW",
|
|
4026
|
+
reason: "No protected file/path referenced in arguments",
|
|
4027
|
+
confidence: 1
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4009
4030
|
const sanitizedArgs = this.sanitizeArgs(args);
|
|
4010
4031
|
const userMessage = JSON.stringify({
|
|
4011
4032
|
tool: toolName,
|
package/hooks/guard.mjs
CHANGED
|
@@ -1134,7 +1134,9 @@ process.stdin.on('end', async () => {
|
|
|
1134
1134
|
|
|
1135
1135
|
// ── AI Judge: semantic intent analysis (runs when policy ALLOWs) ──
|
|
1136
1136
|
if (!reason) {
|
|
1137
|
-
|
|
1137
|
+
let GROQ_KEY = process.env.GROQ_API_KEY || dotenv.GROQ_API_KEY || '';
|
|
1138
|
+
// Skip placeholder values
|
|
1139
|
+
if (GROQ_KEY && (GROQ_KEY.includes('your_') || GROQ_KEY.includes('_here') || GROQ_KEY.length < 10)) GROQ_KEY = '';
|
|
1138
1140
|
let aiJudgeEnabled = false;
|
|
1139
1141
|
let aiJudgeModel = 'llama-3.1-8b-instant';
|
|
1140
1142
|
let aiJudgeEndpoint = 'https://api.groq.com/openai';
|
|
@@ -1192,45 +1194,63 @@ process.stdin.on('end', async () => {
|
|
|
1192
1194
|
}
|
|
1193
1195
|
}
|
|
1194
1196
|
|
|
1197
|
+
// Pre-filter: skip AI Judge if tool args clearly don't touch any protected file/path.
|
|
1198
|
+
// Only call LLM when there's a potential match or obfuscation attempt.
|
|
1199
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
1200
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
1201
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
1202
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
1203
|
+
if (!couldMatchProtected) {
|
|
1204
|
+
// Check if any protected file/path name (without glob chars) appears in args
|
|
1205
|
+
const allProtected = [...protectedFiles, ...protectedPathsList];
|
|
1206
|
+
for (const p of allProtected) {
|
|
1207
|
+
// Strip glob chars to get the core name: "*.env*" → "env", ".solongate" → "solongate"
|
|
1208
|
+
const core = p.replace(/[*?[\]{}]/g, '').replace(/^\.+/, '').toLowerCase();
|
|
1209
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
1210
|
+
couldMatchProtected = true;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// If args clearly don't reference any protected pattern, skip LLM — instant ALLOW
|
|
1217
|
+
if (!couldMatchProtected) {
|
|
1218
|
+
// No need to call AI Judge — nothing to protect here
|
|
1219
|
+
} else {
|
|
1220
|
+
|
|
1195
1221
|
const judgePayload = JSON.stringify({
|
|
1196
1222
|
tool: toolName,
|
|
1197
1223
|
arguments: args,
|
|
1198
1224
|
protected_files: protectedFiles,
|
|
1199
1225
|
protected_paths: protectedPathsList,
|
|
1200
|
-
denied_actions: ['file deletion', 'data exfiltration', 'remote code execution', 'environment variable leak', 'security control bypass'],
|
|
1201
1226
|
});
|
|
1202
1227
|
|
|
1203
|
-
const systemPrompt = `You are a security judge for an AI coding tool.
|
|
1228
|
+
const systemPrompt = `You are a security judge for an AI coding tool. Your ONLY job is to check if a tool call accesses files or paths that are in the protected lists. Nothing else.
|
|
1204
1229
|
|
|
1205
1230
|
You will receive a JSON object with:
|
|
1206
|
-
- "tool": the tool name being called
|
|
1231
|
+
- "tool": the tool name being called
|
|
1207
1232
|
- "arguments": the tool's arguments
|
|
1208
|
-
- "protected_files": EXACT list of
|
|
1209
|
-
- "protected_paths": EXACT list of directories
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
-
|
|
1221
|
-
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
-
|
|
1225
|
-
-
|
|
1226
|
-
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
- The file is NOT in protected_files — even if cat, head, etc. is used. Reading non-protected files is normal.
|
|
1230
|
-
- The action is a normal development operation (ls, git status, npm build, cat app.js, etc.)
|
|
1231
|
-
- The action does not touch any protected file or path
|
|
1232
|
-
|
|
1233
|
-
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.
|
|
1233
|
+
- "protected_files": the EXACT and COMPLETE list of protected files from the user's policy
|
|
1234
|
+
- "protected_paths": the EXACT and COMPLETE list of protected directories from the user's policy
|
|
1235
|
+
|
|
1236
|
+
RULES:
|
|
1237
|
+
1. DENY ONLY if the tool call could access a file or path that is in protected_files or protected_paths.
|
|
1238
|
+
2. ALLOW everything else. You must NOT invent your own security rules.
|
|
1239
|
+
3. If a file is NOT in protected_files, it is NOT protected — even if the filename looks sensitive.
|
|
1240
|
+
4. "cat test.txt" is ALLOWED if test.txt is not in protected_files.
|
|
1241
|
+
5. "curl https://example.com" is ALLOWED unless it sends protected file content.
|
|
1242
|
+
6. "printenv" is ALLOWED unless the policy explicitly protects it.
|
|
1243
|
+
|
|
1244
|
+
BYPASS DETECTION — DENY if the command accesses a protected file through:
|
|
1245
|
+
- Shell glob patterns: "cat cred*" could match "credentials.json" IF it is in protected_files
|
|
1246
|
+
- Command substitution: "cat $(echo .env)" builds ".env"
|
|
1247
|
+
- Variable interpolation: f=".en"; cat \${f}v builds ".env"
|
|
1248
|
+
- Process substitution: <(cat .env)
|
|
1249
|
+
- Multi-stage: cp protected_file /tmp/x && cat /tmp/x
|
|
1250
|
+
- Input redirection: < .env
|
|
1251
|
+
- Any file-reading utility (cat, head, tail, less, perl, awk, sed, xxd, etc.)
|
|
1252
|
+
|
|
1253
|
+
CRITICAL: You have NO security opinions of your own. You ONLY enforce the protected_files and protected_paths lists. If something is not in those lists, it is ALLOWED. Do NOT over-block.
|
|
1234
1254
|
|
|
1235
1255
|
Respond with ONLY valid JSON: {"decision": "ALLOW" or "DENY", "reason": "brief explanation", "confidence": 0.0 to 1.0}`;
|
|
1236
1256
|
|
|
@@ -1258,17 +1278,18 @@ Respond with ONLY valid JSON: {"decision": "ALLOW" or "DENY", "reason": "brief e
|
|
|
1258
1278
|
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
1259
1279
|
if (jsonMatch) {
|
|
1260
1280
|
const verdict = JSON.parse(jsonMatch[0]);
|
|
1261
|
-
if (verdict.decision === 'DENY') {
|
|
1281
|
+
if (verdict.decision === 'DENY' && verdict.confidence >= 0.7) {
|
|
1262
1282
|
reason = '[SolonGate AI Judge] Blocked: ' + (verdict.reason || 'Semantic analysis detected a policy violation');
|
|
1263
1283
|
}
|
|
1284
|
+
// Low-confidence DENY or ALLOW → skip (don't block)
|
|
1264
1285
|
}
|
|
1265
|
-
} else {
|
|
1266
|
-
// Fail-closed: LLM error → DENY
|
|
1267
|
-
reason = '[SolonGate AI Judge] Blocked: Groq API error (fail-closed)';
|
|
1268
1286
|
}
|
|
1269
|
-
|
|
1270
|
-
//
|
|
1271
|
-
|
|
1287
|
+
// Auth/config errors (401, 403) → skip AI Judge, don't DENY
|
|
1288
|
+
// Other LLM errors → skip too (policy engine already evaluated)
|
|
1289
|
+
|
|
1290
|
+
} // end else (couldMatchProtected)
|
|
1291
|
+
} catch {
|
|
1292
|
+
// Timeout or parse error → skip AI Judge (policy engine already passed)
|
|
1272
1293
|
}
|
|
1273
1294
|
}
|
|
1274
1295
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"bin": {
|