@kenhkl/agent-moss 0.7.13 → 0.8.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/agents/__init__.py +92 -0
- package/dist/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-312.pyc +0 -0
- package/dist/agents/base.py +116 -0
- package/dist/agents/xiaoo/README.md +41 -0
- package/dist/agents/xiaoo/rules.json +47 -0
- package/dist/cli.js +2131 -251
- package/dist/cli.js.map +1 -1
- package/dist/data/agent_moss.service +16 -0
- package/dist/templates/security_judge_template.txt +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,20 @@ var init_esm_shims = __esm({
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
// src/config.ts
|
|
32
|
+
var config_exports = {};
|
|
33
|
+
__export(config_exports, {
|
|
34
|
+
detectProvider: () => detectProvider,
|
|
35
|
+
getCustomRules: () => getCustomRules,
|
|
36
|
+
getLLMConfig: () => getLLMConfig,
|
|
37
|
+
getLLMFailMode: () => getLLMFailMode,
|
|
38
|
+
getProviderHeaders: () => getProviderHeaders,
|
|
39
|
+
getRetryConfig: () => getRetryConfig,
|
|
40
|
+
getTimeoutConfig: () => getTimeoutConfig,
|
|
41
|
+
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
42
|
+
isLLMAvailable: () => isLLMAvailable,
|
|
43
|
+
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
44
|
+
updateLLMConfig: () => updateLLMConfig
|
|
45
|
+
});
|
|
32
46
|
import fs from "fs";
|
|
33
47
|
import path2 from "path";
|
|
34
48
|
import os from "os";
|
|
@@ -108,6 +122,9 @@ function loadCustomRules() {
|
|
|
108
122
|
function getLLMConfig() {
|
|
109
123
|
return llmConfig;
|
|
110
124
|
}
|
|
125
|
+
function updateLLMConfig(partial) {
|
|
126
|
+
llmConfig = { ...llmConfig, ...partial };
|
|
127
|
+
}
|
|
111
128
|
function getCustomRules() {
|
|
112
129
|
return customRules;
|
|
113
130
|
}
|
|
@@ -115,6 +132,10 @@ function isLLMAvailable() {
|
|
|
115
132
|
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
116
133
|
return llmConfig.apiKey.length > 0;
|
|
117
134
|
}
|
|
135
|
+
function getLLMFailMode() {
|
|
136
|
+
const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
|
|
137
|
+
return val === "fail_closed" ? "fail_closed" : "fail_open";
|
|
138
|
+
}
|
|
118
139
|
function detectProvider(baseUrl, providerHint = "") {
|
|
119
140
|
if (providerHint) {
|
|
120
141
|
const hint = providerHint.toLowerCase();
|
|
@@ -189,6 +210,56 @@ var init_config = __esm({
|
|
|
189
210
|
}
|
|
190
211
|
});
|
|
191
212
|
|
|
213
|
+
// src/agents/index.ts
|
|
214
|
+
import { readFileSync, existsSync } from "fs";
|
|
215
|
+
import { resolve, dirname } from "path";
|
|
216
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
217
|
+
function resolveAgentsDir() {
|
|
218
|
+
if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
219
|
+
if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
220
|
+
return AGENTS_DIR_PROD;
|
|
221
|
+
}
|
|
222
|
+
function getAgentExtensions(agentId) {
|
|
223
|
+
if (!agentId) return null;
|
|
224
|
+
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
225
|
+
const agentsDir = resolveAgentsDir();
|
|
226
|
+
const rulesFile = resolve(agentsDir, agentId, "rules.json");
|
|
227
|
+
if (!existsSync(rulesFile)) {
|
|
228
|
+
cache.set(agentId, null);
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const raw = readFileSync(rulesFile, "utf-8");
|
|
233
|
+
const ext = JSON.parse(raw);
|
|
234
|
+
if (!ext.agent_id) {
|
|
235
|
+
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
236
|
+
cache.set(agentId, null);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
cache.set(agentId, ext);
|
|
240
|
+
console.log(
|
|
241
|
+
`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills`
|
|
242
|
+
);
|
|
243
|
+
return ext;
|
|
244
|
+
} catch (e) {
|
|
245
|
+
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
246
|
+
cache.set(agentId, null);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var __filename2, __dirname2, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
251
|
+
var init_agents = __esm({
|
|
252
|
+
"src/agents/index.ts"() {
|
|
253
|
+
"use strict";
|
|
254
|
+
init_esm_shims();
|
|
255
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
256
|
+
__dirname2 = dirname(__filename2);
|
|
257
|
+
AGENTS_DIR_PROD = resolve(__dirname2, "agents");
|
|
258
|
+
AGENTS_DIR_DEV_FALLBACK = resolve(__dirname2, "../../../agent_moss/agents");
|
|
259
|
+
cache = /* @__PURE__ */ new Map();
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
192
263
|
// src/engine/heuristic.ts
|
|
193
264
|
function runHeuristicCheck(input) {
|
|
194
265
|
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
@@ -222,8 +293,24 @@ function runHeuristicCheck(input) {
|
|
|
222
293
|
}
|
|
223
294
|
}
|
|
224
295
|
highestRiskType = topRiskType || highestRiskType;
|
|
296
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
297
|
+
if (agentExt?.extra_command_patterns) {
|
|
298
|
+
for (const rule of agentExt.extra_command_patterns) {
|
|
299
|
+
try {
|
|
300
|
+
const re = new RegExp(rule.pattern, "i");
|
|
301
|
+
if (re.test(command)) {
|
|
302
|
+
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
303
|
+
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
304
|
+
highestSeverity = rule.risk_level;
|
|
305
|
+
highestRiskType = rule.risk_type;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
225
312
|
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
226
|
-
const injectionHits = checkInjectionKeywords(injectionText);
|
|
313
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
227
314
|
if (injectionHits.length > 0) {
|
|
228
315
|
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
229
316
|
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
@@ -240,18 +327,26 @@ function runHeuristicCheck(input) {
|
|
|
240
327
|
riskType: highestRiskType,
|
|
241
328
|
confidence: 95,
|
|
242
329
|
source: "heuristic",
|
|
243
|
-
violatedLayers: ["1"]
|
|
330
|
+
violatedLayers: ["1.1"]
|
|
331
|
+
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
244
332
|
};
|
|
245
333
|
}
|
|
246
334
|
return null;
|
|
247
335
|
}
|
|
248
|
-
function checkInjectionKeywords(text) {
|
|
336
|
+
function checkInjectionKeywords(text, extraKeywords) {
|
|
249
337
|
const hits = [];
|
|
250
338
|
for (const entry of INJECTION_KEYWORDS) {
|
|
251
339
|
if (text.includes(entry.keyword.toLowerCase())) {
|
|
252
340
|
hits.push(entry);
|
|
253
341
|
}
|
|
254
342
|
}
|
|
343
|
+
if (extraKeywords) {
|
|
344
|
+
for (const entry of extraKeywords) {
|
|
345
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
346
|
+
hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
255
350
|
return hits;
|
|
256
351
|
}
|
|
257
352
|
function isInlineScriptCommand(actionDetail) {
|
|
@@ -268,6 +363,7 @@ var init_heuristic = __esm({
|
|
|
268
363
|
"use strict";
|
|
269
364
|
init_esm_shims();
|
|
270
365
|
init_config();
|
|
366
|
+
init_agents();
|
|
271
367
|
INJECTION_KEYWORDS = [
|
|
272
368
|
// ---- Instruction Override ----
|
|
273
369
|
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
@@ -321,14 +417,10 @@ var init_heuristic = __esm({
|
|
|
321
417
|
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
322
418
|
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
323
419
|
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
324
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
{ keyword: "disable guard", riskLevel: "critical", lang: "en" },
|
|
329
|
-
{ keyword: "\u7ED5\u8FC7\u5BA1\u8BA1", riskLevel: "critical", lang: "zh" },
|
|
330
|
-
{ keyword: "\u7981\u7528\u5B89\u5168\u68C0\u67E5", riskLevel: "critical", lang: "zh" },
|
|
331
|
-
{ keyword: "\u5173\u95ED\u9632\u62A4", riskLevel: "critical", lang: "zh" }
|
|
420
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
421
|
+
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
422
|
+
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
423
|
+
// 由 agent_ext 机制注入,不再污染通用层。
|
|
332
424
|
];
|
|
333
425
|
DANGEROUS_PATTERNS = [
|
|
334
426
|
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
@@ -626,7 +718,8 @@ function runLogicCheck(input) {
|
|
|
626
718
|
}
|
|
627
719
|
}
|
|
628
720
|
if (!isExcludedType) {
|
|
629
|
-
const
|
|
721
|
+
const agentExt = getAgentExtensions(input.agentId || "");
|
|
722
|
+
const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
|
|
630
723
|
if (pathResult) return pathResult;
|
|
631
724
|
}
|
|
632
725
|
if (actionType !== "file_write" && actionType !== "file_edit") {
|
|
@@ -850,7 +943,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
850
943
|
}
|
|
851
944
|
return null;
|
|
852
945
|
}
|
|
853
|
-
function checkSensitivePath(command, cwd) {
|
|
946
|
+
function checkSensitivePath(command, cwd, agentExt) {
|
|
854
947
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
855
948
|
const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
|
|
856
949
|
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
@@ -872,7 +965,7 @@ function checkSensitivePath(command, cwd) {
|
|
|
872
965
|
violatedLayers: ["2"]
|
|
873
966
|
};
|
|
874
967
|
}
|
|
875
|
-
if (!isWriteOpFlag) {
|
|
968
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
876
969
|
continue;
|
|
877
970
|
}
|
|
878
971
|
return {
|
|
@@ -889,7 +982,7 @@ function checkSensitivePath(command, cwd) {
|
|
|
889
982
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
890
983
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
891
984
|
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
892
|
-
if (!isWriteOpFlag) {
|
|
985
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
893
986
|
continue;
|
|
894
987
|
}
|
|
895
988
|
return {
|
|
@@ -903,6 +996,29 @@ function checkSensitivePath(command, cwd) {
|
|
|
903
996
|
};
|
|
904
997
|
}
|
|
905
998
|
}
|
|
999
|
+
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1000
|
+
for (const sp of agentExt.extra_sensitive_paths) {
|
|
1001
|
+
const spPath = sp.path.toLowerCase();
|
|
1002
|
+
const expanded = `${expandedCwd}/${spPath}`;
|
|
1003
|
+
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
1004
|
+
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
1005
|
+
if (denyMode === "deny_read") {
|
|
1006
|
+
if (isWriteOpFlag) continue;
|
|
1007
|
+
} else if (denyMode === "deny_write") {
|
|
1008
|
+
if (!isWriteOpFlag) continue;
|
|
1009
|
+
}
|
|
1010
|
+
return {
|
|
1011
|
+
allowed: false,
|
|
1012
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1013
|
+
riskLevel: sp.risk_level,
|
|
1014
|
+
riskType: "sensitive_path_access",
|
|
1015
|
+
confidence: 90,
|
|
1016
|
+
source: "logic_rule",
|
|
1017
|
+
violatedLayers: ["2"]
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
906
1022
|
for (const cp of CREDENTIAL_PATHS) {
|
|
907
1023
|
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
908
1024
|
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
@@ -1060,12 +1176,13 @@ var init_logic_rules = __esm({
|
|
|
1060
1176
|
"use strict";
|
|
1061
1177
|
init_esm_shims();
|
|
1062
1178
|
init_inline_analyzer();
|
|
1179
|
+
init_agents();
|
|
1063
1180
|
ROOT_SENSITIVE_PATHS = [
|
|
1064
|
-
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6" },
|
|
1065
|
-
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6" },
|
|
1181
|
+
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1182
|
+
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1066
1183
|
// 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
|
|
1067
1184
|
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
1068
|
-
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E" },
|
|
1185
|
+
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
1069
1186
|
// 系统配置(仅拦截写入)
|
|
1070
1187
|
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
1071
1188
|
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
@@ -1083,19 +1200,19 @@ var init_logic_rules = __esm({
|
|
|
1083
1200
|
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
|
|
1084
1201
|
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
1085
1202
|
// SSH 主机私钥
|
|
1086
|
-
{ path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5" },
|
|
1087
|
-
{ path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5" },
|
|
1088
|
-
{ path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5" }
|
|
1203
|
+
{ path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
|
|
1204
|
+
{ path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
|
|
1205
|
+
{ path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
|
|
1089
1206
|
];
|
|
1090
1207
|
HOME_SENSITIVE_PATHS = [
|
|
1091
|
-
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5" },
|
|
1092
|
-
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)" },
|
|
1208
|
+
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
|
|
1209
|
+
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
|
|
1093
1210
|
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
1094
1211
|
// 持久化与命令劫持(仅拦截写入)
|
|
1095
1212
|
{ path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
|
|
1096
1213
|
{ path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
|
|
1097
1214
|
{ path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
|
|
1098
|
-
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1" },
|
|
1215
|
+
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
|
|
1099
1216
|
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
1100
1217
|
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
1101
1218
|
];
|
|
@@ -1156,21 +1273,21 @@ var init_logic_rules = __esm({
|
|
|
1156
1273
|
});
|
|
1157
1274
|
|
|
1158
1275
|
// src/engine/skill-loader.ts
|
|
1159
|
-
import { readFileSync, readdirSync as readdirSync2, existsSync as
|
|
1160
|
-
import { resolve, dirname } from "path";
|
|
1161
|
-
import { fileURLToPath as
|
|
1276
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
1277
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
1278
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1162
1279
|
function loadAllSkills() {
|
|
1163
1280
|
if (cachedSkills) return cachedSkills;
|
|
1164
1281
|
cachedSkills = {};
|
|
1165
|
-
const dir =
|
|
1166
|
-
if (!
|
|
1282
|
+
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1283
|
+
if (!existsSync3(dir)) {
|
|
1167
1284
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1168
1285
|
return cachedSkills;
|
|
1169
1286
|
}
|
|
1170
1287
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1171
1288
|
for (const f of files) {
|
|
1172
1289
|
const name = f.replace(".md", "");
|
|
1173
|
-
cachedSkills[name] =
|
|
1290
|
+
cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
|
|
1174
1291
|
}
|
|
1175
1292
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1176
1293
|
return cachedSkills;
|
|
@@ -1224,15 +1341,15 @@ function formatSkillsForPrompt(skills) {
|
|
|
1224
1341
|
${s.skillContent}`
|
|
1225
1342
|
).join("\n\n---\n\n");
|
|
1226
1343
|
}
|
|
1227
|
-
var
|
|
1344
|
+
var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1228
1345
|
var init_skill_loader = __esm({
|
|
1229
1346
|
"src/engine/skill-loader.ts"() {
|
|
1230
1347
|
"use strict";
|
|
1231
1348
|
init_esm_shims();
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
SKILLS_DIR =
|
|
1235
|
-
SKILLS_DIR_FALLBACK =
|
|
1349
|
+
__filename3 = fileURLToPath3(import.meta.url);
|
|
1350
|
+
__dirname3 = dirname2(__filename3);
|
|
1351
|
+
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1352
|
+
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1236
1353
|
SKILL_KEYWORD_MAP = {
|
|
1237
1354
|
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
1238
1355
|
"script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
|
|
@@ -1255,44 +1372,44 @@ var init_skill_loader = __esm({
|
|
|
1255
1372
|
});
|
|
1256
1373
|
|
|
1257
1374
|
// src/engine/template-loader.ts
|
|
1258
|
-
import { readFileSync as
|
|
1259
|
-
import { resolve as
|
|
1260
|
-
import { fileURLToPath as
|
|
1375
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1376
|
+
import { resolve as resolve3, dirname as dirname3 } from "path";
|
|
1377
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1261
1378
|
function loadTemplates() {
|
|
1262
1379
|
if (cachedTemplates) return cachedTemplates;
|
|
1263
1380
|
cachedTemplates = {};
|
|
1264
|
-
const dir =
|
|
1265
|
-
if (!
|
|
1381
|
+
const dir = existsSync4(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
1382
|
+
if (!existsSync4(dir)) {
|
|
1266
1383
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
1267
1384
|
return cachedTemplates;
|
|
1268
1385
|
}
|
|
1269
1386
|
for (const file of TEMPLATE_FILES) {
|
|
1270
|
-
const filePath =
|
|
1271
|
-
if (
|
|
1387
|
+
const filePath = resolve3(dir, file);
|
|
1388
|
+
if (existsSync4(filePath)) {
|
|
1272
1389
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
1273
|
-
cachedTemplates[key] =
|
|
1390
|
+
cachedTemplates[key] = readFileSync3(filePath, "utf-8");
|
|
1274
1391
|
}
|
|
1275
1392
|
}
|
|
1276
1393
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
1277
1394
|
return cachedTemplates;
|
|
1278
1395
|
}
|
|
1279
|
-
var
|
|
1396
|
+
var __filename4, __dirname4, TEMPLATES_DIR, TEMPLATES_DIR_FALLBACK, TEMPLATE_FILES, cachedTemplates;
|
|
1280
1397
|
var init_template_loader = __esm({
|
|
1281
1398
|
"src/engine/template-loader.ts"() {
|
|
1282
1399
|
"use strict";
|
|
1283
1400
|
init_esm_shims();
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
TEMPLATES_DIR =
|
|
1287
|
-
TEMPLATES_DIR_FALLBACK =
|
|
1401
|
+
__filename4 = fileURLToPath4(import.meta.url);
|
|
1402
|
+
__dirname4 = dirname3(__filename4);
|
|
1403
|
+
TEMPLATES_DIR = resolve3(__dirname4, "../templates");
|
|
1404
|
+
TEMPLATES_DIR_FALLBACK = resolve3(__dirname4, "../../../agent_moss/templates");
|
|
1288
1405
|
TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
1289
1406
|
cachedTemplates = null;
|
|
1290
1407
|
}
|
|
1291
1408
|
});
|
|
1292
1409
|
|
|
1293
1410
|
// src/engine/script-analyzer.ts
|
|
1294
|
-
import { readFileSync as
|
|
1295
|
-
import { resolve as
|
|
1411
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1412
|
+
import { resolve as resolve4, join } from "path";
|
|
1296
1413
|
function emptyResult() {
|
|
1297
1414
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
1298
1415
|
}
|
|
@@ -1345,8 +1462,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
1345
1462
|
}
|
|
1346
1463
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
1347
1464
|
try {
|
|
1348
|
-
const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) :
|
|
1349
|
-
const raw =
|
|
1465
|
+
const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
|
|
1466
|
+
const raw = readFileSync4(expandedPath, "utf-8");
|
|
1350
1467
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
1351
1468
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
1352
1469
|
} catch (e) {
|
|
@@ -1483,203 +1600,1623 @@ var init_script_analyzer = __esm({
|
|
|
1483
1600
|
}
|
|
1484
1601
|
});
|
|
1485
1602
|
|
|
1486
|
-
// src/engine/
|
|
1487
|
-
import
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1603
|
+
// src/engine/script-content-analyzer.ts
|
|
1604
|
+
import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "fs";
|
|
1605
|
+
import { resolve as resolve5, join as join2, isAbsolute, dirname as dirname4 } from "path";
|
|
1606
|
+
import { homedir } from "os";
|
|
1607
|
+
function emptyChain() {
|
|
1608
|
+
return {
|
|
1609
|
+
nodes: [],
|
|
1610
|
+
entryPath: "",
|
|
1611
|
+
maxDepthReached: false,
|
|
1612
|
+
maxSizeReached: false,
|
|
1613
|
+
totalSize: 0,
|
|
1614
|
+
totalLines: 0,
|
|
1615
|
+
hasCritical: false,
|
|
1616
|
+
hasHigh: false,
|
|
1617
|
+
firstHitNode: null,
|
|
1618
|
+
error: ""
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function newNode(path3, depth) {
|
|
1622
|
+
return {
|
|
1623
|
+
scriptPath: path3,
|
|
1624
|
+
rawContent: "",
|
|
1625
|
+
preprocessedContent: "",
|
|
1626
|
+
lineCount: 0,
|
|
1627
|
+
fileSize: 0,
|
|
1628
|
+
depth,
|
|
1629
|
+
childPaths: [],
|
|
1630
|
+
error: "",
|
|
1631
|
+
l1Hit: false,
|
|
1632
|
+
l1RiskLevel: "",
|
|
1633
|
+
l1RiskType: "",
|
|
1634
|
+
l1Reason: "",
|
|
1635
|
+
l1MatchedPatterns: [],
|
|
1636
|
+
l2Hit: false,
|
|
1637
|
+
l2ViolatedRule: "",
|
|
1638
|
+
l2RiskLevel: "",
|
|
1639
|
+
l2RiskType: "",
|
|
1640
|
+
l2Reason: "",
|
|
1641
|
+
matchedKeywords: [],
|
|
1642
|
+
isSuspicious: false,
|
|
1643
|
+
keywordRiskLevel: "",
|
|
1644
|
+
keywordRiskReason: ""
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
function stripComments(content) {
|
|
1648
|
+
return content.split("\n").filter((line) => !line.trimStart().startsWith("#")).join("\n");
|
|
1649
|
+
}
|
|
1650
|
+
function stripOutputText(content) {
|
|
1651
|
+
let result = content.replace(/echo\s+".*?"/gs, 'echo ""');
|
|
1652
|
+
result = result.replace(/echo\s+'.*?'/gs, "echo ''");
|
|
1653
|
+
result = result.replace(/printf\s+".*?"[^\n]*/gs, 'printf ""');
|
|
1654
|
+
result = result.replace(/printf\s+'.*?'[^\n]*/gs, "printf ''");
|
|
1520
1655
|
return result;
|
|
1521
1656
|
}
|
|
1522
|
-
function
|
|
1523
|
-
const
|
|
1524
|
-
const
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1657
|
+
function expandSimpleVariables(content) {
|
|
1658
|
+
const varMap = {};
|
|
1659
|
+
const assignmentRe = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.+)$/;
|
|
1660
|
+
const collectAssignments = (text) => {
|
|
1661
|
+
for (const line of text.split("\n")) {
|
|
1662
|
+
const m = line.match(assignmentRe);
|
|
1663
|
+
if (m) {
|
|
1664
|
+
const varName = m[1];
|
|
1665
|
+
let varVal = m[2].trim();
|
|
1666
|
+
if (varVal && !varVal.includes("$") && !varVal.includes("`")) {
|
|
1667
|
+
if (varVal.startsWith('"') && varVal.endsWith('"') || varVal.startsWith("'") && varVal.endsWith("'")) {
|
|
1668
|
+
varVal = varVal.slice(1, -1);
|
|
1669
|
+
}
|
|
1670
|
+
varMap[varName] = varVal;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
collectAssignments(content);
|
|
1676
|
+
let expanded = content;
|
|
1677
|
+
for (let round = 0; round < 3; round++) {
|
|
1678
|
+
const sortedVars = Object.keys(varMap).sort((a, b) => b.length - a.length);
|
|
1679
|
+
let newExpanded = expanded;
|
|
1680
|
+
for (const v of sortedVars) {
|
|
1681
|
+
const val = varMap[v];
|
|
1682
|
+
newExpanded = newExpanded.split("${" + v + "}").join(val);
|
|
1683
|
+
newExpanded = newExpanded.split("$" + v).join(val);
|
|
1684
|
+
}
|
|
1685
|
+
if (newExpanded === expanded) break;
|
|
1686
|
+
expanded = newExpanded;
|
|
1687
|
+
collectAssignments(expanded);
|
|
1530
1688
|
}
|
|
1531
|
-
|
|
1532
|
-
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
1533
|
-
- \u4F1A\u8BDD ID: ${input.sessionId}
|
|
1534
|
-
- \u7528\u6237\u4EFB\u52A1: ${input.promptSession || "\u672A\u6307\u5B9A"}
|
|
1535
|
-
- \u64CD\u4F5C\u7CFB\u7EDF: ${input.osType}
|
|
1536
|
-
- \u5DE5\u4F5C\u76EE\u5F55: ${input.cwd || "/"}
|
|
1537
|
-
- \u64CD\u4F5C\u539F\u56E0: ${input.reason || "\u672A\u8BF4\u660E"}
|
|
1538
|
-
- \u5386\u53F2\u64CD\u4F5C\u5E8F\u5217:
|
|
1539
|
-
${historyStr}
|
|
1540
|
-
|
|
1541
|
-
- \u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C:
|
|
1542
|
-
\u7C7B\u578B: ${input.aNext.actionType}
|
|
1543
|
-
\u8BE6\u60C5: ${input.aNext.actionDetail}
|
|
1544
|
-
|
|
1545
|
-
## \u53EF\u7528\u5B89\u5168 Skill
|
|
1546
|
-
${skillsText}
|
|
1547
|
-
|
|
1548
|
-
## \u524D\u7F6E\u68C0\u6D4B\u7ED3\u679C
|
|
1549
|
-
${hintsText}
|
|
1550
|
-
${scriptAnalysisText ? `
|
|
1551
|
-
## \u811A\u672C\u5185\u5BB9\u5206\u6790
|
|
1552
|
-
${scriptAnalysisText}` : ""}
|
|
1553
|
-
|
|
1554
|
-
\u8BF7\u6839\u636E\u4EE5\u4E0A\u4FE1\u606F\uFF0C\u5BF9\u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C\u8FDB\u884C\u5B89\u5168\u5224\u65AD\u3002\u524D\u7F6E\u68C0\u6D4B\u4E3A low/medium \u4E14\u6709\u5408\u7406\u6267\u884C\u7406\u7531\u65F6\u5E94\u5F53\u5141\u8BB8\u3002\u4F60\u5FC5\u987B\u8F93\u51FA\u4E25\u683C\u7684 JSON \u683C\u5F0F\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u6CE8\u91CA\u6216 markdown \u6807\u8BB0\u3002
|
|
1555
|
-
|
|
1556
|
-
\u8F93\u51FA\u683C\u5F0F:
|
|
1557
|
-
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
1689
|
+
return expanded;
|
|
1558
1690
|
}
|
|
1559
|
-
function
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
hints.push(
|
|
1570
|
-
`\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.logic.riskLevel}]:
|
|
1571
|
-
\u7C7B\u578B: ${previous.logic.riskType}
|
|
1572
|
-
\u539F\u56E0: ${previous.logic.reason}`
|
|
1573
|
-
);
|
|
1691
|
+
function preprocessScriptContent(content) {
|
|
1692
|
+
let result = stripComments(content);
|
|
1693
|
+
result = stripOutputText(result);
|
|
1694
|
+
result = expandSimpleVariables(result);
|
|
1695
|
+
return result;
|
|
1696
|
+
}
|
|
1697
|
+
function scanScriptForKeywords2(content) {
|
|
1698
|
+
const matched = [];
|
|
1699
|
+
for (const { name, pattern } of SUSPICIOUS_PATTERNS2) {
|
|
1700
|
+
if (pattern.test(content)) matched.push(name);
|
|
1574
1701
|
}
|
|
1575
|
-
return
|
|
1702
|
+
return matched;
|
|
1576
1703
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
const
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
|
|
1704
|
+
function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
1705
|
+
if (matchedKeywords.length === 0) return { level: "", reason: "" };
|
|
1706
|
+
const keywordSet = new Set(matchedKeywords);
|
|
1707
|
+
for (const rule of KEYWORD_COMBINATION_RISKS2) {
|
|
1708
|
+
const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
|
|
1709
|
+
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
1710
|
+
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
1583
1711
|
}
|
|
1584
|
-
const
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
1592
|
-
});
|
|
1593
|
-
const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || String(retryCfg.maxRetries), 10);
|
|
1594
|
-
const totalTimeoutMs = timeoutCfg.totalTimeout * 1e3;
|
|
1595
|
-
const abortController = new AbortController();
|
|
1596
|
-
const totalTimeoutTimer = setTimeout(() => {
|
|
1597
|
-
console.error(`[AgentMoss] totalTimeout (${timeoutCfg.totalTimeout}s) exceeded, aborting all LLM calls`);
|
|
1598
|
-
abortController.abort();
|
|
1599
|
-
}, totalTimeoutMs);
|
|
1600
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1601
|
-
if (abortController.signal.aborted) {
|
|
1602
|
-
break;
|
|
1603
|
-
}
|
|
1604
|
-
try {
|
|
1605
|
-
const response = await client.chat.completions.create({
|
|
1606
|
-
model: config.model,
|
|
1607
|
-
temperature: config.temperature || 0.1,
|
|
1608
|
-
max_tokens: config.maxTokens || 1024,
|
|
1609
|
-
messages: [
|
|
1610
|
-
{ role: "system", content: "\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002" },
|
|
1611
|
-
{ role: "user", content: prompt }
|
|
1612
|
-
],
|
|
1613
|
-
signal: abortController.signal
|
|
1614
|
-
});
|
|
1615
|
-
const content = response.choices[0]?.message?.content || "{}";
|
|
1616
|
-
console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
|
|
1617
|
-
let jsonStr = content.trim();
|
|
1618
|
-
if (jsonStr.startsWith("```")) {
|
|
1619
|
-
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
1620
|
-
}
|
|
1621
|
-
const parsed = JSON.parse(jsonStr);
|
|
1622
|
-
clearTimeout(totalTimeoutTimer);
|
|
1623
|
-
return {
|
|
1624
|
-
allowed: parsed.allowed !== false,
|
|
1625
|
-
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
1626
|
-
riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
|
|
1627
|
-
riskType: parsed.riskType || "",
|
|
1628
|
-
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
1629
|
-
source: "llm",
|
|
1630
|
-
violatedLayers: ["3"]
|
|
1631
|
-
};
|
|
1632
|
-
} catch (err) {
|
|
1633
|
-
if (abortController.signal.aborted) {
|
|
1634
|
-
console.error(`[AgentMoss] LLM analysis aborted by totalTimeout (${timeoutCfg.totalTimeout}s)`);
|
|
1635
|
-
break;
|
|
1636
|
-
}
|
|
1637
|
-
if (attempt < maxRetries) {
|
|
1638
|
-
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
1639
|
-
await new Promise((resolve4) => setTimeout(resolve4, retryCfg.retryInterval * 1e3));
|
|
1640
|
-
} else {
|
|
1641
|
-
clearTimeout(totalTimeoutTimer);
|
|
1642
|
-
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
1712
|
+
const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
|
|
1713
|
+
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
1714
|
+
if (hasNetworkPost) {
|
|
1715
|
+
const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
|
|
1716
|
+
const contentLower = scriptContent.toLowerCase();
|
|
1717
|
+
for (const p of envPatterns) {
|
|
1718
|
+
if (contentLower.includes(p.toLowerCase())) {
|
|
1643
1719
|
return {
|
|
1644
|
-
|
|
1645
|
-
reason:
|
|
1646
|
-
riskLevel: "medium",
|
|
1647
|
-
riskType: "",
|
|
1648
|
-
confidence: 60,
|
|
1649
|
-
source: "llm_failure",
|
|
1650
|
-
violatedLayers: ["3"]
|
|
1720
|
+
level: "critical",
|
|
1721
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u6536\u96C6\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u811A\u672C\u53EF\u80FD\u5C06\u7528\u6237\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u80FD\u5305\u542B API Key\u3001\u5BC6\u94A5\u7B49\u654F\u611F\u4FE1\u606F\uFF09\u53D1\u9001\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\u3002"
|
|
1651
1722
|
};
|
|
1652
1723
|
}
|
|
1653
1724
|
}
|
|
1654
1725
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1726
|
+
let bestLevel = "";
|
|
1727
|
+
let bestReason = "";
|
|
1728
|
+
for (const kw of matchedKeywords) {
|
|
1729
|
+
const risk = SINGLE_KEYWORD_RISKS2[kw];
|
|
1730
|
+
if (risk && (LEVEL_ORDER2[risk.level] || 0) > (LEVEL_ORDER2[bestLevel] || 0)) {
|
|
1731
|
+
bestLevel = risk.level;
|
|
1732
|
+
bestReason = risk.reason;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return { level: bestLevel, reason: bestReason };
|
|
1736
|
+
}
|
|
1737
|
+
function extractScriptPath2(command) {
|
|
1738
|
+
for (const pattern of SCRIPT_EXEC_PATTERNS2) {
|
|
1739
|
+
const m = command.match(pattern);
|
|
1740
|
+
if (m) return m[1];
|
|
1741
|
+
}
|
|
1742
|
+
return null;
|
|
1743
|
+
}
|
|
1744
|
+
function extractInlineScriptPath(actionDetail) {
|
|
1745
|
+
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
1746
|
+
let m;
|
|
1747
|
+
while ((m = re.exec(actionDetail)) !== null) {
|
|
1748
|
+
const p = m[1];
|
|
1749
|
+
let resolved;
|
|
1750
|
+
try {
|
|
1751
|
+
resolved = resolvePath(p);
|
|
1752
|
+
if (existsSync5(resolved)) {
|
|
1753
|
+
const stat = statSync(resolved);
|
|
1754
|
+
if (stat.isFile()) return resolved;
|
|
1755
|
+
}
|
|
1756
|
+
} catch {
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return null;
|
|
1760
|
+
}
|
|
1761
|
+
function resolvePath(p) {
|
|
1762
|
+
let expanded = p;
|
|
1763
|
+
if (expanded.startsWith("~/") || expanded === "~") {
|
|
1764
|
+
expanded = join2(homedir(), expanded.slice(1));
|
|
1765
|
+
}
|
|
1766
|
+
return resolve5(expanded);
|
|
1767
|
+
}
|
|
1768
|
+
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
1769
|
+
try {
|
|
1770
|
+
const path3 = resolvePath(scriptPath);
|
|
1771
|
+
if (!existsSync5(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
1772
|
+
const stat = statSync(path3);
|
|
1773
|
+
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
1774
|
+
if (stat.size > maxFileSize) {
|
|
1775
|
+
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
1776
|
+
}
|
|
1777
|
+
const content = readFileSync5(path3, "utf-8");
|
|
1778
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
1779
|
+
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
1780
|
+
} catch (e) {
|
|
1781
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1782
|
+
return { content: "", lineCount: 0, fileSize: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
function extractChildScriptPaths(content, baseDir) {
|
|
1786
|
+
const paths = [];
|
|
1787
|
+
for (const pattern of CHILD_SCRIPT_PATTERNS) {
|
|
1788
|
+
pattern.lastIndex = 0;
|
|
1789
|
+
let m;
|
|
1790
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
1791
|
+
const pathStr = m[1];
|
|
1792
|
+
if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
|
|
1793
|
+
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
1794
|
+
const full = isAbsolute(pathStr) ? pathStr : join2(baseDir, pathStr);
|
|
1795
|
+
const resolved = resolvePath(full);
|
|
1796
|
+
if (existsSync5(resolved)) {
|
|
1797
|
+
try {
|
|
1798
|
+
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
1799
|
+
} catch {
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return paths;
|
|
1805
|
+
}
|
|
1806
|
+
function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 1024 * 1024, maxTotalSize = 6 * 1024 * 1024) {
|
|
1807
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1808
|
+
const result = emptyChain();
|
|
1809
|
+
result.entryPath = entryPath;
|
|
1810
|
+
const entry = resolvePath(entryPath);
|
|
1811
|
+
const baseDir = existsSync5(entry) ? dirname4(entry) : input.cwd || process.cwd();
|
|
1812
|
+
resolveRecursive({
|
|
1813
|
+
path: entry,
|
|
1814
|
+
depth: 0,
|
|
1815
|
+
maxDepth,
|
|
1816
|
+
maxFileSize,
|
|
1817
|
+
maxTotalSize,
|
|
1818
|
+
visited,
|
|
1819
|
+
result,
|
|
1820
|
+
baseDir,
|
|
1821
|
+
input
|
|
1822
|
+
});
|
|
1823
|
+
result.hasCritical = result.nodes.some((n) => n.l1RiskLevel === "critical" || n.l2RiskLevel === "critical" || n.keywordRiskLevel === "critical");
|
|
1824
|
+
result.hasHigh = result.nodes.some((n) => ["high", "critical"].includes(n.l1RiskLevel) || ["high", "critical"].includes(n.l2RiskLevel) || ["high", "critical"].includes(n.keywordRiskLevel));
|
|
1825
|
+
result.firstHitNode = result.nodes.find((n) => n.l1Hit || n.l2Hit || n.isSuspicious) || null;
|
|
1826
|
+
return result;
|
|
1827
|
+
}
|
|
1828
|
+
function resolveRecursive(ctx) {
|
|
1829
|
+
const { path: path3, depth, maxDepth, maxFileSize, maxTotalSize, visited, result, baseDir, input } = ctx;
|
|
1830
|
+
if (visited.has(path3)) return;
|
|
1831
|
+
if (depth > maxDepth) {
|
|
1832
|
+
result.maxDepthReached = true;
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
if (result.totalSize >= maxTotalSize) {
|
|
1836
|
+
result.maxSizeReached = true;
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
visited.add(path3);
|
|
1840
|
+
const { content, lineCount, fileSize, error } = readScriptContent2(path3, maxFileSize);
|
|
1841
|
+
if (error) {
|
|
1842
|
+
const node2 = newNode(path3, depth);
|
|
1843
|
+
node2.error = error;
|
|
1844
|
+
node2.fileSize = fileSize;
|
|
1845
|
+
node2.lineCount = lineCount;
|
|
1846
|
+
result.nodes.push(node2);
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
result.totalSize += fileSize;
|
|
1850
|
+
result.totalLines += lineCount;
|
|
1851
|
+
const preprocessed = preprocessScriptContent(content);
|
|
1852
|
+
const node = newNode(path3, depth);
|
|
1853
|
+
node.rawContent = content;
|
|
1854
|
+
node.preprocessedContent = preprocessed;
|
|
1855
|
+
node.lineCount = lineCount;
|
|
1856
|
+
node.fileSize = fileSize;
|
|
1857
|
+
result.nodes.push(node);
|
|
1858
|
+
const virtualInput = {
|
|
1859
|
+
...input,
|
|
1860
|
+
aNext: { actionType: "bash", actionDetail: preprocessed }
|
|
1861
|
+
};
|
|
1862
|
+
const l1 = runHeuristicCheck(virtualInput);
|
|
1863
|
+
if (l1 && !l1.allowed) {
|
|
1864
|
+
node.l1Hit = true;
|
|
1865
|
+
node.l1RiskLevel = l1.riskLevel;
|
|
1866
|
+
node.l1RiskType = l1.riskType;
|
|
1867
|
+
node.l1Reason = l1.reason;
|
|
1868
|
+
node.l1MatchedPatterns = [];
|
|
1869
|
+
if (l1.riskLevel === "critical") {
|
|
1870
|
+
result.hasCritical = true;
|
|
1871
|
+
result.firstHitNode = node;
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
const childPaths = extractChildScriptPaths(preprocessed, baseDir);
|
|
1876
|
+
node.childPaths = childPaths;
|
|
1877
|
+
for (const childPath of childPaths) {
|
|
1878
|
+
resolveRecursive({
|
|
1879
|
+
path: resolvePath(childPath),
|
|
1880
|
+
depth: depth + 1,
|
|
1881
|
+
maxDepth,
|
|
1882
|
+
maxFileSize,
|
|
1883
|
+
maxTotalSize,
|
|
1884
|
+
visited,
|
|
1885
|
+
result,
|
|
1886
|
+
baseDir: dirname4(resolvePath(childPath)),
|
|
1887
|
+
input
|
|
1888
|
+
});
|
|
1889
|
+
if (result.hasCritical) return;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
function scanScriptChainWithL1(chain, input) {
|
|
1893
|
+
for (const node of chain.nodes) {
|
|
1894
|
+
if (node.error || !node.preprocessedContent || node.l1Hit) continue;
|
|
1895
|
+
const virtualInput = {
|
|
1896
|
+
...input,
|
|
1897
|
+
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
1898
|
+
};
|
|
1899
|
+
const l1 = runHeuristicCheck(virtualInput);
|
|
1900
|
+
if (l1 && !l1.allowed) {
|
|
1901
|
+
node.l1Hit = true;
|
|
1902
|
+
node.l1RiskLevel = l1.riskLevel;
|
|
1903
|
+
node.l1RiskType = l1.riskType;
|
|
1904
|
+
node.l1Reason = l1.reason;
|
|
1905
|
+
if (l1.riskLevel === "critical") {
|
|
1906
|
+
chain.hasCritical = true;
|
|
1907
|
+
chain.firstHitNode = node;
|
|
1908
|
+
break;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
return chain;
|
|
1913
|
+
}
|
|
1914
|
+
function scanScriptChainWithL2(chain, input) {
|
|
1915
|
+
for (const node of chain.nodes) {
|
|
1916
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
1917
|
+
if (node.l1RiskLevel === "critical") break;
|
|
1918
|
+
const virtualInput = {
|
|
1919
|
+
...input,
|
|
1920
|
+
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
1921
|
+
};
|
|
1922
|
+
const l2 = runLogicCheck(virtualInput);
|
|
1923
|
+
if (l2 && !l2.allowed) {
|
|
1924
|
+
node.l2Hit = true;
|
|
1925
|
+
node.l2ViolatedRule = `script_logic:${node.scriptPath}`;
|
|
1926
|
+
node.l2RiskLevel = l2.riskLevel;
|
|
1927
|
+
node.l2RiskType = l2.riskType;
|
|
1928
|
+
node.l2Reason = `\u811A\u672C ${node.scriptPath} (\u6DF1\u5EA6 ${node.depth}) \u4E2D: ${l2.reason}`;
|
|
1929
|
+
if (l2.riskLevel === "critical") {
|
|
1930
|
+
chain.hasCritical = true;
|
|
1931
|
+
chain.firstHitNode = node;
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
if (!chain.hasHigh) {
|
|
1937
|
+
chain.hasHigh = chain.nodes.some((n) => ["high", "critical"].includes(n.l2RiskLevel));
|
|
1938
|
+
}
|
|
1939
|
+
return chain;
|
|
1940
|
+
}
|
|
1941
|
+
function scanScriptChainKeywords(chain) {
|
|
1942
|
+
for (const node of chain.nodes) {
|
|
1943
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
1944
|
+
if (node.l1Hit || node.l2Hit) continue;
|
|
1945
|
+
const matched = scanScriptForKeywords2(node.preprocessedContent);
|
|
1946
|
+
node.matchedKeywords = matched;
|
|
1947
|
+
node.isSuspicious = matched.length > 0;
|
|
1948
|
+
if (matched.length > 0) {
|
|
1949
|
+
const risk = assessKeywordCombinationRisk2(matched, node.preprocessedContent);
|
|
1950
|
+
node.keywordRiskLevel = risk.level;
|
|
1951
|
+
node.keywordRiskReason = risk.reason;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return chain;
|
|
1955
|
+
}
|
|
1956
|
+
function analyzeScriptContent2(input) {
|
|
1957
|
+
const actionType = input.aNext.actionType.toLowerCase();
|
|
1958
|
+
if (actionType !== "bash") return emptyChain();
|
|
1959
|
+
const scriptPath = extractScriptPath2(input.aNext.actionDetail);
|
|
1960
|
+
let resolvedPath = scriptPath;
|
|
1961
|
+
if (!resolvedPath) {
|
|
1962
|
+
const inline = extractInlineScriptPath(input.aNext.actionDetail);
|
|
1963
|
+
if (!inline) return emptyChain();
|
|
1964
|
+
resolvedPath = inline;
|
|
1965
|
+
}
|
|
1966
|
+
const chain = resolveScriptChain(resolvedPath, input);
|
|
1967
|
+
if (chain.nodes.length === 0) return chain;
|
|
1968
|
+
scanScriptChainWithL1(chain, input);
|
|
1969
|
+
scanScriptChainWithL2(chain, input);
|
|
1970
|
+
scanScriptChainKeywords(chain);
|
|
1971
|
+
return chain;
|
|
1972
|
+
}
|
|
1973
|
+
function formatScriptChainForPrompt(chain) {
|
|
1974
|
+
if (chain.nodes.length === 0) return "";
|
|
1975
|
+
const errorNodes = chain.nodes.filter((n) => n.error);
|
|
1976
|
+
if (errorNodes.length > 0 && chain.nodes.length === 1) {
|
|
1977
|
+
return `\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${errorNodes[0].error}`;
|
|
1978
|
+
}
|
|
1979
|
+
const suspiciousNodes = chain.nodes.filter((n) => n.isSuspicious && !n.l1Hit && !n.l2Hit);
|
|
1980
|
+
if (suspiciousNodes.length === 0) {
|
|
1981
|
+
const safeNodes = chain.nodes.filter((n) => !n.error);
|
|
1982
|
+
if (safeNodes.length === 0) return "";
|
|
1983
|
+
const parts2 = ["\u2713 \u811A\u672C\u5185\u5BB9\u5DF2\u5206\u6790\uFF0C\u672A\u53D1\u73B0\u5371\u9669/\u53EF\u7591\u64CD\u4F5C\uFF08L1/L2/\u5173\u952E\u8BCD\u626B\u63CF\u5747\u672A\u547D\u4E2D\uFF09\uFF1A"];
|
|
1984
|
+
for (const node of safeNodes) {
|
|
1985
|
+
const pre = node.preprocessedContent || "";
|
|
1986
|
+
let preview = pre.slice(0, 600);
|
|
1987
|
+
if (pre.length > 600) preview += `
|
|
1988
|
+
... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
|
|
1989
|
+
parts2.push(
|
|
1990
|
+
` \u811A\u672C\u8DEF\u5F84: ${node.scriptPath}\uFF08\u9012\u5F52\u6DF1\u5EA6 ${node.depth}\uFF0C${node.lineCount} \u884C\uFF09
|
|
1991
|
+
\u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09:
|
|
1992
|
+
\`\`\`
|
|
1993
|
+
${preview}
|
|
1994
|
+
\`\`\``
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
return parts2.join("\n");
|
|
1998
|
+
}
|
|
1999
|
+
const parts = [];
|
|
2000
|
+
for (const node of suspiciousNodes) {
|
|
2001
|
+
const keywordsStr = node.matchedKeywords.join(", ");
|
|
2002
|
+
let riskHint = "";
|
|
2003
|
+
if (node.keywordRiskLevel && node.keywordRiskReason) {
|
|
2004
|
+
riskHint = `
|
|
2005
|
+
|
|
2006
|
+
\u{1F534} **\u98CE\u9669\u8BC4\u4F30**: ${node.keywordRiskLevel.toUpperCase()} \u7EA7\u522B
|
|
2007
|
+
**\u98CE\u9669\u539F\u56E0**: ${node.keywordRiskReason}
|
|
2008
|
+
**\u5EFA\u8BAE**: \u6B64\u811A\u672C\u5E94\u88AB\u62D2\u7EDD\uFF08Deny\uFF09\uFF0C\u9664\u975E\u6709\u660E\u786E\u7684\u4E1A\u52A1\u9700\u6C42\u4E14\u5DF2\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u5668\u53EF\u4FE1\u3002`;
|
|
2009
|
+
}
|
|
2010
|
+
const pre = node.preprocessedContent;
|
|
2011
|
+
let contentPreview = pre.slice(0, 2e3);
|
|
2012
|
+
if (pre.length > 2e3) contentPreview += `
|
|
2013
|
+
... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
|
|
2014
|
+
parts.push(
|
|
2015
|
+
`\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
2016
|
+
\u811A\u672C\u8DEF\u5F84: ${node.scriptPath}
|
|
2017
|
+
\u9012\u5F52\u6DF1\u5EA6: ${node.depth}
|
|
2018
|
+
\u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
|
|
2019
|
+
\u811A\u672C\u884C\u6570: ${node.lineCount}${riskHint}
|
|
2020
|
+
|
|
2021
|
+
### \u811A\u672C\u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09###
|
|
2022
|
+
\`\`\`
|
|
2023
|
+
${contentPreview}
|
|
2024
|
+
\`\`\`
|
|
2025
|
+
`
|
|
2026
|
+
);
|
|
2027
|
+
}
|
|
2028
|
+
let header = `\u26A0\uFE0F \u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\u94FE\uFF08\u5171 ${chain.nodes.length} \u4E2A\u811A\u672C\uFF09\uFF0C\u5176\u4E2D ${suspiciousNodes.length} \u4E2A\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
2029
|
+
`;
|
|
2030
|
+
if (chain.maxDepthReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u9012\u5F52\u8FFD\u8E2A\u89E6\u8FBE\u6DF1\u5EA6\u4E0A\u9650\uFF0C\u53EF\u80FD\u5B58\u5728\u66F4\u6DF1\u5C42\u7684\u811A\u672C\u8C03\u7528\u672A\u88AB\u626B\u63CF\u3002\n";
|
|
2031
|
+
if (chain.maxSizeReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u7D2F\u8BA1\u6587\u4EF6\u5927\u5C0F\u89E6\u8FBE\u4E0A\u9650\uFF0C\u90E8\u5206\u811A\u672C\u53EF\u80FD\u672A\u88AB\u5B8C\u6574\u8BFB\u53D6\u3002\n";
|
|
2032
|
+
return header + parts.join("\n---\n\n");
|
|
2033
|
+
}
|
|
2034
|
+
var SUSPICIOUS_PATTERNS2, SCRIPT_EXEC_PATTERNS2, KEYWORD_COMBINATION_RISKS2, SINGLE_KEYWORD_RISKS2, LEVEL_ORDER2, CHILD_SCRIPT_PATTERNS;
|
|
2035
|
+
var init_script_content_analyzer = __esm({
|
|
2036
|
+
"src/engine/script-content-analyzer.ts"() {
|
|
2037
|
+
"use strict";
|
|
2038
|
+
init_esm_shims();
|
|
2039
|
+
init_heuristic();
|
|
2040
|
+
init_logic_rules();
|
|
2041
|
+
SUSPICIOUS_PATTERNS2 = [
|
|
2042
|
+
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
2043
|
+
{ name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
|
|
2044
|
+
{ name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
|
|
2045
|
+
{ name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
|
|
2046
|
+
{ name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
|
|
2047
|
+
{ name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
|
|
2048
|
+
{ name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
|
|
2049
|
+
{ name: "/dev/tcp", pattern: /\/dev\/tcp\// },
|
|
2050
|
+
{ name: "/dev/udp", pattern: /\/dev\/udp\// },
|
|
2051
|
+
{ name: "$(base64", pattern: /\$\(\s*base64/ },
|
|
2052
|
+
{ name: "`base64", pattern: /`\s*base64/ },
|
|
2053
|
+
{ name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
|
|
2054
|
+
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
2055
|
+
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
2056
|
+
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
2057
|
+
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
2058
|
+
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
2059
|
+
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
2060
|
+
];
|
|
2061
|
+
SCRIPT_EXEC_PATTERNS2 = [
|
|
2062
|
+
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
2063
|
+
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
2064
|
+
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
2065
|
+
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
2066
|
+
/\.\/(\S+)/,
|
|
2067
|
+
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
2068
|
+
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
2069
|
+
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
2070
|
+
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
2071
|
+
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
2072
|
+
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
2073
|
+
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
2074
|
+
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
2075
|
+
];
|
|
2076
|
+
KEYWORD_COMBINATION_RISKS2 = [
|
|
2077
|
+
{
|
|
2078
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
2079
|
+
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
2080
|
+
level: "critical",
|
|
2081
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
|
|
2082
|
+
},
|
|
2083
|
+
{
|
|
2084
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
2085
|
+
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
2086
|
+
level: "critical",
|
|
2087
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
2091
|
+
group2: /* @__PURE__ */ new Set(),
|
|
2092
|
+
level: "high",
|
|
2093
|
+
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
2094
|
+
},
|
|
2095
|
+
{
|
|
2096
|
+
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
2097
|
+
group2: /* @__PURE__ */ new Set(),
|
|
2098
|
+
level: "high",
|
|
2099
|
+
reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
|
|
2100
|
+
}
|
|
2101
|
+
];
|
|
2102
|
+
SINGLE_KEYWORD_RISKS2 = {
|
|
2103
|
+
"curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
2104
|
+
"curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
2105
|
+
"curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
2106
|
+
"wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
2107
|
+
"wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
|
|
2108
|
+
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
2109
|
+
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
2110
|
+
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
2111
|
+
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
2112
|
+
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
2113
|
+
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
2114
|
+
"base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
2115
|
+
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
2116
|
+
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
2117
|
+
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
2118
|
+
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
2119
|
+
"authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
2120
|
+
"ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
|
|
2121
|
+
};
|
|
2122
|
+
LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
2123
|
+
CHILD_SCRIPT_PATTERNS = [
|
|
2124
|
+
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
2125
|
+
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
2126
|
+
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
2127
|
+
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
2128
|
+
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
2129
|
+
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
2130
|
+
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
2131
|
+
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
2132
|
+
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
2133
|
+
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
2134
|
+
];
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
|
|
2138
|
+
// src/engine/token-stats.ts
|
|
2139
|
+
var token_stats_exports = {};
|
|
2140
|
+
__export(token_stats_exports, {
|
|
2141
|
+
getRecentRecords: () => getRecentRecords,
|
|
2142
|
+
getTokenStats: () => getTokenStats,
|
|
2143
|
+
getTokenTrend: () => getTokenTrend,
|
|
2144
|
+
recordTokenUsage: () => recordTokenUsage,
|
|
2145
|
+
resetTokenStats: () => resetTokenStats
|
|
2146
|
+
});
|
|
2147
|
+
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
|
|
2148
|
+
import { join as join3 } from "path";
|
|
2149
|
+
import { homedir as homedir2 } from "os";
|
|
2150
|
+
function getStatsPath() {
|
|
2151
|
+
const envPath = process.env[ENV_STATS_PATH];
|
|
2152
|
+
return envPath || STATS_FILE;
|
|
2153
|
+
}
|
|
2154
|
+
function loadAllRecords(statsPath) {
|
|
2155
|
+
if (!existsSync6(statsPath)) return [];
|
|
2156
|
+
try {
|
|
2157
|
+
const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
|
|
2158
|
+
if (Array.isArray(data)) return data;
|
|
2159
|
+
if (data && Array.isArray(data.records)) return data.records;
|
|
2160
|
+
return [];
|
|
2161
|
+
} catch (e) {
|
|
2162
|
+
return [];
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
function saveRecords(statsPath, records) {
|
|
2166
|
+
try {
|
|
2167
|
+
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2168
|
+
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
2169
|
+
writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2170
|
+
} catch (e) {
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
function recordTokenUsage(record) {
|
|
2174
|
+
const statsPath = getStatsPath();
|
|
2175
|
+
const records = loadAllRecords(statsPath);
|
|
2176
|
+
records.push({
|
|
2177
|
+
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2178
|
+
session_id: record.sessionId,
|
|
2179
|
+
step: record.step,
|
|
2180
|
+
model: record.model,
|
|
2181
|
+
prompt_tokens: record.promptTokens,
|
|
2182
|
+
completion_tokens: record.completionTokens,
|
|
2183
|
+
total_tokens: record.totalTokens
|
|
2184
|
+
});
|
|
2185
|
+
if (records.length > MAX_RECORDS) {
|
|
2186
|
+
records.splice(0, records.length - MAX_RECORDS);
|
|
2187
|
+
}
|
|
2188
|
+
saveRecords(statsPath, records);
|
|
2189
|
+
}
|
|
2190
|
+
function filterByDate(records, days = 0, startDate, endDate) {
|
|
2191
|
+
if (startDate || endDate) {
|
|
2192
|
+
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
2193
|
+
let endDt = endDate ? new Date(endDate) : void 0;
|
|
2194
|
+
if (endDate) {
|
|
2195
|
+
const e = new Date(endDate);
|
|
2196
|
+
e.setHours(23, 59, 59, 999);
|
|
2197
|
+
endDt = e;
|
|
2198
|
+
}
|
|
2199
|
+
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
2200
|
+
return records.filter((r) => {
|
|
2201
|
+
try {
|
|
2202
|
+
const ts = new Date(r.timestamp).getTime();
|
|
2203
|
+
return ts >= startDt.getTime() && ts <= endMs;
|
|
2204
|
+
} catch {
|
|
2205
|
+
return true;
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
} else if (days > 0) {
|
|
2209
|
+
const cutoff = Date.now() - days * 864e5;
|
|
2210
|
+
return records.filter((r) => {
|
|
2211
|
+
try {
|
|
2212
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
2213
|
+
} catch {
|
|
2214
|
+
return true;
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
return records;
|
|
2219
|
+
}
|
|
2220
|
+
function aggregate(records) {
|
|
2221
|
+
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
2222
|
+
const byStep = {};
|
|
2223
|
+
const byModel = {};
|
|
2224
|
+
const byDate = {};
|
|
2225
|
+
const bucket = (b, key, r) => {
|
|
2226
|
+
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
2227
|
+
e.prompt_tokens += r.prompt_tokens || 0;
|
|
2228
|
+
e.completion_tokens += r.completion_tokens || 0;
|
|
2229
|
+
e.total_tokens += r.total_tokens || 0;
|
|
2230
|
+
e.calls += 1;
|
|
2231
|
+
};
|
|
2232
|
+
for (const r of records) {
|
|
2233
|
+
totalPrompt += r.prompt_tokens || 0;
|
|
2234
|
+
totalCompletion += r.completion_tokens || 0;
|
|
2235
|
+
totalTotal += r.total_tokens || 0;
|
|
2236
|
+
bucket(byStep, r.step || "unknown", r);
|
|
2237
|
+
bucket(byModel, r.model || "unknown", r);
|
|
2238
|
+
let dateKey = "unknown";
|
|
2239
|
+
try {
|
|
2240
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2241
|
+
} catch {
|
|
2242
|
+
}
|
|
2243
|
+
bucket(byDate, dateKey, r);
|
|
2244
|
+
}
|
|
2245
|
+
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
2246
|
+
const sortedByDate = {};
|
|
2247
|
+
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
2248
|
+
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
2249
|
+
}
|
|
2250
|
+
function getTokenStats(days = 0, startDate, endDate) {
|
|
2251
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2252
|
+
const a = aggregate(records);
|
|
2253
|
+
return {
|
|
2254
|
+
total_prompt_tokens: a.totalPrompt,
|
|
2255
|
+
total_completion_tokens: a.totalCompletion,
|
|
2256
|
+
total_tokens: a.totalTotal,
|
|
2257
|
+
total_calls: records.length,
|
|
2258
|
+
by_step: a.byStep,
|
|
2259
|
+
by_model: a.byModel,
|
|
2260
|
+
by_date: a.byDate
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
function getRecentRecords(limit = 20) {
|
|
2264
|
+
const records = loadAllRecords(getStatsPath());
|
|
2265
|
+
return records.slice(-limit);
|
|
2266
|
+
}
|
|
2267
|
+
function getTokenTrend(days = 0, startDate, endDate) {
|
|
2268
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2269
|
+
const series = {};
|
|
2270
|
+
const datesSet = /* @__PURE__ */ new Set();
|
|
2271
|
+
for (const r of records) {
|
|
2272
|
+
let dateKey;
|
|
2273
|
+
try {
|
|
2274
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2275
|
+
} catch {
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
2278
|
+
const model = r.model || "unknown";
|
|
2279
|
+
const tokens = r.total_tokens || 0;
|
|
2280
|
+
datesSet.add(dateKey);
|
|
2281
|
+
series[model] = series[model] || {};
|
|
2282
|
+
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
2283
|
+
}
|
|
2284
|
+
const dates = Array.from(datesSet).sort();
|
|
2285
|
+
const models = Object.keys(series).sort();
|
|
2286
|
+
return { dates, models, series };
|
|
2287
|
+
}
|
|
2288
|
+
function resetTokenStats() {
|
|
2289
|
+
const statsPath = getStatsPath();
|
|
2290
|
+
if (existsSync6(statsPath)) {
|
|
2291
|
+
try {
|
|
2292
|
+
unlinkSync(statsPath);
|
|
2293
|
+
} catch {
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
2298
|
+
var init_token_stats = __esm({
|
|
2299
|
+
"src/engine/token-stats.ts"() {
|
|
2300
|
+
"use strict";
|
|
2301
|
+
init_esm_shims();
|
|
2302
|
+
STATS_DIR = join3(homedir2(), ".config", "agentmoss");
|
|
2303
|
+
STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
|
|
2304
|
+
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
2305
|
+
MAX_RECORDS = 1e4;
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
|
|
2309
|
+
// src/engine/llm-analyzer.ts
|
|
2310
|
+
import { appendFileSync } from "fs";
|
|
2311
|
+
import OpenAI from "openai";
|
|
2312
|
+
async function runLLMAnalysis(input, previous) {
|
|
2313
|
+
if (!isLLMAvailable()) {
|
|
2314
|
+
console.log("[AgentMoss] LLM not configured, skipping deep analysis");
|
|
2315
|
+
return {
|
|
2316
|
+
allowed: true,
|
|
2317
|
+
reason: "LLM \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u6DF1\u5C42\u5206\u6790",
|
|
2318
|
+
riskLevel: "low",
|
|
2319
|
+
riskType: "",
|
|
2320
|
+
confidence: 50,
|
|
2321
|
+
source: "llm",
|
|
2322
|
+
violatedLayers: []
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
const globalConfig = getLLMConfig();
|
|
2326
|
+
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
2327
|
+
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
2328
|
+
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
2329
|
+
const cached = llmCache.get(cacheKey);
|
|
2330
|
+
if (cached) {
|
|
2331
|
+
console.log("[AgentMoss] LLM cache hit");
|
|
2332
|
+
return cached;
|
|
2333
|
+
}
|
|
2334
|
+
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
2335
|
+
const templates = loadTemplates();
|
|
2336
|
+
let scriptAnalysis;
|
|
2337
|
+
let scriptChainText = "";
|
|
2338
|
+
if (previous.scriptChain) {
|
|
2339
|
+
scriptChainText = formatScriptChainForPrompt(previous.scriptChain);
|
|
2340
|
+
} else {
|
|
2341
|
+
scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
2342
|
+
}
|
|
2343
|
+
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
|
|
2344
|
+
logPrompt(prompt, input.aNext, previous.llmLogPath);
|
|
2345
|
+
const result = await analyzeWithLLM(prompt, config);
|
|
2346
|
+
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
2347
|
+
const firstKey = llmCache.keys().next().value;
|
|
2348
|
+
if (firstKey) llmCache.delete(firstKey);
|
|
2349
|
+
}
|
|
2350
|
+
llmCache.set(cacheKey, result);
|
|
2351
|
+
return result;
|
|
2352
|
+
}
|
|
2353
|
+
function logPrompt(prompt, aNext, logPath) {
|
|
2354
|
+
if (!logPath) return;
|
|
2355
|
+
try {
|
|
2356
|
+
const actionType = aNext.actionType || "unknown";
|
|
2357
|
+
const actionDetail = (aNext.actionDetail || "").slice(0, 100);
|
|
2358
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [LLM_PROMPT] action_type=${actionType}, action_detail=${actionDetail}... prompt_length=${prompt.length}
|
|
2359
|
+
${prompt}
|
|
2360
|
+
--- END PROMPT ---
|
|
2361
|
+
`;
|
|
2362
|
+
appendFileSync(logPath, line, "utf-8");
|
|
2363
|
+
} catch (e) {
|
|
2364
|
+
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
2368
|
+
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
2369
|
+
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
2370
|
+
const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
2371
|
+
const hintsText = formatHints(previous);
|
|
2372
|
+
const securityJudgeTemplate = templates["security_judge"];
|
|
2373
|
+
if (securityJudgeTemplate) {
|
|
2374
|
+
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
|
|
2375
|
+
}
|
|
2376
|
+
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
2377
|
+
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
2378
|
+
- \u4F1A\u8BDD ID: ${input.sessionId}
|
|
2379
|
+
- \u7528\u6237\u4EFB\u52A1: ${input.promptSession || "\u672A\u6307\u5B9A"}
|
|
2380
|
+
- \u64CD\u4F5C\u7CFB\u7EDF: ${input.osType}
|
|
2381
|
+
- \u5DE5\u4F5C\u76EE\u5F55: ${input.cwd || "/"}
|
|
2382
|
+
- \u64CD\u4F5C\u539F\u56E0: ${input.reason || "\u672A\u8BF4\u660E"}
|
|
2383
|
+
- \u5386\u53F2\u64CD\u4F5C\u5E8F\u5217:
|
|
2384
|
+
${historyStr}
|
|
2385
|
+
|
|
2386
|
+
- \u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C:
|
|
2387
|
+
\u7C7B\u578B: ${input.aNext.actionType}
|
|
2388
|
+
\u8BE6\u60C5: ${input.aNext.actionDetail}
|
|
2389
|
+
|
|
2390
|
+
## \u53EF\u7528\u5B89\u5168 Skill
|
|
2391
|
+
${skillsText}
|
|
2392
|
+
|
|
2393
|
+
## \u524D\u7F6E\u68C0\u6D4B\u7ED3\u679C
|
|
2394
|
+
${hintsText}
|
|
2395
|
+
${scriptAnalysisText ? `
|
|
2396
|
+
## \u811A\u672C\u5185\u5BB9\u5206\u6790
|
|
2397
|
+
${scriptAnalysisText}` : ""}
|
|
2398
|
+
|
|
2399
|
+
\u8BF7\u6839\u636E\u4EE5\u4E0A\u4FE1\u606F\uFF0C\u5BF9\u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C\u8FDB\u884C\u5B89\u5168\u5224\u65AD\u3002\u524D\u7F6E\u68C0\u6D4B\u4E3A low/medium \u4E14\u6709\u5408\u7406\u6267\u884C\u7406\u7531\u65F6\u5E94\u5F53\u5141\u8BB8\u3002\u4F60\u5FC5\u987B\u8F93\u51FA\u4E25\u683C\u7684 JSON \u683C\u5F0F\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u6CE8\u91CA\u6216 markdown \u6807\u8BB0\u3002
|
|
2400
|
+
|
|
2401
|
+
\u8F93\u51FA\u683C\u5F0F:
|
|
2402
|
+
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
2403
|
+
}
|
|
2404
|
+
function formatHints(previous) {
|
|
2405
|
+
const hints = [];
|
|
2406
|
+
if (previous.heuristic) {
|
|
2407
|
+
hints.push(
|
|
2408
|
+
`\u542F\u53D1\u5F0F\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
2409
|
+
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
2410
|
+
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
if (previous.logic) {
|
|
2414
|
+
hints.push(
|
|
2415
|
+
`\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.logic.riskLevel}]:
|
|
2416
|
+
\u7C7B\u578B: ${previous.logic.riskType}
|
|
2417
|
+
\u539F\u56E0: ${previous.logic.reason}`
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
return hints.length > 0 ? hints.join("\n\n") : "\u542F\u53D1\u5F0F\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
|
|
2421
|
+
}
|
|
2422
|
+
async function analyzeWithLLM(prompt, config) {
|
|
2423
|
+
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
2424
|
+
const provider = detectProvider(config.baseUrl, config.provider);
|
|
2425
|
+
const extraHeaders = getProviderHeaders(provider);
|
|
2426
|
+
if (Object.keys(extraHeaders).length > 0) {
|
|
2427
|
+
console.log(`[AgentMoss] Provider detected: ${provider}, injecting headers: ${Object.keys(extraHeaders).join(", ")}`);
|
|
2428
|
+
}
|
|
2429
|
+
const timeoutCfg = getTimeoutConfig();
|
|
2430
|
+
const retryCfg = getRetryConfig();
|
|
2431
|
+
const client = new OpenAI({
|
|
2432
|
+
baseURL: config.baseUrl,
|
|
2433
|
+
apiKey: config.apiKey,
|
|
2434
|
+
// 单次 LLM 调用超时用 prompt1Timeout(对齐 py);env 覆盖的 config.timeout 仍向后兼容
|
|
2435
|
+
timeout: (config.timeout || timeoutCfg.prompt1Timeout) * 1e3,
|
|
2436
|
+
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
2437
|
+
});
|
|
2438
|
+
const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || String(retryCfg.maxRetries), 10);
|
|
2439
|
+
const totalTimeoutMs = timeoutCfg.totalTimeout * 1e3;
|
|
2440
|
+
const abortController = new AbortController();
|
|
2441
|
+
const totalTimeoutTimer = setTimeout(() => {
|
|
2442
|
+
console.error(`[AgentMoss] totalTimeout (${timeoutCfg.totalTimeout}s) exceeded, aborting all LLM calls`);
|
|
2443
|
+
abortController.abort();
|
|
2444
|
+
}, totalTimeoutMs);
|
|
2445
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2446
|
+
if (abortController.signal.aborted) {
|
|
2447
|
+
break;
|
|
2448
|
+
}
|
|
2449
|
+
try {
|
|
2450
|
+
const response = await client.chat.completions.create({
|
|
2451
|
+
model: config.model,
|
|
2452
|
+
temperature: config.temperature || 0.1,
|
|
2453
|
+
max_tokens: config.maxTokens || 1024,
|
|
2454
|
+
messages: [
|
|
2455
|
+
{ role: "system", content: "\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002" },
|
|
2456
|
+
{ role: "user", content: prompt }
|
|
2457
|
+
],
|
|
2458
|
+
signal: abortController.signal
|
|
2459
|
+
});
|
|
2460
|
+
const content = response.choices[0]?.message?.content || "{}";
|
|
2461
|
+
console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
|
|
2462
|
+
let jsonStr = content.trim();
|
|
2463
|
+
if (jsonStr.startsWith("```")) {
|
|
2464
|
+
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
2465
|
+
}
|
|
2466
|
+
const parsed = JSON.parse(jsonStr);
|
|
2467
|
+
try {
|
|
2468
|
+
const usage = response.usage;
|
|
2469
|
+
const { recordTokenUsage: recordTokenUsage2 } = await Promise.resolve().then(() => (init_token_stats(), token_stats_exports));
|
|
2470
|
+
recordTokenUsage2({
|
|
2471
|
+
sessionId: "audit",
|
|
2472
|
+
step: "L3_security_judge",
|
|
2473
|
+
model: response.model || config.model,
|
|
2474
|
+
promptTokens: usage?.prompt_tokens || 0,
|
|
2475
|
+
completionTokens: usage?.completion_tokens || 0,
|
|
2476
|
+
totalTokens: usage?.total_tokens || 0
|
|
2477
|
+
});
|
|
2478
|
+
} catch {
|
|
2479
|
+
}
|
|
2480
|
+
clearTimeout(totalTimeoutTimer);
|
|
2481
|
+
return {
|
|
2482
|
+
allowed: parsed.allowed !== false,
|
|
2483
|
+
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
2484
|
+
riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
|
|
2485
|
+
riskType: parsed.riskType || "",
|
|
2486
|
+
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
2487
|
+
source: "llm",
|
|
2488
|
+
violatedLayers: ["3"]
|
|
2489
|
+
};
|
|
2490
|
+
} catch (err) {
|
|
2491
|
+
if (abortController.signal.aborted) {
|
|
2492
|
+
console.error(`[AgentMoss] LLM analysis aborted by totalTimeout (${timeoutCfg.totalTimeout}s)`);
|
|
2493
|
+
break;
|
|
2494
|
+
}
|
|
2495
|
+
if (attempt < maxRetries) {
|
|
2496
|
+
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
2497
|
+
await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
|
|
2498
|
+
} else {
|
|
2499
|
+
clearTimeout(totalTimeoutTimer);
|
|
2500
|
+
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
2501
|
+
throw new Error(`LLM \u5206\u6790\u5931\u8D25 (${maxRetries + 1} \u6B21\u5C1D\u8BD5): ${err.message}`);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
clearTimeout(totalTimeoutTimer);
|
|
2506
|
+
throw new Error(`LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09`);
|
|
2507
|
+
}
|
|
2508
|
+
var llmCache, LLM_CACHE_MAX;
|
|
2509
|
+
var init_llm_analyzer = __esm({
|
|
2510
|
+
"src/engine/llm-analyzer.ts"() {
|
|
2511
|
+
"use strict";
|
|
2512
|
+
init_esm_shims();
|
|
2513
|
+
init_config();
|
|
2514
|
+
init_skill_loader();
|
|
2515
|
+
init_template_loader();
|
|
2516
|
+
init_script_analyzer();
|
|
2517
|
+
init_script_content_analyzer();
|
|
2518
|
+
llmCache = /* @__PURE__ */ new Map();
|
|
2519
|
+
LLM_CACHE_MAX = 200;
|
|
2520
|
+
}
|
|
2521
|
+
});
|
|
2522
|
+
|
|
2523
|
+
// src/infra/runtime-config.ts
|
|
2524
|
+
var runtime_config_exports = {};
|
|
2525
|
+
__export(runtime_config_exports, {
|
|
2526
|
+
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
2527
|
+
addCustomRule: () => addCustomRule,
|
|
2528
|
+
addCustomSkill: () => addCustomSkill,
|
|
2529
|
+
deleteCustomRule: () => deleteCustomRule,
|
|
2530
|
+
deleteCustomSkill: () => deleteCustomSkill,
|
|
2531
|
+
filterEnabledRules: () => filterEnabledRules,
|
|
2532
|
+
generateSourceDefaults: () => generateSourceDefaults,
|
|
2533
|
+
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
2534
|
+
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
2535
|
+
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
2536
|
+
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
2537
|
+
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
2538
|
+
getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
|
|
2539
|
+
getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
|
|
2540
|
+
getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
|
|
2541
|
+
getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
|
|
2542
|
+
getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
|
|
2543
|
+
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
2544
|
+
getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
|
|
2545
|
+
getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
|
|
2546
|
+
getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
|
|
2547
|
+
getEnvOverrides: () => getEnvOverrides,
|
|
2548
|
+
getProfileOrDetect: () => getProfileOrDetect,
|
|
2549
|
+
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
2550
|
+
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
2551
|
+
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
2552
|
+
isLayerEnabled: () => isLayerEnabled,
|
|
2553
|
+
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
2554
|
+
mergeConfig: () => mergeConfig,
|
|
2555
|
+
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
2556
|
+
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
2557
|
+
updateLayerEnabled: () => updateLayerEnabled,
|
|
2558
|
+
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
2559
|
+
updateRuleEnabled: () => updateRuleEnabled,
|
|
2560
|
+
updateRuleSkipL3: () => updateRuleSkipL3,
|
|
2561
|
+
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
2562
|
+
updateSkillEnabled: () => updateSkillEnabled
|
|
2563
|
+
});
|
|
2564
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2565
|
+
import { join as join4 } from "path";
|
|
2566
|
+
import { homedir as homedir3 } from "os";
|
|
2567
|
+
function getRuntimeConfigPath() {
|
|
2568
|
+
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
2569
|
+
if (envPath) return envPath;
|
|
2570
|
+
return RUNTIME_CONFIG_PATH;
|
|
2571
|
+
}
|
|
2572
|
+
function saveRuntimeConfig(config) {
|
|
2573
|
+
const p = getRuntimeConfigPath();
|
|
2574
|
+
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2575
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2576
|
+
writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
|
|
2577
|
+
}
|
|
2578
|
+
function emptyRuntimeConfig() {
|
|
2579
|
+
return {
|
|
2580
|
+
version: 1,
|
|
2581
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
2582
|
+
L1_rules: {},
|
|
2583
|
+
L2_rules: {},
|
|
2584
|
+
L3_skills: {}
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
function loadRuntimeConfig() {
|
|
2588
|
+
const p = getRuntimeConfigPath();
|
|
2589
|
+
if (!existsSync7(p)) {
|
|
2590
|
+
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
2591
|
+
try {
|
|
2592
|
+
saveRuntimeConfig(seeded);
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
return seeded;
|
|
2596
|
+
}
|
|
2597
|
+
try {
|
|
2598
|
+
const data = JSON.parse(readFileSync7(p, "utf-8"));
|
|
2599
|
+
const merged = mergeConfig(data, generateSourceDefaults());
|
|
2600
|
+
return {
|
|
2601
|
+
version: data.version || 1,
|
|
2602
|
+
layers: {
|
|
2603
|
+
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
2604
|
+
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
2605
|
+
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
2606
|
+
},
|
|
2607
|
+
L1_rules: merged.L1_rules || {},
|
|
2608
|
+
L2_rules: merged.L2_rules || {},
|
|
2609
|
+
L3_skills: merged.L3_skills || {}
|
|
2610
|
+
};
|
|
2611
|
+
} catch {
|
|
2612
|
+
return emptyRuntimeConfig();
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
function isLayerEnabled(layerKey) {
|
|
2616
|
+
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2617
|
+
if (disableKey && process.env[disableKey] === "1") return false;
|
|
2618
|
+
const runtime = loadRuntimeConfig();
|
|
2619
|
+
const layers = runtime.layers || {};
|
|
2620
|
+
if (layerKey in layers) return !!layers[layerKey];
|
|
2621
|
+
return true;
|
|
2622
|
+
}
|
|
2623
|
+
function getEnvOverrides() {
|
|
2624
|
+
const overrides = {};
|
|
2625
|
+
for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
|
|
2626
|
+
const val = process.env[envKey];
|
|
2627
|
+
if (val !== void 0) {
|
|
2628
|
+
overrides[layerKey] = {
|
|
2629
|
+
env_var: envKey,
|
|
2630
|
+
env_value: val,
|
|
2631
|
+
overridden: val === "1",
|
|
2632
|
+
effect: val === "1" ? "disabled" : "no_override"
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return overrides;
|
|
2637
|
+
}
|
|
2638
|
+
function filterEnabledRules(rules, categoryEnabled = true) {
|
|
2639
|
+
if (!categoryEnabled) return [];
|
|
2640
|
+
return rules.filter((r) => r.enabled !== false);
|
|
2641
|
+
}
|
|
2642
|
+
function getEnabledL1CommandPatterns(runtime) {
|
|
2643
|
+
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
2644
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2645
|
+
pattern: r.pattern || "",
|
|
2646
|
+
risk_level: r.risk_level || "",
|
|
2647
|
+
risk_type: r.risk_type || "",
|
|
2648
|
+
reason: r.reason || ""
|
|
2649
|
+
}));
|
|
2650
|
+
}
|
|
2651
|
+
function getEnabledL2SensitivePaths(runtime) {
|
|
2652
|
+
const cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
2653
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
2654
|
+
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
2655
|
+
if (r.credential) out.credential = true;
|
|
2656
|
+
if (r.read_only) out.read_only = true;
|
|
2657
|
+
return out;
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
function hasRuntimeConfig() {
|
|
2661
|
+
const p = getRuntimeConfigPath();
|
|
2662
|
+
if (!existsSync7(p)) return false;
|
|
2663
|
+
try {
|
|
2664
|
+
JSON.parse(readFileSync7(p, "utf-8"));
|
|
2665
|
+
return true;
|
|
2666
|
+
} catch {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
function getDisabledL2RulesWithSkipL3() {
|
|
2671
|
+
let runtime;
|
|
2672
|
+
try {
|
|
2673
|
+
runtime = loadRuntimeConfig();
|
|
2674
|
+
} catch {
|
|
2675
|
+
return {};
|
|
2676
|
+
}
|
|
2677
|
+
const result = {};
|
|
2678
|
+
const l2 = runtime.L2_rules || {};
|
|
2679
|
+
for (const [catName, cat] of Object.entries(l2)) {
|
|
2680
|
+
if (!cat.category_enabled) continue;
|
|
2681
|
+
const items = [];
|
|
2682
|
+
for (const r of cat.rules || []) {
|
|
2683
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
2684
|
+
if (catName === "sensitive_path_access") {
|
|
2685
|
+
if (r.path) items.push(r.path);
|
|
2686
|
+
} else if (catName === "intent_consistency") {
|
|
2687
|
+
items.push(...r.intent_keywords || []);
|
|
2688
|
+
} else if (r.pattern) {
|
|
2689
|
+
items.push(r.pattern);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
if (items.length > 0) result[catName] = items;
|
|
2694
|
+
}
|
|
2695
|
+
return result;
|
|
2696
|
+
}
|
|
2697
|
+
function _slugify(text) {
|
|
2698
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
|
|
2699
|
+
}
|
|
2700
|
+
function _generatePatternId(pattern) {
|
|
2701
|
+
return `pat_${_slugify(pattern)}`;
|
|
2702
|
+
}
|
|
2703
|
+
function _deriveDenyMode(rule) {
|
|
2704
|
+
if (rule.deny_mode) return rule.deny_mode;
|
|
2705
|
+
if (rule.credential) return "deny_both";
|
|
2706
|
+
if (rule.read_only) return "deny_read";
|
|
2707
|
+
return "deny_write";
|
|
2708
|
+
}
|
|
2709
|
+
function _syncDenyModeFields(rule) {
|
|
2710
|
+
const dm = rule.deny_mode || "deny_write";
|
|
2711
|
+
if (dm === "deny_both") {
|
|
2712
|
+
rule.credential = true;
|
|
2713
|
+
delete rule.read_only;
|
|
2714
|
+
} else if (dm === "deny_read") {
|
|
2715
|
+
delete rule.credential;
|
|
2716
|
+
rule.read_only = true;
|
|
2717
|
+
} else {
|
|
2718
|
+
delete rule.credential;
|
|
2719
|
+
delete rule.read_only;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
function _l1CommandPatternsToRules() {
|
|
2723
|
+
return DANGEROUS_PATTERNS.map((p) => ({
|
|
2724
|
+
id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
|
|
2725
|
+
pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
|
|
2726
|
+
risk_level: p.severity,
|
|
2727
|
+
risk_type: p.riskType,
|
|
2728
|
+
reason: p.description,
|
|
2729
|
+
enabled: true,
|
|
2730
|
+
builtin: true
|
|
2731
|
+
}));
|
|
2732
|
+
}
|
|
2733
|
+
function _l1InjectionKeywordsToRules() {
|
|
2734
|
+
return INJECTION_KEYWORDS.map((kw) => ({
|
|
2735
|
+
id: `inj_${_slugify(kw.keyword)}`,
|
|
2736
|
+
keyword: kw.keyword,
|
|
2737
|
+
risk_level: kw.riskLevel,
|
|
2738
|
+
lang: kw.lang || "en",
|
|
2739
|
+
enabled: true,
|
|
2740
|
+
builtin: true
|
|
2741
|
+
}));
|
|
2742
|
+
}
|
|
2743
|
+
function _l2SensitivePathsToRules() {
|
|
2744
|
+
const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
|
|
2745
|
+
return all.map((sp) => {
|
|
2746
|
+
const denyMode = _deriveDenyMode({
|
|
2747
|
+
deny_mode: sp.deny_mode,
|
|
2748
|
+
credential: sp.credential,
|
|
2749
|
+
read_only: sp.read_only
|
|
2750
|
+
});
|
|
2751
|
+
const rule = {
|
|
2752
|
+
id: `path_${_slugify(sp.path)}`,
|
|
2753
|
+
path: sp.path,
|
|
2754
|
+
risk_level: sp.riskLevel,
|
|
2755
|
+
desc: sp.desc,
|
|
2756
|
+
deny_mode: denyMode,
|
|
2757
|
+
source_deny_mode: denyMode,
|
|
2758
|
+
skip_l3_on_disabled: true,
|
|
2759
|
+
enabled: true,
|
|
2760
|
+
builtin: true
|
|
2761
|
+
};
|
|
2762
|
+
_syncDenyModeFields(rule);
|
|
2763
|
+
return rule;
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
function _l2IntentPatternsToRules() {
|
|
2767
|
+
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
2768
|
+
id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
|
|
2769
|
+
intent_keywords: pat.intentKeywords,
|
|
2770
|
+
dangerous_actions: pat.dangerousActions,
|
|
2771
|
+
reason: pat.reason,
|
|
2772
|
+
skip_l3_on_disabled: true,
|
|
2773
|
+
enabled: true,
|
|
2774
|
+
builtin: true
|
|
2775
|
+
}));
|
|
2776
|
+
}
|
|
2777
|
+
function _l2PasswordPatternsToRules() {
|
|
2778
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => ({
|
|
2779
|
+
id: `pw_${_slugify(p.source)}`,
|
|
2780
|
+
pattern: p.source,
|
|
2781
|
+
skip_l3_on_disabled: true,
|
|
2782
|
+
enabled: true,
|
|
2783
|
+
builtin: true
|
|
2784
|
+
}));
|
|
2785
|
+
}
|
|
2786
|
+
function _l2UserDeletionPatternsToRules() {
|
|
2787
|
+
return USER_DELETION_PATTERNS.map((p) => ({
|
|
2788
|
+
id: `ud_${_slugify(p.source)}`,
|
|
2789
|
+
pattern: p.source,
|
|
2790
|
+
skip_l3_on_disabled: true,
|
|
2791
|
+
enabled: true,
|
|
2792
|
+
builtin: true
|
|
2793
|
+
}));
|
|
2794
|
+
}
|
|
2795
|
+
function _l3SkillsToRules() {
|
|
2796
|
+
const skillCategories = {
|
|
2797
|
+
file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
|
|
2798
|
+
network_security: [
|
|
2799
|
+
"data_exfiltration_guard",
|
|
2800
|
+
"browser_web_access_guard",
|
|
2801
|
+
"lateral_movement_guard",
|
|
2802
|
+
"email_operation_guard"
|
|
2803
|
+
],
|
|
2804
|
+
persistence_supply_chain: [
|
|
2805
|
+
"persistence_backdoor_guard",
|
|
2806
|
+
"supply_chain_guard",
|
|
2807
|
+
"skill_installation_guard"
|
|
2808
|
+
],
|
|
2809
|
+
intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
|
|
2810
|
+
resource: ["resource_exhaustion_guard"]
|
|
2811
|
+
};
|
|
2812
|
+
const out = {};
|
|
2813
|
+
for (const [cat, ids] of Object.entries(skillCategories)) {
|
|
2814
|
+
out[cat] = {
|
|
2815
|
+
category_enabled: true,
|
|
2816
|
+
category_desc: "",
|
|
2817
|
+
skills: ids.map((s) => ({
|
|
2818
|
+
id: s,
|
|
2819
|
+
keywords: SKILL_KEYWORD_MAP[s] || [],
|
|
2820
|
+
enabled: true,
|
|
2821
|
+
builtin: true
|
|
2822
|
+
}))
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
const categorized = new Set(Object.values(skillCategories).flat());
|
|
2826
|
+
for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
|
|
2827
|
+
if (!categorized.has(s)) {
|
|
2828
|
+
(out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
|
|
2829
|
+
id: s,
|
|
2830
|
+
keywords: SKILL_KEYWORD_MAP[s],
|
|
2831
|
+
enabled: true,
|
|
2832
|
+
builtin: true
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
2837
|
+
return out;
|
|
2838
|
+
}
|
|
2839
|
+
function generateSourceDefaults() {
|
|
2840
|
+
return {
|
|
2841
|
+
version: 1,
|
|
2842
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
2843
|
+
L1_rules: {
|
|
2844
|
+
dangerous_commands: {
|
|
2845
|
+
category_enabled: true,
|
|
2846
|
+
category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
|
|
2847
|
+
rules: _l1CommandPatternsToRules()
|
|
2848
|
+
},
|
|
2849
|
+
injection_detection: {
|
|
2850
|
+
category_enabled: true,
|
|
2851
|
+
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
2852
|
+
rules: _l1InjectionKeywordsToRules()
|
|
2853
|
+
},
|
|
2854
|
+
user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
|
|
2855
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
2856
|
+
},
|
|
2857
|
+
L2_rules: {
|
|
2858
|
+
sensitive_path_access: {
|
|
2859
|
+
category_enabled: true,
|
|
2860
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
|
|
2861
|
+
rules: _l2SensitivePathsToRules()
|
|
2862
|
+
},
|
|
2863
|
+
intent_consistency: {
|
|
2864
|
+
category_enabled: true,
|
|
2865
|
+
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
2866
|
+
rules: _l2IntentPatternsToRules()
|
|
2867
|
+
},
|
|
2868
|
+
password_modify_consent: {
|
|
2869
|
+
category_enabled: true,
|
|
2870
|
+
category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
|
|
2871
|
+
rules: _l2PasswordPatternsToRules()
|
|
2872
|
+
},
|
|
2873
|
+
user_deletion_consent: {
|
|
2874
|
+
category_enabled: true,
|
|
2875
|
+
category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
|
|
2876
|
+
rules: _l2UserDeletionPatternsToRules()
|
|
2877
|
+
},
|
|
2878
|
+
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
2879
|
+
dangerous_patterns: { category_enabled: true, category_desc: "\u5371\u9669\u64CD\u4F5C\u6A21\u5F0F\u68C0\u6D4B", rules: [] }
|
|
2880
|
+
},
|
|
2881
|
+
L3_skills: _l3SkillsToRules()
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function _ruleFingerprint(rule) {
|
|
2885
|
+
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
2886
|
+
return JSON.stringify([
|
|
2887
|
+
[...rule.intent_keywords || []].sort(),
|
|
2888
|
+
[...rule.dangerous_actions || []].sort()
|
|
2889
|
+
]);
|
|
2890
|
+
}
|
|
2891
|
+
if (rule.path) return JSON.stringify([rule.path]);
|
|
2892
|
+
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
2893
|
+
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
2894
|
+
return JSON.stringify([rule.id || ""]);
|
|
2895
|
+
}
|
|
2896
|
+
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
2897
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
2898
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
2899
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
2900
|
+
if (!(catName in userLayer)) {
|
|
2901
|
+
userLayer[catName] = srcCat;
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
const userCat = userLayer[catName];
|
|
2905
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
2906
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
2907
|
+
const srcRules = srcCat.rules || [];
|
|
2908
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
2909
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
2910
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
2911
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
2912
|
+
for (const srcR of srcRules) {
|
|
2913
|
+
const fp = _ruleFingerprint(srcR);
|
|
2914
|
+
srcFps.add(fp);
|
|
2915
|
+
if (userByFp.has(fp)) {
|
|
2916
|
+
const existing = userByFp.get(fp);
|
|
2917
|
+
const userEnabled = existing.enabled;
|
|
2918
|
+
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
2919
|
+
const userDenyMode = existing.deny_mode;
|
|
2920
|
+
const userSourceDeny = existing.source_deny_mode;
|
|
2921
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
2922
|
+
Object.assign(existing, srcR);
|
|
2923
|
+
existing.enabled = userEnabled;
|
|
2924
|
+
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
2925
|
+
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
2926
|
+
existing.deny_mode = userDenyMode;
|
|
2927
|
+
}
|
|
2928
|
+
if (existing.deny_mode) _syncDenyModeFields(existing);
|
|
2929
|
+
} else {
|
|
2930
|
+
const newR = { ...srcR, enabled: true };
|
|
2931
|
+
userRules.push(newR);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
for (const r of userRules) {
|
|
2935
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
2936
|
+
r.source_removed = true;
|
|
2937
|
+
r.enabled = false;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
if (catName === "sensitive_path_access") {
|
|
2941
|
+
for (const r of userRules) {
|
|
2942
|
+
if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
|
|
2943
|
+
_syncDenyModeFields(r);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
2949
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
2950
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
2951
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
2952
|
+
if (!(catName in userLayer)) {
|
|
2953
|
+
userLayer[catName] = srcCat;
|
|
2954
|
+
continue;
|
|
2955
|
+
}
|
|
2956
|
+
const userCat = userLayer[catName];
|
|
2957
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
2958
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
2959
|
+
const userSkills = userCat.skills || (userCat.skills = []);
|
|
2960
|
+
const existingIds = new Set(userSkills.map((s) => s.id));
|
|
2961
|
+
for (const srcS of srcCat.skills || []) {
|
|
2962
|
+
if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
function mergeConfig(userCfg, srcDefaults) {
|
|
2967
|
+
const cfg = userCfg;
|
|
2968
|
+
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
2969
|
+
if (!cfg.layers) cfg.layers = {};
|
|
2970
|
+
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
2971
|
+
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
2972
|
+
}
|
|
2973
|
+
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
2974
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2975
|
+
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2976
|
+
return cfg;
|
|
2977
|
+
}
|
|
2978
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
2979
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2980
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2981
|
+
keyword: r.keyword || "",
|
|
2982
|
+
risk_level: r.risk_level || "",
|
|
2983
|
+
lang: r.lang || "en"
|
|
2984
|
+
}));
|
|
2985
|
+
}
|
|
2986
|
+
function getEnabledL2IntentPatterns(runtime) {
|
|
2987
|
+
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2988
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2989
|
+
intent_keywords: r.intent_keywords || [],
|
|
2990
|
+
dangerous_actions: r.dangerous_actions || [],
|
|
2991
|
+
reason: r.reason || ""
|
|
2992
|
+
}));
|
|
2993
|
+
}
|
|
2994
|
+
function getEnabledL2PasswordPatterns(runtime) {
|
|
2995
|
+
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2996
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
2997
|
+
}
|
|
2998
|
+
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2999
|
+
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
3000
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
3001
|
+
}
|
|
3002
|
+
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
3003
|
+
const cat = runtime.L2_rules?.[categoryKey];
|
|
3004
|
+
return cat?.category_enabled ?? true;
|
|
3005
|
+
}
|
|
3006
|
+
function getDisabledSkillIds(runtime) {
|
|
3007
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
3008
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
3009
|
+
if (!catData.category_enabled) {
|
|
3010
|
+
for (const s of catData.skills || []) disabled.add(s.id);
|
|
3011
|
+
} else {
|
|
3012
|
+
for (const s of catData.skills || []) {
|
|
3013
|
+
if (s.enabled === false) disabled.add(s.id);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
return disabled;
|
|
3018
|
+
}
|
|
3019
|
+
function getEnabledL1CommandPatternsOrDefault(runtime) {
|
|
3020
|
+
const enabled = getEnabledL1CommandPatterns(runtime);
|
|
3021
|
+
if (enabled.length) return enabled;
|
|
3022
|
+
if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
|
|
3023
|
+
return [];
|
|
3024
|
+
}
|
|
3025
|
+
function getEnabledL1InjectionKeywordsOrDefault(runtime) {
|
|
3026
|
+
const enabled = getEnabledL1InjectionKeywords(runtime);
|
|
3027
|
+
if (enabled.length) return enabled;
|
|
3028
|
+
if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
|
|
3029
|
+
return [];
|
|
3030
|
+
}
|
|
3031
|
+
function getEnabledL2SensitivePathsOrDefault(runtime) {
|
|
3032
|
+
const enabled = getEnabledL2SensitivePaths(runtime);
|
|
3033
|
+
if (enabled.length) return enabled;
|
|
3034
|
+
if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
|
|
3035
|
+
return [];
|
|
3036
|
+
}
|
|
3037
|
+
function getEnabledL2IntentPatternsOrDefault(runtime) {
|
|
3038
|
+
const enabled = getEnabledL2IntentPatterns(runtime);
|
|
3039
|
+
if (enabled.length) return enabled;
|
|
3040
|
+
if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
|
|
3041
|
+
return [];
|
|
3042
|
+
}
|
|
3043
|
+
function getEnabledL2PasswordPatternsOrDefault(runtime) {
|
|
3044
|
+
const enabled = getEnabledL2PasswordPatterns(runtime);
|
|
3045
|
+
if (enabled.length) return enabled;
|
|
3046
|
+
if (!hasRuntimeConfig()) {
|
|
3047
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
|
|
3048
|
+
}
|
|
3049
|
+
return [];
|
|
3050
|
+
}
|
|
3051
|
+
function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
|
|
3052
|
+
const enabled = getEnabledL2UserDeletionPatterns(runtime);
|
|
3053
|
+
if (enabled.length) return enabled;
|
|
3054
|
+
if (!hasRuntimeConfig()) {
|
|
3055
|
+
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
3056
|
+
}
|
|
3057
|
+
return [];
|
|
3058
|
+
}
|
|
3059
|
+
function _validateSkillId(skillId) {
|
|
3060
|
+
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
3061
|
+
throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
function updateLayerEnabled(layerKey, enabled) {
|
|
3065
|
+
const runtime = loadRuntimeConfig();
|
|
3066
|
+
runtime.layers[layerKey] = enabled;
|
|
3067
|
+
saveRuntimeConfig(runtime);
|
|
3068
|
+
return runtime;
|
|
3069
|
+
}
|
|
3070
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
3071
|
+
const runtime = loadRuntimeConfig();
|
|
3072
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3073
|
+
if (r.id === ruleId) {
|
|
3074
|
+
r.enabled = enabled;
|
|
3075
|
+
break;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
saveRuntimeConfig(runtime);
|
|
3079
|
+
return runtime;
|
|
3080
|
+
}
|
|
3081
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
3082
|
+
const runtime = loadRuntimeConfig();
|
|
3083
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3084
|
+
if (r.id === ruleId) {
|
|
3085
|
+
r.deny_mode = denyMode;
|
|
3086
|
+
_syncDenyModeFields(r);
|
|
3087
|
+
break;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
saveRuntimeConfig(runtime);
|
|
3091
|
+
return runtime;
|
|
3092
|
+
}
|
|
3093
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
3094
|
+
const runtime = loadRuntimeConfig();
|
|
3095
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
3096
|
+
if (r.id === ruleId) {
|
|
3097
|
+
r.skip_l3_on_disabled = skipL3;
|
|
3098
|
+
break;
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
saveRuntimeConfig(runtime);
|
|
3102
|
+
return runtime;
|
|
3103
|
+
}
|
|
3104
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
3105
|
+
const runtime = loadRuntimeConfig();
|
|
3106
|
+
const cat = runtime[layer]?.[category];
|
|
3107
|
+
if (cat) cat.category_enabled = enabled;
|
|
3108
|
+
saveRuntimeConfig(runtime);
|
|
3109
|
+
return runtime;
|
|
3110
|
+
}
|
|
3111
|
+
function addCustomRule(layer, category, rule) {
|
|
3112
|
+
const runtime = loadRuntimeConfig();
|
|
3113
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
3114
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
3115
|
+
const rules = cat.rules || (cat.rules = []);
|
|
3116
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
3117
|
+
if (!newRule.id) {
|
|
3118
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
3119
|
+
}
|
|
3120
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
3121
|
+
rules.push(newRule);
|
|
3122
|
+
saveRuntimeConfig(runtime);
|
|
3123
|
+
return runtime;
|
|
3124
|
+
}
|
|
3125
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
3126
|
+
const runtime = loadRuntimeConfig();
|
|
3127
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
3128
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
3129
|
+
if (idx >= 0) {
|
|
3130
|
+
if (rules[idx].builtin) return null;
|
|
3131
|
+
rules.splice(idx, 1);
|
|
3132
|
+
saveRuntimeConfig(runtime);
|
|
3133
|
+
}
|
|
3134
|
+
return runtime;
|
|
3135
|
+
}
|
|
3136
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
3137
|
+
_validateSkillId(skillId);
|
|
3138
|
+
const runtime = loadRuntimeConfig();
|
|
3139
|
+
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
3140
|
+
try {
|
|
3141
|
+
mkdirSync2(skillsDir, { recursive: true });
|
|
3142
|
+
} catch {
|
|
3143
|
+
}
|
|
3144
|
+
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3145
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3146
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3147
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
3148
|
+
saveRuntimeConfig(runtime);
|
|
3149
|
+
return runtime;
|
|
3150
|
+
}
|
|
3151
|
+
function deleteCustomSkill(skillId, category) {
|
|
3152
|
+
_validateSkillId(skillId);
|
|
3153
|
+
const runtime = loadRuntimeConfig();
|
|
3154
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
3155
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
3156
|
+
if (idx >= 0) {
|
|
3157
|
+
if (skills[idx].builtin) return null;
|
|
3158
|
+
skills.splice(idx, 1);
|
|
3159
|
+
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
3160
|
+
try {
|
|
3161
|
+
if (existsSync7(skillPath)) unlinkSync2(skillPath);
|
|
3162
|
+
} catch {
|
|
3163
|
+
}
|
|
3164
|
+
saveRuntimeConfig(runtime);
|
|
3165
|
+
}
|
|
3166
|
+
return runtime;
|
|
3167
|
+
}
|
|
3168
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
3169
|
+
const runtime = loadRuntimeConfig();
|
|
3170
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
3171
|
+
for (const s of catData.skills || []) {
|
|
3172
|
+
if (s.id === skillId) {
|
|
3173
|
+
s.enabled = enabled;
|
|
3174
|
+
break;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
saveRuntimeConfig(runtime);
|
|
3179
|
+
return runtime;
|
|
3180
|
+
}
|
|
3181
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
3182
|
+
const runtime = loadRuntimeConfig();
|
|
3183
|
+
const cat = runtime.L3_skills?.[category];
|
|
3184
|
+
if (cat) cat.category_enabled = enabled;
|
|
3185
|
+
saveRuntimeConfig(runtime);
|
|
3186
|
+
return runtime;
|
|
3187
|
+
}
|
|
3188
|
+
function getProfileOrDetect() {
|
|
3189
|
+
return null;
|
|
3190
|
+
}
|
|
3191
|
+
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
|
|
3192
|
+
var init_runtime_config = __esm({
|
|
3193
|
+
"src/infra/runtime-config.ts"() {
|
|
3194
|
+
"use strict";
|
|
3195
|
+
init_esm_shims();
|
|
3196
|
+
init_heuristic();
|
|
3197
|
+
init_logic_rules();
|
|
3198
|
+
init_skill_loader();
|
|
3199
|
+
RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
3200
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3201
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3202
|
+
LAYER_DISABLE_ENV = {
|
|
3203
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3204
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
3205
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
3206
|
+
};
|
|
3207
|
+
DENY_MODE_LABELS = {
|
|
3208
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
3209
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
3210
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
3211
|
+
};
|
|
3212
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1677
3213
|
}
|
|
1678
3214
|
});
|
|
1679
3215
|
|
|
1680
3216
|
// src/engine/coordinator.ts
|
|
1681
3217
|
var coordinator_exports = {};
|
|
1682
3218
|
__export(coordinator_exports, {
|
|
3219
|
+
TRUSTED_SKILLS_BASE: () => TRUSTED_SKILLS_BASE,
|
|
1683
3220
|
runAnalysis: () => runAnalysis
|
|
1684
3221
|
});
|
|
1685
3222
|
function extractFirstCommand(command) {
|
|
@@ -1710,6 +3247,25 @@ async function runAnalysis(input) {
|
|
|
1710
3247
|
return heuristicResult;
|
|
1711
3248
|
}
|
|
1712
3249
|
}
|
|
3250
|
+
let scriptChain = null;
|
|
3251
|
+
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
3252
|
+
scriptChain = analyzeScriptContent2(input);
|
|
3253
|
+
if (scriptChain.nodes.length > 0) {
|
|
3254
|
+
for (const node of scriptChain.nodes) {
|
|
3255
|
+
if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
|
|
3256
|
+
return {
|
|
3257
|
+
allowed: false,
|
|
3258
|
+
reason: `\u811A\u672C\u5185\u5BB9\u5305\u542B\u5371\u9669\u64CD\u4F5C: ${node.l1Reason} (\u6587\u4EF6: ${node.scriptPath}, \u6DF1\u5EA6: ${node.depth})`,
|
|
3259
|
+
riskLevel: node.l1RiskLevel,
|
|
3260
|
+
riskType: node.l1RiskType,
|
|
3261
|
+
confidence: 90,
|
|
3262
|
+
source: "heuristic_script_scan",
|
|
3263
|
+
violatedLayers: ["1.1-script"]
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
1713
3269
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
1714
3270
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
1715
3271
|
const lowerDetail = rawDetail.toLowerCase();
|
|
@@ -1724,7 +3280,12 @@ async function runAnalysis(input) {
|
|
|
1724
3280
|
violatedLayers: []
|
|
1725
3281
|
};
|
|
1726
3282
|
}
|
|
1727
|
-
|
|
3283
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
3284
|
+
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
3285
|
+
if (agentExt?.builtin_safe_skills) {
|
|
3286
|
+
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
3287
|
+
}
|
|
3288
|
+
if (actionType === "skill" && trustedSkills.has(lowerDetail)) {
|
|
1728
3289
|
return {
|
|
1729
3290
|
allowed: true,
|
|
1730
3291
|
reason: `\u5185\u7F6E\u5B89\u5168 Skill\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${lowerDetail}`,
|
|
@@ -1764,7 +3325,7 @@ async function runAnalysis(input) {
|
|
|
1764
3325
|
if (logicResult && !logicResult.allowed) {
|
|
1765
3326
|
const isCredentialPath = !!logicResult.isCredential;
|
|
1766
3327
|
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && logicResult.riskType === "sensitive_path_access";
|
|
1767
|
-
if (skipInlineFileAccess
|
|
3328
|
+
if (skipInlineFileAccess) {
|
|
1768
3329
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
1769
3330
|
if (inlineJudgment.actionable) {
|
|
1770
3331
|
if (!inlineJudgment.allowed) {
|
|
@@ -1793,6 +3354,23 @@ async function runAnalysis(input) {
|
|
|
1793
3354
|
return logicResult;
|
|
1794
3355
|
}
|
|
1795
3356
|
}
|
|
3357
|
+
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
3358
|
+
for (const node of scriptChain.nodes) {
|
|
3359
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
3360
|
+
if (node.l1RiskLevel === "critical") break;
|
|
3361
|
+
if (node.l2Hit && (node.l2RiskLevel === "high" || node.l2RiskLevel === "critical")) {
|
|
3362
|
+
return {
|
|
3363
|
+
allowed: false,
|
|
3364
|
+
reason: node.l2Reason,
|
|
3365
|
+
riskLevel: node.l2RiskLevel,
|
|
3366
|
+
riskType: node.l2RiskType,
|
|
3367
|
+
confidence: 85,
|
|
3368
|
+
source: "logic_rule_script_scan",
|
|
3369
|
+
violatedLayers: ["1.2-script"]
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
1796
3374
|
if (skipLLM || !isLLMAvailable()) {
|
|
1797
3375
|
return {
|
|
1798
3376
|
allowed: true,
|
|
@@ -1804,13 +3382,64 @@ async function runAnalysis(input) {
|
|
|
1804
3382
|
violatedLayers: []
|
|
1805
3383
|
};
|
|
1806
3384
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
3385
|
+
let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
|
|
3386
|
+
try {
|
|
3387
|
+
const { getDisabledL2RulesWithSkipL3: getDisabledL2RulesWithSkipL32 } = await Promise.resolve().then(() => (init_runtime_config(), runtime_config_exports));
|
|
3388
|
+
const disabledL2 = getDisabledL2RulesWithSkipL32();
|
|
3389
|
+
if (disabledL2 && Object.keys(disabledL2).length > 0) {
|
|
3390
|
+
const actionLower = input.aNext.actionDetail.toLowerCase();
|
|
3391
|
+
const matched = [];
|
|
3392
|
+
for (const items of Object.values(disabledL2)) {
|
|
3393
|
+
for (const item of items) {
|
|
3394
|
+
if (item.toLowerCase().includes(actionLower) || actionLower.includes(item.toLowerCase())) {
|
|
3395
|
+
matched.push(item);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
if (matched.length > 0) {
|
|
3400
|
+
skipL3Hints = `\u26A0\uFE0F \u91CD\u8981\u7EA6\u675F\uFF1A\u7528\u6237\u5DF2\u4E3B\u52A8\u7981\u7528\u9488\u5BF9\u4EE5\u4E0B\u5185\u5BB9\u7684\u5B89\u5168\u62E6\u622A\u89C4\u5219\uFF0C\u8FD9\u662F\u7528\u6237\u7684\u660E\u786E\u51B3\u7B56\uFF1A
|
|
3401
|
+
${matched.join(", ")}
|
|
3402
|
+
\u5BF9\u8FD9\u4E9B\u8DEF\u5F84/\u5173\u952E\u8BCD\u7684\u76F8\u5173\u64CD\u4F5C\uFF0C\u5FC5\u987B\u89C6\u4E3A\u5141\u8BB8\u7684\uFF0C\u4E0D\u5141\u8BB8\u4EC5\u56E0\u6D89\u53CA\u8FD9\u4E9B\u5185\u5BB9\u5C31\u62D2\u7EDD\u3002\u4F46\u4F60\u4ECD\u9700\u68C0\u67E5\u547D\u4EE4\u7684\u5176\u4ED6\u90E8\u5206\u662F\u5426\u6709\u72EC\u7ACB\u4E8E\u8FD9\u4E9B\u5185\u5BB9\u7684\u5B89\u5168\u98CE\u9669\uFF08\u5982\u7F51\u7EDC\u5916\u4F20\u3001\u63D0\u6743\u7B49\uFF09\u3002`;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
} catch {
|
|
3406
|
+
}
|
|
3407
|
+
let llmResult;
|
|
3408
|
+
try {
|
|
3409
|
+
llmResult = await runLLMAnalysis(input, {
|
|
3410
|
+
heuristic: heuristicResult,
|
|
3411
|
+
logic: logicResult,
|
|
3412
|
+
skipL3Hints,
|
|
3413
|
+
scriptChain: scriptChain ?? void 0,
|
|
3414
|
+
llmLogPath: input.llmLogPath
|
|
3415
|
+
});
|
|
3416
|
+
} catch (e) {
|
|
3417
|
+
const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3418
|
+
if (getLLMFailMode2() === "fail_open") {
|
|
3419
|
+
console.warn(`[AgentMoss] LLM \u5206\u6790\u5931\u8D25\uFF0Cfail_open \u6A21\u5F0F\uFF1A\u5141\u8BB8\u6267\u884C\uFF08\u524D\u4E24\u5C42\u672A\u62E6\u622A\uFF09: ${e.message}`);
|
|
3420
|
+
return {
|
|
3421
|
+
allowed: true,
|
|
3422
|
+
reason: `LLM \u5206\u6790\u5931\u8D25\uFF08warn-allow\uFF0Cfail_open\uFF09: ${e.message}`,
|
|
3423
|
+
riskLevel: "low",
|
|
3424
|
+
riskType: "",
|
|
3425
|
+
confidence: 50,
|
|
3426
|
+
source: "llm_failure_fallback",
|
|
3427
|
+
violatedLayers: []
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
return {
|
|
3431
|
+
allowed: false,
|
|
3432
|
+
reason: `LLM \u5206\u6790\u5931\u8D25\uFF0C\u56E0\u65E0\u6CD5\u5B8C\u6210\u6DF1\u5EA6\u8BED\u4E49\u5206\u6790\uFF0C\u6309 fail-closed \u539F\u5219\u62D2\u7EDD: ${e.message}`,
|
|
3433
|
+
riskLevel: "medium",
|
|
3434
|
+
riskType: "",
|
|
3435
|
+
confidence: 60,
|
|
3436
|
+
source: "llm_failure_fallback",
|
|
3437
|
+
violatedLayers: ["3"]
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
1811
3440
|
return llmResult;
|
|
1812
3441
|
}
|
|
1813
|
-
var FULLY_SAFE_TOOLS,
|
|
3442
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, FULLY_SAFE_BASH_PATTERNS, READONLY_SENSITIVE_BASH_PATTERNS;
|
|
1814
3443
|
var init_coordinator = __esm({
|
|
1815
3444
|
"src/engine/coordinator.ts"() {
|
|
1816
3445
|
"use strict";
|
|
@@ -1819,7 +3448,9 @@ var init_coordinator = __esm({
|
|
|
1819
3448
|
init_logic_rules();
|
|
1820
3449
|
init_inline_analyzer();
|
|
1821
3450
|
init_llm_analyzer();
|
|
3451
|
+
init_script_content_analyzer();
|
|
1822
3452
|
init_config();
|
|
3453
|
+
init_agents();
|
|
1823
3454
|
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
1824
3455
|
"ask_user_question",
|
|
1825
3456
|
"glob",
|
|
@@ -1828,7 +3459,7 @@ var init_coordinator = __esm({
|
|
|
1828
3459
|
"count_text_length",
|
|
1829
3460
|
"filemgr-globfiles"
|
|
1830
3461
|
]);
|
|
1831
|
-
|
|
3462
|
+
TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
1832
3463
|
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
1833
3464
|
"read",
|
|
1834
3465
|
"file_read",
|
|
@@ -1894,9 +3525,10 @@ init_esm_shims();
|
|
|
1894
3525
|
|
|
1895
3526
|
// src/server.ts
|
|
1896
3527
|
init_esm_shims();
|
|
1897
|
-
import { Hono } from "hono";
|
|
3528
|
+
import { Hono as Hono2 } from "hono";
|
|
1898
3529
|
import { cors } from "hono/cors";
|
|
1899
3530
|
import { logger } from "hono/logger";
|
|
3531
|
+
import net from "net";
|
|
1900
3532
|
|
|
1901
3533
|
// src/routes.ts
|
|
1902
3534
|
init_esm_shims();
|
|
@@ -1925,6 +3557,7 @@ var AnalyzeRequestSchema = z.object({
|
|
|
1925
3557
|
reason: z.string().default("").describe("Reason for executing this action"),
|
|
1926
3558
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
1927
3559
|
cwd: z.string().default("").describe("Current working directory"),
|
|
3560
|
+
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
1928
3561
|
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
1929
3562
|
});
|
|
1930
3563
|
var AnalyzeResponseSchema = z.object({
|
|
@@ -2074,7 +3707,9 @@ async function analyzeRoute(c) {
|
|
|
2074
3707
|
reason: req.reason,
|
|
2075
3708
|
osType,
|
|
2076
3709
|
cwd: req.cwd,
|
|
2077
|
-
|
|
3710
|
+
agentId: req.agent_id,
|
|
3711
|
+
llmConfig: req.metadata?.llm_config,
|
|
3712
|
+
llmLogPath: req.metadata?.llm_log_path
|
|
2078
3713
|
});
|
|
2079
3714
|
const durationMs = performance.now() - t0;
|
|
2080
3715
|
try {
|
|
@@ -2119,15 +3754,216 @@ async function analyzeRoute(c) {
|
|
|
2119
3754
|
}
|
|
2120
3755
|
}
|
|
2121
3756
|
|
|
3757
|
+
// src/console/index.ts
|
|
3758
|
+
init_esm_shims();
|
|
3759
|
+
|
|
3760
|
+
// src/console/router.ts
|
|
3761
|
+
init_esm_shims();
|
|
3762
|
+
init_runtime_config();
|
|
3763
|
+
init_token_stats();
|
|
3764
|
+
import { Hono } from "hono";
|
|
3765
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
3766
|
+
import { join as join5, dirname as dirname5 } from "path";
|
|
3767
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3768
|
+
var __dirname5 = dirname5(fileURLToPath5(import.meta.url));
|
|
3769
|
+
var STATIC_DIR = join5(__dirname5, "static");
|
|
3770
|
+
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
3771
|
+
function getConsoleToken() {
|
|
3772
|
+
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
3773
|
+
}
|
|
3774
|
+
function verifyAuth(req) {
|
|
3775
|
+
const token = getConsoleToken();
|
|
3776
|
+
if (!token) return true;
|
|
3777
|
+
const auth = req.headers.get("Authorization") || "";
|
|
3778
|
+
if (auth.startsWith("Bearer ")) return auth.slice(7) === token;
|
|
3779
|
+
return false;
|
|
3780
|
+
}
|
|
3781
|
+
function authMiddleware() {
|
|
3782
|
+
return async (c, next) => {
|
|
3783
|
+
if (!verifyAuth(c.req.raw)) {
|
|
3784
|
+
return c.json({ error: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
3785
|
+
}
|
|
3786
|
+
await next();
|
|
3787
|
+
};
|
|
3788
|
+
}
|
|
3789
|
+
function createConsoleRouter() {
|
|
3790
|
+
const console2 = new Hono();
|
|
3791
|
+
console2.use("/api/*", authMiddleware());
|
|
3792
|
+
const serveIndex = (c) => {
|
|
3793
|
+
const indexFile = join5(STATIC_DIR, "index.html");
|
|
3794
|
+
if (existsSync9(indexFile)) {
|
|
3795
|
+
return c.html(readFileSync8(indexFile, "utf-8"));
|
|
3796
|
+
}
|
|
3797
|
+
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
3798
|
+
};
|
|
3799
|
+
console2.get("/", serveIndex);
|
|
3800
|
+
console2.get("", serveIndex);
|
|
3801
|
+
console2.get("/api/layers", (c) => {
|
|
3802
|
+
const rc = loadRuntimeConfig();
|
|
3803
|
+
return c.json({ layers: rc.layers });
|
|
3804
|
+
});
|
|
3805
|
+
console2.put("/api/layers", async (c) => {
|
|
3806
|
+
const body = await c.req.json();
|
|
3807
|
+
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
3808
|
+
return c.json({ layers: rc.layers });
|
|
3809
|
+
});
|
|
3810
|
+
console2.get("/api/rules", (c) => {
|
|
3811
|
+
const rc = loadRuntimeConfig();
|
|
3812
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3813
|
+
});
|
|
3814
|
+
console2.put("/api/rules/enabled", async (c) => {
|
|
3815
|
+
const b = await c.req.json();
|
|
3816
|
+
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
3817
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3818
|
+
});
|
|
3819
|
+
console2.put("/api/rules/deny_mode", async (c) => {
|
|
3820
|
+
const b = await c.req.json();
|
|
3821
|
+
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
3822
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3823
|
+
});
|
|
3824
|
+
console2.put("/api/rules/skip_l3", async (c) => {
|
|
3825
|
+
const b = await c.req.json();
|
|
3826
|
+
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
3827
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3828
|
+
});
|
|
3829
|
+
console2.put("/api/categories/enabled", async (c) => {
|
|
3830
|
+
const b = await c.req.json();
|
|
3831
|
+
const rc = updateCategoryEnabled(b.layer, b.category, b.enabled);
|
|
3832
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3833
|
+
});
|
|
3834
|
+
console2.post("/api/rules", async (c) => {
|
|
3835
|
+
const b = await c.req.json();
|
|
3836
|
+
const rc = addCustomRule(b.layer, b.category, {
|
|
3837
|
+
pattern: b.pattern,
|
|
3838
|
+
risk_level: b.risk_level,
|
|
3839
|
+
risk_type: b.risk_type,
|
|
3840
|
+
reason: b.reason,
|
|
3841
|
+
keyword: b.keyword,
|
|
3842
|
+
lang: b.lang,
|
|
3843
|
+
path: b.path,
|
|
3844
|
+
desc: b.desc,
|
|
3845
|
+
credential: b.credential,
|
|
3846
|
+
deny_mode: b.deny_mode
|
|
3847
|
+
});
|
|
3848
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3849
|
+
});
|
|
3850
|
+
console2.delete("/api/rules", async (c) => {
|
|
3851
|
+
const b = await c.req.json();
|
|
3852
|
+
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
3853
|
+
const rc = loadRuntimeConfig();
|
|
3854
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
3855
|
+
});
|
|
3856
|
+
console2.get("/api/skills", (c) => {
|
|
3857
|
+
const rc = loadRuntimeConfig();
|
|
3858
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3859
|
+
});
|
|
3860
|
+
console2.put("/api/skills/enabled", async (c) => {
|
|
3861
|
+
const b = await c.req.json();
|
|
3862
|
+
const rc = updateSkillEnabled(b.skill_id, b.enabled);
|
|
3863
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3864
|
+
});
|
|
3865
|
+
console2.put("/api/skill-categories/enabled", async (c) => {
|
|
3866
|
+
const b = await c.req.json();
|
|
3867
|
+
const rc = updateSkillCategoryEnabled(b.category, b.enabled);
|
|
3868
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3869
|
+
});
|
|
3870
|
+
console2.post("/api/skills", async (c) => {
|
|
3871
|
+
const b = await c.req.json();
|
|
3872
|
+
const rc = addCustomSkill(b.skill_id, b.category || "user_custom", b.keywords || [], b.content);
|
|
3873
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3874
|
+
});
|
|
3875
|
+
console2.delete("/api/skills", async (c) => {
|
|
3876
|
+
const b = await c.req.json();
|
|
3877
|
+
deleteCustomSkill(b.skill_id, b.category);
|
|
3878
|
+
const rc = loadRuntimeConfig();
|
|
3879
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
3880
|
+
});
|
|
3881
|
+
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
3882
|
+
const skillId = c.req.param("skill_id");
|
|
3883
|
+
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
3884
|
+
return c.json({ error: "invalid skill_id" }, 400);
|
|
3885
|
+
}
|
|
3886
|
+
const skillPath = join5(dirname5(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
3887
|
+
if (!existsSync9(skillPath)) {
|
|
3888
|
+
const builtinPath = join5(__dirname5, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
3889
|
+
if (existsSync9(builtinPath)) return c.text(readFileSync8(builtinPath, "utf-8"));
|
|
3890
|
+
return c.json({ error: "skill not found" }, 404);
|
|
3891
|
+
}
|
|
3892
|
+
return c.text(readFileSync8(skillPath, "utf-8"));
|
|
3893
|
+
});
|
|
3894
|
+
console2.get("/api/config", (c) => {
|
|
3895
|
+
const rc = loadRuntimeConfig();
|
|
3896
|
+
return c.json({ config: rc, config_path: getRuntimeConfigPath() });
|
|
3897
|
+
});
|
|
3898
|
+
console2.get("/api/env-overrides", (c) => {
|
|
3899
|
+
return c.json({ overrides: getEnvOverrides() });
|
|
3900
|
+
});
|
|
3901
|
+
console2.post("/api/reset", (c) => {
|
|
3902
|
+
const defaults = generateSourceDefaults();
|
|
3903
|
+
saveRuntimeConfig(defaults);
|
|
3904
|
+
return c.json({ config: defaults });
|
|
3905
|
+
});
|
|
3906
|
+
console2.get("/api/token-stats", (c) => {
|
|
3907
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
3908
|
+
return c.json(getTokenStats(days));
|
|
3909
|
+
});
|
|
3910
|
+
console2.get("/api/token-stats/recent", (c) => {
|
|
3911
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
3912
|
+
return c.json({ records: getRecentRecords(limit) });
|
|
3913
|
+
});
|
|
3914
|
+
console2.get("/api/token-stats/trend", (c) => {
|
|
3915
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
3916
|
+
return c.json(getTokenTrend(days));
|
|
3917
|
+
});
|
|
3918
|
+
console2.post("/api/token-stats/reset", (c) => {
|
|
3919
|
+
resetTokenStats();
|
|
3920
|
+
return c.json({ reset: true });
|
|
3921
|
+
});
|
|
3922
|
+
return console2;
|
|
3923
|
+
}
|
|
3924
|
+
|
|
2122
3925
|
// src/server.ts
|
|
2123
3926
|
function createApp() {
|
|
2124
|
-
const app = new
|
|
2125
|
-
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"] }));
|
|
3927
|
+
const app = new Hono2();
|
|
3928
|
+
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
2126
3929
|
app.use("*", logger());
|
|
2127
3930
|
app.get("/api/v1/health", healthRoute);
|
|
2128
3931
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
3932
|
+
app.route("/console", createConsoleRouter());
|
|
3933
|
+
app.get("/console/", (c) => c.redirect("/console"));
|
|
2129
3934
|
return app;
|
|
2130
3935
|
}
|
|
3936
|
+
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
3937
|
+
for (let port = startPort; port < startPort + scan; port++) {
|
|
3938
|
+
const free = await new Promise((resolve6) => {
|
|
3939
|
+
const server = net.createServer();
|
|
3940
|
+
server.listen(port, host, () => {
|
|
3941
|
+
server.close(() => resolve6(true));
|
|
3942
|
+
});
|
|
3943
|
+
server.on("error", () => resolve6(false));
|
|
3944
|
+
});
|
|
3945
|
+
if (free) {
|
|
3946
|
+
if (port !== startPort) {
|
|
3947
|
+
console.warn(`[AgentMoss] Port ${startPort} in use, using ${port} instead`);
|
|
3948
|
+
}
|
|
3949
|
+
return port;
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
return startPort;
|
|
3953
|
+
}
|
|
3954
|
+
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
3955
|
+
const actualPort = await findFreePort(port, host);
|
|
3956
|
+
const app = createApp();
|
|
3957
|
+
return {
|
|
3958
|
+
port: actualPort,
|
|
3959
|
+
serve: () => {
|
|
3960
|
+
console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
|
|
3961
|
+
import("@hono/node-server").then(({ serve: honoServe }) => {
|
|
3962
|
+
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
3963
|
+
});
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
2131
3967
|
|
|
2132
3968
|
// src/cli.ts
|
|
2133
3969
|
function printHelp() {
|
|
@@ -2137,10 +3973,14 @@ Commands:
|
|
|
2137
3973
|
init Generate input.json template
|
|
2138
3974
|
analyze <input> Run security analysis on input JSON
|
|
2139
3975
|
server Start HTTP API server
|
|
3976
|
+
install Install systemd service for AgentMoss
|
|
2140
3977
|
|
|
2141
3978
|
Server options:
|
|
2142
3979
|
--host <host> Listen host (default: 127.0.0.1)
|
|
2143
|
-
--port <port> Listen port (default: 9090)
|
|
3980
|
+
--port <port> Listen port (default: 9090, 0=auto assign)
|
|
3981
|
+
|
|
3982
|
+
Install options:
|
|
3983
|
+
--enable Enable service on boot (systemctl enable)
|
|
2144
3984
|
`);
|
|
2145
3985
|
}
|
|
2146
3986
|
async function main() {
|
|
@@ -2190,7 +4030,8 @@ async function main() {
|
|
|
2190
4030
|
},
|
|
2191
4031
|
reason: data.reason || "",
|
|
2192
4032
|
osType: data.os_type || (process.platform === "win32" ? "windows" : "linux"),
|
|
2193
|
-
cwd: data.cwd || process.cwd()
|
|
4033
|
+
cwd: data.cwd || process.cwd(),
|
|
4034
|
+
agentId: data.agent_id || ""
|
|
2194
4035
|
});
|
|
2195
4036
|
console.log(JSON.stringify(result, null, 2));
|
|
2196
4037
|
process.exit(result.allowed ? 0 : 1);
|
|
@@ -2203,12 +4044,51 @@ async function main() {
|
|
|
2203
4044
|
if (args[i] === "--host" && args[i + 1]) host = args[++i];
|
|
2204
4045
|
else if (args[i] === "--port" && args[i + 1]) port = parseInt(args[++i], 10);
|
|
2205
4046
|
}
|
|
2206
|
-
const
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
4047
|
+
const server = await startServer(host, port);
|
|
4048
|
+
console.log(`Listening on http://${host}:${server.port}`);
|
|
4049
|
+
server.serve();
|
|
4050
|
+
break;
|
|
4051
|
+
}
|
|
4052
|
+
case "install": {
|
|
4053
|
+
const enable = args.includes("--enable");
|
|
4054
|
+
const { execSync } = await import("child_process");
|
|
4055
|
+
const fs4 = await import("fs");
|
|
4056
|
+
const path3 = await import("path");
|
|
4057
|
+
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
4058
|
+
if (process.getuid?.() !== 0) {
|
|
4059
|
+
const sudoArgs = ["sudo", process.execPath, ...process.argv.slice(1)];
|
|
4060
|
+
if (enable) sudoArgs.push("--enable");
|
|
4061
|
+
console.log("\u9700\u8981 root \u6743\u9650\u5B89\u88C5 systemd service\uFF0C\u6B63\u5728\u901A\u8FC7 sudo \u91CD\u65B0\u6267\u884C...");
|
|
4062
|
+
execSync(sudoArgs.join(" "), { stdio: "inherit" });
|
|
4063
|
+
process.exit(0);
|
|
4064
|
+
}
|
|
4065
|
+
const __filename5 = fileURLToPath6(import.meta.url);
|
|
4066
|
+
const __dirname6 = path3.dirname(__filename5);
|
|
4067
|
+
let srcPath = path3.join(__dirname6, "data", "agent_moss.service");
|
|
4068
|
+
if (!fs4.existsSync(srcPath)) {
|
|
4069
|
+
srcPath = path3.join(__dirname6, "..", "src", "data", "agent_moss.service");
|
|
4070
|
+
}
|
|
4071
|
+
if (!fs4.existsSync(srcPath)) {
|
|
4072
|
+
console.error("\u9519\u8BEF\uFF1A\u627E\u4E0D\u5230 agent_moss.service \u6A21\u677F\u6587\u4EF6");
|
|
4073
|
+
process.exit(1);
|
|
4074
|
+
}
|
|
4075
|
+
const which = execSync("which agent-moss 2>/dev/null || true", { encoding: "utf-8" }).trim();
|
|
4076
|
+
const binPath = which || `${process.execPath} ${path3.join(__dirname6, "cli.js")}`;
|
|
4077
|
+
let content = fs4.readFileSync(srcPath, "utf-8");
|
|
4078
|
+
content = content.replace("__AGENT_MOSS_BIN__", binPath);
|
|
4079
|
+
const dst = "/etc/systemd/system/agent_moss.service";
|
|
4080
|
+
fs4.writeFileSync(dst, content, "utf-8");
|
|
4081
|
+
fs4.chmodSync(dst, 420);
|
|
4082
|
+
console.log(`\u2705 \u5DF2\u5B89\u88C5 systemd service: ${dst}`);
|
|
4083
|
+
execSync("systemctl daemon-reload", { stdio: "inherit" });
|
|
4084
|
+
console.log("\u2705 systemctl daemon-reload \u5B8C\u6210");
|
|
4085
|
+
if (enable) {
|
|
4086
|
+
execSync("systemctl enable agent_moss", { stdio: "inherit" });
|
|
4087
|
+
console.log("\u2705 systemctl enable agent_moss \u5B8C\u6210");
|
|
4088
|
+
}
|
|
4089
|
+
console.log("\n\u542F\u52A8\u670D\u52A1\uFF1A sudo systemctl start agent_moss");
|
|
4090
|
+
console.log("\u67E5\u770B\u72B6\u6001\uFF1A sudo systemctl status agent_moss");
|
|
4091
|
+
console.log("\u67E5\u770B\u65E5\u5FD7\uFF1A journalctl -u agent_moss -f");
|
|
2212
4092
|
break;
|
|
2213
4093
|
}
|
|
2214
4094
|
default:
|