@kenhkl/agent-moss 0.7.12 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +1180 -112
- package/dist/cli.js.map +1 -1
- 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") {
|
|
@@ -655,7 +748,7 @@ function runLogicCheck(input) {
|
|
|
655
748
|
};
|
|
656
749
|
}
|
|
657
750
|
}
|
|
658
|
-
|
|
751
|
+
var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
659
752
|
"cat",
|
|
660
753
|
"head",
|
|
661
754
|
"tail",
|
|
@@ -736,8 +829,8 @@ function runLogicCheck(input) {
|
|
|
736
829
|
"modinfo",
|
|
737
830
|
"sysctl"
|
|
738
831
|
]);
|
|
739
|
-
|
|
740
|
-
|
|
832
|
+
var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
833
|
+
var _WRITE_COMMAND_RES = [
|
|
741
834
|
/\brm\b/,
|
|
742
835
|
/\bunlink\b/,
|
|
743
836
|
/\brmdir\b/,
|
|
@@ -850,29 +943,12 @@ 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
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (WRITE_COMMAND_RES.some((rx) => rx.test(command))) {
|
|
860
|
-
return true;
|
|
861
|
-
}
|
|
862
|
-
if (/\bsed\s+-[a-zA-Z]*i/.test(command)) {
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
if (command.includes(">")) {
|
|
866
|
-
const withoutDevNull = command.replace(DEVNULL_REDIRECT_RE, "");
|
|
867
|
-
if (withoutDevNull.includes(">")) {
|
|
868
|
-
const firstWord = command.trim().split(/\s+/)[0]?.toLowerCase() || "";
|
|
869
|
-
if (!READ_ONLY_COMMANDS.has(firstWord)) {
|
|
870
|
-
return true;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
return false;
|
|
875
|
-
})();
|
|
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/];
|
|
949
|
+
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
950
|
+
const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
|
|
951
|
+
const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
|
|
876
952
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
877
953
|
if (command.includes(sp.path.toLowerCase())) {
|
|
878
954
|
if (sp.path === "/dev/random") {
|
|
@@ -889,7 +965,7 @@ function checkSensitivePath(command, cwd) {
|
|
|
889
965
|
violatedLayers: ["2"]
|
|
890
966
|
};
|
|
891
967
|
}
|
|
892
|
-
if (!isWriteOpFlag) {
|
|
968
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
893
969
|
continue;
|
|
894
970
|
}
|
|
895
971
|
return {
|
|
@@ -906,7 +982,7 @@ function checkSensitivePath(command, cwd) {
|
|
|
906
982
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
907
983
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
908
984
|
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
909
|
-
if (!isWriteOpFlag) {
|
|
985
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
910
986
|
continue;
|
|
911
987
|
}
|
|
912
988
|
return {
|
|
@@ -920,6 +996,29 @@ function checkSensitivePath(command, cwd) {
|
|
|
920
996
|
};
|
|
921
997
|
}
|
|
922
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
|
+
}
|
|
923
1022
|
for (const cp of CREDENTIAL_PATHS) {
|
|
924
1023
|
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
925
1024
|
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
@@ -1002,7 +1101,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1002
1101
|
return null;
|
|
1003
1102
|
}
|
|
1004
1103
|
const lowerDetail = actionDetail.toLowerCase();
|
|
1005
|
-
const hasTraversal =
|
|
1104
|
+
const hasTraversal = DIR_TRAVERSAL_KW.some((kw) => lowerDetail.includes(kw));
|
|
1006
1105
|
if (!hasTraversal) {
|
|
1007
1106
|
return null;
|
|
1008
1107
|
}
|
|
@@ -1071,18 +1170,19 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1071
1170
|
violatedLayers: ["2"]
|
|
1072
1171
|
};
|
|
1073
1172
|
}
|
|
1074
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE,
|
|
1173
|
+
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1075
1174
|
var init_logic_rules = __esm({
|
|
1076
1175
|
"src/engine/logic-rules.ts"() {
|
|
1077
1176
|
"use strict";
|
|
1078
1177
|
init_esm_shims();
|
|
1079
1178
|
init_inline_analyzer();
|
|
1179
|
+
init_agents();
|
|
1080
1180
|
ROOT_SENSITIVE_PATHS = [
|
|
1081
|
-
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6" },
|
|
1082
|
-
{ 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 },
|
|
1083
1183
|
// 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
|
|
1084
1184
|
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
1085
|
-
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E" },
|
|
1185
|
+
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
1086
1186
|
// 系统配置(仅拦截写入)
|
|
1087
1187
|
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
1088
1188
|
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
@@ -1100,19 +1200,19 @@ var init_logic_rules = __esm({
|
|
|
1100
1200
|
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
|
|
1101
1201
|
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
1102
1202
|
// SSH 主机私钥
|
|
1103
|
-
{ path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5" },
|
|
1104
|
-
{ path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5" },
|
|
1105
|
-
{ 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 }
|
|
1106
1206
|
];
|
|
1107
1207
|
HOME_SENSITIVE_PATHS = [
|
|
1108
|
-
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5" },
|
|
1109
|
-
{ 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 },
|
|
1110
1210
|
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
1111
1211
|
// 持久化与命令劫持(仅拦截写入)
|
|
1112
1212
|
{ path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
|
|
1113
1213
|
{ path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
|
|
1114
1214
|
{ path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
|
|
1115
|
-
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1" },
|
|
1215
|
+
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
|
|
1116
1216
|
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
1117
1217
|
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
1118
1218
|
];
|
|
@@ -1157,7 +1257,7 @@ var init_logic_rules = __esm({
|
|
|
1157
1257
|
}
|
|
1158
1258
|
];
|
|
1159
1259
|
INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
1160
|
-
|
|
1260
|
+
DIR_TRAVERSAL_KW = [
|
|
1161
1261
|
"os.listdir",
|
|
1162
1262
|
"os.walk",
|
|
1163
1263
|
"os.scandir",
|
|
@@ -1173,21 +1273,21 @@ var init_logic_rules = __esm({
|
|
|
1173
1273
|
});
|
|
1174
1274
|
|
|
1175
1275
|
// src/engine/skill-loader.ts
|
|
1176
|
-
import { readFileSync, readdirSync as readdirSync2, existsSync as
|
|
1177
|
-
import { resolve, dirname } from "path";
|
|
1178
|
-
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";
|
|
1179
1279
|
function loadAllSkills() {
|
|
1180
1280
|
if (cachedSkills) return cachedSkills;
|
|
1181
1281
|
cachedSkills = {};
|
|
1182
|
-
const dir =
|
|
1183
|
-
if (!
|
|
1282
|
+
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1283
|
+
if (!existsSync3(dir)) {
|
|
1184
1284
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1185
1285
|
return cachedSkills;
|
|
1186
1286
|
}
|
|
1187
1287
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1188
1288
|
for (const f of files) {
|
|
1189
1289
|
const name = f.replace(".md", "");
|
|
1190
|
-
cachedSkills[name] =
|
|
1290
|
+
cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
|
|
1191
1291
|
}
|
|
1192
1292
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1193
1293
|
return cachedSkills;
|
|
@@ -1241,15 +1341,15 @@ function formatSkillsForPrompt(skills) {
|
|
|
1241
1341
|
${s.skillContent}`
|
|
1242
1342
|
).join("\n\n---\n\n");
|
|
1243
1343
|
}
|
|
1244
|
-
var
|
|
1344
|
+
var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1245
1345
|
var init_skill_loader = __esm({
|
|
1246
1346
|
"src/engine/skill-loader.ts"() {
|
|
1247
1347
|
"use strict";
|
|
1248
1348
|
init_esm_shims();
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
SKILLS_DIR =
|
|
1252
|
-
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");
|
|
1253
1353
|
SKILL_KEYWORD_MAP = {
|
|
1254
1354
|
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
1255
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"],
|
|
@@ -1272,44 +1372,44 @@ var init_skill_loader = __esm({
|
|
|
1272
1372
|
});
|
|
1273
1373
|
|
|
1274
1374
|
// src/engine/template-loader.ts
|
|
1275
|
-
import { readFileSync as
|
|
1276
|
-
import { resolve as
|
|
1277
|
-
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";
|
|
1278
1378
|
function loadTemplates() {
|
|
1279
1379
|
if (cachedTemplates) return cachedTemplates;
|
|
1280
1380
|
cachedTemplates = {};
|
|
1281
|
-
const dir =
|
|
1282
|
-
if (!
|
|
1381
|
+
const dir = existsSync4(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
1382
|
+
if (!existsSync4(dir)) {
|
|
1283
1383
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
1284
1384
|
return cachedTemplates;
|
|
1285
1385
|
}
|
|
1286
1386
|
for (const file of TEMPLATE_FILES) {
|
|
1287
|
-
const filePath =
|
|
1288
|
-
if (
|
|
1387
|
+
const filePath = resolve3(dir, file);
|
|
1388
|
+
if (existsSync4(filePath)) {
|
|
1289
1389
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
1290
|
-
cachedTemplates[key] =
|
|
1390
|
+
cachedTemplates[key] = readFileSync3(filePath, "utf-8");
|
|
1291
1391
|
}
|
|
1292
1392
|
}
|
|
1293
1393
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
1294
1394
|
return cachedTemplates;
|
|
1295
1395
|
}
|
|
1296
|
-
var
|
|
1396
|
+
var __filename4, __dirname4, TEMPLATES_DIR, TEMPLATES_DIR_FALLBACK, TEMPLATE_FILES, cachedTemplates;
|
|
1297
1397
|
var init_template_loader = __esm({
|
|
1298
1398
|
"src/engine/template-loader.ts"() {
|
|
1299
1399
|
"use strict";
|
|
1300
1400
|
init_esm_shims();
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
TEMPLATES_DIR =
|
|
1304
|
-
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");
|
|
1305
1405
|
TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
1306
1406
|
cachedTemplates = null;
|
|
1307
1407
|
}
|
|
1308
1408
|
});
|
|
1309
1409
|
|
|
1310
1410
|
// src/engine/script-analyzer.ts
|
|
1311
|
-
import { readFileSync as
|
|
1312
|
-
import { resolve as
|
|
1411
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1412
|
+
import { resolve as resolve4, join } from "path";
|
|
1313
1413
|
function emptyResult() {
|
|
1314
1414
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
1315
1415
|
}
|
|
@@ -1362,8 +1462,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
1362
1462
|
}
|
|
1363
1463
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
1364
1464
|
try {
|
|
1365
|
-
const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) :
|
|
1366
|
-
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");
|
|
1367
1467
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
1368
1468
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
1369
1469
|
} catch (e) {
|
|
@@ -1500,6 +1600,712 @@ var init_script_analyzer = __esm({
|
|
|
1500
1600
|
}
|
|
1501
1601
|
});
|
|
1502
1602
|
|
|
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 ''");
|
|
1655
|
+
return result;
|
|
1656
|
+
}
|
|
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);
|
|
1688
|
+
}
|
|
1689
|
+
return expanded;
|
|
1690
|
+
}
|
|
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);
|
|
1701
|
+
}
|
|
1702
|
+
return matched;
|
|
1703
|
+
}
|
|
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 };
|
|
1711
|
+
}
|
|
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())) {
|
|
1719
|
+
return {
|
|
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"
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
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
|
+
|
|
1503
2309
|
// src/engine/llm-analyzer.ts
|
|
1504
2310
|
import OpenAI from "openai";
|
|
1505
2311
|
async function runLLMAnalysis(input, previous) {
|
|
@@ -1526,8 +2332,14 @@ async function runLLMAnalysis(input, previous) {
|
|
|
1526
2332
|
}
|
|
1527
2333
|
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
1528
2334
|
const templates = loadTemplates();
|
|
1529
|
-
|
|
1530
|
-
|
|
2335
|
+
let scriptAnalysis;
|
|
2336
|
+
let scriptChainText = "";
|
|
2337
|
+
if (previous.scriptChain) {
|
|
2338
|
+
scriptChainText = formatScriptChainForPrompt(previous.scriptChain);
|
|
2339
|
+
} else {
|
|
2340
|
+
scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
2341
|
+
}
|
|
2342
|
+
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
|
|
1531
2343
|
const result = await analyzeWithLLM(prompt, config);
|
|
1532
2344
|
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
1533
2345
|
const firstKey = llmCache.keys().next().value;
|
|
@@ -1536,14 +2348,14 @@ async function runLLMAnalysis(input, previous) {
|
|
|
1536
2348
|
llmCache.set(cacheKey, result);
|
|
1537
2349
|
return result;
|
|
1538
2350
|
}
|
|
1539
|
-
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis) {
|
|
2351
|
+
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
1540
2352
|
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
1541
2353
|
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
1542
|
-
const scriptAnalysisText = scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
2354
|
+
const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
1543
2355
|
const hintsText = formatHints(previous);
|
|
1544
2356
|
const securityJudgeTemplate = templates["security_judge"];
|
|
1545
2357
|
if (securityJudgeTemplate) {
|
|
1546
|
-
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("{{", "{").replaceAll("}}", "}");
|
|
2358
|
+
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("}}", "}");
|
|
1547
2359
|
}
|
|
1548
2360
|
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
1549
2361
|
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
@@ -1636,6 +2448,19 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1636
2448
|
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
1637
2449
|
}
|
|
1638
2450
|
const parsed = JSON.parse(jsonStr);
|
|
2451
|
+
try {
|
|
2452
|
+
const usage = response.usage;
|
|
2453
|
+
const { recordTokenUsage: recordTokenUsage2 } = await Promise.resolve().then(() => (init_token_stats(), token_stats_exports));
|
|
2454
|
+
recordTokenUsage2({
|
|
2455
|
+
sessionId: "audit",
|
|
2456
|
+
step: "L3_security_judge",
|
|
2457
|
+
model: response.model || config.model,
|
|
2458
|
+
promptTokens: usage?.prompt_tokens || 0,
|
|
2459
|
+
completionTokens: usage?.completion_tokens || 0,
|
|
2460
|
+
totalTokens: usage?.total_tokens || 0
|
|
2461
|
+
});
|
|
2462
|
+
} catch {
|
|
2463
|
+
}
|
|
1639
2464
|
clearTimeout(totalTimeoutTimer);
|
|
1640
2465
|
return {
|
|
1641
2466
|
allowed: parsed.allowed !== false,
|
|
@@ -1653,32 +2478,16 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1653
2478
|
}
|
|
1654
2479
|
if (attempt < maxRetries) {
|
|
1655
2480
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
1656
|
-
await new Promise((
|
|
2481
|
+
await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
|
|
1657
2482
|
} else {
|
|
1658
2483
|
clearTimeout(totalTimeoutTimer);
|
|
1659
2484
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
1660
|
-
|
|
1661
|
-
allowed: false,
|
|
1662
|
-
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 (${maxRetries + 1} \u6B21\u5C1D\u8BD5): ${err.message}`,
|
|
1663
|
-
riskLevel: "medium",
|
|
1664
|
-
riskType: "",
|
|
1665
|
-
confidence: 60,
|
|
1666
|
-
source: "llm_failure",
|
|
1667
|
-
violatedLayers: ["3"]
|
|
1668
|
-
};
|
|
2485
|
+
throw new Error(`LLM \u5206\u6790\u5931\u8D25 (${maxRetries + 1} \u6B21\u5C1D\u8BD5): ${err.message}`);
|
|
1669
2486
|
}
|
|
1670
2487
|
}
|
|
1671
2488
|
}
|
|
1672
2489
|
clearTimeout(totalTimeoutTimer);
|
|
1673
|
-
|
|
1674
|
-
allowed: false,
|
|
1675
|
-
reason: `LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09\uFF0C\u6309 fail-closed \u539F\u5219\u62D2\u7EDD`,
|
|
1676
|
-
riskLevel: "medium",
|
|
1677
|
-
riskType: "analysis_timeout",
|
|
1678
|
-
confidence: 60,
|
|
1679
|
-
source: "llm_timeout",
|
|
1680
|
-
violatedLayers: ["3"]
|
|
1681
|
-
};
|
|
2490
|
+
throw new Error(`LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09`);
|
|
1682
2491
|
}
|
|
1683
2492
|
var llmCache, LLM_CACHE_MAX;
|
|
1684
2493
|
var init_llm_analyzer = __esm({
|
|
@@ -1689,14 +2498,177 @@ var init_llm_analyzer = __esm({
|
|
|
1689
2498
|
init_skill_loader();
|
|
1690
2499
|
init_template_loader();
|
|
1691
2500
|
init_script_analyzer();
|
|
2501
|
+
init_script_content_analyzer();
|
|
1692
2502
|
llmCache = /* @__PURE__ */ new Map();
|
|
1693
2503
|
LLM_CACHE_MAX = 200;
|
|
1694
2504
|
}
|
|
1695
2505
|
});
|
|
1696
2506
|
|
|
2507
|
+
// src/infra/runtime-config.ts
|
|
2508
|
+
var runtime_config_exports = {};
|
|
2509
|
+
__export(runtime_config_exports, {
|
|
2510
|
+
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
2511
|
+
filterEnabledRules: () => filterEnabledRules,
|
|
2512
|
+
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
2513
|
+
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
2514
|
+
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
2515
|
+
getEnvOverrides: () => getEnvOverrides,
|
|
2516
|
+
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
2517
|
+
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
2518
|
+
isLayerEnabled: () => isLayerEnabled,
|
|
2519
|
+
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
2520
|
+
saveRuntimeConfig: () => saveRuntimeConfig
|
|
2521
|
+
});
|
|
2522
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
2523
|
+
import { join as join4 } from "path";
|
|
2524
|
+
import { homedir as homedir3 } from "os";
|
|
2525
|
+
function getRuntimeConfigPath() {
|
|
2526
|
+
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
2527
|
+
if (envPath) return envPath;
|
|
2528
|
+
return RUNTIME_CONFIG_PATH;
|
|
2529
|
+
}
|
|
2530
|
+
function saveRuntimeConfig(config) {
|
|
2531
|
+
const p = getRuntimeConfigPath();
|
|
2532
|
+
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2533
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2534
|
+
writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
|
|
2535
|
+
}
|
|
2536
|
+
function emptyRuntimeConfig() {
|
|
2537
|
+
return {
|
|
2538
|
+
version: 1,
|
|
2539
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
2540
|
+
L1_rules: {},
|
|
2541
|
+
L2_rules: {},
|
|
2542
|
+
L3_skills: {}
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
function loadRuntimeConfig() {
|
|
2546
|
+
const p = getRuntimeConfigPath();
|
|
2547
|
+
if (!existsSync7(p)) return emptyRuntimeConfig();
|
|
2548
|
+
try {
|
|
2549
|
+
const data = JSON.parse(readFileSync7(p, "utf-8"));
|
|
2550
|
+
return {
|
|
2551
|
+
version: data.version || 1,
|
|
2552
|
+
layers: {
|
|
2553
|
+
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
2554
|
+
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
2555
|
+
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
2556
|
+
},
|
|
2557
|
+
L1_rules: data.L1_rules || {},
|
|
2558
|
+
L2_rules: data.L2_rules || {},
|
|
2559
|
+
L3_skills: data.L3_skills || {}
|
|
2560
|
+
};
|
|
2561
|
+
} catch {
|
|
2562
|
+
return emptyRuntimeConfig();
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
function isLayerEnabled(layerKey) {
|
|
2566
|
+
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2567
|
+
if (disableKey && process.env[disableKey] === "1") return false;
|
|
2568
|
+
const runtime = loadRuntimeConfig();
|
|
2569
|
+
const layers = runtime.layers || {};
|
|
2570
|
+
if (layerKey in layers) return !!layers[layerKey];
|
|
2571
|
+
return true;
|
|
2572
|
+
}
|
|
2573
|
+
function getEnvOverrides() {
|
|
2574
|
+
const overrides = {};
|
|
2575
|
+
for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
|
|
2576
|
+
const val = process.env[envKey];
|
|
2577
|
+
if (val !== void 0) {
|
|
2578
|
+
overrides[layerKey] = {
|
|
2579
|
+
env_var: envKey,
|
|
2580
|
+
env_value: val,
|
|
2581
|
+
overridden: val === "1",
|
|
2582
|
+
effect: val === "1" ? "disabled" : "no_override"
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
return overrides;
|
|
2587
|
+
}
|
|
2588
|
+
function filterEnabledRules(rules, categoryEnabled = true) {
|
|
2589
|
+
if (!categoryEnabled) return [];
|
|
2590
|
+
return rules.filter((r) => r.enabled !== false);
|
|
2591
|
+
}
|
|
2592
|
+
function getEnabledL1CommandPatterns(runtime) {
|
|
2593
|
+
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
2594
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2595
|
+
pattern: r.pattern || "",
|
|
2596
|
+
risk_level: r.risk_level || "",
|
|
2597
|
+
risk_type: r.risk_type || "",
|
|
2598
|
+
reason: r.reason || ""
|
|
2599
|
+
}));
|
|
2600
|
+
}
|
|
2601
|
+
function getEnabledL2SensitivePaths(runtime) {
|
|
2602
|
+
const cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
2603
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
2604
|
+
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
2605
|
+
if (r.credential) out.credential = true;
|
|
2606
|
+
if (r.read_only) out.read_only = true;
|
|
2607
|
+
return out;
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
function hasRuntimeConfig() {
|
|
2611
|
+
const p = getRuntimeConfigPath();
|
|
2612
|
+
if (!existsSync7(p)) return false;
|
|
2613
|
+
try {
|
|
2614
|
+
JSON.parse(readFileSync7(p, "utf-8"));
|
|
2615
|
+
return true;
|
|
2616
|
+
} catch {
|
|
2617
|
+
return false;
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
function getDisabledL2RulesWithSkipL3() {
|
|
2621
|
+
let runtime;
|
|
2622
|
+
try {
|
|
2623
|
+
runtime = loadRuntimeConfig();
|
|
2624
|
+
} catch {
|
|
2625
|
+
return {};
|
|
2626
|
+
}
|
|
2627
|
+
const result = {};
|
|
2628
|
+
const l2 = runtime.L2_rules || {};
|
|
2629
|
+
for (const [catName, cat] of Object.entries(l2)) {
|
|
2630
|
+
if (!cat.category_enabled) continue;
|
|
2631
|
+
const items = [];
|
|
2632
|
+
for (const r of cat.rules || []) {
|
|
2633
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
2634
|
+
if (catName === "sensitive_path_access") {
|
|
2635
|
+
if (r.path) items.push(r.path);
|
|
2636
|
+
} else if (catName === "intent_consistency") {
|
|
2637
|
+
items.push(...r.intent_keywords || []);
|
|
2638
|
+
} else if (r.pattern) {
|
|
2639
|
+
items.push(r.pattern);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
if (items.length > 0) result[catName] = items;
|
|
2644
|
+
}
|
|
2645
|
+
return result;
|
|
2646
|
+
}
|
|
2647
|
+
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS;
|
|
2648
|
+
var init_runtime_config = __esm({
|
|
2649
|
+
"src/infra/runtime-config.ts"() {
|
|
2650
|
+
"use strict";
|
|
2651
|
+
init_esm_shims();
|
|
2652
|
+
RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2653
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2654
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2655
|
+
LAYER_DISABLE_ENV = {
|
|
2656
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2657
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2658
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2659
|
+
};
|
|
2660
|
+
DENY_MODE_LABELS = {
|
|
2661
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2662
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2663
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
});
|
|
2667
|
+
|
|
1697
2668
|
// src/engine/coordinator.ts
|
|
1698
2669
|
var coordinator_exports = {};
|
|
1699
2670
|
__export(coordinator_exports, {
|
|
2671
|
+
TRUSTED_SKILLS_BASE: () => TRUSTED_SKILLS_BASE,
|
|
1700
2672
|
runAnalysis: () => runAnalysis
|
|
1701
2673
|
});
|
|
1702
2674
|
function extractFirstCommand(command) {
|
|
@@ -1727,6 +2699,25 @@ async function runAnalysis(input) {
|
|
|
1727
2699
|
return heuristicResult;
|
|
1728
2700
|
}
|
|
1729
2701
|
}
|
|
2702
|
+
let scriptChain = null;
|
|
2703
|
+
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
2704
|
+
scriptChain = analyzeScriptContent2(input);
|
|
2705
|
+
if (scriptChain.nodes.length > 0) {
|
|
2706
|
+
for (const node of scriptChain.nodes) {
|
|
2707
|
+
if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
|
|
2708
|
+
return {
|
|
2709
|
+
allowed: false,
|
|
2710
|
+
reason: `\u811A\u672C\u5185\u5BB9\u5305\u542B\u5371\u9669\u64CD\u4F5C: ${node.l1Reason} (\u6587\u4EF6: ${node.scriptPath}, \u6DF1\u5EA6: ${node.depth})`,
|
|
2711
|
+
riskLevel: node.l1RiskLevel,
|
|
2712
|
+
riskType: node.l1RiskType,
|
|
2713
|
+
confidence: 90,
|
|
2714
|
+
source: "heuristic_script_scan",
|
|
2715
|
+
violatedLayers: ["1.1-script"]
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
1730
2721
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
1731
2722
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
1732
2723
|
const lowerDetail = rawDetail.toLowerCase();
|
|
@@ -1741,7 +2732,12 @@ async function runAnalysis(input) {
|
|
|
1741
2732
|
violatedLayers: []
|
|
1742
2733
|
};
|
|
1743
2734
|
}
|
|
1744
|
-
|
|
2735
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
2736
|
+
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
2737
|
+
if (agentExt?.builtin_safe_skills) {
|
|
2738
|
+
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
2739
|
+
}
|
|
2740
|
+
if (actionType === "skill" && trustedSkills.has(lowerDetail)) {
|
|
1745
2741
|
return {
|
|
1746
2742
|
allowed: true,
|
|
1747
2743
|
reason: `\u5185\u7F6E\u5B89\u5168 Skill\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${lowerDetail}`,
|
|
@@ -1781,7 +2777,7 @@ async function runAnalysis(input) {
|
|
|
1781
2777
|
if (logicResult && !logicResult.allowed) {
|
|
1782
2778
|
const isCredentialPath = !!logicResult.isCredential;
|
|
1783
2779
|
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && logicResult.riskType === "sensitive_path_access";
|
|
1784
|
-
if (skipInlineFileAccess
|
|
2780
|
+
if (skipInlineFileAccess) {
|
|
1785
2781
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
1786
2782
|
if (inlineJudgment.actionable) {
|
|
1787
2783
|
if (!inlineJudgment.allowed) {
|
|
@@ -1810,6 +2806,23 @@ async function runAnalysis(input) {
|
|
|
1810
2806
|
return logicResult;
|
|
1811
2807
|
}
|
|
1812
2808
|
}
|
|
2809
|
+
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
2810
|
+
for (const node of scriptChain.nodes) {
|
|
2811
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
2812
|
+
if (node.l1RiskLevel === "critical") break;
|
|
2813
|
+
if (node.l2Hit && (node.l2RiskLevel === "high" || node.l2RiskLevel === "critical")) {
|
|
2814
|
+
return {
|
|
2815
|
+
allowed: false,
|
|
2816
|
+
reason: node.l2Reason,
|
|
2817
|
+
riskLevel: node.l2RiskLevel,
|
|
2818
|
+
riskType: node.l2RiskType,
|
|
2819
|
+
confidence: 85,
|
|
2820
|
+
source: "logic_rule_script_scan",
|
|
2821
|
+
violatedLayers: ["1.2-script"]
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
1813
2826
|
if (skipLLM || !isLLMAvailable()) {
|
|
1814
2827
|
return {
|
|
1815
2828
|
allowed: true,
|
|
@@ -1821,13 +2834,63 @@ async function runAnalysis(input) {
|
|
|
1821
2834
|
violatedLayers: []
|
|
1822
2835
|
};
|
|
1823
2836
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
2837
|
+
let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
|
|
2838
|
+
try {
|
|
2839
|
+
const { getDisabledL2RulesWithSkipL3: getDisabledL2RulesWithSkipL32 } = await Promise.resolve().then(() => (init_runtime_config(), runtime_config_exports));
|
|
2840
|
+
const disabledL2 = getDisabledL2RulesWithSkipL32();
|
|
2841
|
+
if (disabledL2 && Object.keys(disabledL2).length > 0) {
|
|
2842
|
+
const actionLower = input.aNext.actionDetail.toLowerCase();
|
|
2843
|
+
const matched = [];
|
|
2844
|
+
for (const items of Object.values(disabledL2)) {
|
|
2845
|
+
for (const item of items) {
|
|
2846
|
+
if (item.toLowerCase().includes(actionLower) || actionLower.includes(item.toLowerCase())) {
|
|
2847
|
+
matched.push(item);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
if (matched.length > 0) {
|
|
2852
|
+
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
|
|
2853
|
+
${matched.join(", ")}
|
|
2854
|
+
\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`;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
} catch {
|
|
2858
|
+
}
|
|
2859
|
+
let llmResult;
|
|
2860
|
+
try {
|
|
2861
|
+
llmResult = await runLLMAnalysis(input, {
|
|
2862
|
+
heuristic: heuristicResult,
|
|
2863
|
+
logic: logicResult,
|
|
2864
|
+
skipL3Hints,
|
|
2865
|
+
scriptChain: scriptChain ?? void 0
|
|
2866
|
+
});
|
|
2867
|
+
} catch (e) {
|
|
2868
|
+
const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2869
|
+
if (getLLMFailMode2() === "fail_open") {
|
|
2870
|
+
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}`);
|
|
2871
|
+
return {
|
|
2872
|
+
allowed: true,
|
|
2873
|
+
reason: `LLM \u5206\u6790\u5931\u8D25\uFF08warn-allow\uFF0Cfail_open\uFF09: ${e.message}`,
|
|
2874
|
+
riskLevel: "low",
|
|
2875
|
+
riskType: "",
|
|
2876
|
+
confidence: 50,
|
|
2877
|
+
source: "llm_failure_fallback",
|
|
2878
|
+
violatedLayers: []
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
return {
|
|
2882
|
+
allowed: false,
|
|
2883
|
+
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}`,
|
|
2884
|
+
riskLevel: "medium",
|
|
2885
|
+
riskType: "",
|
|
2886
|
+
confidence: 60,
|
|
2887
|
+
source: "llm_failure_fallback",
|
|
2888
|
+
violatedLayers: ["3"]
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
1828
2891
|
return llmResult;
|
|
1829
2892
|
}
|
|
1830
|
-
var FULLY_SAFE_TOOLS,
|
|
2893
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, FULLY_SAFE_BASH_PATTERNS, READONLY_SENSITIVE_BASH_PATTERNS;
|
|
1831
2894
|
var init_coordinator = __esm({
|
|
1832
2895
|
"src/engine/coordinator.ts"() {
|
|
1833
2896
|
"use strict";
|
|
@@ -1836,7 +2899,9 @@ var init_coordinator = __esm({
|
|
|
1836
2899
|
init_logic_rules();
|
|
1837
2900
|
init_inline_analyzer();
|
|
1838
2901
|
init_llm_analyzer();
|
|
2902
|
+
init_script_content_analyzer();
|
|
1839
2903
|
init_config();
|
|
2904
|
+
init_agents();
|
|
1840
2905
|
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
1841
2906
|
"ask_user_question",
|
|
1842
2907
|
"glob",
|
|
@@ -1845,7 +2910,7 @@ var init_coordinator = __esm({
|
|
|
1845
2910
|
"count_text_length",
|
|
1846
2911
|
"filemgr-globfiles"
|
|
1847
2912
|
]);
|
|
1848
|
-
|
|
2913
|
+
TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
1849
2914
|
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
1850
2915
|
"read",
|
|
1851
2916
|
"file_read",
|
|
@@ -1942,6 +3007,7 @@ var AnalyzeRequestSchema = z.object({
|
|
|
1942
3007
|
reason: z.string().default("").describe("Reason for executing this action"),
|
|
1943
3008
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
1944
3009
|
cwd: z.string().default("").describe("Current working directory"),
|
|
3010
|
+
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
1945
3011
|
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
1946
3012
|
});
|
|
1947
3013
|
var AnalyzeResponseSchema = z.object({
|
|
@@ -2091,6 +3157,7 @@ async function analyzeRoute(c) {
|
|
|
2091
3157
|
reason: req.reason,
|
|
2092
3158
|
osType,
|
|
2093
3159
|
cwd: req.cwd,
|
|
3160
|
+
agentId: req.agent_id,
|
|
2094
3161
|
llmConfig: req.metadata?.llm_config
|
|
2095
3162
|
});
|
|
2096
3163
|
const durationMs = performance.now() - t0;
|
|
@@ -2207,7 +3274,8 @@ async function main() {
|
|
|
2207
3274
|
},
|
|
2208
3275
|
reason: data.reason || "",
|
|
2209
3276
|
osType: data.os_type || (process.platform === "win32" ? "windows" : "linux"),
|
|
2210
|
-
cwd: data.cwd || process.cwd()
|
|
3277
|
+
cwd: data.cwd || process.cwd(),
|
|
3278
|
+
agentId: data.agent_id || ""
|
|
2211
3279
|
});
|
|
2212
3280
|
console.log(JSON.stringify(result, null, 2));
|
|
2213
3281
|
process.exit(result.allowed ? 0 : 1);
|