@kenhkl/agent-moss 0.2.1 → 0.3.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/README.md +119 -372
- package/dist/cli.js +896 -150
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,11 +18,114 @@ var init_esm_shims = __esm({
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
// src/config.ts
|
|
22
|
+
function loadCustomRules() {
|
|
23
|
+
try {
|
|
24
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
25
|
+
if (!raw) return [];
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
if (!Array.isArray(parsed)) return [];
|
|
28
|
+
return parsed.filter((r) => r.pattern && r.action);
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getLLMConfig() {
|
|
34
|
+
return llmConfig;
|
|
35
|
+
}
|
|
36
|
+
function getCustomRules() {
|
|
37
|
+
return customRules;
|
|
38
|
+
}
|
|
39
|
+
function isLLMAvailable() {
|
|
40
|
+
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
41
|
+
return llmConfig.apiKey.length > 0;
|
|
42
|
+
}
|
|
43
|
+
function detectProvider(baseUrl, providerHint = "") {
|
|
44
|
+
if (providerHint) {
|
|
45
|
+
const hint = providerHint.toLowerCase();
|
|
46
|
+
if (["claude", "anthropic"].includes(hint)) return "anthropic";
|
|
47
|
+
if (["google", "gemini"].includes(hint)) return "gemini";
|
|
48
|
+
if (["zai-coding-plan", "zhipu-coding-plan", "zhipuai-coding-plan"].includes(hint)) return "zai-coding-plan";
|
|
49
|
+
if (["zai", "z-ai", "z.ai", "zai-cn", "zai-china", "zhipu", "glm-cn", "bigmodel"].includes(hint)) return "zhipu";
|
|
50
|
+
if (["xai", "xai-grok"].includes(hint)) return "xai";
|
|
51
|
+
if (["minimax", "minimax-openai"].includes(hint)) return "minimax";
|
|
52
|
+
if (["minimax-anthropic"].includes(hint)) return "minimax-anthropic";
|
|
53
|
+
if (["other", "custom", "local"].includes(hint)) return "openai-compatible";
|
|
54
|
+
return hint;
|
|
55
|
+
}
|
|
56
|
+
const url = baseUrl.toLowerCase();
|
|
57
|
+
if (url.includes("openrouter.ai")) return "openrouter";
|
|
58
|
+
if (url.includes("api.openai.com")) return "openai";
|
|
59
|
+
if (url.includes("anthropic.com") || url.includes("api.anthropic.com")) return "anthropic";
|
|
60
|
+
if (url.includes("generativelanguage.googleapis.com") || url.includes("gemini")) return "gemini";
|
|
61
|
+
if (url.includes("api.deepseek.com")) return "deepseek";
|
|
62
|
+
if (url.includes("api.groq.com")) return "groq";
|
|
63
|
+
if (url.includes("api.mistral.ai")) return "mistral";
|
|
64
|
+
if (url.includes("api.together.xyz")) return "together";
|
|
65
|
+
if (url.includes("api.x.ai")) return "xai";
|
|
66
|
+
if (url.includes("api.minimaxi.com")) return "minimax";
|
|
67
|
+
if (url.includes("api-ai.gitcode.com")) return "gitcode";
|
|
68
|
+
if (url.includes("api.z.ai")) return "zai-coding-plan";
|
|
69
|
+
if (url.includes("open.bigmodel.cn") || url.includes("bigmodel")) return "zhipu";
|
|
70
|
+
if (url.includes(":11434")) return "ollama";
|
|
71
|
+
return "openai-compatible";
|
|
72
|
+
}
|
|
73
|
+
function getProviderHeaders(provider) {
|
|
74
|
+
return PROVIDER_HEADERS[provider] || {};
|
|
75
|
+
}
|
|
76
|
+
function isHeuristicEnabled() {
|
|
77
|
+
return process.env.AGENT_MOSS_DISABLE_HEURISTIC !== "1";
|
|
78
|
+
}
|
|
79
|
+
function isLogicRulesEnabled() {
|
|
80
|
+
return process.env.AGENT_MOSS_DISABLE_LOGIC_RULES !== "1";
|
|
81
|
+
}
|
|
82
|
+
var llmConfig, customRules, PROVIDER_HEADERS;
|
|
83
|
+
var init_config = __esm({
|
|
84
|
+
"src/config.ts"() {
|
|
85
|
+
"use strict";
|
|
86
|
+
init_esm_shims();
|
|
87
|
+
llmConfig = {
|
|
88
|
+
provider: "openai",
|
|
89
|
+
baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || "https://api.openai.com/v1",
|
|
90
|
+
apiKey: process.env.AGENT_MOSS_LLM_API_KEY || process.env.OPENAI_API_KEY || "",
|
|
91
|
+
model: process.env.AGENT_MOSS_LLM_MODEL || "gpt-4o",
|
|
92
|
+
temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || "0.1"),
|
|
93
|
+
maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || "4096", 10),
|
|
94
|
+
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || "300", 10)
|
|
95
|
+
};
|
|
96
|
+
customRules = loadCustomRules();
|
|
97
|
+
PROVIDER_HEADERS = {
|
|
98
|
+
"openrouter": {
|
|
99
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM",
|
|
100
|
+
"X-OpenRouter-Title": "AgentMoss"
|
|
101
|
+
},
|
|
102
|
+
"xai": {
|
|
103
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM"
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
21
109
|
// src/engine/heuristic.ts
|
|
22
110
|
function runHeuristicCheck(input) {
|
|
23
111
|
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
24
112
|
const matched = [];
|
|
25
113
|
let highestSeverity = "low";
|
|
114
|
+
const userRules = getCustomRules();
|
|
115
|
+
for (const rule of userRules) {
|
|
116
|
+
try {
|
|
117
|
+
const re = new RegExp(rule.pattern);
|
|
118
|
+
if (re.test(command)) {
|
|
119
|
+
if (rule.action === "Deny") {
|
|
120
|
+
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
121
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
122
|
+
highestSeverity = rule.severity;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
26
129
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
27
130
|
if (rule.pattern.test(command)) {
|
|
28
131
|
matched.push(rule.description);
|
|
@@ -31,12 +134,22 @@ function runHeuristicCheck(input) {
|
|
|
31
134
|
}
|
|
32
135
|
}
|
|
33
136
|
}
|
|
137
|
+
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
138
|
+
const injectionHits = checkInjectionKeywords(injectionText);
|
|
139
|
+
if (injectionHits.length > 0) {
|
|
140
|
+
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
141
|
+
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
142
|
+
if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
|
|
143
|
+
highestSeverity = topHit.riskLevel;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
34
146
|
if (matched.length > 0) {
|
|
147
|
+
const riskType = injectionHits.length > 0 ? "prompt_injection" : "dangerous_command";
|
|
35
148
|
return {
|
|
36
149
|
allowed: false,
|
|
37
150
|
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
38
151
|
riskLevel: highestSeverity,
|
|
39
|
-
riskType
|
|
152
|
+
riskType,
|
|
40
153
|
confidence: 95,
|
|
41
154
|
source: "heuristic",
|
|
42
155
|
violatedLayers: ["1"]
|
|
@@ -44,67 +157,197 @@ function runHeuristicCheck(input) {
|
|
|
44
157
|
}
|
|
45
158
|
return null;
|
|
46
159
|
}
|
|
160
|
+
function checkInjectionKeywords(text) {
|
|
161
|
+
const hits = [];
|
|
162
|
+
for (const entry of INJECTION_KEYWORDS) {
|
|
163
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
164
|
+
hits.push(entry);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return hits;
|
|
168
|
+
}
|
|
47
169
|
function severityRank(s) {
|
|
48
170
|
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
49
171
|
return ranks[s] || 0;
|
|
50
172
|
}
|
|
51
|
-
var DANGEROUS_PATTERNS;
|
|
173
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS;
|
|
52
174
|
var init_heuristic = __esm({
|
|
53
175
|
"src/engine/heuristic.ts"() {
|
|
54
176
|
"use strict";
|
|
55
177
|
init_esm_shims();
|
|
178
|
+
init_config();
|
|
179
|
+
INJECTION_KEYWORDS = [
|
|
180
|
+
// ---- Instruction Override ----
|
|
181
|
+
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
182
|
+
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
183
|
+
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
184
|
+
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
185
|
+
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
186
|
+
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
187
|
+
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
188
|
+
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
189
|
+
// ---- Role Hijacking ----
|
|
190
|
+
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
191
|
+
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
192
|
+
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
193
|
+
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
194
|
+
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
195
|
+
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
196
|
+
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
197
|
+
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
198
|
+
// ---- Command Injection ----
|
|
199
|
+
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
200
|
+
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
201
|
+
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
202
|
+
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
203
|
+
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
204
|
+
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
205
|
+
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
206
|
+
// ---- Social Engineering ----
|
|
207
|
+
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
208
|
+
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
209
|
+
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
210
|
+
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
211
|
+
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
212
|
+
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
213
|
+
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
214
|
+
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
215
|
+
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
216
|
+
// ---- XSS ----
|
|
217
|
+
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
218
|
+
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
219
|
+
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
220
|
+
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
221
|
+
// ---- Fake Boundaries ----
|
|
222
|
+
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
223
|
+
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
224
|
+
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
225
|
+
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
226
|
+
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
227
|
+
// ---- Developer Mode ----
|
|
228
|
+
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
229
|
+
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
230
|
+
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
231
|
+
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
232
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" },
|
|
233
|
+
// ---- xiaoO-specific bypass ----
|
|
234
|
+
{ keyword: "bypass audit", riskLevel: "critical", lang: "en" },
|
|
235
|
+
{ keyword: "skip security check", riskLevel: "critical", lang: "en" },
|
|
236
|
+
{ keyword: "disable guard", riskLevel: "critical", lang: "en" },
|
|
237
|
+
{ keyword: "\u7ED5\u8FC7\u5BA1\u8BA1", riskLevel: "critical", lang: "zh" },
|
|
238
|
+
{ keyword: "\u7981\u7528\u5B89\u5168\u68C0\u67E5", riskLevel: "critical", lang: "zh" },
|
|
239
|
+
{ keyword: "\u5173\u95ED\u9632\u62A4", riskLevel: "critical", lang: "zh" }
|
|
240
|
+
];
|
|
56
241
|
DANGEROUS_PATTERNS = [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{ pattern:
|
|
60
|
-
|
|
61
|
-
{ pattern:
|
|
62
|
-
|
|
63
|
-
{ pattern: /
|
|
64
|
-
|
|
65
|
-
{ pattern:
|
|
66
|
-
|
|
67
|
-
{ pattern:
|
|
68
|
-
{ pattern: /
|
|
69
|
-
{ pattern:
|
|
70
|
-
{ pattern:
|
|
71
|
-
|
|
72
|
-
{ pattern: /
|
|
73
|
-
{ pattern: /
|
|
242
|
+
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
243
|
+
// 1. rm -rf root dir
|
|
244
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/\s/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)" },
|
|
245
|
+
// 2. rm -rf absolute path (both -r and -f)
|
|
246
|
+
{ pattern: /\brm\s+-(?=[^\s]*r)(?=[^\s]*f)[a-zA-Z]*\s+\//i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u7EDD\u5BF9\u8DEF\u5F84 (rm -rf /...)" },
|
|
247
|
+
// 3. rm with -r or -f targeting system dirs
|
|
248
|
+
{ pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55" },
|
|
249
|
+
// 4. rm -r absolute path (no -f)
|
|
250
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\//i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)" },
|
|
251
|
+
// 5. rm -f absolute path (no -r)
|
|
252
|
+
{ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\//i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)" },
|
|
253
|
+
{ pattern: /dd\s+if=\/dev\/zero|dd\s+if=.*of=\/dev\//i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6/\u76F4\u63A5\u5199\u5165" },
|
|
254
|
+
{ pattern: /mkfs\b/i, severity: "critical", description: "\u683C\u5F0F\u5316\u6587\u4EF6\u7CFB\u7EDF" },
|
|
255
|
+
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb" },
|
|
256
|
+
// --- Critical: remote code execution ---
|
|
257
|
+
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C" },
|
|
258
|
+
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C" },
|
|
259
|
+
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C" },
|
|
260
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C" },
|
|
261
|
+
// --- Critical: reverse shell ---
|
|
262
|
+
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell" },
|
|
263
|
+
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5" },
|
|
264
|
+
// --- Critical: sensitive file access ---
|
|
265
|
+
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)" },
|
|
266
|
+
{ pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6" },
|
|
267
|
+
// --- Critical: privilege escalation / container escape ---
|
|
268
|
+
{ pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038" },
|
|
269
|
+
// --- High: dangerous permissions / ownership ---
|
|
270
|
+
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)" },
|
|
271
|
+
{ pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005" },
|
|
272
|
+
{ pattern: /sudo\s+rm\s+-rf/i, severity: "high", description: "sudo \u5F3A\u5236\u5220\u9664" },
|
|
273
|
+
{ pattern: /sudo\s+/i, severity: "high", description: "\u63D0\u6743\u6267\u884C (sudo)" },
|
|
274
|
+
// --- High: system config overwrite ---
|
|
275
|
+
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55" },
|
|
276
|
+
// --- High: download script + execute risk ---
|
|
277
|
+
{ pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730" },
|
|
278
|
+
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C" },
|
|
279
|
+
// --- High: package manager pipe-to-shell ---
|
|
280
|
+
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell" },
|
|
281
|
+
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell" },
|
|
282
|
+
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell" },
|
|
283
|
+
// --- High: destructive git operations ---
|
|
284
|
+
{ pattern: /git\s+push\s+.*--force/i, severity: "high", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)" },
|
|
285
|
+
// --- Medium: credential export ---
|
|
74
286
|
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1" },
|
|
75
|
-
|
|
287
|
+
// --- High: device file access ---
|
|
288
|
+
{ pattern: /\/dev\/(mem|kmsg)\b/i, severity: "high", description: "\u8BBF\u95EE\u8BBE\u5907\u6587\u4EF6 (\u7269\u7406\u5185\u5B58/\u5185\u6838\u6D88\u606F)" }
|
|
76
289
|
];
|
|
77
290
|
}
|
|
78
291
|
});
|
|
79
292
|
|
|
80
293
|
// src/engine/logic-rules.ts
|
|
294
|
+
import * as fs from "fs";
|
|
81
295
|
function runLogicCheck(input) {
|
|
82
296
|
const { actionHistory, aNext, cwd } = input;
|
|
83
|
-
const
|
|
297
|
+
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
298
|
+
const actionType = aNext.actionType.toLowerCase();
|
|
84
299
|
const resolvedCwd = cwd || "/";
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
|
|
300
|
+
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
|
|
301
|
+
if (rbwResult) return rbwResult;
|
|
302
|
+
if (input.promptSession && input.promptSession.trim()) {
|
|
303
|
+
const promptLower = input.promptSession.toLowerCase();
|
|
304
|
+
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
305
|
+
const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
|
|
306
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => actionDetail.includes(kw));
|
|
307
|
+
if (intentMatched && dangerousMatched) {
|
|
308
|
+
return {
|
|
309
|
+
allowed: false,
|
|
310
|
+
reason: pattern.reason,
|
|
311
|
+
riskLevel: "high",
|
|
312
|
+
riskType: "intent_deviation",
|
|
313
|
+
confidence: 85,
|
|
314
|
+
source: "logic_rule",
|
|
315
|
+
violatedLayers: ["2"]
|
|
316
|
+
};
|
|
317
|
+
}
|
|
90
318
|
}
|
|
91
319
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
320
|
+
const pathResult = checkSensitivePath(actionDetail, resolvedCwd);
|
|
321
|
+
if (pathResult) return pathResult;
|
|
322
|
+
if (actionType !== "file_write" && actionType !== "file_edit") {
|
|
323
|
+
if ((actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
324
|
+
return {
|
|
325
|
+
allowed: false,
|
|
326
|
+
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
327
|
+
riskLevel: "high",
|
|
328
|
+
riskType: "script_execution",
|
|
329
|
+
confidence: 90,
|
|
330
|
+
source: "logic_rule",
|
|
331
|
+
violatedLayers: ["2"]
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
335
|
+
return {
|
|
336
|
+
allowed: false,
|
|
337
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
338
|
+
riskLevel: "critical",
|
|
339
|
+
riskType: "file_access",
|
|
340
|
+
confidence: 95,
|
|
341
|
+
source: "logic_rule",
|
|
342
|
+
violatedLayers: ["2"]
|
|
343
|
+
};
|
|
344
|
+
}
|
|
102
345
|
}
|
|
103
|
-
if (
|
|
346
|
+
if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
|
|
104
347
|
const adminContext = actionHistory.some(
|
|
105
|
-
(h) => h.actionDetail.includes("sudo") || h.actionDetail.includes("apt") || h.actionDetail.includes("systemctl")
|
|
348
|
+
(h) => h.actionDetail.includes("sudo") || h.actionDetail.includes("apt") || h.actionDetail.includes("systemctl") || h.actionDetail.includes("yum") || h.actionDetail.includes("dnf") || h.actionDetail.includes("pacman") || h.actionDetail.includes("zypper") || h.actionDetail.includes("apk")
|
|
106
349
|
);
|
|
107
|
-
if (!adminContext
|
|
350
|
+
if (!adminContext) {
|
|
108
351
|
return {
|
|
109
352
|
allowed: false,
|
|
110
353
|
reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
|
|
@@ -116,8 +359,8 @@ function runLogicCheck(input) {
|
|
|
116
359
|
};
|
|
117
360
|
}
|
|
118
361
|
}
|
|
119
|
-
if (
|
|
120
|
-
const targetHost = extractRemoteHost(
|
|
362
|
+
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
363
|
+
const targetHost = extractRemoteHost(actionDetail);
|
|
121
364
|
if (targetHost && !isLocalHost(targetHost)) {
|
|
122
365
|
return {
|
|
123
366
|
allowed: false,
|
|
@@ -132,18 +375,100 @@ function runLogicCheck(input) {
|
|
|
132
375
|
}
|
|
133
376
|
return null;
|
|
134
377
|
}
|
|
135
|
-
function
|
|
378
|
+
function extractFilePaths(text) {
|
|
379
|
+
const paths = [];
|
|
380
|
+
const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
|
|
381
|
+
if (unixPaths) paths.push(...unixPaths.map((p) => p.trim()));
|
|
382
|
+
const quotedPaths = text.match(/["']\/([^"']+)["']/g);
|
|
383
|
+
if (quotedPaths) paths.push(...quotedPaths.map((p) => p.replace(/["']/g, "")));
|
|
384
|
+
return [...new Set(paths)];
|
|
385
|
+
}
|
|
386
|
+
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd) {
|
|
387
|
+
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
388
|
+
const isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
|
|
389
|
+
if (!isWrite) return null;
|
|
390
|
+
const targetPaths = extractFilePaths(actionDetail);
|
|
391
|
+
if (targetPaths.length === 0) return null;
|
|
392
|
+
const historyReadPaths = /* @__PURE__ */ new Set();
|
|
393
|
+
for (const action of actionHistory) {
|
|
394
|
+
const histDetail = action.actionDetail.toLowerCase();
|
|
395
|
+
const histType = action.actionType.toLowerCase();
|
|
396
|
+
const isRead = READ_KEYWORDS.some((kw) => histType.includes(kw) || histDetail.includes(kw));
|
|
397
|
+
if (isRead) {
|
|
398
|
+
for (const p of extractFilePaths(histDetail)) {
|
|
399
|
+
historyReadPaths.add(p);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const unreadPaths = [];
|
|
404
|
+
for (const path2 of targetPaths) {
|
|
405
|
+
const hasRead = [...historyReadPaths].some(
|
|
406
|
+
(rp) => path2 === rp || path2.startsWith(rp) || rp.startsWith(path2)
|
|
407
|
+
);
|
|
408
|
+
if (!hasRead) {
|
|
409
|
+
let isCreate = !fs.existsSync(path2);
|
|
410
|
+
if (!isCreate) {
|
|
411
|
+
isCreate = CREATE_KEYWORDS.some((kw) => reason.toLowerCase().includes(kw) || actionDetail.includes(kw));
|
|
412
|
+
}
|
|
413
|
+
if (!isCreate) {
|
|
414
|
+
unreadPaths.push(path2);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (unreadPaths.length > 0) {
|
|
419
|
+
return {
|
|
420
|
+
allowed: false,
|
|
421
|
+
reason: `\u8FDD\u53CD read_before_write \u5B89\u5168\u539F\u5219\uFF1A\u5BF9\u6587\u4EF6 ${unreadPaths[0]} \u6267\u884C\u5199\u5165\u64CD\u4F5C\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5BF9\u8BE5\u6587\u4EF6\u7684\u8BFB\u53D6\u64CD\u4F5C`,
|
|
422
|
+
riskLevel: "high",
|
|
423
|
+
riskType: "read_before_write",
|
|
424
|
+
confidence: 85,
|
|
425
|
+
source: "logic_rule",
|
|
426
|
+
violatedLayers: ["2"]
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
function checkSensitivePath(command, cwd) {
|
|
136
432
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
137
|
-
for (const
|
|
138
|
-
if (command.includes(
|
|
433
|
+
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
434
|
+
if (command.includes(sp.path.toLowerCase())) {
|
|
435
|
+
return {
|
|
436
|
+
allowed: false,
|
|
437
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
438
|
+
riskLevel: sp.riskLevel,
|
|
439
|
+
riskType: "sensitive_path_access",
|
|
440
|
+
confidence: 90,
|
|
441
|
+
source: "logic_rule",
|
|
442
|
+
violatedLayers: ["2"]
|
|
443
|
+
};
|
|
444
|
+
}
|
|
139
445
|
}
|
|
140
|
-
for (const
|
|
141
|
-
|
|
446
|
+
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
447
|
+
const expanded = `${expandedCwd}/${sp.path}`;
|
|
448
|
+
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
449
|
+
return {
|
|
450
|
+
allowed: false,
|
|
451
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
452
|
+
riskLevel: sp.riskLevel,
|
|
453
|
+
riskType: "sensitive_path_access",
|
|
454
|
+
confidence: 90,
|
|
455
|
+
source: "logic_rule",
|
|
456
|
+
violatedLayers: ["2"]
|
|
457
|
+
};
|
|
458
|
+
}
|
|
142
459
|
}
|
|
143
460
|
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
144
|
-
return
|
|
461
|
+
return {
|
|
462
|
+
allowed: false,
|
|
463
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
464
|
+
riskLevel: "critical",
|
|
465
|
+
riskType: "sensitive_path_access",
|
|
466
|
+
confidence: 90,
|
|
467
|
+
source: "logic_rule",
|
|
468
|
+
violatedLayers: ["2"]
|
|
469
|
+
};
|
|
145
470
|
}
|
|
146
|
-
return
|
|
471
|
+
return null;
|
|
147
472
|
}
|
|
148
473
|
function extractRemoteHost(command) {
|
|
149
474
|
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
@@ -153,75 +478,123 @@ function isLocalHost(host) {
|
|
|
153
478
|
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
154
479
|
return localNames.includes(host.replace(/.*@/, ""));
|
|
155
480
|
}
|
|
156
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS;
|
|
481
|
+
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, NON_WRITE_TOOLS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, INTENT_DEVIATION_PATTERNS;
|
|
157
482
|
var init_logic_rules = __esm({
|
|
158
483
|
"src/engine/logic-rules.ts"() {
|
|
159
484
|
"use strict";
|
|
160
485
|
init_esm_shims();
|
|
161
486
|
ROOT_SENSITIVE_PATHS = [
|
|
162
|
-
"/etc/shadow",
|
|
163
|
-
"/etc/passwd",
|
|
164
|
-
"/etc/sudoers",
|
|
165
|
-
"/
|
|
166
|
-
"/
|
|
167
|
-
"/
|
|
168
|
-
"/
|
|
169
|
-
"/
|
|
487
|
+
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6" },
|
|
488
|
+
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
489
|
+
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E" },
|
|
490
|
+
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
491
|
+
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
492
|
+
{ path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
|
|
493
|
+
{ path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
|
|
494
|
+
{ path: "/root/.ssh", riskLevel: "critical", desc: "root SSH \u76EE\u5F55" },
|
|
495
|
+
{ path: "/root/.bashrc", riskLevel: "high", desc: "root shell \u914D\u7F6E" },
|
|
496
|
+
{ path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
|
|
497
|
+
{ path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
|
|
498
|
+
{ path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
|
|
499
|
+
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
|
|
500
|
+
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" }
|
|
170
501
|
];
|
|
171
502
|
HOME_SENSITIVE_PATHS = [
|
|
172
|
-
".ssh/id_rsa",
|
|
173
|
-
".ssh/
|
|
174
|
-
".
|
|
175
|
-
".
|
|
176
|
-
".
|
|
503
|
+
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5" },
|
|
504
|
+
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)" },
|
|
505
|
+
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
506
|
+
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1" },
|
|
507
|
+
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
508
|
+
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
509
|
+
];
|
|
510
|
+
NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
|
|
511
|
+
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362"];
|
|
512
|
+
READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
|
|
513
|
+
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
514
|
+
INTENT_DEVIATION_PATTERNS = [
|
|
515
|
+
{
|
|
516
|
+
intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
|
|
517
|
+
dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
|
|
518
|
+
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
|
|
522
|
+
dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
|
|
523
|
+
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
|
|
524
|
+
}
|
|
177
525
|
];
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// src/config.ts
|
|
182
|
-
function getLLMConfig() {
|
|
183
|
-
return llmConfig;
|
|
184
|
-
}
|
|
185
|
-
function isLLMAvailable() {
|
|
186
|
-
return llmConfig.apiKey.length > 0;
|
|
187
|
-
}
|
|
188
|
-
var llmConfig;
|
|
189
|
-
var init_config = __esm({
|
|
190
|
-
"src/config.ts"() {
|
|
191
|
-
"use strict";
|
|
192
|
-
init_esm_shims();
|
|
193
|
-
llmConfig = {
|
|
194
|
-
provider: "openai",
|
|
195
|
-
baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || "https://api.openai.com/v1",
|
|
196
|
-
apiKey: process.env.AGENT_MOSS_LLM_API_KEY || "",
|
|
197
|
-
model: process.env.AGENT_MOSS_LLM_MODEL || "gpt-4o",
|
|
198
|
-
temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || "0.1"),
|
|
199
|
-
maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || "4096", 10)
|
|
200
|
-
};
|
|
201
526
|
}
|
|
202
527
|
});
|
|
203
528
|
|
|
204
529
|
// src/engine/skill-loader.ts
|
|
205
|
-
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
530
|
+
import { readFileSync, readdirSync, existsSync as existsSync2 } from "fs";
|
|
206
531
|
import { resolve, dirname } from "path";
|
|
207
532
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
208
|
-
function
|
|
533
|
+
function loadAllSkills() {
|
|
209
534
|
if (cachedSkills) return cachedSkills;
|
|
210
|
-
|
|
535
|
+
cachedSkills = {};
|
|
536
|
+
if (!existsSync2(SKILLS_DIR)) {
|
|
211
537
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR);
|
|
212
|
-
cachedSkills = [];
|
|
213
538
|
return cachedSkills;
|
|
214
539
|
}
|
|
215
540
|
const files = readdirSync(SKILLS_DIR).filter((f) => f.endsWith(".md"));
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
console.log(`[AgentMoss] Loaded ${cachedSkills.length} skills`);
|
|
541
|
+
for (const f of files) {
|
|
542
|
+
const name = f.replace(".md", "");
|
|
543
|
+
cachedSkills[name] = readFileSync(resolve(SKILLS_DIR, f), "utf-8");
|
|
544
|
+
}
|
|
545
|
+
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills`);
|
|
222
546
|
return cachedSkills;
|
|
223
547
|
}
|
|
224
|
-
|
|
548
|
+
function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
549
|
+
const skills = loadAllSkills();
|
|
550
|
+
const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
|
|
551
|
+
const actionDetailLower = actionDetail.toLowerCase();
|
|
552
|
+
const actionTypeLower = actionType.toLowerCase();
|
|
553
|
+
const reasonLower = reason.toLowerCase();
|
|
554
|
+
const scores = {};
|
|
555
|
+
for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
|
|
556
|
+
let score = 0;
|
|
557
|
+
for (const kw of keywords) {
|
|
558
|
+
if (combined.includes(kw)) {
|
|
559
|
+
if (kw === actionTypeLower) score += 2;
|
|
560
|
+
else if (actionDetailLower.includes(kw)) score += 1.5;
|
|
561
|
+
else if (reasonLower.includes(kw)) score += 1;
|
|
562
|
+
else score += 0.5;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (score > 0) scores[skillName] = score;
|
|
566
|
+
}
|
|
567
|
+
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
568
|
+
const top = sorted.slice(0, maxSkills);
|
|
569
|
+
const maxScore = top[0]?.[1] || 1;
|
|
570
|
+
const results = [];
|
|
571
|
+
for (const [name, score] of top) {
|
|
572
|
+
const content = skills[name];
|
|
573
|
+
if (content) {
|
|
574
|
+
results.push({
|
|
575
|
+
skillName: name,
|
|
576
|
+
relevance: Math.min(score / maxScore, 1),
|
|
577
|
+
skillContent: content
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (results.length === 0) {
|
|
582
|
+
const fallback = skills["general_tool_risk_guard"];
|
|
583
|
+
if (fallback) {
|
|
584
|
+
results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return results;
|
|
588
|
+
}
|
|
589
|
+
function formatSkillsForPrompt(skills) {
|
|
590
|
+
if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
|
|
591
|
+
return skills.map(
|
|
592
|
+
(s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
|
|
593
|
+
|
|
594
|
+
${s.skillContent}`
|
|
595
|
+
).join("\n\n---\n\n");
|
|
596
|
+
}
|
|
597
|
+
var __filename2, __dirname2, SKILLS_DIR, SKILL_KEYWORD_MAP, cachedSkills;
|
|
225
598
|
var init_skill_loader = __esm({
|
|
226
599
|
"src/engine/skill-loader.ts"() {
|
|
227
600
|
"use strict";
|
|
@@ -229,24 +602,38 @@ var init_skill_loader = __esm({
|
|
|
229
602
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
230
603
|
__dirname2 = dirname(__filename2);
|
|
231
604
|
SKILLS_DIR = resolve(__dirname2, "../../agent_moss/skills");
|
|
605
|
+
SKILL_KEYWORD_MAP = {
|
|
606
|
+
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
607
|
+
"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"],
|
|
608
|
+
"data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
|
|
609
|
+
"browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
|
|
610
|
+
"email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
|
|
611
|
+
"general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
|
|
612
|
+
"lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
|
|
613
|
+
"persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys"],
|
|
614
|
+
"resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory", "\u5185\u5B58", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
|
|
615
|
+
"skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
|
|
616
|
+
"supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
|
|
617
|
+
"intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"]
|
|
618
|
+
};
|
|
232
619
|
cachedSkills = null;
|
|
233
620
|
}
|
|
234
621
|
});
|
|
235
622
|
|
|
236
623
|
// src/engine/template-loader.ts
|
|
237
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
624
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
238
625
|
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
239
626
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
240
627
|
function loadTemplates() {
|
|
241
628
|
if (cachedTemplates) return cachedTemplates;
|
|
242
629
|
cachedTemplates = {};
|
|
243
|
-
if (!
|
|
630
|
+
if (!existsSync3(TEMPLATES_DIR)) {
|
|
244
631
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR);
|
|
245
632
|
return cachedTemplates;
|
|
246
633
|
}
|
|
247
634
|
for (const file of TEMPLATE_FILES) {
|
|
248
635
|
const filePath = resolve2(TEMPLATES_DIR, file);
|
|
249
|
-
if (
|
|
636
|
+
if (existsSync3(filePath)) {
|
|
250
637
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
251
638
|
cachedTemplates[key] = readFileSync2(filePath, "utf-8");
|
|
252
639
|
}
|
|
@@ -267,6 +654,199 @@ var init_template_loader = __esm({
|
|
|
267
654
|
}
|
|
268
655
|
});
|
|
269
656
|
|
|
657
|
+
// src/engine/script-analyzer.ts
|
|
658
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
659
|
+
import { resolve as resolve3, join } from "path";
|
|
660
|
+
function emptyResult() {
|
|
661
|
+
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
662
|
+
}
|
|
663
|
+
function extractScriptPath(command) {
|
|
664
|
+
for (const pattern of SCRIPT_EXEC_PATTERNS) {
|
|
665
|
+
const m = command.match(pattern);
|
|
666
|
+
if (m) return m[1];
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
function scanScriptForKeywords(content) {
|
|
671
|
+
const matched = [];
|
|
672
|
+
for (const { name, pattern } of SUSPICIOUS_PATTERNS) {
|
|
673
|
+
if (pattern.test(content)) matched.push(name);
|
|
674
|
+
}
|
|
675
|
+
return matched;
|
|
676
|
+
}
|
|
677
|
+
function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
678
|
+
if (matchedKeywords.length === 0) return { level: "", reason: "" };
|
|
679
|
+
const keywordSet = new Set(matchedKeywords);
|
|
680
|
+
for (const rule of KEYWORD_COMBINATION_RISKS) {
|
|
681
|
+
const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
|
|
682
|
+
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
683
|
+
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
684
|
+
}
|
|
685
|
+
const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
|
|
686
|
+
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
687
|
+
if (hasNetworkPost) {
|
|
688
|
+
const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
|
|
689
|
+
const contentLower = scriptContent.toLowerCase();
|
|
690
|
+
for (const p of envPatterns) {
|
|
691
|
+
if (contentLower.includes(p.toLowerCase())) {
|
|
692
|
+
return {
|
|
693
|
+
level: "critical",
|
|
694
|
+
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"
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
let bestLevel = "";
|
|
700
|
+
let bestReason = "";
|
|
701
|
+
for (const kw of matchedKeywords) {
|
|
702
|
+
const risk = SINGLE_KEYWORD_RISKS[kw];
|
|
703
|
+
if (risk && (LEVEL_ORDER[risk.level] || 0) > (LEVEL_ORDER[bestLevel] || 0)) {
|
|
704
|
+
bestLevel = risk.level;
|
|
705
|
+
bestReason = risk.reason;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return { level: bestLevel, reason: bestReason };
|
|
709
|
+
}
|
|
710
|
+
function readScriptContent(scriptPath, maxLines = 500) {
|
|
711
|
+
try {
|
|
712
|
+
const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve3(scriptPath);
|
|
713
|
+
const raw = readFileSync3(expandedPath, "utf-8");
|
|
714
|
+
const lines = raw.split("\n").slice(0, maxLines);
|
|
715
|
+
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
716
|
+
} catch (e) {
|
|
717
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
718
|
+
return { content: "", lineCount: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
function analyzeScriptContent(actionType, actionDetail, maxLines = 500) {
|
|
722
|
+
if (actionType.toLowerCase() !== "bash") return emptyResult();
|
|
723
|
+
const scriptPath = extractScriptPath(actionDetail);
|
|
724
|
+
if (!scriptPath) return emptyResult();
|
|
725
|
+
const { content, lineCount, error } = readScriptContent(scriptPath, maxLines);
|
|
726
|
+
if (error) return { ...emptyResult(), scriptPath, error };
|
|
727
|
+
const matchedKeywords = scanScriptForKeywords(content);
|
|
728
|
+
const isSuspicious = matchedKeywords.length > 0;
|
|
729
|
+
let riskLevel = "";
|
|
730
|
+
let riskReason = "";
|
|
731
|
+
if (isSuspicious) {
|
|
732
|
+
const risk = assessKeywordCombinationRisk(matchedKeywords, content);
|
|
733
|
+
riskLevel = risk.level;
|
|
734
|
+
riskReason = risk.reason;
|
|
735
|
+
}
|
|
736
|
+
return { scriptPath, scriptContent: content, matchedKeywords, lineCount, isSuspicious, error: "", riskLevel, riskReason };
|
|
737
|
+
}
|
|
738
|
+
function formatScriptAnalysisForPrompt(result) {
|
|
739
|
+
if (!result.scriptPath) return "";
|
|
740
|
+
if (result.error) return `\u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${result.error}`;
|
|
741
|
+
if (!result.isSuspicious) return "";
|
|
742
|
+
const keywordsStr = result.matchedKeywords.join(", ");
|
|
743
|
+
let riskHint = "";
|
|
744
|
+
if (result.riskLevel && result.riskReason) {
|
|
745
|
+
riskHint = `
|
|
746
|
+
|
|
747
|
+
\u98CE\u9669\u8BC4\u4F30: ${result.riskLevel.toUpperCase()} \u7EA7\u522B
|
|
748
|
+
\u98CE\u9669\u539F\u56E0: ${result.riskReason}
|
|
749
|
+
\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`;
|
|
750
|
+
}
|
|
751
|
+
return `\u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\uFF0C\u4E14\u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
752
|
+
\u811A\u672C\u8DEF\u5F84: ${result.scriptPath}
|
|
753
|
+
\u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
|
|
754
|
+
\u811A\u672C\u884C\u6570: ${result.lineCount}${riskHint}
|
|
755
|
+
|
|
756
|
+
### \u811A\u672C\u5185\u5BB9 ###
|
|
757
|
+
\`\`\`
|
|
758
|
+
${result.scriptContent}
|
|
759
|
+
\`\`\`
|
|
760
|
+
`;
|
|
761
|
+
}
|
|
762
|
+
var SUSPICIOUS_PATTERNS, SCRIPT_EXEC_PATTERNS, KEYWORD_COMBINATION_RISKS, SINGLE_KEYWORD_RISKS, LEVEL_ORDER;
|
|
763
|
+
var init_script_analyzer = __esm({
|
|
764
|
+
"src/engine/script-analyzer.ts"() {
|
|
765
|
+
"use strict";
|
|
766
|
+
init_esm_shims();
|
|
767
|
+
SUSPICIOUS_PATTERNS = [
|
|
768
|
+
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
769
|
+
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
770
|
+
{ name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
|
|
771
|
+
{ name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
|
|
772
|
+
{ name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
|
|
773
|
+
{ name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
|
|
774
|
+
// Reverse shell
|
|
775
|
+
{ name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
|
|
776
|
+
{ name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
|
|
777
|
+
{ name: "/dev/tcp", pattern: /\/dev\/tcp\// },
|
|
778
|
+
{ name: "/dev/udp", pattern: /\/dev\/udp\// },
|
|
779
|
+
// Base64 obfuscation
|
|
780
|
+
{ name: "$(base64", pattern: /\$\(\s*base64/ },
|
|
781
|
+
{ name: "`base64", pattern: /`\s*base64/ },
|
|
782
|
+
{ name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
|
|
783
|
+
// Sensitive file access
|
|
784
|
+
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
785
|
+
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
786
|
+
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
787
|
+
// Persistence backdoor
|
|
788
|
+
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
789
|
+
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
790
|
+
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
791
|
+
];
|
|
792
|
+
SCRIPT_EXEC_PATTERNS = [
|
|
793
|
+
/bash\s+(\S+\.sh)/,
|
|
794
|
+
/sh\s+(\S+\.sh)/,
|
|
795
|
+
/\.\/(\S+\.sh)/,
|
|
796
|
+
/source\s+(\S+\.sh)/,
|
|
797
|
+
/\.\s+(\S+\.sh)/,
|
|
798
|
+
/zsh\s+(\S+\.sh)/
|
|
799
|
+
];
|
|
800
|
+
KEYWORD_COMBINATION_RISKS = [
|
|
801
|
+
{
|
|
802
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
803
|
+
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
804
|
+
level: "critical",
|
|
805
|
+
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"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
809
|
+
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
810
|
+
level: "critical",
|
|
811
|
+
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"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
815
|
+
group2: /* @__PURE__ */ new Set(),
|
|
816
|
+
level: "high",
|
|
817
|
+
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
821
|
+
group2: /* @__PURE__ */ new Set(),
|
|
822
|
+
level: "high",
|
|
823
|
+
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"
|
|
824
|
+
}
|
|
825
|
+
];
|
|
826
|
+
SINGLE_KEYWORD_RISKS = {
|
|
827
|
+
"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" },
|
|
828
|
+
"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" },
|
|
829
|
+
"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" },
|
|
830
|
+
"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" },
|
|
831
|
+
"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" },
|
|
832
|
+
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
833
|
+
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
834
|
+
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
835
|
+
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
836
|
+
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
837
|
+
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
838
|
+
"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" },
|
|
839
|
+
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
840
|
+
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
841
|
+
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
842
|
+
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
843
|
+
"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" },
|
|
844
|
+
"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" }
|
|
845
|
+
};
|
|
846
|
+
LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
270
850
|
// src/engine/llm-analyzer.ts
|
|
271
851
|
import OpenAI from "openai";
|
|
272
852
|
async function runLLMAnalysis(input, previous) {
|
|
@@ -283,15 +863,17 @@ async function runLLMAnalysis(input, previous) {
|
|
|
283
863
|
};
|
|
284
864
|
}
|
|
285
865
|
const config = getLLMConfig();
|
|
286
|
-
const
|
|
866
|
+
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
867
|
+
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
287
868
|
const cached = llmCache.get(cacheKey);
|
|
288
869
|
if (cached) {
|
|
289
870
|
console.log("[AgentMoss] LLM cache hit");
|
|
290
871
|
return cached;
|
|
291
872
|
}
|
|
292
|
-
const
|
|
873
|
+
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
293
874
|
const templates = loadTemplates();
|
|
294
|
-
const
|
|
875
|
+
const scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
876
|
+
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis);
|
|
295
877
|
const result = await analyzeWithLLM(prompt, config);
|
|
296
878
|
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
297
879
|
const firstKey = llmCache.keys().next().value;
|
|
@@ -300,11 +882,13 @@ async function runLLMAnalysis(input, previous) {
|
|
|
300
882
|
llmCache.set(cacheKey, result);
|
|
301
883
|
return result;
|
|
302
884
|
}
|
|
303
|
-
function buildPrompt(input, previous,
|
|
304
|
-
const
|
|
305
|
-
const historyStr = input.actionHistory.map((h, i) => `${i + 1}.
|
|
885
|
+
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis) {
|
|
886
|
+
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
887
|
+
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
306
888
|
const templateName = previous.heuristic || previous.logic ? "prompt2" : "prompt1";
|
|
307
889
|
const baseTemplate = templates[templateName] || templates["prompt1"] || "";
|
|
890
|
+
const scriptAnalysisText = scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
891
|
+
const hintsText = formatHints(previous);
|
|
308
892
|
return `${baseTemplate}
|
|
309
893
|
|
|
310
894
|
## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
@@ -321,7 +905,13 @@ ${historyStr}
|
|
|
321
905
|
\u8BE6\u60C5: ${input.aNext.actionDetail}
|
|
322
906
|
|
|
323
907
|
## \u53EF\u7528\u5B89\u5168 Skill
|
|
324
|
-
${
|
|
908
|
+
${skillsText}
|
|
909
|
+
|
|
910
|
+
## \u524D\u7F6E\u68C0\u6D4B\u7ED3\u679C
|
|
911
|
+
${hintsText}
|
|
912
|
+
${scriptAnalysisText ? `
|
|
913
|
+
## \u811A\u672C\u5185\u5BB9\u5206\u6790
|
|
914
|
+
${scriptAnalysisText}` : ""}
|
|
325
915
|
|
|
326
916
|
\u8BF7\u6839\u636E\u4EE5\u4E0A\u4FE1\u606F\uFF0C\u5BF9\u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C\u8FDB\u884C\u5B89\u5168\u5224\u65AD\u3002\u4F60\u5FC5\u987B\u8F93\u51FA\u4E25\u683C\u7684 JSON \u683C\u5F0F\uFF0C
|
|
327
917
|
\u4E0D\u8981\u5305\u542B\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u6CE8\u91CA\u6216 markdown \u6807\u8BB0\u3002
|
|
@@ -329,52 +919,92 @@ ${skillContext}
|
|
|
329
919
|
\u8F93\u51FA\u683C\u5F0F:
|
|
330
920
|
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
331
921
|
}
|
|
922
|
+
function formatHints(previous) {
|
|
923
|
+
const hints = [];
|
|
924
|
+
if (previous.heuristic) {
|
|
925
|
+
hints.push(
|
|
926
|
+
`\u542F\u53D1\u5F0F\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
927
|
+
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
928
|
+
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
if (previous.logic) {
|
|
932
|
+
hints.push(
|
|
933
|
+
`\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.logic.riskLevel}]:
|
|
934
|
+
\u7C7B\u578B: ${previous.logic.riskType}
|
|
935
|
+
\u539F\u56E0: ${previous.logic.reason}`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
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";
|
|
939
|
+
}
|
|
332
940
|
async function analyzeWithLLM(prompt, config) {
|
|
333
941
|
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
942
|
+
const provider = detectProvider(config.baseUrl, config.provider);
|
|
943
|
+
const extraHeaders = getProviderHeaders(provider);
|
|
944
|
+
if (Object.keys(extraHeaders).length > 0) {
|
|
945
|
+
console.log(`[AgentMoss] Provider detected: ${provider}, injecting headers: ${Object.keys(extraHeaders).join(", ")}`);
|
|
946
|
+
}
|
|
334
947
|
const client = new OpenAI({
|
|
335
948
|
baseURL: config.baseUrl,
|
|
336
949
|
apiKey: config.apiKey,
|
|
337
|
-
timeout:
|
|
950
|
+
timeout: (config.timeout || 300) * 1e3,
|
|
951
|
+
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
338
952
|
});
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
jsonStr =
|
|
953
|
+
const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || "2", 10);
|
|
954
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
955
|
+
try {
|
|
956
|
+
const response = await client.chat.completions.create({
|
|
957
|
+
model: config.model,
|
|
958
|
+
temperature: config.temperature || 0.1,
|
|
959
|
+
max_tokens: config.maxTokens || 1024,
|
|
960
|
+
messages: [
|
|
961
|
+
{ role: "system", content: "\u4F60\u662F\u4E00\u4E2A AI Agent \u5B89\u5168\u5BA1\u8BA1\u4E13\u5BB6\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u5206\u6790\u5373\u5C06\u6267\u884C\u7684\u547D\u4EE4\u662F\u5426\u5B89\u5168\u3002\u4F60\u5FC5\u987B\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55 markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u5176\u4ED6\u6587\u5B57\u3002" },
|
|
962
|
+
{ role: "user", content: prompt }
|
|
963
|
+
]
|
|
964
|
+
});
|
|
965
|
+
const content = response.choices[0]?.message?.content || "{}";
|
|
966
|
+
console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
|
|
967
|
+
let jsonStr = content.trim();
|
|
968
|
+
if (jsonStr.startsWith("```")) {
|
|
969
|
+
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
970
|
+
}
|
|
971
|
+
const parsed = JSON.parse(jsonStr);
|
|
972
|
+
return {
|
|
973
|
+
allowed: parsed.allowed !== false,
|
|
974
|
+
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
975
|
+
riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
|
|
976
|
+
riskType: parsed.riskType || "",
|
|
977
|
+
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
978
|
+
source: "llm",
|
|
979
|
+
violatedLayers: ["3"]
|
|
980
|
+
};
|
|
981
|
+
} catch (err) {
|
|
982
|
+
if (attempt < maxRetries) {
|
|
983
|
+
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in 1s:`, err.message);
|
|
984
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
985
|
+
} else {
|
|
986
|
+
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
987
|
+
return {
|
|
988
|
+
allowed: false,
|
|
989
|
+
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}`,
|
|
990
|
+
riskLevel: "medium",
|
|
991
|
+
riskType: "",
|
|
992
|
+
confidence: 60,
|
|
993
|
+
source: "llm_failure",
|
|
994
|
+
violatedLayers: ["3"]
|
|
995
|
+
};
|
|
996
|
+
}
|
|
354
997
|
}
|
|
355
|
-
const parsed = JSON.parse(jsonStr);
|
|
356
|
-
return {
|
|
357
|
-
allowed: parsed.allowed !== false,
|
|
358
|
-
// default to Allow if missing
|
|
359
|
-
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
360
|
-
riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
|
|
361
|
-
riskType: parsed.riskType || "",
|
|
362
|
-
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
363
|
-
source: "llm",
|
|
364
|
-
violatedLayers: ["3"]
|
|
365
|
-
};
|
|
366
|
-
} catch (err) {
|
|
367
|
-
console.error("[AgentMoss] LLM call failed:", err.message);
|
|
368
|
-
return {
|
|
369
|
-
allowed: true,
|
|
370
|
-
reason: `LLM \u8C03\u7528\u5931\u8D25: ${err.message}`,
|
|
371
|
-
riskLevel: "low",
|
|
372
|
-
riskType: "",
|
|
373
|
-
confidence: 30,
|
|
374
|
-
source: "llm",
|
|
375
|
-
violatedLayers: []
|
|
376
|
-
};
|
|
377
998
|
}
|
|
999
|
+
return {
|
|
1000
|
+
allowed: false,
|
|
1001
|
+
reason: "LLM \u5206\u6790\u5F02\u5E38",
|
|
1002
|
+
riskLevel: "medium",
|
|
1003
|
+
riskType: "",
|
|
1004
|
+
confidence: 60,
|
|
1005
|
+
source: "llm_failure",
|
|
1006
|
+
violatedLayers: ["3"]
|
|
1007
|
+
};
|
|
378
1008
|
}
|
|
379
1009
|
var llmCache, LLM_CACHE_MAX;
|
|
380
1010
|
var init_llm_analyzer = __esm({
|
|
@@ -384,6 +1014,7 @@ var init_llm_analyzer = __esm({
|
|
|
384
1014
|
init_config();
|
|
385
1015
|
init_skill_loader();
|
|
386
1016
|
init_template_loader();
|
|
1017
|
+
init_script_analyzer();
|
|
387
1018
|
llmCache = /* @__PURE__ */ new Map();
|
|
388
1019
|
LLM_CACHE_MAX = 200;
|
|
389
1020
|
}
|
|
@@ -394,21 +1025,83 @@ var coordinator_exports = {};
|
|
|
394
1025
|
__export(coordinator_exports, {
|
|
395
1026
|
runAnalysis: () => runAnalysis
|
|
396
1027
|
});
|
|
1028
|
+
function extractFirstCommand(command) {
|
|
1029
|
+
let main2 = command.split("|")[0].trim();
|
|
1030
|
+
for (const sep of ["&&", "||"]) {
|
|
1031
|
+
const idx = main2.indexOf(sep);
|
|
1032
|
+
if (idx !== -1) {
|
|
1033
|
+
main2 = main2.substring(0, idx).trim();
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return main2;
|
|
1038
|
+
}
|
|
1039
|
+
function extractRawCommand(detail) {
|
|
1040
|
+
const m = detail.match(/command=([^,]+)/);
|
|
1041
|
+
return m ? m[1].trim() : detail;
|
|
1042
|
+
}
|
|
1043
|
+
function matchesAny(cmd, patterns) {
|
|
1044
|
+
return patterns.some((p) => p.test(cmd));
|
|
1045
|
+
}
|
|
397
1046
|
async function runAnalysis(input) {
|
|
398
|
-
const heuristicResult = runHeuristicCheck(input);
|
|
1047
|
+
const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
|
|
399
1048
|
if (heuristicResult && !heuristicResult.allowed) {
|
|
400
1049
|
return heuristicResult;
|
|
401
1050
|
}
|
|
402
|
-
const
|
|
1051
|
+
const actionType = input.aNext.actionType.toLowerCase();
|
|
1052
|
+
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
1053
|
+
const lowerDetail = rawDetail.toLowerCase();
|
|
1054
|
+
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
1055
|
+
return {
|
|
1056
|
+
allowed: true,
|
|
1057
|
+
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
1058
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
1059
|
+
riskType: "",
|
|
1060
|
+
confidence: 95,
|
|
1061
|
+
source: "whitelist_bypass",
|
|
1062
|
+
violatedLayers: []
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
let skipLLM = READONLY_SENSITIVE_TOOLS.has(actionType);
|
|
1066
|
+
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
1067
|
+
const firstCmd = extractFirstCommand(lowerDetail);
|
|
1068
|
+
if (matchesAny(firstCmd, FULLY_SAFE_BASH_PATTERNS)) {
|
|
1069
|
+
return {
|
|
1070
|
+
allowed: true,
|
|
1071
|
+
reason: `\u5B89\u5168\u7684\u53EA\u8BFB bash \u547D\u4EE4: ${firstCmd}`,
|
|
1072
|
+
riskLevel: "low",
|
|
1073
|
+
riskType: "",
|
|
1074
|
+
confidence: 95,
|
|
1075
|
+
source: "whitelist_bypass",
|
|
1076
|
+
violatedLayers: []
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
if (matchesAny(firstCmd, READONLY_SENSITIVE_BASH_PATTERNS)) {
|
|
1080
|
+
skipLLM = true;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
|
|
403
1084
|
if (logicResult && !logicResult.allowed) {
|
|
404
1085
|
return logicResult;
|
|
405
1086
|
}
|
|
1087
|
+
if (skipLLM || !isLLMAvailable()) {
|
|
1088
|
+
return {
|
|
1089
|
+
allowed: true,
|
|
1090
|
+
reason: "\u53EA\u8BFB\u64CD\u4F5C\u4E14\u65E0\u903B\u8F91\u89C4\u5219\u8FDD\u89C4\uFF0C\u8DF3\u8FC7 LLM \u5206\u6790",
|
|
1091
|
+
riskLevel: logicResult?.riskLevel || "low",
|
|
1092
|
+
riskType: "",
|
|
1093
|
+
confidence: 85,
|
|
1094
|
+
source: "heuristic_and_logic_only",
|
|
1095
|
+
violatedLayers: []
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
406
1098
|
const llmResult = await runLLMAnalysis(input, {
|
|
407
1099
|
heuristic: heuristicResult,
|
|
408
1100
|
logic: logicResult
|
|
409
1101
|
});
|
|
410
1102
|
return llmResult;
|
|
411
1103
|
}
|
|
1104
|
+
var FULLY_SAFE_TOOLS, READONLY_SENSITIVE_TOOLS, FULLY_SAFE_BASH_PATTERNS, READONLY_SENSITIVE_BASH_PATTERNS;
|
|
412
1105
|
var init_coordinator = __esm({
|
|
413
1106
|
"src/engine/coordinator.ts"() {
|
|
414
1107
|
"use strict";
|
|
@@ -416,6 +1109,59 @@ var init_coordinator = __esm({
|
|
|
416
1109
|
init_heuristic();
|
|
417
1110
|
init_logic_rules();
|
|
418
1111
|
init_llm_analyzer();
|
|
1112
|
+
init_config();
|
|
1113
|
+
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
1114
|
+
"ask_user_question",
|
|
1115
|
+
"glob",
|
|
1116
|
+
"list_dir",
|
|
1117
|
+
"ls",
|
|
1118
|
+
"count_text_length",
|
|
1119
|
+
"filemgr-globfiles"
|
|
1120
|
+
]);
|
|
1121
|
+
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
1122
|
+
"read",
|
|
1123
|
+
"file_read",
|
|
1124
|
+
"read_file",
|
|
1125
|
+
"head",
|
|
1126
|
+
"tail",
|
|
1127
|
+
"grep",
|
|
1128
|
+
"filemgr-readfile",
|
|
1129
|
+
"filemgr-grepfiles"
|
|
1130
|
+
]);
|
|
1131
|
+
FULLY_SAFE_BASH_PATTERNS = [
|
|
1132
|
+
/^ls(?:\s|$)/i,
|
|
1133
|
+
/^dir(?:\s|$)/i,
|
|
1134
|
+
/^pwd(?:\s|$)/i,
|
|
1135
|
+
/^which\s+/i,
|
|
1136
|
+
/^whereis\s+/i,
|
|
1137
|
+
/^realpath\s+/i,
|
|
1138
|
+
/^basename\s+/i,
|
|
1139
|
+
/^dirname\s+/i,
|
|
1140
|
+
/^file\s+/i,
|
|
1141
|
+
/^stat\s+/i,
|
|
1142
|
+
/^du(?:\s|$)/i,
|
|
1143
|
+
/^echo\s+/i,
|
|
1144
|
+
/^printf\s+/i,
|
|
1145
|
+
/^type\s+/i,
|
|
1146
|
+
/^command\s+/i
|
|
1147
|
+
];
|
|
1148
|
+
READONLY_SENSITIVE_BASH_PATTERNS = [
|
|
1149
|
+
/^cat\s+/i,
|
|
1150
|
+
/^head(?:\s|$)/i,
|
|
1151
|
+
/^tail(?:\s|$)/i,
|
|
1152
|
+
/^less(?:\s|$)/i,
|
|
1153
|
+
/^more(?:\s|$)/i,
|
|
1154
|
+
/^wc(?:\s|$)/i,
|
|
1155
|
+
/^grep(?:\s|$)/i,
|
|
1156
|
+
/^find\s+/i,
|
|
1157
|
+
/^ag(?:\s|$)/i,
|
|
1158
|
+
/^rg(?:\s|$)/i,
|
|
1159
|
+
/^awk\s+/i,
|
|
1160
|
+
/^sed\s+/i,
|
|
1161
|
+
/^cut\s+/i,
|
|
1162
|
+
/^sort(?:\s|$)/i,
|
|
1163
|
+
/^uniq(?:\s|$)/i
|
|
1164
|
+
];
|
|
419
1165
|
}
|
|
420
1166
|
});
|
|
421
1167
|
|
|
@@ -553,7 +1299,7 @@ async function main() {
|
|
|
553
1299
|
switch (command) {
|
|
554
1300
|
case "init": {
|
|
555
1301
|
const output = args[2] || "input.json";
|
|
556
|
-
const
|
|
1302
|
+
const fs2 = await import("fs");
|
|
557
1303
|
const template = {
|
|
558
1304
|
session_id: "my-session-001",
|
|
559
1305
|
prompt_session: "Read /var/log/app.log and analyze errors",
|
|
@@ -563,7 +1309,7 @@ async function main() {
|
|
|
563
1309
|
os_type: "",
|
|
564
1310
|
cwd: "/home/user/project"
|
|
565
1311
|
};
|
|
566
|
-
|
|
1312
|
+
fs2.writeFileSync(output, JSON.stringify(template, null, 2), "utf-8");
|
|
567
1313
|
console.log(`Template written to ${output}`);
|
|
568
1314
|
break;
|
|
569
1315
|
}
|
|
@@ -572,11 +1318,11 @@ async function main() {
|
|
|
572
1318
|
console.error("Usage: agent-moss analyze <input.json>");
|
|
573
1319
|
process.exit(1);
|
|
574
1320
|
}
|
|
575
|
-
const
|
|
1321
|
+
const fs2 = await import("fs");
|
|
576
1322
|
const path2 = await import("path");
|
|
577
1323
|
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
578
1324
|
const filePath = path2.resolve(args[1]);
|
|
579
|
-
const data = JSON.parse(
|
|
1325
|
+
const data = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
580
1326
|
const result = await runAnalysis2({
|
|
581
1327
|
sessionId: data.session_id || "unknown",
|
|
582
1328
|
promptSession: data.prompt_session || "",
|