@kenhkl/agent-moss 0.9.0 → 0.10.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/README.md +3 -3
- package/dist/agents/__init__.py +35 -22
- 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 +5 -0
- package/dist/agents/opendesk/rules.json +29 -0
- package/dist/cli.js +2559 -2319
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.js +1219 -1020
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/server.js +1260 -1020
- package/dist/server.js.map +1 -1
- package/dist/skills/intent_deviation_guard.md +1 -1
- package/dist/templates/security_judge_template.txt +4 -4
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
2
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
3
5
|
var __esm = (fn, res) => function __init() {
|
|
4
6
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
7
|
};
|
|
@@ -7,6 +9,15 @@ var __export = (target, all) => {
|
|
|
7
9
|
for (var name in all)
|
|
8
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
11
|
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
10
21
|
|
|
11
22
|
// node_modules/tsup/assets/esm_shims.js
|
|
12
23
|
import path from "path";
|
|
@@ -203,380 +214,89 @@ var init_config = __esm({
|
|
|
203
214
|
}
|
|
204
215
|
});
|
|
205
216
|
|
|
206
|
-
// src/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
function resolveAgentsDir() {
|
|
211
|
-
if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
212
|
-
if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
213
|
-
return AGENTS_DIR_PROD;
|
|
217
|
+
// src/engine/inline-analyzer.ts
|
|
218
|
+
function isLiteralTextCommand(command) {
|
|
219
|
+
ECHO_CMD_RE.lastIndex = 0;
|
|
220
|
+
return ECHO_CMD_RE.test(command);
|
|
214
221
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return null;
|
|
222
|
+
function extractInlineCode(command) {
|
|
223
|
+
const parts = [];
|
|
224
|
+
let m;
|
|
225
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
226
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
227
|
+
const code = m[1] ?? m[2];
|
|
228
|
+
if (code) parts.push(code);
|
|
223
229
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
228
|
-
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
229
|
-
cache.set(agentId, null);
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
cache.set(agentId, ext);
|
|
233
|
-
console.log(
|
|
234
|
-
`[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`
|
|
235
|
-
);
|
|
236
|
-
return ext;
|
|
237
|
-
} catch (e) {
|
|
238
|
-
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
239
|
-
cache.set(agentId, null);
|
|
240
|
-
return null;
|
|
230
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
231
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
232
|
+
const code = m[1] ?? m[2];
|
|
233
|
+
if (code) parts.push(code);
|
|
241
234
|
}
|
|
235
|
+
return parts.join("\n");
|
|
242
236
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
function
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
let highestRiskType = "";
|
|
262
|
-
const userRules = getCustomRules();
|
|
263
|
-
for (const rule of userRules) {
|
|
264
|
-
try {
|
|
265
|
-
const re = new RegExp(rule.pattern);
|
|
266
|
-
if (re.test(command)) {
|
|
267
|
-
if (rule.action === "Deny") {
|
|
268
|
-
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
269
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
270
|
-
highestSeverity = rule.severity;
|
|
271
|
-
highestRiskType = "custom_rule";
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
} catch {
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
let topRiskType = "";
|
|
279
|
-
for (const rule of DANGEROUS_PATTERNS) {
|
|
280
|
-
if (rule.pattern.test(command)) {
|
|
281
|
-
matched.push(rule.description);
|
|
282
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
283
|
-
highestSeverity = rule.severity;
|
|
284
|
-
topRiskType = rule.riskType;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
highestRiskType = topRiskType || highestRiskType;
|
|
289
|
-
const agentExt = getAgentExtensions(input.agentId);
|
|
290
|
-
if (agentExt?.extra_command_patterns) {
|
|
291
|
-
for (const rule of agentExt.extra_command_patterns) {
|
|
292
|
-
try {
|
|
293
|
-
const re = new RegExp(rule.pattern, "i");
|
|
294
|
-
if (re.test(command)) {
|
|
295
|
-
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
296
|
-
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
297
|
-
highestSeverity = rule.risk_level;
|
|
298
|
-
highestRiskType = rule.risk_type;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
} catch {
|
|
302
|
-
}
|
|
303
|
-
}
|
|
237
|
+
function hasWriteExecChain(command) {
|
|
238
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
239
|
+
p.lastIndex = 0;
|
|
240
|
+
return p.test(command);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function hasDirTraversal(code) {
|
|
244
|
+
const lower = code.toLowerCase();
|
|
245
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
246
|
+
}
|
|
247
|
+
function hasReadWriteKeyword(code) {
|
|
248
|
+
const lower = code.toLowerCase();
|
|
249
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
250
|
+
}
|
|
251
|
+
function analyzeInlineScript(command) {
|
|
252
|
+
const code = extractInlineCode(command);
|
|
253
|
+
if (!code) {
|
|
254
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
304
255
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
256
|
+
if (hasWriteExecChain(command)) {
|
|
257
|
+
return {
|
|
258
|
+
actionable: true,
|
|
259
|
+
allowed: false,
|
|
260
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
261
|
+
riskLevel: "critical",
|
|
262
|
+
riskType: "script_execution"
|
|
263
|
+
};
|
|
314
264
|
}
|
|
315
|
-
|
|
265
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
266
|
+
if (redirMatch) {
|
|
316
267
|
return {
|
|
268
|
+
actionable: true,
|
|
317
269
|
allowed: false,
|
|
318
|
-
reason: `\
|
|
319
|
-
riskLevel:
|
|
320
|
-
riskType:
|
|
321
|
-
confidence: 95,
|
|
322
|
-
source: "heuristic",
|
|
323
|
-
violatedLayers: ["1.1"]
|
|
324
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
270
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
271
|
+
riskLevel: "critical",
|
|
272
|
+
riskType: "file_access"
|
|
325
273
|
};
|
|
326
274
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const hits = [];
|
|
331
|
-
for (const entry of INJECTION_KEYWORDS) {
|
|
332
|
-
if (text.includes(entry.keyword.toLowerCase())) {
|
|
333
|
-
hits.push(entry);
|
|
334
|
-
}
|
|
275
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
276
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
277
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
335
278
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
279
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
280
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
281
|
+
return {
|
|
282
|
+
actionable: true,
|
|
283
|
+
allowed: false,
|
|
284
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
285
|
+
riskLevel: "critical",
|
|
286
|
+
riskType: "file_access"
|
|
287
|
+
};
|
|
342
288
|
}
|
|
343
|
-
return
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
351
|
-
return ranks[s] || 0;
|
|
289
|
+
return {
|
|
290
|
+
actionable: true,
|
|
291
|
+
allowed: true,
|
|
292
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
293
|
+
riskLevel: "low",
|
|
294
|
+
riskType: "file_access"
|
|
295
|
+
};
|
|
352
296
|
}
|
|
353
|
-
var
|
|
354
|
-
var
|
|
355
|
-
"src/engine/
|
|
356
|
-
"use strict";
|
|
357
|
-
init_esm_shims();
|
|
358
|
-
init_config();
|
|
359
|
-
init_agents();
|
|
360
|
-
INJECTION_KEYWORDS = [
|
|
361
|
-
// ---- Instruction Override ----
|
|
362
|
-
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
363
|
-
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
364
|
-
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
365
|
-
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
366
|
-
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
367
|
-
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
368
|
-
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
369
|
-
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
370
|
-
// ---- Role Hijacking ----
|
|
371
|
-
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
372
|
-
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
373
|
-
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
374
|
-
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
375
|
-
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
376
|
-
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
377
|
-
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
378
|
-
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
379
|
-
// ---- Command Injection ----
|
|
380
|
-
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
381
|
-
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
382
|
-
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
383
|
-
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
384
|
-
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
385
|
-
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
386
|
-
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
387
|
-
// ---- Social Engineering ----
|
|
388
|
-
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
389
|
-
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
390
|
-
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
391
|
-
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
392
|
-
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
393
|
-
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
394
|
-
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
395
|
-
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
396
|
-
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
397
|
-
// ---- XSS ----
|
|
398
|
-
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
399
|
-
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
400
|
-
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
401
|
-
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
402
|
-
// ---- Fake Boundaries ----
|
|
403
|
-
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
404
|
-
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
405
|
-
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
406
|
-
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
407
|
-
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
408
|
-
// ---- Developer Mode ----
|
|
409
|
-
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
410
|
-
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
411
|
-
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
412
|
-
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
413
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
414
|
-
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
415
|
-
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
416
|
-
// 由 agent_ext 机制注入,不再污染通用层。
|
|
417
|
-
];
|
|
418
|
-
DANGEROUS_PATTERNS = [
|
|
419
|
-
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
420
|
-
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
421
|
-
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
|
|
422
|
-
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
423
|
-
// 2. rm with -r or -f targeting system dirs
|
|
424
|
-
{ 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", riskType: "script_execution" },
|
|
425
|
-
// 3. rm -r absolute path (no -f)
|
|
426
|
-
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\//i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
|
|
427
|
-
// 4. rm -f absolute path (no -r)
|
|
428
|
-
{ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\//i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
|
|
429
|
-
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
430
|
-
{ pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
|
|
431
|
-
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
432
|
-
{ pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
|
|
433
|
-
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
434
|
-
// --- Critical: remote code execution ---
|
|
435
|
-
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
436
|
-
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
437
|
-
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
438
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
439
|
-
// --- Critical: reverse shell ---
|
|
440
|
-
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
441
|
-
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
442
|
-
// --- Critical: sensitive file access ---
|
|
443
|
-
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
444
|
-
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
445
|
-
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
446
|
-
{ pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
447
|
-
// --- Critical: privilege escalation / container escape ---
|
|
448
|
-
{ pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
|
|
449
|
-
// --- High: dangerous permissions / ownership ---
|
|
450
|
-
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
451
|
-
{ 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", riskType: "script_execution" },
|
|
452
|
-
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
453
|
-
{ pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
|
|
454
|
-
// --- High: system config overwrite ---
|
|
455
|
-
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
456
|
-
// --- High: download script + execute risk ---
|
|
457
|
-
{ pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
|
|
458
|
-
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
459
|
-
// --- High: package manager pipe-to-shell ---
|
|
460
|
-
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
461
|
-
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
462
|
-
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
463
|
-
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
464
|
-
{ pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
|
|
465
|
-
// --- High: network port scanning (横向移动侦察) ---
|
|
466
|
-
{ pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
467
|
-
{ pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
468
|
-
// --- Medium: credential export ---
|
|
469
|
-
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
470
|
-
// --- High: shell rc file append (persistence) ---
|
|
471
|
-
{ pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
|
|
472
|
-
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
473
|
-
{ pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
|
|
474
|
-
// --- High: non-interactive password modification ---
|
|
475
|
-
{ pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
|
|
476
|
-
{ pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
|
|
477
|
-
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
478
|
-
{ pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
|
|
479
|
-
{ pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
|
|
480
|
-
{ pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
|
|
481
|
-
// --- High: non-interactive password modification (extended) ---
|
|
482
|
-
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
483
|
-
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
484
|
-
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
485
|
-
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
486
|
-
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
487
|
-
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
488
|
-
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
489
|
-
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
490
|
-
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
491
|
-
// --- High: generic command -p parameter password setting ---
|
|
492
|
-
{ pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
|
|
493
|
-
];
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
// src/engine/inline-analyzer.ts
|
|
498
|
-
function isLiteralTextCommand(command) {
|
|
499
|
-
ECHO_CMD_RE.lastIndex = 0;
|
|
500
|
-
return ECHO_CMD_RE.test(command);
|
|
501
|
-
}
|
|
502
|
-
function extractInlineCode(command) {
|
|
503
|
-
const parts = [];
|
|
504
|
-
let m;
|
|
505
|
-
INLINE_CODE_RE.lastIndex = 0;
|
|
506
|
-
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
507
|
-
const code = m[1] ?? m[2];
|
|
508
|
-
if (code) parts.push(code);
|
|
509
|
-
}
|
|
510
|
-
ECHO_STRING_RE.lastIndex = 0;
|
|
511
|
-
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
512
|
-
const code = m[1] ?? m[2];
|
|
513
|
-
if (code) parts.push(code);
|
|
514
|
-
}
|
|
515
|
-
return parts.join("\n");
|
|
516
|
-
}
|
|
517
|
-
function hasWriteExecChain(command) {
|
|
518
|
-
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
519
|
-
p.lastIndex = 0;
|
|
520
|
-
return p.test(command);
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
function hasDirTraversal(code) {
|
|
524
|
-
const lower = code.toLowerCase();
|
|
525
|
-
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
526
|
-
}
|
|
527
|
-
function hasReadWriteKeyword(code) {
|
|
528
|
-
const lower = code.toLowerCase();
|
|
529
|
-
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
530
|
-
}
|
|
531
|
-
function analyzeInlineScript(command) {
|
|
532
|
-
const code = extractInlineCode(command);
|
|
533
|
-
if (!code) {
|
|
534
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
535
|
-
}
|
|
536
|
-
if (hasWriteExecChain(command)) {
|
|
537
|
-
return {
|
|
538
|
-
actionable: true,
|
|
539
|
-
allowed: false,
|
|
540
|
-
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
541
|
-
riskLevel: "critical",
|
|
542
|
-
riskType: "script_execution"
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
546
|
-
if (redirMatch) {
|
|
547
|
-
return {
|
|
548
|
-
actionable: true,
|
|
549
|
-
allowed: false,
|
|
550
|
-
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
551
|
-
riskLevel: "critical",
|
|
552
|
-
riskType: "file_access"
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
556
|
-
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
557
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
558
|
-
}
|
|
559
|
-
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
560
|
-
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
561
|
-
return {
|
|
562
|
-
actionable: true,
|
|
563
|
-
allowed: false,
|
|
564
|
-
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
565
|
-
riskLevel: "critical",
|
|
566
|
-
riskType: "file_access"
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
return {
|
|
570
|
-
actionable: true,
|
|
571
|
-
allowed: true,
|
|
572
|
-
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
573
|
-
riskLevel: "low",
|
|
574
|
-
riskType: "file_access"
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
578
|
-
var init_inline_analyzer = __esm({
|
|
579
|
-
"src/engine/inline-analyzer.ts"() {
|
|
297
|
+
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
298
|
+
var init_inline_analyzer = __esm({
|
|
299
|
+
"src/engine/inline-analyzer.ts"() {
|
|
580
300
|
"use strict";
|
|
581
301
|
init_esm_shims();
|
|
582
302
|
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
@@ -710,165 +430,26 @@ function runLogicCheck(input) {
|
|
|
710
430
|
}
|
|
711
431
|
}
|
|
712
432
|
}
|
|
713
|
-
if (!isExcludedType) {
|
|
714
|
-
const agentExt = getAgentExtensions(input.agentId || "");
|
|
715
|
-
const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
|
|
716
|
-
if (pathResult) return pathResult;
|
|
717
|
-
}
|
|
718
|
-
if (actionType !== "file_write" && actionType !== "file_edit") {
|
|
719
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
720
|
-
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
721
|
-
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
722
|
-
return {
|
|
723
|
-
allowed: false,
|
|
724
|
-
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
725
|
-
riskLevel: "high",
|
|
726
|
-
riskType: "script_execution",
|
|
727
|
-
confidence: 90,
|
|
728
|
-
source: "logic_rule",
|
|
729
|
-
violatedLayers: ["2"]
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
733
|
-
return {
|
|
734
|
-
allowed: false,
|
|
735
|
-
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
736
|
-
riskLevel: "critical",
|
|
737
|
-
riskType: "file_access",
|
|
738
|
-
confidence: 95,
|
|
739
|
-
source: "logic_rule",
|
|
740
|
-
violatedLayers: ["2"]
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
745
|
-
"cat",
|
|
746
|
-
"head",
|
|
747
|
-
"tail",
|
|
748
|
-
"less",
|
|
749
|
-
"more",
|
|
750
|
-
"grep",
|
|
751
|
-
"find",
|
|
752
|
-
"awk",
|
|
753
|
-
"sed",
|
|
754
|
-
"sort",
|
|
755
|
-
"uniq",
|
|
756
|
-
"wc",
|
|
757
|
-
"cut",
|
|
758
|
-
"strings",
|
|
759
|
-
"od",
|
|
760
|
-
"xxd",
|
|
761
|
-
"hexdump",
|
|
762
|
-
"tr",
|
|
763
|
-
"file",
|
|
764
|
-
"stat",
|
|
765
|
-
"du",
|
|
766
|
-
"df",
|
|
767
|
-
"ls",
|
|
768
|
-
"dir",
|
|
769
|
-
"tree",
|
|
770
|
-
"nl",
|
|
771
|
-
"tac",
|
|
772
|
-
"rev",
|
|
773
|
-
"lsblk",
|
|
774
|
-
"blockdev",
|
|
775
|
-
"smartctl",
|
|
776
|
-
"udevadm",
|
|
777
|
-
"dmidecode",
|
|
778
|
-
"lscpu",
|
|
779
|
-
"lspci",
|
|
780
|
-
"lsusb",
|
|
781
|
-
"lsmem",
|
|
782
|
-
"lsns",
|
|
783
|
-
"lsof",
|
|
784
|
-
"hwinfo",
|
|
785
|
-
"inxi",
|
|
786
|
-
"fdisk",
|
|
787
|
-
"parted",
|
|
788
|
-
"dmesg",
|
|
789
|
-
"journalctl",
|
|
790
|
-
"systemctl",
|
|
791
|
-
"hostnamectl",
|
|
792
|
-
"localectl",
|
|
793
|
-
"timedatectl",
|
|
794
|
-
"ps",
|
|
795
|
-
"top",
|
|
796
|
-
"free",
|
|
797
|
-
"vmstat",
|
|
798
|
-
"iostat",
|
|
799
|
-
"mpstat",
|
|
800
|
-
"pidof",
|
|
801
|
-
"pgrep",
|
|
802
|
-
"ip",
|
|
803
|
-
"ifconfig",
|
|
804
|
-
"route",
|
|
805
|
-
"ss",
|
|
806
|
-
"netstat",
|
|
807
|
-
"arp",
|
|
808
|
-
"ethtool",
|
|
809
|
-
"nmcli",
|
|
810
|
-
"uname",
|
|
811
|
-
"arch",
|
|
812
|
-
"nproc",
|
|
813
|
-
"getconf",
|
|
814
|
-
"getent",
|
|
815
|
-
"id",
|
|
816
|
-
"whoami",
|
|
817
|
-
"who",
|
|
818
|
-
"w",
|
|
819
|
-
"last",
|
|
820
|
-
"uptime",
|
|
821
|
-
"lsmod",
|
|
822
|
-
"modinfo",
|
|
823
|
-
"sysctl"
|
|
824
|
-
]);
|
|
825
|
-
var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
826
|
-
var _WRITE_COMMAND_RES = [
|
|
827
|
-
/\brm\b/,
|
|
828
|
-
/\bunlink\b/,
|
|
829
|
-
/\brmdir\b/,
|
|
830
|
-
/\bshred\b/,
|
|
831
|
-
/\bcp\b/,
|
|
832
|
-
/\bmv\b/,
|
|
833
|
-
/\binstall\b/,
|
|
834
|
-
/\btee\b/,
|
|
835
|
-
/\bdd\b/,
|
|
836
|
-
/\bmkfs\b/,
|
|
837
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
838
|
-
/\bchmod\b/,
|
|
839
|
-
/\bchown\b/,
|
|
840
|
-
/\btruncate\b/
|
|
841
|
-
];
|
|
842
433
|
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
|
|
843
434
|
if (indirectResult) return indirectResult;
|
|
844
435
|
if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
)
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (targetHost && !isLocalHost(targetHost)) {
|
|
863
|
-
return {
|
|
864
|
-
allowed: false,
|
|
865
|
-
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
866
|
-
riskLevel: "medium",
|
|
867
|
-
riskType: "lateral_movement",
|
|
868
|
-
confidence: 80,
|
|
869
|
-
source: "logic_rule",
|
|
870
|
-
violatedLayers: ["2"]
|
|
871
|
-
};
|
|
436
|
+
const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
|
|
437
|
+
const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
|
|
438
|
+
if (!isAdminCmd) {
|
|
439
|
+
const adminContext = actionHistory.some(
|
|
440
|
+
(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")
|
|
441
|
+
);
|
|
442
|
+
if (!adminContext) {
|
|
443
|
+
return {
|
|
444
|
+
allowed: false,
|
|
445
|
+
reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
|
|
446
|
+
riskLevel: "high",
|
|
447
|
+
riskType: "privilege_escalation",
|
|
448
|
+
confidence: 85,
|
|
449
|
+
source: "logic_rule",
|
|
450
|
+
violatedLayers: ["2"]
|
|
451
|
+
};
|
|
452
|
+
}
|
|
872
453
|
}
|
|
873
454
|
}
|
|
874
455
|
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
|
|
@@ -889,9 +470,12 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
889
470
|
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
890
471
|
let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
|
|
891
472
|
if (!isWrite && actionDetail.includes(">")) {
|
|
892
|
-
const
|
|
893
|
-
if (
|
|
894
|
-
|
|
473
|
+
const detailWithoutDevnull = actionDetail.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
474
|
+
if (detailWithoutDevnull.includes(">")) {
|
|
475
|
+
const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
|
|
476
|
+
if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
|
|
477
|
+
isWrite = true;
|
|
478
|
+
}
|
|
895
479
|
}
|
|
896
480
|
}
|
|
897
481
|
if (!isWrite) return null;
|
|
@@ -936,119 +520,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
936
520
|
}
|
|
937
521
|
return null;
|
|
938
522
|
}
|
|
939
|
-
function checkSensitivePath(command, cwd, agentExt) {
|
|
940
|
-
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
941
|
-
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/];
|
|
942
|
-
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
943
|
-
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"]);
|
|
944
|
-
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));
|
|
945
|
-
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
946
|
-
if (command.includes(sp.path.toLowerCase())) {
|
|
947
|
-
if (sp.path === "/dev/random") {
|
|
948
|
-
if (isWriteOpFlag) {
|
|
949
|
-
continue;
|
|
950
|
-
}
|
|
951
|
-
return {
|
|
952
|
-
allowed: false,
|
|
953
|
-
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
954
|
-
riskLevel: sp.riskLevel,
|
|
955
|
-
riskType: "sensitive_path_access",
|
|
956
|
-
confidence: 90,
|
|
957
|
-
source: "logic_rule",
|
|
958
|
-
violatedLayers: ["2"]
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
if (!isWriteOpFlag && !sp.credential) {
|
|
962
|
-
continue;
|
|
963
|
-
}
|
|
964
|
-
return {
|
|
965
|
-
allowed: false,
|
|
966
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
967
|
-
riskLevel: sp.riskLevel,
|
|
968
|
-
riskType: "sensitive_path_access",
|
|
969
|
-
confidence: 90,
|
|
970
|
-
source: "logic_rule",
|
|
971
|
-
violatedLayers: ["2"]
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
976
|
-
const expanded = `${expandedCwd}/${sp.path}`;
|
|
977
|
-
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
978
|
-
if (!isWriteOpFlag && !sp.credential) {
|
|
979
|
-
continue;
|
|
980
|
-
}
|
|
981
|
-
return {
|
|
982
|
-
allowed: false,
|
|
983
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
984
|
-
riskLevel: sp.riskLevel,
|
|
985
|
-
riskType: "sensitive_path_access",
|
|
986
|
-
confidence: 90,
|
|
987
|
-
source: "logic_rule",
|
|
988
|
-
violatedLayers: ["2"]
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
993
|
-
for (const sp of agentExt.extra_sensitive_paths) {
|
|
994
|
-
const spPath = sp.path.toLowerCase();
|
|
995
|
-
const expanded = `${expandedCwd}/${spPath}`;
|
|
996
|
-
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
997
|
-
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
998
|
-
if (denyMode === "deny_read") {
|
|
999
|
-
if (isWriteOpFlag) continue;
|
|
1000
|
-
} else if (denyMode === "deny_write") {
|
|
1001
|
-
if (!isWriteOpFlag) continue;
|
|
1002
|
-
}
|
|
1003
|
-
return {
|
|
1004
|
-
allowed: false,
|
|
1005
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1006
|
-
riskLevel: sp.risk_level,
|
|
1007
|
-
riskType: "sensitive_path_access",
|
|
1008
|
-
confidence: 90,
|
|
1009
|
-
source: "logic_rule",
|
|
1010
|
-
violatedLayers: ["2"]
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
for (const cp of CREDENTIAL_PATHS) {
|
|
1016
|
-
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1017
|
-
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
1018
|
-
if (re.test(command)) {
|
|
1019
|
-
return {
|
|
1020
|
-
allowed: false,
|
|
1021
|
-
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
1022
|
-
riskLevel: cp.riskLevel,
|
|
1023
|
-
riskType: "sensitive_path_access",
|
|
1024
|
-
confidence: 90,
|
|
1025
|
-
source: "logic_rule",
|
|
1026
|
-
violatedLayers: ["2"],
|
|
1027
|
-
isCredential: true
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
1032
|
-
return {
|
|
1033
|
-
allowed: false,
|
|
1034
|
-
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
1035
|
-
riskLevel: "critical",
|
|
1036
|
-
riskType: "sensitive_path_access",
|
|
1037
|
-
confidence: 90,
|
|
1038
|
-
source: "logic_rule",
|
|
1039
|
-
violatedLayers: ["2"]
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
return null;
|
|
1043
|
-
}
|
|
1044
|
-
function extractRemoteHost(command) {
|
|
1045
|
-
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
1046
|
-
return m ? m[1] : null;
|
|
1047
|
-
}
|
|
1048
|
-
function isLocalHost(host) {
|
|
1049
|
-
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
1050
|
-
return localNames.includes(host.replace(/.*@/, ""));
|
|
1051
|
-
}
|
|
1052
523
|
function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
1053
524
|
if (actionType !== "bash") return null;
|
|
1054
525
|
const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
|
|
@@ -1169,11 +640,10 @@ var init_logic_rules = __esm({
|
|
|
1169
640
|
"use strict";
|
|
1170
641
|
init_esm_shims();
|
|
1171
642
|
init_inline_analyzer();
|
|
1172
|
-
init_agents();
|
|
1173
643
|
ROOT_SENSITIVE_PATHS = [
|
|
1174
644
|
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1175
645
|
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1176
|
-
// 注意:/etc/passwd 仅从 L1
|
|
646
|
+
// 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
|
|
1177
647
|
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
1178
648
|
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
1179
649
|
// 系统配置(仅拦截写入)
|
|
@@ -1266,21 +736,21 @@ var init_logic_rules = __esm({
|
|
|
1266
736
|
});
|
|
1267
737
|
|
|
1268
738
|
// src/engine/skill-loader.ts
|
|
1269
|
-
import { readFileSync
|
|
1270
|
-
import { resolve
|
|
1271
|
-
import { fileURLToPath as
|
|
739
|
+
import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
740
|
+
import { resolve, dirname } from "path";
|
|
741
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1272
742
|
function loadAllSkills() {
|
|
1273
743
|
if (cachedSkills) return cachedSkills;
|
|
1274
744
|
cachedSkills = {};
|
|
1275
|
-
const dir =
|
|
1276
|
-
if (!
|
|
745
|
+
const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
746
|
+
if (!existsSync2(dir)) {
|
|
1277
747
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1278
748
|
return cachedSkills;
|
|
1279
749
|
}
|
|
1280
750
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1281
751
|
for (const f of files) {
|
|
1282
752
|
const name = f.replace(".md", "");
|
|
1283
|
-
cachedSkills[name] =
|
|
753
|
+
cachedSkills[name] = readFileSync(resolve(dir, f), "utf-8");
|
|
1284
754
|
}
|
|
1285
755
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1286
756
|
return cachedSkills;
|
|
@@ -1334,15 +804,15 @@ function formatSkillsForPrompt(skills) {
|
|
|
1334
804
|
${s.skillContent}`
|
|
1335
805
|
).join("\n\n---\n\n");
|
|
1336
806
|
}
|
|
1337
|
-
var
|
|
807
|
+
var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1338
808
|
var init_skill_loader = __esm({
|
|
1339
809
|
"src/engine/skill-loader.ts"() {
|
|
1340
810
|
"use strict";
|
|
1341
811
|
init_esm_shims();
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
SKILLS_DIR =
|
|
1345
|
-
SKILLS_DIR_FALLBACK =
|
|
812
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
813
|
+
__dirname2 = dirname(__filename2);
|
|
814
|
+
SKILLS_DIR = resolve(__dirname2, "../skills");
|
|
815
|
+
SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
|
|
1346
816
|
SKILL_KEYWORD_MAP = {
|
|
1347
817
|
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
1348
818
|
"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"],
|
|
@@ -1364,189 +834,22 @@ var init_skill_loader = __esm({
|
|
|
1364
834
|
}
|
|
1365
835
|
});
|
|
1366
836
|
|
|
1367
|
-
// src/engine/token-stats.ts
|
|
1368
|
-
var token_stats_exports = {};
|
|
1369
|
-
__export(token_stats_exports, {
|
|
1370
|
-
getRecentRecords: () => getRecentRecords,
|
|
1371
|
-
getTokenStats: () => getTokenStats,
|
|
1372
|
-
getTokenTrend: () => getTokenTrend,
|
|
1373
|
-
recordTokenUsage: () => recordTokenUsage,
|
|
1374
|
-
resetTokenStats: () => resetTokenStats
|
|
1375
|
-
});
|
|
1376
|
-
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
|
|
1377
|
-
import { join as join3 } from "path";
|
|
1378
|
-
import { homedir as homedir2 } from "os";
|
|
1379
|
-
function getStatsPath() {
|
|
1380
|
-
const envPath = process.env[ENV_STATS_PATH];
|
|
1381
|
-
return envPath || STATS_FILE;
|
|
1382
|
-
}
|
|
1383
|
-
function loadAllRecords(statsPath) {
|
|
1384
|
-
if (!existsSync6(statsPath)) return [];
|
|
1385
|
-
try {
|
|
1386
|
-
const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
|
|
1387
|
-
if (Array.isArray(data)) return data;
|
|
1388
|
-
if (data && Array.isArray(data.records)) return data.records;
|
|
1389
|
-
return [];
|
|
1390
|
-
} catch (e) {
|
|
1391
|
-
return [];
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
function saveRecords(statsPath, records) {
|
|
1395
|
-
try {
|
|
1396
|
-
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
1397
|
-
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
1398
|
-
writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
1399
|
-
} catch (e) {
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
function recordTokenUsage(record) {
|
|
1403
|
-
const statsPath = getStatsPath();
|
|
1404
|
-
const records = loadAllRecords(statsPath);
|
|
1405
|
-
records.push({
|
|
1406
|
-
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1407
|
-
session_id: record.sessionId,
|
|
1408
|
-
step: record.step,
|
|
1409
|
-
model: record.model,
|
|
1410
|
-
prompt_tokens: record.promptTokens,
|
|
1411
|
-
completion_tokens: record.completionTokens,
|
|
1412
|
-
total_tokens: record.totalTokens
|
|
1413
|
-
});
|
|
1414
|
-
if (records.length > MAX_RECORDS) {
|
|
1415
|
-
records.splice(0, records.length - MAX_RECORDS);
|
|
1416
|
-
}
|
|
1417
|
-
saveRecords(statsPath, records);
|
|
1418
|
-
}
|
|
1419
|
-
function filterByDate(records, days = 0, startDate, endDate) {
|
|
1420
|
-
if (startDate || endDate) {
|
|
1421
|
-
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
1422
|
-
let endDt = endDate ? new Date(endDate) : void 0;
|
|
1423
|
-
if (endDate) {
|
|
1424
|
-
const e = new Date(endDate);
|
|
1425
|
-
e.setHours(23, 59, 59, 999);
|
|
1426
|
-
endDt = e;
|
|
1427
|
-
}
|
|
1428
|
-
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
1429
|
-
return records.filter((r) => {
|
|
1430
|
-
try {
|
|
1431
|
-
const ts = new Date(r.timestamp).getTime();
|
|
1432
|
-
return ts >= startDt.getTime() && ts <= endMs;
|
|
1433
|
-
} catch {
|
|
1434
|
-
return true;
|
|
1435
|
-
}
|
|
1436
|
-
});
|
|
1437
|
-
} else if (days > 0) {
|
|
1438
|
-
const cutoff = Date.now() - days * 864e5;
|
|
1439
|
-
return records.filter((r) => {
|
|
1440
|
-
try {
|
|
1441
|
-
return new Date(r.timestamp).getTime() >= cutoff;
|
|
1442
|
-
} catch {
|
|
1443
|
-
return true;
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
return records;
|
|
1448
|
-
}
|
|
1449
|
-
function aggregate(records) {
|
|
1450
|
-
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
1451
|
-
const byStep = {};
|
|
1452
|
-
const byModel = {};
|
|
1453
|
-
const byDate = {};
|
|
1454
|
-
const bucket = (b, key, r) => {
|
|
1455
|
-
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
1456
|
-
e.prompt_tokens += r.prompt_tokens || 0;
|
|
1457
|
-
e.completion_tokens += r.completion_tokens || 0;
|
|
1458
|
-
e.total_tokens += r.total_tokens || 0;
|
|
1459
|
-
e.calls += 1;
|
|
1460
|
-
};
|
|
1461
|
-
for (const r of records) {
|
|
1462
|
-
totalPrompt += r.prompt_tokens || 0;
|
|
1463
|
-
totalCompletion += r.completion_tokens || 0;
|
|
1464
|
-
totalTotal += r.total_tokens || 0;
|
|
1465
|
-
bucket(byStep, r.step || "unknown", r);
|
|
1466
|
-
bucket(byModel, r.model || "unknown", r);
|
|
1467
|
-
let dateKey = "unknown";
|
|
1468
|
-
try {
|
|
1469
|
-
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1470
|
-
} catch {
|
|
1471
|
-
}
|
|
1472
|
-
bucket(byDate, dateKey, r);
|
|
1473
|
-
}
|
|
1474
|
-
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
1475
|
-
const sortedByDate = {};
|
|
1476
|
-
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
1477
|
-
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
1478
|
-
}
|
|
1479
|
-
function getTokenStats(days = 0, startDate, endDate) {
|
|
1480
|
-
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1481
|
-
const a = aggregate(records);
|
|
1482
|
-
return {
|
|
1483
|
-
total_prompt_tokens: a.totalPrompt,
|
|
1484
|
-
total_completion_tokens: a.totalCompletion,
|
|
1485
|
-
total_tokens: a.totalTotal,
|
|
1486
|
-
total_calls: records.length,
|
|
1487
|
-
by_step: a.byStep,
|
|
1488
|
-
by_model: a.byModel,
|
|
1489
|
-
by_date: a.byDate
|
|
1490
|
-
};
|
|
1491
|
-
}
|
|
1492
|
-
function getRecentRecords(limit = 20) {
|
|
1493
|
-
const records = loadAllRecords(getStatsPath());
|
|
1494
|
-
return records.slice(-limit);
|
|
1495
|
-
}
|
|
1496
|
-
function getTokenTrend(days = 0, startDate, endDate) {
|
|
1497
|
-
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1498
|
-
const series = {};
|
|
1499
|
-
const datesSet = /* @__PURE__ */ new Set();
|
|
1500
|
-
for (const r of records) {
|
|
1501
|
-
let dateKey;
|
|
1502
|
-
try {
|
|
1503
|
-
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1504
|
-
} catch {
|
|
1505
|
-
continue;
|
|
1506
|
-
}
|
|
1507
|
-
const model = r.model || "unknown";
|
|
1508
|
-
const tokens = r.total_tokens || 0;
|
|
1509
|
-
datesSet.add(dateKey);
|
|
1510
|
-
series[model] = series[model] || {};
|
|
1511
|
-
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
1512
|
-
}
|
|
1513
|
-
const dates = Array.from(datesSet).sort();
|
|
1514
|
-
const models = Object.keys(series).sort();
|
|
1515
|
-
return { dates, models, series };
|
|
1516
|
-
}
|
|
1517
|
-
function resetTokenStats() {
|
|
1518
|
-
const statsPath = getStatsPath();
|
|
1519
|
-
if (existsSync6(statsPath)) {
|
|
1520
|
-
try {
|
|
1521
|
-
unlinkSync(statsPath);
|
|
1522
|
-
} catch {
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
1527
|
-
var init_token_stats = __esm({
|
|
1528
|
-
"src/engine/token-stats.ts"() {
|
|
1529
|
-
"use strict";
|
|
1530
|
-
init_esm_shims();
|
|
1531
|
-
STATS_DIR = join3(homedir2(), ".config", "agentmoss");
|
|
1532
|
-
STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
|
|
1533
|
-
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
1534
|
-
MAX_RECORDS = 1e4;
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
|
|
1538
837
|
// src/infra/runtime-config.ts
|
|
1539
838
|
var runtime_config_exports = {};
|
|
1540
839
|
__export(runtime_config_exports, {
|
|
1541
840
|
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
841
|
+
addAgentRule: () => addAgentRule,
|
|
1542
842
|
addCustomRule: () => addCustomRule,
|
|
1543
843
|
addCustomSkill: () => addCustomSkill,
|
|
844
|
+
deleteAgentRule: () => deleteAgentRule,
|
|
1544
845
|
deleteCustomRule: () => deleteCustomRule,
|
|
1545
846
|
deleteCustomSkill: () => deleteCustomSkill,
|
|
1546
847
|
filterEnabledRules: () => filterEnabledRules,
|
|
1547
848
|
generateSourceDefaults: () => generateSourceDefaults,
|
|
849
|
+
getAgentRules: () => getAgentRules,
|
|
1548
850
|
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
1549
851
|
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
852
|
+
getEnabledAgentExtensions: () => getEnabledAgentExtensions,
|
|
1550
853
|
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
1551
854
|
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
1552
855
|
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
@@ -1568,6 +871,8 @@ __export(runtime_config_exports, {
|
|
|
1568
871
|
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
1569
872
|
mergeConfig: () => mergeConfig,
|
|
1570
873
|
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
874
|
+
updateAgentCategoryEnabled: () => updateAgentCategoryEnabled,
|
|
875
|
+
updateAgentRuleEnabled: () => updateAgentRuleEnabled,
|
|
1571
876
|
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
1572
877
|
updateLayerEnabled: () => updateLayerEnabled,
|
|
1573
878
|
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
@@ -1576,9 +881,11 @@ __export(runtime_config_exports, {
|
|
|
1576
881
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1577
882
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1578
883
|
});
|
|
1579
|
-
import { readFileSync as
|
|
1580
|
-
import { join
|
|
1581
|
-
import { homedir
|
|
884
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
885
|
+
import { join } from "path";
|
|
886
|
+
import { homedir } from "os";
|
|
887
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
888
|
+
import { dirname as dirname2 } from "path";
|
|
1582
889
|
function getRuntimeConfigPath() {
|
|
1583
890
|
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
1584
891
|
if (envPath) return envPath;
|
|
@@ -1587,8 +894,8 @@ function getRuntimeConfigPath() {
|
|
|
1587
894
|
function saveRuntimeConfig(config) {
|
|
1588
895
|
const p = getRuntimeConfigPath();
|
|
1589
896
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1590
|
-
if (dir && !
|
|
1591
|
-
|
|
897
|
+
if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
898
|
+
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1592
899
|
}
|
|
1593
900
|
function emptyRuntimeConfig() {
|
|
1594
901
|
return {
|
|
@@ -1596,12 +903,13 @@ function emptyRuntimeConfig() {
|
|
|
1596
903
|
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
1597
904
|
L1_rules: {},
|
|
1598
905
|
L2_rules: {},
|
|
1599
|
-
L3_skills: {}
|
|
906
|
+
L3_skills: {},
|
|
907
|
+
agent_rules: {}
|
|
1600
908
|
};
|
|
1601
909
|
}
|
|
1602
910
|
function loadRuntimeConfig() {
|
|
1603
911
|
const p = getRuntimeConfigPath();
|
|
1604
|
-
if (!
|
|
912
|
+
if (!existsSync3(p)) {
|
|
1605
913
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1606
914
|
try {
|
|
1607
915
|
saveRuntimeConfig(seeded);
|
|
@@ -1610,7 +918,7 @@ function loadRuntimeConfig() {
|
|
|
1610
918
|
return seeded;
|
|
1611
919
|
}
|
|
1612
920
|
try {
|
|
1613
|
-
const data = JSON.parse(
|
|
921
|
+
const data = JSON.parse(readFileSync2(p, "utf-8"));
|
|
1614
922
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1615
923
|
return {
|
|
1616
924
|
version: data.version || 1,
|
|
@@ -1621,7 +929,8 @@ function loadRuntimeConfig() {
|
|
|
1621
929
|
},
|
|
1622
930
|
L1_rules: merged.L1_rules || {},
|
|
1623
931
|
L2_rules: merged.L2_rules || {},
|
|
1624
|
-
L3_skills: merged.L3_skills || {}
|
|
932
|
+
L3_skills: merged.L3_skills || {},
|
|
933
|
+
agent_rules: merged.agent_rules || {}
|
|
1625
934
|
};
|
|
1626
935
|
} catch {
|
|
1627
936
|
return emptyRuntimeConfig();
|
|
@@ -1664,7 +973,10 @@ function getEnabledL1CommandPatterns(runtime) {
|
|
|
1664
973
|
}));
|
|
1665
974
|
}
|
|
1666
975
|
function getEnabledL2SensitivePaths(runtime) {
|
|
1667
|
-
|
|
976
|
+
let cat = runtime.L1_rules?.sensitive_path_access;
|
|
977
|
+
if (!cat || !cat.rules || cat.rules.length === 0) {
|
|
978
|
+
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
979
|
+
}
|
|
1668
980
|
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
1669
981
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1670
982
|
if (r.credential) out.credential = true;
|
|
@@ -1674,9 +986,9 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1674
986
|
}
|
|
1675
987
|
function hasRuntimeConfig() {
|
|
1676
988
|
const p = getRuntimeConfigPath();
|
|
1677
|
-
if (!
|
|
989
|
+
if (!existsSync3(p)) return false;
|
|
1678
990
|
try {
|
|
1679
|
-
JSON.parse(
|
|
991
|
+
JSON.parse(readFileSync2(p, "utf-8"));
|
|
1680
992
|
return true;
|
|
1681
993
|
} catch {
|
|
1682
994
|
return false;
|
|
@@ -1690,22 +1002,24 @@ function getDisabledL2RulesWithSkipL3() {
|
|
|
1690
1002
|
return {};
|
|
1691
1003
|
}
|
|
1692
1004
|
const result = {};
|
|
1693
|
-
const
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
if (
|
|
1700
|
-
if (
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1005
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
1006
|
+
const layer = runtime[layerKey] || {};
|
|
1007
|
+
for (const [catName, cat] of Object.entries(layer)) {
|
|
1008
|
+
if (!cat.category_enabled) continue;
|
|
1009
|
+
const items = [];
|
|
1010
|
+
for (const r of cat.rules || []) {
|
|
1011
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
1012
|
+
if (catName === "sensitive_path_access") {
|
|
1013
|
+
if (r.path) items.push(r.path);
|
|
1014
|
+
} else if (catName === "intent_consistency") {
|
|
1015
|
+
items.push(...r.intent_keywords || []);
|
|
1016
|
+
} else if (r.pattern) {
|
|
1017
|
+
items.push(r.pattern);
|
|
1018
|
+
}
|
|
1705
1019
|
}
|
|
1706
1020
|
}
|
|
1021
|
+
if (items.length > 0) result[catName] = items;
|
|
1707
1022
|
}
|
|
1708
|
-
if (items.length > 0) result[catName] = items;
|
|
1709
1023
|
}
|
|
1710
1024
|
return result;
|
|
1711
1025
|
}
|
|
@@ -1851,6 +1165,55 @@ function _l3SkillsToRules() {
|
|
|
1851
1165
|
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
1852
1166
|
return out;
|
|
1853
1167
|
}
|
|
1168
|
+
function _agentRulesToDefaults() {
|
|
1169
|
+
const out = {};
|
|
1170
|
+
const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
1171
|
+
if (!existsSync3(agentsDir)) return out;
|
|
1172
|
+
let entries;
|
|
1173
|
+
try {
|
|
1174
|
+
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
1175
|
+
} catch {
|
|
1176
|
+
return out;
|
|
1177
|
+
}
|
|
1178
|
+
for (const agentId of entries) {
|
|
1179
|
+
const rulesFile = join(agentsDir, agentId, "rules.json");
|
|
1180
|
+
if (!existsSync3(rulesFile)) continue;
|
|
1181
|
+
let data;
|
|
1182
|
+
try {
|
|
1183
|
+
data = JSON.parse(readFileSync2(rulesFile, "utf-8"));
|
|
1184
|
+
} catch {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
out[agentId] = {
|
|
1188
|
+
builtin_safe_action_types: {
|
|
1189
|
+
category_enabled: true,
|
|
1190
|
+
category_desc: "agent \u5B8C\u5168\u5B89\u5168\u64CD\u4F5C\u767D\u540D\u5355\uFF08\u524D\u7F00\u5C3E\u968F - \u6216\u7CBE\u786E\u540D\uFF0C\u547D\u4E2D\u8DF3 L2+L3\uFF09",
|
|
1191
|
+
rules: (data.builtin_safe_action_types || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1192
|
+
},
|
|
1193
|
+
builtin_safe_skills: {
|
|
1194
|
+
category_enabled: true,
|
|
1195
|
+
category_desc: "agent \u4FE1\u4EFB\u7684\u5185\u7F6E Skill\uFF08\u547D\u4E2D\u8DF3 L2+L3\uFF09",
|
|
1196
|
+
rules: (data.builtin_safe_skills || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1197
|
+
},
|
|
1198
|
+
extra_command_patterns: {
|
|
1199
|
+
category_enabled: true,
|
|
1200
|
+
category_desc: "agent \u4E13\u5C5E\u5371\u9669\u547D\u4EE4\u6B63\u5219\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1201
|
+
rules: (data.extra_command_patterns || []).map((p) => ({ ...p, enabled: true, builtin: true }))
|
|
1202
|
+
},
|
|
1203
|
+
extra_injection_keywords: {
|
|
1204
|
+
category_enabled: true,
|
|
1205
|
+
category_desc: "agent \u4E13\u5C5E\u6CE8\u5165\u5173\u952E\u8BCD\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1206
|
+
rules: (data.extra_injection_keywords || []).map((k) => ({ ...k, enabled: true, builtin: true }))
|
|
1207
|
+
},
|
|
1208
|
+
extra_sensitive_paths: {
|
|
1209
|
+
category_enabled: true,
|
|
1210
|
+
category_desc: "agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1211
|
+
rules: (data.extra_sensitive_paths || []).map((sp) => ({ ...sp, enabled: true, builtin: true }))
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
return out;
|
|
1216
|
+
}
|
|
1854
1217
|
function generateSourceDefaults() {
|
|
1855
1218
|
return {
|
|
1856
1219
|
version: 1,
|
|
@@ -1866,15 +1229,14 @@ function generateSourceDefaults() {
|
|
|
1866
1229
|
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
1867
1230
|
rules: _l1InjectionKeywordsToRules()
|
|
1868
1231
|
},
|
|
1869
|
-
user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
|
|
1870
|
-
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1871
|
-
},
|
|
1872
|
-
L2_rules: {
|
|
1873
1232
|
sensitive_path_access: {
|
|
1874
1233
|
category_enabled: true,
|
|
1875
|
-
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
|
|
1234
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
|
|
1876
1235
|
rules: _l2SensitivePathsToRules()
|
|
1877
1236
|
},
|
|
1237
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1238
|
+
},
|
|
1239
|
+
L2_rules: {
|
|
1878
1240
|
intent_consistency: {
|
|
1879
1241
|
category_enabled: true,
|
|
1880
1242
|
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
@@ -1891,9 +1253,10 @@ function generateSourceDefaults() {
|
|
|
1891
1253
|
rules: _l2UserDeletionPatternsToRules()
|
|
1892
1254
|
},
|
|
1893
1255
|
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
1894
|
-
|
|
1256
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1895
1257
|
},
|
|
1896
|
-
L3_skills: _l3SkillsToRules()
|
|
1258
|
+
L3_skills: _l3SkillsToRules(),
|
|
1259
|
+
agent_rules: _agentRulesToDefaults()
|
|
1897
1260
|
};
|
|
1898
1261
|
}
|
|
1899
1262
|
function _ruleFingerprint(rule) {
|
|
@@ -1906,6 +1269,7 @@ function _ruleFingerprint(rule) {
|
|
|
1906
1269
|
if (rule.path) return JSON.stringify([rule.path]);
|
|
1907
1270
|
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1908
1271
|
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1272
|
+
if (rule.value) return JSON.stringify([rule.value]);
|
|
1909
1273
|
return JSON.stringify([rule.id || ""]);
|
|
1910
1274
|
}
|
|
1911
1275
|
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
@@ -1959,6 +1323,10 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
1959
1323
|
}
|
|
1960
1324
|
}
|
|
1961
1325
|
}
|
|
1326
|
+
for (const catName of Object.keys(userLayer)) {
|
|
1327
|
+
if (catName === "user_custom") continue;
|
|
1328
|
+
if (!(catName in srcLayer)) delete userLayer[catName];
|
|
1329
|
+
}
|
|
1962
1330
|
}
|
|
1963
1331
|
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
1964
1332
|
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
@@ -1978,6 +1346,50 @@ function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
|
1978
1346
|
}
|
|
1979
1347
|
}
|
|
1980
1348
|
}
|
|
1349
|
+
function _mergeAgentRules(userCfg, srcDefaults) {
|
|
1350
|
+
const userAgent = userCfg.agent_rules || (userCfg.agent_rules = {});
|
|
1351
|
+
const srcAgent = srcDefaults.agent_rules || {};
|
|
1352
|
+
for (const [agentId, srcCats] of Object.entries(srcAgent)) {
|
|
1353
|
+
if (!(agentId in userAgent)) {
|
|
1354
|
+
userAgent[agentId] = JSON.parse(JSON.stringify(srcCats));
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
const userCats = userAgent[agentId];
|
|
1358
|
+
for (const [catName, srcCat] of Object.entries(srcCats)) {
|
|
1359
|
+
if (!(catName in userCats)) {
|
|
1360
|
+
userCats[catName] = JSON.parse(JSON.stringify(srcCat));
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
const userCat = userCats[catName];
|
|
1364
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1365
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1366
|
+
const srcRules = srcCat.rules || [];
|
|
1367
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
1368
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
1369
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
1370
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
1371
|
+
for (const srcR of srcRules) {
|
|
1372
|
+
const fp = _ruleFingerprint(srcR);
|
|
1373
|
+
srcFps.add(fp);
|
|
1374
|
+
if (userByFp.has(fp)) {
|
|
1375
|
+
const existing = userByFp.get(fp);
|
|
1376
|
+
const userEnabled = existing.enabled;
|
|
1377
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1378
|
+
Object.assign(existing, srcR);
|
|
1379
|
+
existing.enabled = userEnabled;
|
|
1380
|
+
} else {
|
|
1381
|
+
userRules.push({ ...srcR, enabled: true });
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
for (const r of userRules) {
|
|
1385
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
1386
|
+
r.source_removed = true;
|
|
1387
|
+
r.enabled = false;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1981
1393
|
function mergeConfig(userCfg, srcDefaults) {
|
|
1982
1394
|
const cfg = userCfg;
|
|
1983
1395
|
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
@@ -1985,15 +1397,47 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
1985
1397
|
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
1986
1398
|
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
1987
1399
|
}
|
|
1400
|
+
_migrateCategoriesAcrossLayers(
|
|
1401
|
+
cfg,
|
|
1402
|
+
"L2_rules",
|
|
1403
|
+
"L1_rules",
|
|
1404
|
+
["sensitive_path_access"]
|
|
1405
|
+
);
|
|
1988
1406
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1989
1407
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
1990
1408
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1409
|
+
_mergeAgentRules(cfg, srcDefaults);
|
|
1991
1410
|
return cfg;
|
|
1992
1411
|
}
|
|
1993
|
-
function
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
|
|
1412
|
+
function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
1413
|
+
const src = cfg[srcLayer] || {};
|
|
1414
|
+
const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
|
|
1415
|
+
for (const catName of catNames) {
|
|
1416
|
+
if (!(catName in src)) continue;
|
|
1417
|
+
const srcCat = src[catName];
|
|
1418
|
+
if (!dst[catName]) {
|
|
1419
|
+
dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1420
|
+
}
|
|
1421
|
+
const dstCat = dst[catName];
|
|
1422
|
+
if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
|
|
1423
|
+
if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
|
|
1424
|
+
if (srcCat.rules && srcCat.rules.length > 0) {
|
|
1425
|
+
const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
|
|
1426
|
+
for (const r of srcCat.rules) {
|
|
1427
|
+
const fp = _ruleFingerprint(r);
|
|
1428
|
+
if (!dstFps.has(fp)) {
|
|
1429
|
+
(dstCat.rules = dstCat.rules || []).push(r);
|
|
1430
|
+
dstFps.add(fp);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
delete src[catName];
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
1438
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
1439
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1440
|
+
keyword: r.keyword || "",
|
|
1997
1441
|
risk_level: r.risk_level || "",
|
|
1998
1442
|
lang: r.lang || "en"
|
|
1999
1443
|
}));
|
|
@@ -2069,162 +1513,898 @@ function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
|
|
|
2069
1513
|
if (!hasRuntimeConfig()) {
|
|
2070
1514
|
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
2071
1515
|
}
|
|
2072
|
-
return [];
|
|
1516
|
+
return [];
|
|
1517
|
+
}
|
|
1518
|
+
function _validateSkillId(skillId) {
|
|
1519
|
+
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
1520
|
+
throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
function updateLayerEnabled(layerKey, enabled) {
|
|
1524
|
+
const runtime = loadRuntimeConfig();
|
|
1525
|
+
runtime.layers[layerKey] = enabled;
|
|
1526
|
+
saveRuntimeConfig(runtime);
|
|
1527
|
+
return runtime;
|
|
1528
|
+
}
|
|
1529
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
1530
|
+
const runtime = loadRuntimeConfig();
|
|
1531
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1532
|
+
if (r.id === ruleId) {
|
|
1533
|
+
r.enabled = enabled;
|
|
1534
|
+
break;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
saveRuntimeConfig(runtime);
|
|
1538
|
+
return runtime;
|
|
1539
|
+
}
|
|
1540
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
1541
|
+
const runtime = loadRuntimeConfig();
|
|
1542
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1543
|
+
if (r.id === ruleId) {
|
|
1544
|
+
r.deny_mode = denyMode;
|
|
1545
|
+
_syncDenyModeFields(r);
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
saveRuntimeConfig(runtime);
|
|
1550
|
+
return runtime;
|
|
1551
|
+
}
|
|
1552
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
1553
|
+
const runtime = loadRuntimeConfig();
|
|
1554
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1555
|
+
if (r.id === ruleId) {
|
|
1556
|
+
r.skip_l3_on_disabled = skipL3;
|
|
1557
|
+
break;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
saveRuntimeConfig(runtime);
|
|
1561
|
+
return runtime;
|
|
1562
|
+
}
|
|
1563
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
1564
|
+
const runtime = loadRuntimeConfig();
|
|
1565
|
+
const cat = runtime[layer]?.[category];
|
|
1566
|
+
if (cat) cat.category_enabled = enabled;
|
|
1567
|
+
saveRuntimeConfig(runtime);
|
|
1568
|
+
return runtime;
|
|
1569
|
+
}
|
|
1570
|
+
function addCustomRule(layer, category, rule) {
|
|
1571
|
+
const runtime = loadRuntimeConfig();
|
|
1572
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
1573
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
1574
|
+
const rules = cat.rules || (cat.rules = []);
|
|
1575
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
1576
|
+
if (!newRule.id) {
|
|
1577
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
1578
|
+
}
|
|
1579
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
1580
|
+
rules.push(newRule);
|
|
1581
|
+
saveRuntimeConfig(runtime);
|
|
1582
|
+
return runtime;
|
|
1583
|
+
}
|
|
1584
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
1585
|
+
const runtime = loadRuntimeConfig();
|
|
1586
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
1587
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
1588
|
+
if (idx >= 0) {
|
|
1589
|
+
const r = rules[idx];
|
|
1590
|
+
if (r.builtin && !r.source_removed) return null;
|
|
1591
|
+
rules.splice(idx, 1);
|
|
1592
|
+
saveRuntimeConfig(runtime);
|
|
1593
|
+
}
|
|
1594
|
+
return runtime;
|
|
1595
|
+
}
|
|
1596
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
1597
|
+
_validateSkillId(skillId);
|
|
1598
|
+
const runtime = loadRuntimeConfig();
|
|
1599
|
+
const skillsDir = join(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
1600
|
+
try {
|
|
1601
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
1602
|
+
} catch {
|
|
1603
|
+
}
|
|
1604
|
+
writeFileSync(join(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
1605
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
1606
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
1607
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
1608
|
+
saveRuntimeConfig(runtime);
|
|
1609
|
+
return runtime;
|
|
1610
|
+
}
|
|
1611
|
+
function deleteCustomSkill(skillId, category) {
|
|
1612
|
+
_validateSkillId(skillId);
|
|
1613
|
+
const runtime = loadRuntimeConfig();
|
|
1614
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
1615
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
1616
|
+
if (idx >= 0) {
|
|
1617
|
+
if (skills[idx].builtin) return null;
|
|
1618
|
+
skills.splice(idx, 1);
|
|
1619
|
+
const skillPath = join(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
1620
|
+
try {
|
|
1621
|
+
if (existsSync3(skillPath)) unlinkSync(skillPath);
|
|
1622
|
+
} catch {
|
|
1623
|
+
}
|
|
1624
|
+
saveRuntimeConfig(runtime);
|
|
1625
|
+
}
|
|
1626
|
+
return runtime;
|
|
1627
|
+
}
|
|
1628
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
1629
|
+
const runtime = loadRuntimeConfig();
|
|
1630
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
1631
|
+
for (const s of catData.skills || []) {
|
|
1632
|
+
if (s.id === skillId) {
|
|
1633
|
+
s.enabled = enabled;
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
saveRuntimeConfig(runtime);
|
|
1639
|
+
return runtime;
|
|
1640
|
+
}
|
|
1641
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
1642
|
+
const runtime = loadRuntimeConfig();
|
|
1643
|
+
const cat = runtime.L3_skills?.[category];
|
|
1644
|
+
if (cat) cat.category_enabled = enabled;
|
|
1645
|
+
saveRuntimeConfig(runtime);
|
|
1646
|
+
return runtime;
|
|
1647
|
+
}
|
|
1648
|
+
function getProfileOrDetect() {
|
|
1649
|
+
return null;
|
|
1650
|
+
}
|
|
1651
|
+
function getAgentRules(runtime) {
|
|
1652
|
+
return runtime.agent_rules || {};
|
|
1653
|
+
}
|
|
1654
|
+
function _agentCategoryEnabledRules(cat) {
|
|
1655
|
+
if (!cat || cat.category_enabled === false) return [];
|
|
1656
|
+
return (cat.rules || []).filter((r) => r.enabled !== false);
|
|
1657
|
+
}
|
|
1658
|
+
function getEnabledAgentExtensions(runtime, agentId) {
|
|
1659
|
+
const agentData = runtime.agent_rules?.[agentId];
|
|
1660
|
+
if (!agentData) return null;
|
|
1661
|
+
const safeAction = _agentCategoryEnabledRules(agentData.builtin_safe_action_types);
|
|
1662
|
+
const safeSkills = _agentCategoryEnabledRules(agentData.builtin_safe_skills);
|
|
1663
|
+
const cmdPatterns = _agentCategoryEnabledRules(agentData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
1664
|
+
pattern: r.pattern,
|
|
1665
|
+
risk_level: r.risk_level || "",
|
|
1666
|
+
risk_type: r.risk_type || "",
|
|
1667
|
+
reason: r.reason || ""
|
|
1668
|
+
}));
|
|
1669
|
+
const injKeywords = _agentCategoryEnabledRules(agentData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
1670
|
+
keyword: r.keyword,
|
|
1671
|
+
risk_level: r.risk_level || "",
|
|
1672
|
+
lang: r.lang || "en"
|
|
1673
|
+
}));
|
|
1674
|
+
const sensitivePaths = _agentCategoryEnabledRules(agentData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
1675
|
+
path: r.path,
|
|
1676
|
+
risk_level: r.risk_level || "",
|
|
1677
|
+
desc: r.desc || "",
|
|
1678
|
+
credential: r.credential,
|
|
1679
|
+
read_only: r.read_only,
|
|
1680
|
+
deny_mode: r.deny_mode
|
|
1681
|
+
}));
|
|
1682
|
+
return {
|
|
1683
|
+
agent_id: agentId,
|
|
1684
|
+
description: "",
|
|
1685
|
+
extra_command_patterns: cmdPatterns,
|
|
1686
|
+
extra_injection_keywords: injKeywords,
|
|
1687
|
+
extra_sensitive_paths: sensitivePaths,
|
|
1688
|
+
builtin_safe_skills: safeSkills.filter((r) => r.value).map((r) => r.value),
|
|
1689
|
+
builtin_safe_action_types: safeAction.filter((r) => r.value).map((r) => r.value),
|
|
1690
|
+
extra_skills: []
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
|
|
1694
|
+
const runtime = loadRuntimeConfig();
|
|
1695
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1696
|
+
if (cat) {
|
|
1697
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1698
|
+
for (const r of cat.rules || []) {
|
|
1699
|
+
if (_ruleFingerprint(r) === targetFp) {
|
|
1700
|
+
r.enabled = enabled;
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
saveRuntimeConfig(runtime);
|
|
1706
|
+
return runtime;
|
|
1707
|
+
}
|
|
1708
|
+
function addAgentRule(agentId, category, rule) {
|
|
1709
|
+
const runtime = loadRuntimeConfig();
|
|
1710
|
+
if (!runtime.agent_rules) runtime.agent_rules = {};
|
|
1711
|
+
if (!runtime.agent_rules[agentId]) runtime.agent_rules[agentId] = {};
|
|
1712
|
+
const cats = runtime.agent_rules[agentId];
|
|
1713
|
+
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1714
|
+
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
1715
|
+
saveRuntimeConfig(runtime);
|
|
1716
|
+
return runtime;
|
|
1717
|
+
}
|
|
1718
|
+
function deleteAgentRule(agentId, category, ruleFp) {
|
|
1719
|
+
const runtime = loadRuntimeConfig();
|
|
1720
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1721
|
+
if (cat) {
|
|
1722
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1723
|
+
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
1724
|
+
}
|
|
1725
|
+
saveRuntimeConfig(runtime);
|
|
1726
|
+
return runtime;
|
|
1727
|
+
}
|
|
1728
|
+
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
1729
|
+
const runtime = loadRuntimeConfig();
|
|
1730
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1731
|
+
if (cat) cat.category_enabled = enabled;
|
|
1732
|
+
saveRuntimeConfig(runtime);
|
|
1733
|
+
return runtime;
|
|
1734
|
+
}
|
|
1735
|
+
var __dirname3, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, SKILL_ID_RE;
|
|
1736
|
+
var init_runtime_config = __esm({
|
|
1737
|
+
"src/infra/runtime-config.ts"() {
|
|
1738
|
+
"use strict";
|
|
1739
|
+
init_esm_shims();
|
|
1740
|
+
init_heuristic();
|
|
1741
|
+
init_logic_rules();
|
|
1742
|
+
init_skill_loader();
|
|
1743
|
+
__dirname3 = dirname2(fileURLToPath3(import.meta.url));
|
|
1744
|
+
RUNTIME_CONFIG_DIR = join(homedir(), ".config", "agentmoss");
|
|
1745
|
+
RUNTIME_CONFIG_PATH = join(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
1746
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
1747
|
+
LAYER_DISABLE_ENV = {
|
|
1748
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
1749
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
1750
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
1751
|
+
};
|
|
1752
|
+
DENY_MODE_LABELS = {
|
|
1753
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
1754
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
1755
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
1756
|
+
};
|
|
1757
|
+
_AGENTS_DIR_PROD = join(__dirname3, "agents");
|
|
1758
|
+
_AGENTS_DIR_DEV = join(__dirname3, "..", "..", "..", "agent_moss", "agents");
|
|
1759
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
// src/agents/index.ts
|
|
1764
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1765
|
+
import { resolve as resolve2, dirname as dirname3 } from "path";
|
|
1766
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1767
|
+
function resolveAgentsDir() {
|
|
1768
|
+
if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
1769
|
+
if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
1770
|
+
return AGENTS_DIR_PROD;
|
|
1771
|
+
}
|
|
1772
|
+
function getAgentExtensions(agentId) {
|
|
1773
|
+
if (!agentId) return null;
|
|
1774
|
+
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
1775
|
+
let ext = null;
|
|
1776
|
+
try {
|
|
1777
|
+
const { hasRuntimeConfig: hasRuntimeConfig2, loadRuntimeConfig: loadRuntimeConfig2, getEnabledAgentExtensions: getEnabledAgentExtensions2 } = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
1778
|
+
if (hasRuntimeConfig2()) {
|
|
1779
|
+
ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
|
|
1780
|
+
}
|
|
1781
|
+
} catch {
|
|
1782
|
+
}
|
|
1783
|
+
if (ext === null) {
|
|
1784
|
+
const agentsDir = resolveAgentsDir();
|
|
1785
|
+
const rulesFile = resolve2(agentsDir, agentId, "rules.json");
|
|
1786
|
+
if (!existsSync4(rulesFile)) {
|
|
1787
|
+
cache.set(agentId, null);
|
|
1788
|
+
return null;
|
|
1789
|
+
}
|
|
1790
|
+
try {
|
|
1791
|
+
const raw = readFileSync3(rulesFile, "utf-8");
|
|
1792
|
+
ext = JSON.parse(raw);
|
|
1793
|
+
if (!ext.agent_id) {
|
|
1794
|
+
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
1795
|
+
cache.set(agentId, null);
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
} catch (e) {
|
|
1799
|
+
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
1800
|
+
cache.set(agentId, null);
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
cache.set(agentId, ext);
|
|
1805
|
+
console.log(
|
|
1806
|
+
`[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, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
|
|
1807
|
+
);
|
|
1808
|
+
return ext;
|
|
1809
|
+
}
|
|
1810
|
+
var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
1811
|
+
var init_agents = __esm({
|
|
1812
|
+
"src/agents/index.ts"() {
|
|
1813
|
+
"use strict";
|
|
1814
|
+
init_esm_shims();
|
|
1815
|
+
__filename3 = fileURLToPath4(import.meta.url);
|
|
1816
|
+
__dirname4 = dirname3(__filename3);
|
|
1817
|
+
AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
|
|
1818
|
+
AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
|
|
1819
|
+
cache = /* @__PURE__ */ new Map();
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1823
|
+
// src/engine/heuristic.ts
|
|
1824
|
+
function runHeuristicCheck(input) {
|
|
1825
|
+
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
1826
|
+
const matched = [];
|
|
1827
|
+
let highestSeverity = "low";
|
|
1828
|
+
let highestRiskType = "";
|
|
1829
|
+
const userRules = getCustomRules();
|
|
1830
|
+
for (const rule of userRules) {
|
|
1831
|
+
try {
|
|
1832
|
+
const re = new RegExp(rule.pattern);
|
|
1833
|
+
if (re.test(command)) {
|
|
1834
|
+
if (rule.action === "Deny") {
|
|
1835
|
+
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
1836
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1837
|
+
highestSeverity = rule.severity;
|
|
1838
|
+
highestRiskType = "custom_rule";
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
} catch {
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
let topRiskType = "";
|
|
1846
|
+
for (const rule of DANGEROUS_PATTERNS) {
|
|
1847
|
+
if (rule.pattern.test(command)) {
|
|
1848
|
+
matched.push(rule.description);
|
|
1849
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1850
|
+
highestSeverity = rule.severity;
|
|
1851
|
+
topRiskType = rule.riskType;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
highestRiskType = topRiskType || highestRiskType;
|
|
1856
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
1857
|
+
if (agentExt?.extra_command_patterns) {
|
|
1858
|
+
for (const rule of agentExt.extra_command_patterns) {
|
|
1859
|
+
try {
|
|
1860
|
+
const re = new RegExp(rule.pattern, "i");
|
|
1861
|
+
if (re.test(command)) {
|
|
1862
|
+
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
1863
|
+
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
1864
|
+
highestSeverity = rule.risk_level;
|
|
1865
|
+
highestRiskType = rule.risk_type;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
} catch {
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
1873
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
1874
|
+
if (injectionHits.length > 0) {
|
|
1875
|
+
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
1876
|
+
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
1877
|
+
if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
|
|
1878
|
+
highestSeverity = topHit.riskLevel;
|
|
1879
|
+
highestRiskType = "prompt_injection";
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
const actionTypeL = input.aNext.actionType.toLowerCase();
|
|
1883
|
+
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
1884
|
+
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
1885
|
+
if (!isExcludedType) {
|
|
1886
|
+
const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
1887
|
+
if (pathResult) return pathResult;
|
|
1888
|
+
}
|
|
1889
|
+
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
1890
|
+
if (dangerousResult) return dangerousResult;
|
|
1891
|
+
const lateralResult = checkLateralMovement(detailL);
|
|
1892
|
+
if (lateralResult) return lateralResult;
|
|
1893
|
+
if (matched.length > 0) {
|
|
1894
|
+
return {
|
|
1895
|
+
allowed: false,
|
|
1896
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
1897
|
+
riskLevel: highestSeverity,
|
|
1898
|
+
riskType: highestRiskType,
|
|
1899
|
+
confidence: 95,
|
|
1900
|
+
source: "heuristic",
|
|
1901
|
+
violatedLayers: ["1.1"]
|
|
1902
|
+
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
function checkInjectionKeywords(text, extraKeywords) {
|
|
1908
|
+
const hits = [];
|
|
1909
|
+
for (const entry of INJECTION_KEYWORDS) {
|
|
1910
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1911
|
+
hits.push(entry);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
if (extraKeywords) {
|
|
1915
|
+
for (const entry of extraKeywords) {
|
|
1916
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1917
|
+
hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return hits;
|
|
1922
|
+
}
|
|
1923
|
+
function isInlineScriptCommand(actionDetail) {
|
|
1924
|
+
const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
1925
|
+
return re.test(actionDetail);
|
|
1926
|
+
}
|
|
1927
|
+
function severityRank(s) {
|
|
1928
|
+
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
1929
|
+
return ranks[s] || 0;
|
|
1930
|
+
}
|
|
1931
|
+
function checkSensitivePath(command, cwd, agentExt) {
|
|
1932
|
+
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
1933
|
+
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/];
|
|
1934
|
+
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
1935
|
+
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"]);
|
|
1936
|
+
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));
|
|
1937
|
+
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
1938
|
+
if (command.includes(sp.path.toLowerCase())) {
|
|
1939
|
+
if (sp.path === "/dev/random") {
|
|
1940
|
+
if (isWriteOpFlag) {
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
return {
|
|
1944
|
+
allowed: false,
|
|
1945
|
+
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1946
|
+
riskLevel: sp.riskLevel,
|
|
1947
|
+
riskType: "file_access",
|
|
1948
|
+
confidence: 90,
|
|
1949
|
+
source: "heuristic",
|
|
1950
|
+
violatedLayers: ["1.1"]
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
allowed: false,
|
|
1958
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1959
|
+
riskLevel: sp.riskLevel,
|
|
1960
|
+
riskType: "file_access",
|
|
1961
|
+
confidence: 90,
|
|
1962
|
+
source: "heuristic",
|
|
1963
|
+
violatedLayers: ["1.1"],
|
|
1964
|
+
isCredential: !!sp.credential
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
1969
|
+
const expanded = `${expandedCwd}/${sp.path}`;
|
|
1970
|
+
const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1971
|
+
const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1972
|
+
if (pathRe.test(command) || expandedRe.test(command)) {
|
|
1973
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
return {
|
|
1977
|
+
allowed: false,
|
|
1978
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1979
|
+
riskLevel: sp.riskLevel,
|
|
1980
|
+
riskType: "file_access",
|
|
1981
|
+
confidence: 90,
|
|
1982
|
+
source: "heuristic",
|
|
1983
|
+
violatedLayers: ["1.1"],
|
|
1984
|
+
isCredential: !!sp.credential
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1989
|
+
for (const sp of agentExt.extra_sensitive_paths) {
|
|
1990
|
+
const spPath = sp.path.toLowerCase();
|
|
1991
|
+
const expanded = `${expandedCwd}/${spPath}`;
|
|
1992
|
+
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
1993
|
+
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
1994
|
+
if (denyMode === "deny_read") {
|
|
1995
|
+
if (isWriteOpFlag) continue;
|
|
1996
|
+
} else if (denyMode === "deny_write") {
|
|
1997
|
+
if (!isWriteOpFlag) continue;
|
|
1998
|
+
}
|
|
1999
|
+
return {
|
|
2000
|
+
allowed: false,
|
|
2001
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
2002
|
+
riskLevel: sp.risk_level,
|
|
2003
|
+
riskType: "file_access",
|
|
2004
|
+
confidence: 90,
|
|
2005
|
+
source: "heuristic",
|
|
2006
|
+
violatedLayers: ["1.1"],
|
|
2007
|
+
isCredential: !!sp.credential
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
for (const cp of CREDENTIAL_PATHS) {
|
|
2013
|
+
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2014
|
+
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
2015
|
+
if (re.test(command)) {
|
|
2016
|
+
return {
|
|
2017
|
+
allowed: false,
|
|
2018
|
+
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
2019
|
+
riskLevel: cp.riskLevel,
|
|
2020
|
+
riskType: "file_access",
|
|
2021
|
+
confidence: 90,
|
|
2022
|
+
source: "heuristic",
|
|
2023
|
+
violatedLayers: ["1.1"],
|
|
2024
|
+
isCredential: true
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
2029
|
+
return {
|
|
2030
|
+
allowed: false,
|
|
2031
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
2032
|
+
riskLevel: "critical",
|
|
2033
|
+
riskType: "file_access",
|
|
2034
|
+
confidence: 90,
|
|
2035
|
+
source: "heuristic",
|
|
2036
|
+
violatedLayers: ["1.1"]
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
return null;
|
|
2040
|
+
}
|
|
2041
|
+
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2042
|
+
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2043
|
+
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
2044
|
+
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
2045
|
+
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
2046
|
+
return {
|
|
2047
|
+
allowed: false,
|
|
2048
|
+
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
2049
|
+
riskLevel: "high",
|
|
2050
|
+
riskType: "script_execution",
|
|
2051
|
+
confidence: 90,
|
|
2052
|
+
source: "heuristic",
|
|
2053
|
+
violatedLayers: ["1.1"]
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
2057
|
+
return {
|
|
2058
|
+
allowed: false,
|
|
2059
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
2060
|
+
riskLevel: "critical",
|
|
2061
|
+
riskType: "file_access",
|
|
2062
|
+
confidence: 95,
|
|
2063
|
+
source: "heuristic",
|
|
2064
|
+
violatedLayers: ["1.1"]
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
return null;
|
|
2068
|
+
}
|
|
2069
|
+
function extractRemoteHost(command) {
|
|
2070
|
+
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
2071
|
+
return m ? m[1] : null;
|
|
2072
|
+
}
|
|
2073
|
+
function isLocalHost(host) {
|
|
2074
|
+
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
2075
|
+
return localNames.includes(host.replace(/.*@/, ""));
|
|
2076
|
+
}
|
|
2077
|
+
function checkLateralMovement(actionDetail) {
|
|
2078
|
+
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
2079
|
+
const targetHost = extractRemoteHost(actionDetail);
|
|
2080
|
+
if (targetHost && !isLocalHost(targetHost)) {
|
|
2081
|
+
return {
|
|
2082
|
+
allowed: false,
|
|
2083
|
+
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
2084
|
+
riskLevel: "medium",
|
|
2085
|
+
riskType: "lateral_movement",
|
|
2086
|
+
confidence: 80,
|
|
2087
|
+
source: "heuristic",
|
|
2088
|
+
violatedLayers: ["1.1"]
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return null;
|
|
2093
|
+
}
|
|
2094
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
|
|
2095
|
+
var init_heuristic = __esm({
|
|
2096
|
+
"src/engine/heuristic.ts"() {
|
|
2097
|
+
"use strict";
|
|
2098
|
+
init_esm_shims();
|
|
2099
|
+
init_config();
|
|
2100
|
+
init_agents();
|
|
2101
|
+
init_logic_rules();
|
|
2102
|
+
INJECTION_KEYWORDS = [
|
|
2103
|
+
// ---- Instruction Override ----
|
|
2104
|
+
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
2105
|
+
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
2106
|
+
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
2107
|
+
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
2108
|
+
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
2109
|
+
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
2110
|
+
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
2111
|
+
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
2112
|
+
// ---- Role Hijacking ----
|
|
2113
|
+
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
2114
|
+
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
2115
|
+
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
2116
|
+
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
2117
|
+
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2118
|
+
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2119
|
+
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
2120
|
+
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
2121
|
+
// ---- Command Injection ----
|
|
2122
|
+
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
2123
|
+
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
2124
|
+
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
2125
|
+
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
2126
|
+
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
2127
|
+
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
2128
|
+
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
2129
|
+
// ---- Social Engineering ----
|
|
2130
|
+
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
2131
|
+
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
2132
|
+
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
2133
|
+
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
2134
|
+
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
2135
|
+
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
2136
|
+
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
2137
|
+
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
2138
|
+
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
2139
|
+
// ---- XSS ----
|
|
2140
|
+
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
2141
|
+
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
2142
|
+
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
2143
|
+
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
2144
|
+
// ---- Fake Boundaries ----
|
|
2145
|
+
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
2146
|
+
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
2147
|
+
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
2148
|
+
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
2149
|
+
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
2150
|
+
// ---- Developer Mode ----
|
|
2151
|
+
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
2152
|
+
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
2153
|
+
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
2154
|
+
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
2155
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
2156
|
+
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
2157
|
+
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
2158
|
+
// 由 agent_ext 机制注入,不再污染通用层。
|
|
2159
|
+
];
|
|
2160
|
+
DANGEROUS_PATTERNS = [
|
|
2161
|
+
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
2162
|
+
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
2163
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
|
|
2164
|
+
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
2165
|
+
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
2166
|
+
{ pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var(?!\/tmp)|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
|
|
2167
|
+
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
2168
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
|
|
2169
|
+
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
2170
|
+
{ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
|
|
2171
|
+
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
2172
|
+
{ pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
|
|
2173
|
+
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
2174
|
+
{ pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
|
|
2175
|
+
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2176
|
+
// --- Critical: remote code execution ---
|
|
2177
|
+
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2178
|
+
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2179
|
+
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2180
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2181
|
+
// --- Critical: reverse shell ---
|
|
2182
|
+
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2183
|
+
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
2184
|
+
// --- Critical: sensitive file access ---
|
|
2185
|
+
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
2186
|
+
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
2187
|
+
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
2188
|
+
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2189
|
+
// --- Critical: privilege escalation / container escape ---
|
|
2190
|
+
{ pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
|
|
2191
|
+
// --- High: dangerous permissions / ownership ---
|
|
2192
|
+
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
2193
|
+
{ 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", riskType: "script_execution" },
|
|
2194
|
+
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2195
|
+
{ pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
|
|
2196
|
+
// --- High: system config overwrite ---
|
|
2197
|
+
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2198
|
+
// --- High: download script + execute risk ---
|
|
2199
|
+
{ pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
|
|
2200
|
+
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
2201
|
+
// --- High: package manager pipe-to-shell ---
|
|
2202
|
+
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
2203
|
+
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
2204
|
+
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
2205
|
+
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
2206
|
+
{ pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
|
|
2207
|
+
// --- High: network port scanning (横向移动侦察) ---
|
|
2208
|
+
{ pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
2209
|
+
{ pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
2210
|
+
// --- Medium: credential export ---
|
|
2211
|
+
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2212
|
+
// --- High: shell rc file append (persistence) ---
|
|
2213
|
+
{ pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
|
|
2214
|
+
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2215
|
+
{ pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
|
|
2216
|
+
// --- High: non-interactive password modification ---
|
|
2217
|
+
{ pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
|
|
2218
|
+
{ pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
|
|
2219
|
+
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
2220
|
+
{ pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
|
|
2221
|
+
{ pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
|
|
2222
|
+
{ pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
|
|
2223
|
+
// --- High: non-interactive password modification (extended) ---
|
|
2224
|
+
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
2225
|
+
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
2226
|
+
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
2227
|
+
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
2228
|
+
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
2229
|
+
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
2230
|
+
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
2231
|
+
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
2232
|
+
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
2233
|
+
// --- High: generic command -p parameter password setting ---
|
|
2234
|
+
{ pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
|
|
2235
|
+
];
|
|
2236
|
+
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
|
|
2240
|
+
// src/engine/token-stats.ts
|
|
2241
|
+
var token_stats_exports = {};
|
|
2242
|
+
__export(token_stats_exports, {
|
|
2243
|
+
getRecentRecords: () => getRecentRecords,
|
|
2244
|
+
getTokenStats: () => getTokenStats,
|
|
2245
|
+
getTokenTrend: () => getTokenTrend,
|
|
2246
|
+
recordTokenUsage: () => recordTokenUsage,
|
|
2247
|
+
resetTokenStats: () => resetTokenStats
|
|
2248
|
+
});
|
|
2249
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2250
|
+
import { join as join4 } from "path";
|
|
2251
|
+
import { homedir as homedir3 } from "os";
|
|
2252
|
+
function getStatsPath() {
|
|
2253
|
+
const envPath = process.env[ENV_STATS_PATH];
|
|
2254
|
+
return envPath || STATS_FILE;
|
|
2255
|
+
}
|
|
2256
|
+
function loadAllRecords(statsPath) {
|
|
2257
|
+
if (!existsSync7(statsPath)) return [];
|
|
2258
|
+
try {
|
|
2259
|
+
const data = JSON.parse(readFileSync7(statsPath, "utf-8"));
|
|
2260
|
+
if (Array.isArray(data)) return data;
|
|
2261
|
+
if (data && Array.isArray(data.records)) return data.records;
|
|
2262
|
+
return [];
|
|
2263
|
+
} catch (e) {
|
|
2264
|
+
return [];
|
|
2265
|
+
}
|
|
2073
2266
|
}
|
|
2074
|
-
function
|
|
2075
|
-
|
|
2076
|
-
|
|
2267
|
+
function saveRecords(statsPath, records) {
|
|
2268
|
+
try {
|
|
2269
|
+
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2270
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2271
|
+
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2272
|
+
} catch (e) {
|
|
2077
2273
|
}
|
|
2078
2274
|
}
|
|
2079
|
-
function
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2275
|
+
function recordTokenUsage(record) {
|
|
2276
|
+
const statsPath = getStatsPath();
|
|
2277
|
+
const records = loadAllRecords(statsPath);
|
|
2278
|
+
records.push({
|
|
2279
|
+
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2280
|
+
session_id: record.sessionId,
|
|
2281
|
+
step: record.step,
|
|
2282
|
+
model: record.model,
|
|
2283
|
+
prompt_tokens: record.promptTokens,
|
|
2284
|
+
completion_tokens: record.completionTokens,
|
|
2285
|
+
total_tokens: record.totalTokens
|
|
2286
|
+
});
|
|
2287
|
+
if (records.length > MAX_RECORDS) {
|
|
2288
|
+
records.splice(0, records.length - MAX_RECORDS);
|
|
2092
2289
|
}
|
|
2093
|
-
|
|
2094
|
-
return runtime;
|
|
2290
|
+
saveRecords(statsPath, records);
|
|
2095
2291
|
}
|
|
2096
|
-
function
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2292
|
+
function filterByDate(records, days = 0, startDate, endDate) {
|
|
2293
|
+
if (startDate || endDate) {
|
|
2294
|
+
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
2295
|
+
let endDt = endDate ? new Date(endDate) : void 0;
|
|
2296
|
+
if (endDate) {
|
|
2297
|
+
const e = new Date(endDate);
|
|
2298
|
+
e.setHours(23, 59, 59, 999);
|
|
2299
|
+
endDt = e;
|
|
2103
2300
|
}
|
|
2301
|
+
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
2302
|
+
return records.filter((r) => {
|
|
2303
|
+
try {
|
|
2304
|
+
const ts = new Date(r.timestamp).getTime();
|
|
2305
|
+
return ts >= startDt.getTime() && ts <= endMs;
|
|
2306
|
+
} catch {
|
|
2307
|
+
return true;
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
} else if (days > 0) {
|
|
2311
|
+
const cutoff = Date.now() - days * 864e5;
|
|
2312
|
+
return records.filter((r) => {
|
|
2313
|
+
try {
|
|
2314
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
2315
|
+
} catch {
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2104
2319
|
}
|
|
2105
|
-
|
|
2106
|
-
return runtime;
|
|
2320
|
+
return records;
|
|
2107
2321
|
}
|
|
2108
|
-
function
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2322
|
+
function aggregate(records) {
|
|
2323
|
+
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
2324
|
+
const byStep = {};
|
|
2325
|
+
const byModel = {};
|
|
2326
|
+
const byDate = {};
|
|
2327
|
+
const bucket = (b, key, r) => {
|
|
2328
|
+
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
2329
|
+
e.prompt_tokens += r.prompt_tokens || 0;
|
|
2330
|
+
e.completion_tokens += r.completion_tokens || 0;
|
|
2331
|
+
e.total_tokens += r.total_tokens || 0;
|
|
2332
|
+
e.calls += 1;
|
|
2333
|
+
};
|
|
2334
|
+
for (const r of records) {
|
|
2335
|
+
totalPrompt += r.prompt_tokens || 0;
|
|
2336
|
+
totalCompletion += r.completion_tokens || 0;
|
|
2337
|
+
totalTotal += r.total_tokens || 0;
|
|
2338
|
+
bucket(byStep, r.step || "unknown", r);
|
|
2339
|
+
bucket(byModel, r.model || "unknown", r);
|
|
2340
|
+
let dateKey = "unknown";
|
|
2341
|
+
try {
|
|
2342
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2343
|
+
} catch {
|
|
2114
2344
|
}
|
|
2345
|
+
bucket(byDate, dateKey, r);
|
|
2115
2346
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
const runtime = loadRuntimeConfig();
|
|
2121
|
-
const cat = runtime[layer]?.[category];
|
|
2122
|
-
if (cat) cat.category_enabled = enabled;
|
|
2123
|
-
saveRuntimeConfig(runtime);
|
|
2124
|
-
return runtime;
|
|
2125
|
-
}
|
|
2126
|
-
function addCustomRule(layer, category, rule) {
|
|
2127
|
-
const runtime = loadRuntimeConfig();
|
|
2128
|
-
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
2129
|
-
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
2130
|
-
const rules = cat.rules || (cat.rules = []);
|
|
2131
|
-
const newRule = { ...rule, builtin: false, enabled: true };
|
|
2132
|
-
if (!newRule.id) {
|
|
2133
|
-
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
2134
|
-
}
|
|
2135
|
-
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
2136
|
-
rules.push(newRule);
|
|
2137
|
-
saveRuntimeConfig(runtime);
|
|
2138
|
-
return runtime;
|
|
2347
|
+
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
2348
|
+
const sortedByDate = {};
|
|
2349
|
+
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
2350
|
+
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
2139
2351
|
}
|
|
2140
|
-
function
|
|
2141
|
-
const
|
|
2142
|
-
const
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2352
|
+
function getTokenStats(days = 0, startDate, endDate) {
|
|
2353
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2354
|
+
const a = aggregate(records);
|
|
2355
|
+
return {
|
|
2356
|
+
total_prompt_tokens: a.totalPrompt,
|
|
2357
|
+
total_completion_tokens: a.totalCompletion,
|
|
2358
|
+
total_tokens: a.totalTotal,
|
|
2359
|
+
total_calls: records.length,
|
|
2360
|
+
by_step: a.byStep,
|
|
2361
|
+
by_model: a.byModel,
|
|
2362
|
+
by_date: a.byDate
|
|
2363
|
+
};
|
|
2150
2364
|
}
|
|
2151
|
-
function
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2155
|
-
try {
|
|
2156
|
-
mkdirSync2(skillsDir, { recursive: true });
|
|
2157
|
-
} catch {
|
|
2158
|
-
}
|
|
2159
|
-
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2160
|
-
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2161
|
-
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2162
|
-
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
2163
|
-
saveRuntimeConfig(runtime);
|
|
2164
|
-
return runtime;
|
|
2365
|
+
function getRecentRecords(limit = 20) {
|
|
2366
|
+
const records = loadAllRecords(getStatsPath());
|
|
2367
|
+
return records.slice(-limit);
|
|
2165
2368
|
}
|
|
2166
|
-
function
|
|
2167
|
-
|
|
2168
|
-
const
|
|
2169
|
-
const
|
|
2170
|
-
const
|
|
2171
|
-
|
|
2172
|
-
if (skills[idx].builtin) return null;
|
|
2173
|
-
skills.splice(idx, 1);
|
|
2174
|
-
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2369
|
+
function getTokenTrend(days = 0, startDate, endDate) {
|
|
2370
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2371
|
+
const series = {};
|
|
2372
|
+
const datesSet = /* @__PURE__ */ new Set();
|
|
2373
|
+
for (const r of records) {
|
|
2374
|
+
let dateKey;
|
|
2175
2375
|
try {
|
|
2176
|
-
|
|
2376
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2177
2377
|
} catch {
|
|
2378
|
+
continue;
|
|
2178
2379
|
}
|
|
2179
|
-
|
|
2380
|
+
const model = r.model || "unknown";
|
|
2381
|
+
const tokens = r.total_tokens || 0;
|
|
2382
|
+
datesSet.add(dateKey);
|
|
2383
|
+
series[model] = series[model] || {};
|
|
2384
|
+
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
2180
2385
|
}
|
|
2181
|
-
|
|
2386
|
+
const dates = Array.from(datesSet).sort();
|
|
2387
|
+
const models = Object.keys(series).sort();
|
|
2388
|
+
return { dates, models, series };
|
|
2182
2389
|
}
|
|
2183
|
-
function
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
break;
|
|
2190
|
-
}
|
|
2390
|
+
function resetTokenStats() {
|
|
2391
|
+
const statsPath = getStatsPath();
|
|
2392
|
+
if (existsSync7(statsPath)) {
|
|
2393
|
+
try {
|
|
2394
|
+
unlinkSync2(statsPath);
|
|
2395
|
+
} catch {
|
|
2191
2396
|
}
|
|
2192
2397
|
}
|
|
2193
|
-
saveRuntimeConfig(runtime);
|
|
2194
|
-
return runtime;
|
|
2195
|
-
}
|
|
2196
|
-
function updateSkillCategoryEnabled(category, enabled) {
|
|
2197
|
-
const runtime = loadRuntimeConfig();
|
|
2198
|
-
const cat = runtime.L3_skills?.[category];
|
|
2199
|
-
if (cat) cat.category_enabled = enabled;
|
|
2200
|
-
saveRuntimeConfig(runtime);
|
|
2201
|
-
return runtime;
|
|
2202
2398
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
|
|
2207
|
-
var init_runtime_config = __esm({
|
|
2208
|
-
"src/infra/runtime-config.ts"() {
|
|
2399
|
+
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
2400
|
+
var init_token_stats = __esm({
|
|
2401
|
+
"src/engine/token-stats.ts"() {
|
|
2209
2402
|
"use strict";
|
|
2210
2403
|
init_esm_shims();
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2216
|
-
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2217
|
-
LAYER_DISABLE_ENV = {
|
|
2218
|
-
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2219
|
-
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2220
|
-
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2221
|
-
};
|
|
2222
|
-
DENY_MODE_LABELS = {
|
|
2223
|
-
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2224
|
-
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2225
|
-
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2226
|
-
};
|
|
2227
|
-
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2404
|
+
STATS_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2405
|
+
STATS_FILE = join4(STATS_DIR, "agent_moss_token_stats.json");
|
|
2406
|
+
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
2407
|
+
MAX_RECORDS = 1e4;
|
|
2228
2408
|
}
|
|
2229
2409
|
});
|
|
2230
2410
|
|
|
@@ -2241,28 +2421,28 @@ init_skill_loader();
|
|
|
2241
2421
|
|
|
2242
2422
|
// src/engine/template-loader.ts
|
|
2243
2423
|
init_esm_shims();
|
|
2244
|
-
import { readFileSync as
|
|
2245
|
-
import { resolve as resolve3, dirname as
|
|
2246
|
-
import { fileURLToPath as
|
|
2247
|
-
var __filename4 =
|
|
2248
|
-
var
|
|
2249
|
-
var TEMPLATES_DIR = resolve3(
|
|
2250
|
-
var TEMPLATES_DIR_FALLBACK = resolve3(
|
|
2424
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
2425
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
2426
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
2427
|
+
var __filename4 = fileURLToPath5(import.meta.url);
|
|
2428
|
+
var __dirname5 = dirname4(__filename4);
|
|
2429
|
+
var TEMPLATES_DIR = resolve3(__dirname5, "../templates");
|
|
2430
|
+
var TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
|
|
2251
2431
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
2252
2432
|
var cachedTemplates = null;
|
|
2253
2433
|
function loadTemplates() {
|
|
2254
2434
|
if (cachedTemplates) return cachedTemplates;
|
|
2255
2435
|
cachedTemplates = {};
|
|
2256
|
-
const dir =
|
|
2257
|
-
if (!
|
|
2436
|
+
const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
2437
|
+
if (!existsSync5(dir)) {
|
|
2258
2438
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
2259
2439
|
return cachedTemplates;
|
|
2260
2440
|
}
|
|
2261
2441
|
for (const file of TEMPLATE_FILES) {
|
|
2262
2442
|
const filePath = resolve3(dir, file);
|
|
2263
|
-
if (
|
|
2443
|
+
if (existsSync5(filePath)) {
|
|
2264
2444
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
2265
|
-
cachedTemplates[key] =
|
|
2445
|
+
cachedTemplates[key] = readFileSync4(filePath, "utf-8");
|
|
2266
2446
|
}
|
|
2267
2447
|
}
|
|
2268
2448
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -2271,8 +2451,8 @@ function loadTemplates() {
|
|
|
2271
2451
|
|
|
2272
2452
|
// src/engine/script-analyzer.ts
|
|
2273
2453
|
init_esm_shims();
|
|
2274
|
-
import { readFileSync as
|
|
2275
|
-
import { resolve as resolve4, join } from "path";
|
|
2454
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2455
|
+
import { resolve as resolve4, join as join2 } from "path";
|
|
2276
2456
|
var SUSPICIOUS_PATTERNS = [
|
|
2277
2457
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
2278
2458
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -2405,8 +2585,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
2405
2585
|
}
|
|
2406
2586
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
2407
2587
|
try {
|
|
2408
|
-
const expandedPath = scriptPath.startsWith("~") ?
|
|
2409
|
-
const raw =
|
|
2588
|
+
const expandedPath = scriptPath.startsWith("~") ? join2(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
|
|
2589
|
+
const raw = readFileSync5(expandedPath, "utf-8");
|
|
2410
2590
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
2411
2591
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
2412
2592
|
} catch (e) {
|
|
@@ -2460,9 +2640,9 @@ ${result.scriptContent}
|
|
|
2460
2640
|
init_esm_shims();
|
|
2461
2641
|
init_heuristic();
|
|
2462
2642
|
init_logic_rules();
|
|
2463
|
-
import { readFileSync as
|
|
2464
|
-
import { resolve as resolve5, join as
|
|
2465
|
-
import { homedir } from "os";
|
|
2643
|
+
import { readFileSync as readFileSync6, statSync, existsSync as existsSync6 } from "fs";
|
|
2644
|
+
import { resolve as resolve5, join as join3, isAbsolute, dirname as dirname5 } from "path";
|
|
2645
|
+
import { homedir as homedir2 } from "os";
|
|
2466
2646
|
function emptyChain() {
|
|
2467
2647
|
return {
|
|
2468
2648
|
nodes: [],
|
|
@@ -2690,7 +2870,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
2690
2870
|
let resolved;
|
|
2691
2871
|
try {
|
|
2692
2872
|
resolved = resolvePath(p);
|
|
2693
|
-
if (
|
|
2873
|
+
if (existsSync6(resolved)) {
|
|
2694
2874
|
const stat = statSync(resolved);
|
|
2695
2875
|
if (stat.isFile()) return resolved;
|
|
2696
2876
|
}
|
|
@@ -2702,20 +2882,20 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
2702
2882
|
function resolvePath(p) {
|
|
2703
2883
|
let expanded = p;
|
|
2704
2884
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
2705
|
-
expanded =
|
|
2885
|
+
expanded = join3(homedir2(), expanded.slice(1));
|
|
2706
2886
|
}
|
|
2707
2887
|
return resolve5(expanded);
|
|
2708
2888
|
}
|
|
2709
2889
|
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
2710
2890
|
try {
|
|
2711
2891
|
const path3 = resolvePath(scriptPath);
|
|
2712
|
-
if (!
|
|
2892
|
+
if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
2713
2893
|
const stat = statSync(path3);
|
|
2714
2894
|
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
2715
2895
|
if (stat.size > maxFileSize) {
|
|
2716
2896
|
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
2717
2897
|
}
|
|
2718
|
-
const content =
|
|
2898
|
+
const content = readFileSync6(path3, "utf-8");
|
|
2719
2899
|
const lineCount = content ? content.split("\n").length : 0;
|
|
2720
2900
|
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
2721
2901
|
} catch (e) {
|
|
@@ -2732,9 +2912,9 @@ function extractChildScriptPaths(content, baseDir) {
|
|
|
2732
2912
|
const pathStr = m[1];
|
|
2733
2913
|
if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
|
|
2734
2914
|
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
2735
|
-
const full = isAbsolute(pathStr) ? pathStr :
|
|
2915
|
+
const full = isAbsolute(pathStr) ? pathStr : join3(baseDir, pathStr);
|
|
2736
2916
|
const resolved = resolvePath(full);
|
|
2737
|
-
if (
|
|
2917
|
+
if (existsSync6(resolved)) {
|
|
2738
2918
|
try {
|
|
2739
2919
|
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
2740
2920
|
} catch {
|
|
@@ -2749,7 +2929,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
|
|
|
2749
2929
|
const result = emptyChain();
|
|
2750
2930
|
result.entryPath = entryPath;
|
|
2751
2931
|
const entry = resolvePath(entryPath);
|
|
2752
|
-
const baseDir =
|
|
2932
|
+
const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
|
|
2753
2933
|
resolveRecursive({
|
|
2754
2934
|
path: entry,
|
|
2755
2935
|
depth: 0,
|
|
@@ -2824,7 +3004,7 @@ function resolveRecursive(ctx) {
|
|
|
2824
3004
|
maxTotalSize,
|
|
2825
3005
|
visited,
|
|
2826
3006
|
result,
|
|
2827
|
-
baseDir:
|
|
3007
|
+
baseDir: dirname5(resolvePath(childPath)),
|
|
2828
3008
|
input
|
|
2829
3009
|
});
|
|
2830
3010
|
if (result.hasCritical) return;
|
|
@@ -3086,7 +3266,7 @@ function formatHints(previous) {
|
|
|
3086
3266
|
const hints = [];
|
|
3087
3267
|
if (previous.heuristic) {
|
|
3088
3268
|
hints.push(
|
|
3089
|
-
`\
|
|
3269
|
+
`\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
3090
3270
|
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
3091
3271
|
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
3092
3272
|
);
|
|
@@ -3098,7 +3278,7 @@ function formatHints(previous) {
|
|
|
3098
3278
|
\u539F\u56E0: ${previous.logic.reason}`
|
|
3099
3279
|
);
|
|
3100
3280
|
}
|
|
3101
|
-
return hints.length > 0 ? hints.join("\n\n") : "\
|
|
3281
|
+
return hints.length > 0 ? hints.join("\n\n") : "\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
|
|
3102
3282
|
}
|
|
3103
3283
|
async function analyzeWithLLM(prompt, config) {
|
|
3104
3284
|
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
@@ -3306,6 +3486,7 @@ async function runAnalysis(input) {
|
|
|
3306
3486
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
3307
3487
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
3308
3488
|
const lowerDetail = rawDetail.toLowerCase();
|
|
3489
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
3309
3490
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
3310
3491
|
return {
|
|
3311
3492
|
allowed: true,
|
|
@@ -3317,7 +3498,23 @@ async function runAnalysis(input) {
|
|
|
3317
3498
|
violatedLayers: []
|
|
3318
3499
|
};
|
|
3319
3500
|
}
|
|
3320
|
-
|
|
3501
|
+
if (agentExt?.builtin_safe_action_types && agentExt.builtin_safe_action_types.length > 0) {
|
|
3502
|
+
const hit = agentExt.builtin_safe_action_types.some((t) => {
|
|
3503
|
+
const tl = t.toLowerCase();
|
|
3504
|
+
return tl.endsWith("-") ? actionType.startsWith(tl) : actionType === tl;
|
|
3505
|
+
});
|
|
3506
|
+
if (hit) {
|
|
3507
|
+
return {
|
|
3508
|
+
allowed: true,
|
|
3509
|
+
reason: `agent \u5185\u90E8\u5B89\u5168\u64CD\u4F5C\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
3510
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
3511
|
+
riskType: "",
|
|
3512
|
+
confidence: 95,
|
|
3513
|
+
source: "whitelist_bypass",
|
|
3514
|
+
violatedLayers: []
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3321
3518
|
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
3322
3519
|
if (agentExt?.builtin_safe_skills) {
|
|
3323
3520
|
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
@@ -3359,9 +3556,11 @@ async function runAnalysis(input) {
|
|
|
3359
3556
|
}
|
|
3360
3557
|
}
|
|
3361
3558
|
const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3559
|
+
const l1FileAccessHit = !!(heuristicResult && !heuristicResult.allowed && heuristicResult.riskType === "file_access");
|
|
3560
|
+
const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
|
|
3561
|
+
if (effectiveResult) {
|
|
3562
|
+
const isCredentialPath = !!effectiveResult.isCredential;
|
|
3563
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && effectiveResult.riskType === "file_access";
|
|
3365
3564
|
if (skipInlineFileAccess) {
|
|
3366
3565
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
3367
3566
|
if (inlineJudgment.actionable) {
|
|
@@ -3387,8 +3586,8 @@ async function runAnalysis(input) {
|
|
|
3387
3586
|
};
|
|
3388
3587
|
}
|
|
3389
3588
|
}
|
|
3390
|
-
if ((
|
|
3391
|
-
return
|
|
3589
|
+
if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
|
|
3590
|
+
return effectiveResult;
|
|
3392
3591
|
}
|
|
3393
3592
|
}
|
|
3394
3593
|
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|