@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
package/dist/server.js
CHANGED
|
@@ -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 __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
6
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
7
|
}) : x)(function(x) {
|
|
@@ -16,6 +18,15 @@ var __export = (target, all) => {
|
|
|
16
18
|
for (var name in all)
|
|
17
19
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
20
|
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
30
|
|
|
20
31
|
// node_modules/tsup/assets/esm_shims.js
|
|
21
32
|
import path from "path";
|
|
@@ -213,380 +224,89 @@ var init_config = __esm({
|
|
|
213
224
|
}
|
|
214
225
|
});
|
|
215
226
|
|
|
216
|
-
// src/
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
function resolveAgentsDir() {
|
|
221
|
-
if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
222
|
-
if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
223
|
-
return AGENTS_DIR_PROD;
|
|
227
|
+
// src/engine/inline-analyzer.ts
|
|
228
|
+
function isLiteralTextCommand(command) {
|
|
229
|
+
ECHO_CMD_RE.lastIndex = 0;
|
|
230
|
+
return ECHO_CMD_RE.test(command);
|
|
224
231
|
}
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return null;
|
|
232
|
+
function extractInlineCode(command) {
|
|
233
|
+
const parts = [];
|
|
234
|
+
let m;
|
|
235
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
236
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
237
|
+
const code = m[1] ?? m[2];
|
|
238
|
+
if (code) parts.push(code);
|
|
233
239
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
if (
|
|
238
|
-
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
239
|
-
cache.set(agentId, null);
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
cache.set(agentId, ext);
|
|
243
|
-
console.log(
|
|
244
|
-
`[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`
|
|
245
|
-
);
|
|
246
|
-
return ext;
|
|
247
|
-
} catch (e) {
|
|
248
|
-
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
249
|
-
cache.set(agentId, null);
|
|
250
|
-
return null;
|
|
240
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
241
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
242
|
+
const code = m[1] ?? m[2];
|
|
243
|
+
if (code) parts.push(code);
|
|
251
244
|
}
|
|
245
|
+
return parts.join("\n");
|
|
252
246
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
function
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
let highestRiskType = "";
|
|
272
|
-
const userRules = getCustomRules();
|
|
273
|
-
for (const rule of userRules) {
|
|
274
|
-
try {
|
|
275
|
-
const re = new RegExp(rule.pattern);
|
|
276
|
-
if (re.test(command)) {
|
|
277
|
-
if (rule.action === "Deny") {
|
|
278
|
-
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
279
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
280
|
-
highestSeverity = rule.severity;
|
|
281
|
-
highestRiskType = "custom_rule";
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
let topRiskType = "";
|
|
289
|
-
for (const rule of DANGEROUS_PATTERNS) {
|
|
290
|
-
if (rule.pattern.test(command)) {
|
|
291
|
-
matched.push(rule.description);
|
|
292
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
293
|
-
highestSeverity = rule.severity;
|
|
294
|
-
topRiskType = rule.riskType;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
highestRiskType = topRiskType || highestRiskType;
|
|
299
|
-
const agentExt = getAgentExtensions(input.agentId);
|
|
300
|
-
if (agentExt?.extra_command_patterns) {
|
|
301
|
-
for (const rule of agentExt.extra_command_patterns) {
|
|
302
|
-
try {
|
|
303
|
-
const re = new RegExp(rule.pattern, "i");
|
|
304
|
-
if (re.test(command)) {
|
|
305
|
-
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
306
|
-
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
307
|
-
highestSeverity = rule.risk_level;
|
|
308
|
-
highestRiskType = rule.risk_type;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
} catch {
|
|
312
|
-
}
|
|
313
|
-
}
|
|
247
|
+
function hasWriteExecChain(command) {
|
|
248
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
249
|
+
p.lastIndex = 0;
|
|
250
|
+
return p.test(command);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function hasDirTraversal(code) {
|
|
254
|
+
const lower = code.toLowerCase();
|
|
255
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
256
|
+
}
|
|
257
|
+
function hasReadWriteKeyword(code) {
|
|
258
|
+
const lower = code.toLowerCase();
|
|
259
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
260
|
+
}
|
|
261
|
+
function analyzeInlineScript(command) {
|
|
262
|
+
const code = extractInlineCode(command);
|
|
263
|
+
if (!code) {
|
|
264
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
314
265
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
266
|
+
if (hasWriteExecChain(command)) {
|
|
267
|
+
return {
|
|
268
|
+
actionable: true,
|
|
269
|
+
allowed: false,
|
|
270
|
+
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",
|
|
271
|
+
riskLevel: "critical",
|
|
272
|
+
riskType: "script_execution"
|
|
273
|
+
};
|
|
324
274
|
}
|
|
325
|
-
|
|
275
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
276
|
+
if (redirMatch) {
|
|
326
277
|
return {
|
|
278
|
+
actionable: true,
|
|
327
279
|
allowed: false,
|
|
328
|
-
reason: `\
|
|
329
|
-
riskLevel:
|
|
330
|
-
riskType:
|
|
331
|
-
confidence: 95,
|
|
332
|
-
source: "heuristic",
|
|
333
|
-
violatedLayers: ["1.1"]
|
|
334
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
280
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
281
|
+
riskLevel: "critical",
|
|
282
|
+
riskType: "file_access"
|
|
335
283
|
};
|
|
336
284
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const hits = [];
|
|
341
|
-
for (const entry of INJECTION_KEYWORDS) {
|
|
342
|
-
if (text.includes(entry.keyword.toLowerCase())) {
|
|
343
|
-
hits.push(entry);
|
|
344
|
-
}
|
|
285
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
286
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
287
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
345
288
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
289
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
290
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
291
|
+
return {
|
|
292
|
+
actionable: true,
|
|
293
|
+
allowed: false,
|
|
294
|
+
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",
|
|
295
|
+
riskLevel: "critical",
|
|
296
|
+
riskType: "file_access"
|
|
297
|
+
};
|
|
352
298
|
}
|
|
353
|
-
return
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
361
|
-
return ranks[s] || 0;
|
|
299
|
+
return {
|
|
300
|
+
actionable: true,
|
|
301
|
+
allowed: true,
|
|
302
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
303
|
+
riskLevel: "low",
|
|
304
|
+
riskType: "file_access"
|
|
305
|
+
};
|
|
362
306
|
}
|
|
363
|
-
var
|
|
364
|
-
var
|
|
365
|
-
"src/engine/
|
|
366
|
-
"use strict";
|
|
367
|
-
init_esm_shims();
|
|
368
|
-
init_config();
|
|
369
|
-
init_agents();
|
|
370
|
-
INJECTION_KEYWORDS = [
|
|
371
|
-
// ---- Instruction Override ----
|
|
372
|
-
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
373
|
-
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
374
|
-
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
375
|
-
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
376
|
-
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
377
|
-
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
378
|
-
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
379
|
-
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
380
|
-
// ---- Role Hijacking ----
|
|
381
|
-
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
382
|
-
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
383
|
-
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
384
|
-
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
385
|
-
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
386
|
-
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
387
|
-
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
388
|
-
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
389
|
-
// ---- Command Injection ----
|
|
390
|
-
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
391
|
-
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
392
|
-
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
393
|
-
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
394
|
-
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
395
|
-
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
396
|
-
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
397
|
-
// ---- Social Engineering ----
|
|
398
|
-
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
399
|
-
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
400
|
-
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
401
|
-
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
402
|
-
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
403
|
-
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
404
|
-
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
405
|
-
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
406
|
-
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
407
|
-
// ---- XSS ----
|
|
408
|
-
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
409
|
-
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
410
|
-
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
411
|
-
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
412
|
-
// ---- Fake Boundaries ----
|
|
413
|
-
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
414
|
-
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
415
|
-
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
416
|
-
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
417
|
-
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
418
|
-
// ---- Developer Mode ----
|
|
419
|
-
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
420
|
-
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
421
|
-
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
422
|
-
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
423
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
424
|
-
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
425
|
-
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
426
|
-
// 由 agent_ext 机制注入,不再污染通用层。
|
|
427
|
-
];
|
|
428
|
-
DANGEROUS_PATTERNS = [
|
|
429
|
-
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
430
|
-
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
431
|
-
{ 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" },
|
|
432
|
-
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
433
|
-
// 2. rm with -r or -f targeting system dirs
|
|
434
|
-
{ 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" },
|
|
435
|
-
// 3. rm -r absolute path (no -f)
|
|
436
|
-
{ 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" },
|
|
437
|
-
// 4. rm -f absolute path (no -r)
|
|
438
|
-
{ 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" },
|
|
439
|
-
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
440
|
-
{ 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" },
|
|
441
|
-
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
442
|
-
{ 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" },
|
|
443
|
-
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
444
|
-
// --- Critical: remote code execution ---
|
|
445
|
-
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
446
|
-
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
447
|
-
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
448
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
449
|
-
// --- Critical: reverse shell ---
|
|
450
|
-
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
451
|
-
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
452
|
-
// --- Critical: sensitive file access ---
|
|
453
|
-
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
454
|
-
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
455
|
-
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
456
|
-
{ pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
457
|
-
// --- Critical: privilege escalation / container escape ---
|
|
458
|
-
{ 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" },
|
|
459
|
-
// --- High: dangerous permissions / ownership ---
|
|
460
|
-
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
461
|
-
{ 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" },
|
|
462
|
-
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
463
|
-
{ 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" },
|
|
464
|
-
// --- High: system config overwrite ---
|
|
465
|
-
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
466
|
-
// --- High: download script + execute risk ---
|
|
467
|
-
{ 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" },
|
|
468
|
-
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
469
|
-
// --- High: package manager pipe-to-shell ---
|
|
470
|
-
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
471
|
-
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
472
|
-
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
473
|
-
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
474
|
-
{ 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" },
|
|
475
|
-
// --- High: network port scanning (横向移动侦察) ---
|
|
476
|
-
{ 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" },
|
|
477
|
-
{ 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" },
|
|
478
|
-
// --- Medium: credential export ---
|
|
479
|
-
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
480
|
-
// --- High: shell rc file append (persistence) ---
|
|
481
|
-
{ 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" },
|
|
482
|
-
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
483
|
-
{ 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" },
|
|
484
|
-
// --- High: non-interactive password modification ---
|
|
485
|
-
{ 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" },
|
|
486
|
-
{ 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" },
|
|
487
|
-
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
488
|
-
{ 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" },
|
|
489
|
-
{ 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" },
|
|
490
|
-
{ 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" },
|
|
491
|
-
// --- High: non-interactive password modification (extended) ---
|
|
492
|
-
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
493
|
-
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
494
|
-
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
495
|
-
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
496
|
-
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
497
|
-
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
498
|
-
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
499
|
-
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
500
|
-
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
501
|
-
// --- High: generic command -p parameter password setting ---
|
|
502
|
-
{ 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" }
|
|
503
|
-
];
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
// src/engine/inline-analyzer.ts
|
|
508
|
-
function isLiteralTextCommand(command) {
|
|
509
|
-
ECHO_CMD_RE.lastIndex = 0;
|
|
510
|
-
return ECHO_CMD_RE.test(command);
|
|
511
|
-
}
|
|
512
|
-
function extractInlineCode(command) {
|
|
513
|
-
const parts = [];
|
|
514
|
-
let m;
|
|
515
|
-
INLINE_CODE_RE.lastIndex = 0;
|
|
516
|
-
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
517
|
-
const code = m[1] ?? m[2];
|
|
518
|
-
if (code) parts.push(code);
|
|
519
|
-
}
|
|
520
|
-
ECHO_STRING_RE.lastIndex = 0;
|
|
521
|
-
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
522
|
-
const code = m[1] ?? m[2];
|
|
523
|
-
if (code) parts.push(code);
|
|
524
|
-
}
|
|
525
|
-
return parts.join("\n");
|
|
526
|
-
}
|
|
527
|
-
function hasWriteExecChain(command) {
|
|
528
|
-
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
529
|
-
p.lastIndex = 0;
|
|
530
|
-
return p.test(command);
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
function hasDirTraversal(code) {
|
|
534
|
-
const lower = code.toLowerCase();
|
|
535
|
-
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
536
|
-
}
|
|
537
|
-
function hasReadWriteKeyword(code) {
|
|
538
|
-
const lower = code.toLowerCase();
|
|
539
|
-
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
540
|
-
}
|
|
541
|
-
function analyzeInlineScript(command) {
|
|
542
|
-
const code = extractInlineCode(command);
|
|
543
|
-
if (!code) {
|
|
544
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
545
|
-
}
|
|
546
|
-
if (hasWriteExecChain(command)) {
|
|
547
|
-
return {
|
|
548
|
-
actionable: true,
|
|
549
|
-
allowed: false,
|
|
550
|
-
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",
|
|
551
|
-
riskLevel: "critical",
|
|
552
|
-
riskType: "script_execution"
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
556
|
-
if (redirMatch) {
|
|
557
|
-
return {
|
|
558
|
-
actionable: true,
|
|
559
|
-
allowed: false,
|
|
560
|
-
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
561
|
-
riskLevel: "critical",
|
|
562
|
-
riskType: "file_access"
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
566
|
-
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
567
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
568
|
-
}
|
|
569
|
-
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
570
|
-
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
571
|
-
return {
|
|
572
|
-
actionable: true,
|
|
573
|
-
allowed: false,
|
|
574
|
-
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",
|
|
575
|
-
riskLevel: "critical",
|
|
576
|
-
riskType: "file_access"
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
return {
|
|
580
|
-
actionable: true,
|
|
581
|
-
allowed: true,
|
|
582
|
-
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
583
|
-
riskLevel: "low",
|
|
584
|
-
riskType: "file_access"
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
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;
|
|
588
|
-
var init_inline_analyzer = __esm({
|
|
589
|
-
"src/engine/inline-analyzer.ts"() {
|
|
307
|
+
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;
|
|
308
|
+
var init_inline_analyzer = __esm({
|
|
309
|
+
"src/engine/inline-analyzer.ts"() {
|
|
590
310
|
"use strict";
|
|
591
311
|
init_esm_shims();
|
|
592
312
|
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
@@ -720,165 +440,26 @@ function runLogicCheck(input) {
|
|
|
720
440
|
}
|
|
721
441
|
}
|
|
722
442
|
}
|
|
723
|
-
if (!isExcludedType) {
|
|
724
|
-
const agentExt = getAgentExtensions(input.agentId || "");
|
|
725
|
-
const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
|
|
726
|
-
if (pathResult) return pathResult;
|
|
727
|
-
}
|
|
728
|
-
if (actionType !== "file_write" && actionType !== "file_edit") {
|
|
729
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
730
|
-
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
731
|
-
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
732
|
-
return {
|
|
733
|
-
allowed: false,
|
|
734
|
-
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
735
|
-
riskLevel: "high",
|
|
736
|
-
riskType: "script_execution",
|
|
737
|
-
confidence: 90,
|
|
738
|
-
source: "logic_rule",
|
|
739
|
-
violatedLayers: ["2"]
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
743
|
-
return {
|
|
744
|
-
allowed: false,
|
|
745
|
-
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
746
|
-
riskLevel: "critical",
|
|
747
|
-
riskType: "file_access",
|
|
748
|
-
confidence: 95,
|
|
749
|
-
source: "logic_rule",
|
|
750
|
-
violatedLayers: ["2"]
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
755
|
-
"cat",
|
|
756
|
-
"head",
|
|
757
|
-
"tail",
|
|
758
|
-
"less",
|
|
759
|
-
"more",
|
|
760
|
-
"grep",
|
|
761
|
-
"find",
|
|
762
|
-
"awk",
|
|
763
|
-
"sed",
|
|
764
|
-
"sort",
|
|
765
|
-
"uniq",
|
|
766
|
-
"wc",
|
|
767
|
-
"cut",
|
|
768
|
-
"strings",
|
|
769
|
-
"od",
|
|
770
|
-
"xxd",
|
|
771
|
-
"hexdump",
|
|
772
|
-
"tr",
|
|
773
|
-
"file",
|
|
774
|
-
"stat",
|
|
775
|
-
"du",
|
|
776
|
-
"df",
|
|
777
|
-
"ls",
|
|
778
|
-
"dir",
|
|
779
|
-
"tree",
|
|
780
|
-
"nl",
|
|
781
|
-
"tac",
|
|
782
|
-
"rev",
|
|
783
|
-
"lsblk",
|
|
784
|
-
"blockdev",
|
|
785
|
-
"smartctl",
|
|
786
|
-
"udevadm",
|
|
787
|
-
"dmidecode",
|
|
788
|
-
"lscpu",
|
|
789
|
-
"lspci",
|
|
790
|
-
"lsusb",
|
|
791
|
-
"lsmem",
|
|
792
|
-
"lsns",
|
|
793
|
-
"lsof",
|
|
794
|
-
"hwinfo",
|
|
795
|
-
"inxi",
|
|
796
|
-
"fdisk",
|
|
797
|
-
"parted",
|
|
798
|
-
"dmesg",
|
|
799
|
-
"journalctl",
|
|
800
|
-
"systemctl",
|
|
801
|
-
"hostnamectl",
|
|
802
|
-
"localectl",
|
|
803
|
-
"timedatectl",
|
|
804
|
-
"ps",
|
|
805
|
-
"top",
|
|
806
|
-
"free",
|
|
807
|
-
"vmstat",
|
|
808
|
-
"iostat",
|
|
809
|
-
"mpstat",
|
|
810
|
-
"pidof",
|
|
811
|
-
"pgrep",
|
|
812
|
-
"ip",
|
|
813
|
-
"ifconfig",
|
|
814
|
-
"route",
|
|
815
|
-
"ss",
|
|
816
|
-
"netstat",
|
|
817
|
-
"arp",
|
|
818
|
-
"ethtool",
|
|
819
|
-
"nmcli",
|
|
820
|
-
"uname",
|
|
821
|
-
"arch",
|
|
822
|
-
"nproc",
|
|
823
|
-
"getconf",
|
|
824
|
-
"getent",
|
|
825
|
-
"id",
|
|
826
|
-
"whoami",
|
|
827
|
-
"who",
|
|
828
|
-
"w",
|
|
829
|
-
"last",
|
|
830
|
-
"uptime",
|
|
831
|
-
"lsmod",
|
|
832
|
-
"modinfo",
|
|
833
|
-
"sysctl"
|
|
834
|
-
]);
|
|
835
|
-
var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
836
|
-
var _WRITE_COMMAND_RES = [
|
|
837
|
-
/\brm\b/,
|
|
838
|
-
/\bunlink\b/,
|
|
839
|
-
/\brmdir\b/,
|
|
840
|
-
/\bshred\b/,
|
|
841
|
-
/\bcp\b/,
|
|
842
|
-
/\bmv\b/,
|
|
843
|
-
/\binstall\b/,
|
|
844
|
-
/\btee\b/,
|
|
845
|
-
/\bdd\b/,
|
|
846
|
-
/\bmkfs\b/,
|
|
847
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
848
|
-
/\bchmod\b/,
|
|
849
|
-
/\bchown\b/,
|
|
850
|
-
/\btruncate\b/
|
|
851
|
-
];
|
|
852
443
|
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
|
|
853
444
|
if (indirectResult) return indirectResult;
|
|
854
445
|
if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
)
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
if (targetHost && !isLocalHost(targetHost)) {
|
|
873
|
-
return {
|
|
874
|
-
allowed: false,
|
|
875
|
-
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
876
|
-
riskLevel: "medium",
|
|
877
|
-
riskType: "lateral_movement",
|
|
878
|
-
confidence: 80,
|
|
879
|
-
source: "logic_rule",
|
|
880
|
-
violatedLayers: ["2"]
|
|
881
|
-
};
|
|
446
|
+
const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
|
|
447
|
+
const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
|
|
448
|
+
if (!isAdminCmd) {
|
|
449
|
+
const adminContext = actionHistory.some(
|
|
450
|
+
(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")
|
|
451
|
+
);
|
|
452
|
+
if (!adminContext) {
|
|
453
|
+
return {
|
|
454
|
+
allowed: false,
|
|
455
|
+
reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
|
|
456
|
+
riskLevel: "high",
|
|
457
|
+
riskType: "privilege_escalation",
|
|
458
|
+
confidence: 85,
|
|
459
|
+
source: "logic_rule",
|
|
460
|
+
violatedLayers: ["2"]
|
|
461
|
+
};
|
|
462
|
+
}
|
|
882
463
|
}
|
|
883
464
|
}
|
|
884
465
|
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
|
|
@@ -899,9 +480,12 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
899
480
|
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
900
481
|
let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
|
|
901
482
|
if (!isWrite && actionDetail.includes(">")) {
|
|
902
|
-
const
|
|
903
|
-
if (
|
|
904
|
-
|
|
483
|
+
const detailWithoutDevnull = actionDetail.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
484
|
+
if (detailWithoutDevnull.includes(">")) {
|
|
485
|
+
const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
|
|
486
|
+
if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
|
|
487
|
+
isWrite = true;
|
|
488
|
+
}
|
|
905
489
|
}
|
|
906
490
|
}
|
|
907
491
|
if (!isWrite) return null;
|
|
@@ -946,119 +530,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
946
530
|
}
|
|
947
531
|
return null;
|
|
948
532
|
}
|
|
949
|
-
function checkSensitivePath(command, cwd, agentExt) {
|
|
950
|
-
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
951
|
-
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/];
|
|
952
|
-
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
953
|
-
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"]);
|
|
954
|
-
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));
|
|
955
|
-
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
956
|
-
if (command.includes(sp.path.toLowerCase())) {
|
|
957
|
-
if (sp.path === "/dev/random") {
|
|
958
|
-
if (isWriteOpFlag) {
|
|
959
|
-
continue;
|
|
960
|
-
}
|
|
961
|
-
return {
|
|
962
|
-
allowed: false,
|
|
963
|
-
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
964
|
-
riskLevel: sp.riskLevel,
|
|
965
|
-
riskType: "sensitive_path_access",
|
|
966
|
-
confidence: 90,
|
|
967
|
-
source: "logic_rule",
|
|
968
|
-
violatedLayers: ["2"]
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
if (!isWriteOpFlag && !sp.credential) {
|
|
972
|
-
continue;
|
|
973
|
-
}
|
|
974
|
-
return {
|
|
975
|
-
allowed: false,
|
|
976
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
977
|
-
riskLevel: sp.riskLevel,
|
|
978
|
-
riskType: "sensitive_path_access",
|
|
979
|
-
confidence: 90,
|
|
980
|
-
source: "logic_rule",
|
|
981
|
-
violatedLayers: ["2"]
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
986
|
-
const expanded = `${expandedCwd}/${sp.path}`;
|
|
987
|
-
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
988
|
-
if (!isWriteOpFlag && !sp.credential) {
|
|
989
|
-
continue;
|
|
990
|
-
}
|
|
991
|
-
return {
|
|
992
|
-
allowed: false,
|
|
993
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
994
|
-
riskLevel: sp.riskLevel,
|
|
995
|
-
riskType: "sensitive_path_access",
|
|
996
|
-
confidence: 90,
|
|
997
|
-
source: "logic_rule",
|
|
998
|
-
violatedLayers: ["2"]
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1003
|
-
for (const sp of agentExt.extra_sensitive_paths) {
|
|
1004
|
-
const spPath = sp.path.toLowerCase();
|
|
1005
|
-
const expanded = `${expandedCwd}/${spPath}`;
|
|
1006
|
-
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
1007
|
-
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
1008
|
-
if (denyMode === "deny_read") {
|
|
1009
|
-
if (isWriteOpFlag) continue;
|
|
1010
|
-
} else if (denyMode === "deny_write") {
|
|
1011
|
-
if (!isWriteOpFlag) continue;
|
|
1012
|
-
}
|
|
1013
|
-
return {
|
|
1014
|
-
allowed: false,
|
|
1015
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1016
|
-
riskLevel: sp.risk_level,
|
|
1017
|
-
riskType: "sensitive_path_access",
|
|
1018
|
-
confidence: 90,
|
|
1019
|
-
source: "logic_rule",
|
|
1020
|
-
violatedLayers: ["2"]
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
for (const cp of CREDENTIAL_PATHS) {
|
|
1026
|
-
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1027
|
-
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
1028
|
-
if (re.test(command)) {
|
|
1029
|
-
return {
|
|
1030
|
-
allowed: false,
|
|
1031
|
-
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
1032
|
-
riskLevel: cp.riskLevel,
|
|
1033
|
-
riskType: "sensitive_path_access",
|
|
1034
|
-
confidence: 90,
|
|
1035
|
-
source: "logic_rule",
|
|
1036
|
-
violatedLayers: ["2"],
|
|
1037
|
-
isCredential: true
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
1042
|
-
return {
|
|
1043
|
-
allowed: false,
|
|
1044
|
-
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
1045
|
-
riskLevel: "critical",
|
|
1046
|
-
riskType: "sensitive_path_access",
|
|
1047
|
-
confidence: 90,
|
|
1048
|
-
source: "logic_rule",
|
|
1049
|
-
violatedLayers: ["2"]
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
return null;
|
|
1053
|
-
}
|
|
1054
|
-
function extractRemoteHost(command) {
|
|
1055
|
-
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
1056
|
-
return m ? m[1] : null;
|
|
1057
|
-
}
|
|
1058
|
-
function isLocalHost(host) {
|
|
1059
|
-
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
1060
|
-
return localNames.includes(host.replace(/.*@/, ""));
|
|
1061
|
-
}
|
|
1062
533
|
function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
1063
534
|
if (actionType !== "bash") return null;
|
|
1064
535
|
const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
|
|
@@ -1179,11 +650,10 @@ var init_logic_rules = __esm({
|
|
|
1179
650
|
"use strict";
|
|
1180
651
|
init_esm_shims();
|
|
1181
652
|
init_inline_analyzer();
|
|
1182
|
-
init_agents();
|
|
1183
653
|
ROOT_SENSITIVE_PATHS = [
|
|
1184
654
|
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1185
655
|
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1186
|
-
// 注意:/etc/passwd 仅从 L1
|
|
656
|
+
// 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
|
|
1187
657
|
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
1188
658
|
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
1189
659
|
// 系统配置(仅拦截写入)
|
|
@@ -1276,21 +746,21 @@ var init_logic_rules = __esm({
|
|
|
1276
746
|
});
|
|
1277
747
|
|
|
1278
748
|
// src/engine/skill-loader.ts
|
|
1279
|
-
import { readFileSync
|
|
1280
|
-
import { resolve
|
|
1281
|
-
import { fileURLToPath as
|
|
749
|
+
import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
750
|
+
import { resolve, dirname } from "path";
|
|
751
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1282
752
|
function loadAllSkills() {
|
|
1283
753
|
if (cachedSkills) return cachedSkills;
|
|
1284
754
|
cachedSkills = {};
|
|
1285
|
-
const dir =
|
|
1286
|
-
if (!
|
|
755
|
+
const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
756
|
+
if (!existsSync2(dir)) {
|
|
1287
757
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1288
758
|
return cachedSkills;
|
|
1289
759
|
}
|
|
1290
760
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1291
761
|
for (const f of files) {
|
|
1292
762
|
const name = f.replace(".md", "");
|
|
1293
|
-
cachedSkills[name] =
|
|
763
|
+
cachedSkills[name] = readFileSync(resolve(dir, f), "utf-8");
|
|
1294
764
|
}
|
|
1295
765
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1296
766
|
return cachedSkills;
|
|
@@ -1344,15 +814,15 @@ function formatSkillsForPrompt(skills) {
|
|
|
1344
814
|
${s.skillContent}`
|
|
1345
815
|
).join("\n\n---\n\n");
|
|
1346
816
|
}
|
|
1347
|
-
var
|
|
817
|
+
var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1348
818
|
var init_skill_loader = __esm({
|
|
1349
819
|
"src/engine/skill-loader.ts"() {
|
|
1350
820
|
"use strict";
|
|
1351
821
|
init_esm_shims();
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
SKILLS_DIR =
|
|
1355
|
-
SKILLS_DIR_FALLBACK =
|
|
822
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
823
|
+
__dirname2 = dirname(__filename2);
|
|
824
|
+
SKILLS_DIR = resolve(__dirname2, "../skills");
|
|
825
|
+
SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
|
|
1356
826
|
SKILL_KEYWORD_MAP = {
|
|
1357
827
|
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
1358
828
|
"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"],
|
|
@@ -1374,189 +844,22 @@ var init_skill_loader = __esm({
|
|
|
1374
844
|
}
|
|
1375
845
|
});
|
|
1376
846
|
|
|
1377
|
-
// src/engine/token-stats.ts
|
|
1378
|
-
var token_stats_exports = {};
|
|
1379
|
-
__export(token_stats_exports, {
|
|
1380
|
-
getRecentRecords: () => getRecentRecords,
|
|
1381
|
-
getTokenStats: () => getTokenStats,
|
|
1382
|
-
getTokenTrend: () => getTokenTrend,
|
|
1383
|
-
recordTokenUsage: () => recordTokenUsage,
|
|
1384
|
-
resetTokenStats: () => resetTokenStats
|
|
1385
|
-
});
|
|
1386
|
-
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
|
|
1387
|
-
import { join as join3 } from "path";
|
|
1388
|
-
import { homedir as homedir2 } from "os";
|
|
1389
|
-
function getStatsPath() {
|
|
1390
|
-
const envPath = process.env[ENV_STATS_PATH];
|
|
1391
|
-
return envPath || STATS_FILE;
|
|
1392
|
-
}
|
|
1393
|
-
function loadAllRecords(statsPath) {
|
|
1394
|
-
if (!existsSync6(statsPath)) return [];
|
|
1395
|
-
try {
|
|
1396
|
-
const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
|
|
1397
|
-
if (Array.isArray(data)) return data;
|
|
1398
|
-
if (data && Array.isArray(data.records)) return data.records;
|
|
1399
|
-
return [];
|
|
1400
|
-
} catch (e) {
|
|
1401
|
-
return [];
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
function saveRecords(statsPath, records) {
|
|
1405
|
-
try {
|
|
1406
|
-
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
1407
|
-
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
1408
|
-
writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
1409
|
-
} catch (e) {
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
function recordTokenUsage(record) {
|
|
1413
|
-
const statsPath = getStatsPath();
|
|
1414
|
-
const records = loadAllRecords(statsPath);
|
|
1415
|
-
records.push({
|
|
1416
|
-
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1417
|
-
session_id: record.sessionId,
|
|
1418
|
-
step: record.step,
|
|
1419
|
-
model: record.model,
|
|
1420
|
-
prompt_tokens: record.promptTokens,
|
|
1421
|
-
completion_tokens: record.completionTokens,
|
|
1422
|
-
total_tokens: record.totalTokens
|
|
1423
|
-
});
|
|
1424
|
-
if (records.length > MAX_RECORDS) {
|
|
1425
|
-
records.splice(0, records.length - MAX_RECORDS);
|
|
1426
|
-
}
|
|
1427
|
-
saveRecords(statsPath, records);
|
|
1428
|
-
}
|
|
1429
|
-
function filterByDate(records, days = 0, startDate, endDate) {
|
|
1430
|
-
if (startDate || endDate) {
|
|
1431
|
-
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
1432
|
-
let endDt = endDate ? new Date(endDate) : void 0;
|
|
1433
|
-
if (endDate) {
|
|
1434
|
-
const e = new Date(endDate);
|
|
1435
|
-
e.setHours(23, 59, 59, 999);
|
|
1436
|
-
endDt = e;
|
|
1437
|
-
}
|
|
1438
|
-
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
1439
|
-
return records.filter((r) => {
|
|
1440
|
-
try {
|
|
1441
|
-
const ts = new Date(r.timestamp).getTime();
|
|
1442
|
-
return ts >= startDt.getTime() && ts <= endMs;
|
|
1443
|
-
} catch {
|
|
1444
|
-
return true;
|
|
1445
|
-
}
|
|
1446
|
-
});
|
|
1447
|
-
} else if (days > 0) {
|
|
1448
|
-
const cutoff = Date.now() - days * 864e5;
|
|
1449
|
-
return records.filter((r) => {
|
|
1450
|
-
try {
|
|
1451
|
-
return new Date(r.timestamp).getTime() >= cutoff;
|
|
1452
|
-
} catch {
|
|
1453
|
-
return true;
|
|
1454
|
-
}
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
return records;
|
|
1458
|
-
}
|
|
1459
|
-
function aggregate(records) {
|
|
1460
|
-
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
1461
|
-
const byStep = {};
|
|
1462
|
-
const byModel = {};
|
|
1463
|
-
const byDate = {};
|
|
1464
|
-
const bucket = (b, key, r) => {
|
|
1465
|
-
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
1466
|
-
e.prompt_tokens += r.prompt_tokens || 0;
|
|
1467
|
-
e.completion_tokens += r.completion_tokens || 0;
|
|
1468
|
-
e.total_tokens += r.total_tokens || 0;
|
|
1469
|
-
e.calls += 1;
|
|
1470
|
-
};
|
|
1471
|
-
for (const r of records) {
|
|
1472
|
-
totalPrompt += r.prompt_tokens || 0;
|
|
1473
|
-
totalCompletion += r.completion_tokens || 0;
|
|
1474
|
-
totalTotal += r.total_tokens || 0;
|
|
1475
|
-
bucket(byStep, r.step || "unknown", r);
|
|
1476
|
-
bucket(byModel, r.model || "unknown", r);
|
|
1477
|
-
let dateKey = "unknown";
|
|
1478
|
-
try {
|
|
1479
|
-
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1480
|
-
} catch {
|
|
1481
|
-
}
|
|
1482
|
-
bucket(byDate, dateKey, r);
|
|
1483
|
-
}
|
|
1484
|
-
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
1485
|
-
const sortedByDate = {};
|
|
1486
|
-
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
1487
|
-
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
1488
|
-
}
|
|
1489
|
-
function getTokenStats(days = 0, startDate, endDate) {
|
|
1490
|
-
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1491
|
-
const a = aggregate(records);
|
|
1492
|
-
return {
|
|
1493
|
-
total_prompt_tokens: a.totalPrompt,
|
|
1494
|
-
total_completion_tokens: a.totalCompletion,
|
|
1495
|
-
total_tokens: a.totalTotal,
|
|
1496
|
-
total_calls: records.length,
|
|
1497
|
-
by_step: a.byStep,
|
|
1498
|
-
by_model: a.byModel,
|
|
1499
|
-
by_date: a.byDate
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
function getRecentRecords(limit = 20) {
|
|
1503
|
-
const records = loadAllRecords(getStatsPath());
|
|
1504
|
-
return records.slice(-limit);
|
|
1505
|
-
}
|
|
1506
|
-
function getTokenTrend(days = 0, startDate, endDate) {
|
|
1507
|
-
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1508
|
-
const series = {};
|
|
1509
|
-
const datesSet = /* @__PURE__ */ new Set();
|
|
1510
|
-
for (const r of records) {
|
|
1511
|
-
let dateKey;
|
|
1512
|
-
try {
|
|
1513
|
-
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1514
|
-
} catch {
|
|
1515
|
-
continue;
|
|
1516
|
-
}
|
|
1517
|
-
const model = r.model || "unknown";
|
|
1518
|
-
const tokens = r.total_tokens || 0;
|
|
1519
|
-
datesSet.add(dateKey);
|
|
1520
|
-
series[model] = series[model] || {};
|
|
1521
|
-
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
1522
|
-
}
|
|
1523
|
-
const dates = Array.from(datesSet).sort();
|
|
1524
|
-
const models = Object.keys(series).sort();
|
|
1525
|
-
return { dates, models, series };
|
|
1526
|
-
}
|
|
1527
|
-
function resetTokenStats() {
|
|
1528
|
-
const statsPath = getStatsPath();
|
|
1529
|
-
if (existsSync6(statsPath)) {
|
|
1530
|
-
try {
|
|
1531
|
-
unlinkSync(statsPath);
|
|
1532
|
-
} catch {
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
1537
|
-
var init_token_stats = __esm({
|
|
1538
|
-
"src/engine/token-stats.ts"() {
|
|
1539
|
-
"use strict";
|
|
1540
|
-
init_esm_shims();
|
|
1541
|
-
STATS_DIR = join3(homedir2(), ".config", "agentmoss");
|
|
1542
|
-
STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
|
|
1543
|
-
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
1544
|
-
MAX_RECORDS = 1e4;
|
|
1545
|
-
}
|
|
1546
|
-
});
|
|
1547
|
-
|
|
1548
847
|
// src/infra/runtime-config.ts
|
|
1549
848
|
var runtime_config_exports = {};
|
|
1550
849
|
__export(runtime_config_exports, {
|
|
1551
850
|
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
851
|
+
addAgentRule: () => addAgentRule,
|
|
1552
852
|
addCustomRule: () => addCustomRule,
|
|
1553
853
|
addCustomSkill: () => addCustomSkill,
|
|
854
|
+
deleteAgentRule: () => deleteAgentRule,
|
|
1554
855
|
deleteCustomRule: () => deleteCustomRule,
|
|
1555
856
|
deleteCustomSkill: () => deleteCustomSkill,
|
|
1556
857
|
filterEnabledRules: () => filterEnabledRules,
|
|
1557
858
|
generateSourceDefaults: () => generateSourceDefaults,
|
|
859
|
+
getAgentRules: () => getAgentRules,
|
|
1558
860
|
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
1559
861
|
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
862
|
+
getEnabledAgentExtensions: () => getEnabledAgentExtensions,
|
|
1560
863
|
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
1561
864
|
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
1562
865
|
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
@@ -1578,6 +881,8 @@ __export(runtime_config_exports, {
|
|
|
1578
881
|
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
1579
882
|
mergeConfig: () => mergeConfig,
|
|
1580
883
|
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
884
|
+
updateAgentCategoryEnabled: () => updateAgentCategoryEnabled,
|
|
885
|
+
updateAgentRuleEnabled: () => updateAgentRuleEnabled,
|
|
1581
886
|
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
1582
887
|
updateLayerEnabled: () => updateLayerEnabled,
|
|
1583
888
|
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
@@ -1586,9 +891,11 @@ __export(runtime_config_exports, {
|
|
|
1586
891
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1587
892
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1588
893
|
});
|
|
1589
|
-
import { readFileSync as
|
|
1590
|
-
import { join
|
|
1591
|
-
import { homedir
|
|
894
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
895
|
+
import { join } from "path";
|
|
896
|
+
import { homedir } from "os";
|
|
897
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
898
|
+
import { dirname as dirname2 } from "path";
|
|
1592
899
|
function getRuntimeConfigPath() {
|
|
1593
900
|
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
1594
901
|
if (envPath) return envPath;
|
|
@@ -1597,8 +904,8 @@ function getRuntimeConfigPath() {
|
|
|
1597
904
|
function saveRuntimeConfig(config) {
|
|
1598
905
|
const p = getRuntimeConfigPath();
|
|
1599
906
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1600
|
-
if (dir && !
|
|
1601
|
-
|
|
907
|
+
if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
908
|
+
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1602
909
|
}
|
|
1603
910
|
function emptyRuntimeConfig() {
|
|
1604
911
|
return {
|
|
@@ -1606,12 +913,13 @@ function emptyRuntimeConfig() {
|
|
|
1606
913
|
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
1607
914
|
L1_rules: {},
|
|
1608
915
|
L2_rules: {},
|
|
1609
|
-
L3_skills: {}
|
|
916
|
+
L3_skills: {},
|
|
917
|
+
agent_rules: {}
|
|
1610
918
|
};
|
|
1611
919
|
}
|
|
1612
920
|
function loadRuntimeConfig() {
|
|
1613
921
|
const p = getRuntimeConfigPath();
|
|
1614
|
-
if (!
|
|
922
|
+
if (!existsSync3(p)) {
|
|
1615
923
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1616
924
|
try {
|
|
1617
925
|
saveRuntimeConfig(seeded);
|
|
@@ -1620,7 +928,7 @@ function loadRuntimeConfig() {
|
|
|
1620
928
|
return seeded;
|
|
1621
929
|
}
|
|
1622
930
|
try {
|
|
1623
|
-
const data = JSON.parse(
|
|
931
|
+
const data = JSON.parse(readFileSync2(p, "utf-8"));
|
|
1624
932
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1625
933
|
return {
|
|
1626
934
|
version: data.version || 1,
|
|
@@ -1631,7 +939,8 @@ function loadRuntimeConfig() {
|
|
|
1631
939
|
},
|
|
1632
940
|
L1_rules: merged.L1_rules || {},
|
|
1633
941
|
L2_rules: merged.L2_rules || {},
|
|
1634
|
-
L3_skills: merged.L3_skills || {}
|
|
942
|
+
L3_skills: merged.L3_skills || {},
|
|
943
|
+
agent_rules: merged.agent_rules || {}
|
|
1635
944
|
};
|
|
1636
945
|
} catch {
|
|
1637
946
|
return emptyRuntimeConfig();
|
|
@@ -1674,7 +983,10 @@ function getEnabledL1CommandPatterns(runtime) {
|
|
|
1674
983
|
}));
|
|
1675
984
|
}
|
|
1676
985
|
function getEnabledL2SensitivePaths(runtime) {
|
|
1677
|
-
|
|
986
|
+
let cat = runtime.L1_rules?.sensitive_path_access;
|
|
987
|
+
if (!cat || !cat.rules || cat.rules.length === 0) {
|
|
988
|
+
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
989
|
+
}
|
|
1678
990
|
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
1679
991
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1680
992
|
if (r.credential) out.credential = true;
|
|
@@ -1684,9 +996,9 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1684
996
|
}
|
|
1685
997
|
function hasRuntimeConfig() {
|
|
1686
998
|
const p = getRuntimeConfigPath();
|
|
1687
|
-
if (!
|
|
999
|
+
if (!existsSync3(p)) return false;
|
|
1688
1000
|
try {
|
|
1689
|
-
JSON.parse(
|
|
1001
|
+
JSON.parse(readFileSync2(p, "utf-8"));
|
|
1690
1002
|
return true;
|
|
1691
1003
|
} catch {
|
|
1692
1004
|
return false;
|
|
@@ -1700,22 +1012,24 @@ function getDisabledL2RulesWithSkipL3() {
|
|
|
1700
1012
|
return {};
|
|
1701
1013
|
}
|
|
1702
1014
|
const result = {};
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
if (
|
|
1710
|
-
if (
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1015
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
1016
|
+
const layer = runtime[layerKey] || {};
|
|
1017
|
+
for (const [catName, cat] of Object.entries(layer)) {
|
|
1018
|
+
if (!cat.category_enabled) continue;
|
|
1019
|
+
const items = [];
|
|
1020
|
+
for (const r of cat.rules || []) {
|
|
1021
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
1022
|
+
if (catName === "sensitive_path_access") {
|
|
1023
|
+
if (r.path) items.push(r.path);
|
|
1024
|
+
} else if (catName === "intent_consistency") {
|
|
1025
|
+
items.push(...r.intent_keywords || []);
|
|
1026
|
+
} else if (r.pattern) {
|
|
1027
|
+
items.push(r.pattern);
|
|
1028
|
+
}
|
|
1715
1029
|
}
|
|
1716
1030
|
}
|
|
1031
|
+
if (items.length > 0) result[catName] = items;
|
|
1717
1032
|
}
|
|
1718
|
-
if (items.length > 0) result[catName] = items;
|
|
1719
1033
|
}
|
|
1720
1034
|
return result;
|
|
1721
1035
|
}
|
|
@@ -1861,6 +1175,55 @@ function _l3SkillsToRules() {
|
|
|
1861
1175
|
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
1862
1176
|
return out;
|
|
1863
1177
|
}
|
|
1178
|
+
function _agentRulesToDefaults() {
|
|
1179
|
+
const out = {};
|
|
1180
|
+
const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
1181
|
+
if (!existsSync3(agentsDir)) return out;
|
|
1182
|
+
let entries;
|
|
1183
|
+
try {
|
|
1184
|
+
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
1185
|
+
} catch {
|
|
1186
|
+
return out;
|
|
1187
|
+
}
|
|
1188
|
+
for (const agentId of entries) {
|
|
1189
|
+
const rulesFile = join(agentsDir, agentId, "rules.json");
|
|
1190
|
+
if (!existsSync3(rulesFile)) continue;
|
|
1191
|
+
let data;
|
|
1192
|
+
try {
|
|
1193
|
+
data = JSON.parse(readFileSync2(rulesFile, "utf-8"));
|
|
1194
|
+
} catch {
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
out[agentId] = {
|
|
1198
|
+
builtin_safe_action_types: {
|
|
1199
|
+
category_enabled: true,
|
|
1200
|
+
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",
|
|
1201
|
+
rules: (data.builtin_safe_action_types || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1202
|
+
},
|
|
1203
|
+
builtin_safe_skills: {
|
|
1204
|
+
category_enabled: true,
|
|
1205
|
+
category_desc: "agent \u4FE1\u4EFB\u7684\u5185\u7F6E Skill\uFF08\u547D\u4E2D\u8DF3 L2+L3\uFF09",
|
|
1206
|
+
rules: (data.builtin_safe_skills || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1207
|
+
},
|
|
1208
|
+
extra_command_patterns: {
|
|
1209
|
+
category_enabled: true,
|
|
1210
|
+
category_desc: "agent \u4E13\u5C5E\u5371\u9669\u547D\u4EE4\u6B63\u5219\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1211
|
+
rules: (data.extra_command_patterns || []).map((p) => ({ ...p, enabled: true, builtin: true }))
|
|
1212
|
+
},
|
|
1213
|
+
extra_injection_keywords: {
|
|
1214
|
+
category_enabled: true,
|
|
1215
|
+
category_desc: "agent \u4E13\u5C5E\u6CE8\u5165\u5173\u952E\u8BCD\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1216
|
+
rules: (data.extra_injection_keywords || []).map((k) => ({ ...k, enabled: true, builtin: true }))
|
|
1217
|
+
},
|
|
1218
|
+
extra_sensitive_paths: {
|
|
1219
|
+
category_enabled: true,
|
|
1220
|
+
category_desc: "agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1221
|
+
rules: (data.extra_sensitive_paths || []).map((sp) => ({ ...sp, enabled: true, builtin: true }))
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
return out;
|
|
1226
|
+
}
|
|
1864
1227
|
function generateSourceDefaults() {
|
|
1865
1228
|
return {
|
|
1866
1229
|
version: 1,
|
|
@@ -1876,15 +1239,14 @@ function generateSourceDefaults() {
|
|
|
1876
1239
|
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
1877
1240
|
rules: _l1InjectionKeywordsToRules()
|
|
1878
1241
|
},
|
|
1879
|
-
user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
|
|
1880
|
-
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1881
|
-
},
|
|
1882
|
-
L2_rules: {
|
|
1883
1242
|
sensitive_path_access: {
|
|
1884
1243
|
category_enabled: true,
|
|
1885
|
-
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
|
|
1244
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
|
|
1886
1245
|
rules: _l2SensitivePathsToRules()
|
|
1887
1246
|
},
|
|
1247
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1248
|
+
},
|
|
1249
|
+
L2_rules: {
|
|
1888
1250
|
intent_consistency: {
|
|
1889
1251
|
category_enabled: true,
|
|
1890
1252
|
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
@@ -1901,9 +1263,10 @@ function generateSourceDefaults() {
|
|
|
1901
1263
|
rules: _l2UserDeletionPatternsToRules()
|
|
1902
1264
|
},
|
|
1903
1265
|
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
1904
|
-
|
|
1266
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1905
1267
|
},
|
|
1906
|
-
L3_skills: _l3SkillsToRules()
|
|
1268
|
+
L3_skills: _l3SkillsToRules(),
|
|
1269
|
+
agent_rules: _agentRulesToDefaults()
|
|
1907
1270
|
};
|
|
1908
1271
|
}
|
|
1909
1272
|
function _ruleFingerprint(rule) {
|
|
@@ -1916,6 +1279,7 @@ function _ruleFingerprint(rule) {
|
|
|
1916
1279
|
if (rule.path) return JSON.stringify([rule.path]);
|
|
1917
1280
|
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1918
1281
|
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1282
|
+
if (rule.value) return JSON.stringify([rule.value]);
|
|
1919
1283
|
return JSON.stringify([rule.id || ""]);
|
|
1920
1284
|
}
|
|
1921
1285
|
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
@@ -1969,6 +1333,10 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
1969
1333
|
}
|
|
1970
1334
|
}
|
|
1971
1335
|
}
|
|
1336
|
+
for (const catName of Object.keys(userLayer)) {
|
|
1337
|
+
if (catName === "user_custom") continue;
|
|
1338
|
+
if (!(catName in srcLayer)) delete userLayer[catName];
|
|
1339
|
+
}
|
|
1972
1340
|
}
|
|
1973
1341
|
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
1974
1342
|
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
@@ -1988,6 +1356,50 @@ function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
|
1988
1356
|
}
|
|
1989
1357
|
}
|
|
1990
1358
|
}
|
|
1359
|
+
function _mergeAgentRules(userCfg, srcDefaults) {
|
|
1360
|
+
const userAgent = userCfg.agent_rules || (userCfg.agent_rules = {});
|
|
1361
|
+
const srcAgent = srcDefaults.agent_rules || {};
|
|
1362
|
+
for (const [agentId, srcCats] of Object.entries(srcAgent)) {
|
|
1363
|
+
if (!(agentId in userAgent)) {
|
|
1364
|
+
userAgent[agentId] = JSON.parse(JSON.stringify(srcCats));
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
const userCats = userAgent[agentId];
|
|
1368
|
+
for (const [catName, srcCat] of Object.entries(srcCats)) {
|
|
1369
|
+
if (!(catName in userCats)) {
|
|
1370
|
+
userCats[catName] = JSON.parse(JSON.stringify(srcCat));
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
const userCat = userCats[catName];
|
|
1374
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1375
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1376
|
+
const srcRules = srcCat.rules || [];
|
|
1377
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
1378
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
1379
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
1380
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
1381
|
+
for (const srcR of srcRules) {
|
|
1382
|
+
const fp = _ruleFingerprint(srcR);
|
|
1383
|
+
srcFps.add(fp);
|
|
1384
|
+
if (userByFp.has(fp)) {
|
|
1385
|
+
const existing = userByFp.get(fp);
|
|
1386
|
+
const userEnabled = existing.enabled;
|
|
1387
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1388
|
+
Object.assign(existing, srcR);
|
|
1389
|
+
existing.enabled = userEnabled;
|
|
1390
|
+
} else {
|
|
1391
|
+
userRules.push({ ...srcR, enabled: true });
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
for (const r of userRules) {
|
|
1395
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
1396
|
+
r.source_removed = true;
|
|
1397
|
+
r.enabled = false;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1991
1403
|
function mergeConfig(userCfg, srcDefaults) {
|
|
1992
1404
|
const cfg = userCfg;
|
|
1993
1405
|
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
@@ -1995,15 +1407,47 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
1995
1407
|
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
1996
1408
|
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
1997
1409
|
}
|
|
1410
|
+
_migrateCategoriesAcrossLayers(
|
|
1411
|
+
cfg,
|
|
1412
|
+
"L2_rules",
|
|
1413
|
+
"L1_rules",
|
|
1414
|
+
["sensitive_path_access"]
|
|
1415
|
+
);
|
|
1998
1416
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1999
1417
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2000
1418
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1419
|
+
_mergeAgentRules(cfg, srcDefaults);
|
|
2001
1420
|
return cfg;
|
|
2002
1421
|
}
|
|
2003
|
-
function
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2006
|
-
|
|
1422
|
+
function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
1423
|
+
const src = cfg[srcLayer] || {};
|
|
1424
|
+
const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
|
|
1425
|
+
for (const catName of catNames) {
|
|
1426
|
+
if (!(catName in src)) continue;
|
|
1427
|
+
const srcCat = src[catName];
|
|
1428
|
+
if (!dst[catName]) {
|
|
1429
|
+
dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1430
|
+
}
|
|
1431
|
+
const dstCat = dst[catName];
|
|
1432
|
+
if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
|
|
1433
|
+
if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
|
|
1434
|
+
if (srcCat.rules && srcCat.rules.length > 0) {
|
|
1435
|
+
const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
|
|
1436
|
+
for (const r of srcCat.rules) {
|
|
1437
|
+
const fp = _ruleFingerprint(r);
|
|
1438
|
+
if (!dstFps.has(fp)) {
|
|
1439
|
+
(dstCat.rules = dstCat.rules || []).push(r);
|
|
1440
|
+
dstFps.add(fp);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
delete src[catName];
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
1448
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
1449
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1450
|
+
keyword: r.keyword || "",
|
|
2007
1451
|
risk_level: r.risk_level || "",
|
|
2008
1452
|
lang: r.lang || "en"
|
|
2009
1453
|
}));
|
|
@@ -2092,149 +1536,885 @@ function updateLayerEnabled(layerKey, enabled) {
|
|
|
2092
1536
|
saveRuntimeConfig(runtime);
|
|
2093
1537
|
return runtime;
|
|
2094
1538
|
}
|
|
2095
|
-
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
2096
|
-
const runtime = loadRuntimeConfig();
|
|
2097
|
-
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2098
|
-
if (r.id === ruleId) {
|
|
2099
|
-
r.enabled = enabled;
|
|
2100
|
-
break;
|
|
2101
|
-
}
|
|
1539
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
1540
|
+
const runtime = loadRuntimeConfig();
|
|
1541
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1542
|
+
if (r.id === ruleId) {
|
|
1543
|
+
r.enabled = enabled;
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
saveRuntimeConfig(runtime);
|
|
1548
|
+
return runtime;
|
|
1549
|
+
}
|
|
1550
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
1551
|
+
const runtime = loadRuntimeConfig();
|
|
1552
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1553
|
+
if (r.id === ruleId) {
|
|
1554
|
+
r.deny_mode = denyMode;
|
|
1555
|
+
_syncDenyModeFields(r);
|
|
1556
|
+
break;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
saveRuntimeConfig(runtime);
|
|
1560
|
+
return runtime;
|
|
1561
|
+
}
|
|
1562
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
1563
|
+
const runtime = loadRuntimeConfig();
|
|
1564
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1565
|
+
if (r.id === ruleId) {
|
|
1566
|
+
r.skip_l3_on_disabled = skipL3;
|
|
1567
|
+
break;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
saveRuntimeConfig(runtime);
|
|
1571
|
+
return runtime;
|
|
1572
|
+
}
|
|
1573
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
1574
|
+
const runtime = loadRuntimeConfig();
|
|
1575
|
+
const cat = runtime[layer]?.[category];
|
|
1576
|
+
if (cat) cat.category_enabled = enabled;
|
|
1577
|
+
saveRuntimeConfig(runtime);
|
|
1578
|
+
return runtime;
|
|
1579
|
+
}
|
|
1580
|
+
function addCustomRule(layer, category, rule) {
|
|
1581
|
+
const runtime = loadRuntimeConfig();
|
|
1582
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
1583
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
1584
|
+
const rules = cat.rules || (cat.rules = []);
|
|
1585
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
1586
|
+
if (!newRule.id) {
|
|
1587
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
1588
|
+
}
|
|
1589
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
1590
|
+
rules.push(newRule);
|
|
1591
|
+
saveRuntimeConfig(runtime);
|
|
1592
|
+
return runtime;
|
|
1593
|
+
}
|
|
1594
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
1595
|
+
const runtime = loadRuntimeConfig();
|
|
1596
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
1597
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
1598
|
+
if (idx >= 0) {
|
|
1599
|
+
const r = rules[idx];
|
|
1600
|
+
if (r.builtin && !r.source_removed) return null;
|
|
1601
|
+
rules.splice(idx, 1);
|
|
1602
|
+
saveRuntimeConfig(runtime);
|
|
1603
|
+
}
|
|
1604
|
+
return runtime;
|
|
1605
|
+
}
|
|
1606
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
1607
|
+
_validateSkillId(skillId);
|
|
1608
|
+
const runtime = loadRuntimeConfig();
|
|
1609
|
+
const skillsDir = join(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
1610
|
+
try {
|
|
1611
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
writeFileSync(join(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
1615
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
1616
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
1617
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
1618
|
+
saveRuntimeConfig(runtime);
|
|
1619
|
+
return runtime;
|
|
1620
|
+
}
|
|
1621
|
+
function deleteCustomSkill(skillId, category) {
|
|
1622
|
+
_validateSkillId(skillId);
|
|
1623
|
+
const runtime = loadRuntimeConfig();
|
|
1624
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
1625
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
1626
|
+
if (idx >= 0) {
|
|
1627
|
+
if (skills[idx].builtin) return null;
|
|
1628
|
+
skills.splice(idx, 1);
|
|
1629
|
+
const skillPath = join(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
1630
|
+
try {
|
|
1631
|
+
if (existsSync3(skillPath)) unlinkSync(skillPath);
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
saveRuntimeConfig(runtime);
|
|
1635
|
+
}
|
|
1636
|
+
return runtime;
|
|
1637
|
+
}
|
|
1638
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
1639
|
+
const runtime = loadRuntimeConfig();
|
|
1640
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
1641
|
+
for (const s of catData.skills || []) {
|
|
1642
|
+
if (s.id === skillId) {
|
|
1643
|
+
s.enabled = enabled;
|
|
1644
|
+
break;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
saveRuntimeConfig(runtime);
|
|
1649
|
+
return runtime;
|
|
1650
|
+
}
|
|
1651
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
1652
|
+
const runtime = loadRuntimeConfig();
|
|
1653
|
+
const cat = runtime.L3_skills?.[category];
|
|
1654
|
+
if (cat) cat.category_enabled = enabled;
|
|
1655
|
+
saveRuntimeConfig(runtime);
|
|
1656
|
+
return runtime;
|
|
1657
|
+
}
|
|
1658
|
+
function getProfileOrDetect() {
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
function getAgentRules(runtime) {
|
|
1662
|
+
return runtime.agent_rules || {};
|
|
1663
|
+
}
|
|
1664
|
+
function _agentCategoryEnabledRules(cat) {
|
|
1665
|
+
if (!cat || cat.category_enabled === false) return [];
|
|
1666
|
+
return (cat.rules || []).filter((r) => r.enabled !== false);
|
|
1667
|
+
}
|
|
1668
|
+
function getEnabledAgentExtensions(runtime, agentId) {
|
|
1669
|
+
const agentData = runtime.agent_rules?.[agentId];
|
|
1670
|
+
if (!agentData) return null;
|
|
1671
|
+
const safeAction = _agentCategoryEnabledRules(agentData.builtin_safe_action_types);
|
|
1672
|
+
const safeSkills = _agentCategoryEnabledRules(agentData.builtin_safe_skills);
|
|
1673
|
+
const cmdPatterns = _agentCategoryEnabledRules(agentData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
1674
|
+
pattern: r.pattern,
|
|
1675
|
+
risk_level: r.risk_level || "",
|
|
1676
|
+
risk_type: r.risk_type || "",
|
|
1677
|
+
reason: r.reason || ""
|
|
1678
|
+
}));
|
|
1679
|
+
const injKeywords = _agentCategoryEnabledRules(agentData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
1680
|
+
keyword: r.keyword,
|
|
1681
|
+
risk_level: r.risk_level || "",
|
|
1682
|
+
lang: r.lang || "en"
|
|
1683
|
+
}));
|
|
1684
|
+
const sensitivePaths = _agentCategoryEnabledRules(agentData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
1685
|
+
path: r.path,
|
|
1686
|
+
risk_level: r.risk_level || "",
|
|
1687
|
+
desc: r.desc || "",
|
|
1688
|
+
credential: r.credential,
|
|
1689
|
+
read_only: r.read_only,
|
|
1690
|
+
deny_mode: r.deny_mode
|
|
1691
|
+
}));
|
|
1692
|
+
return {
|
|
1693
|
+
agent_id: agentId,
|
|
1694
|
+
description: "",
|
|
1695
|
+
extra_command_patterns: cmdPatterns,
|
|
1696
|
+
extra_injection_keywords: injKeywords,
|
|
1697
|
+
extra_sensitive_paths: sensitivePaths,
|
|
1698
|
+
builtin_safe_skills: safeSkills.filter((r) => r.value).map((r) => r.value),
|
|
1699
|
+
builtin_safe_action_types: safeAction.filter((r) => r.value).map((r) => r.value),
|
|
1700
|
+
extra_skills: []
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
|
|
1704
|
+
const runtime = loadRuntimeConfig();
|
|
1705
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1706
|
+
if (cat) {
|
|
1707
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1708
|
+
for (const r of cat.rules || []) {
|
|
1709
|
+
if (_ruleFingerprint(r) === targetFp) {
|
|
1710
|
+
r.enabled = enabled;
|
|
1711
|
+
break;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
saveRuntimeConfig(runtime);
|
|
1716
|
+
return runtime;
|
|
1717
|
+
}
|
|
1718
|
+
function addAgentRule(agentId, category, rule) {
|
|
1719
|
+
const runtime = loadRuntimeConfig();
|
|
1720
|
+
if (!runtime.agent_rules) runtime.agent_rules = {};
|
|
1721
|
+
if (!runtime.agent_rules[agentId]) runtime.agent_rules[agentId] = {};
|
|
1722
|
+
const cats = runtime.agent_rules[agentId];
|
|
1723
|
+
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1724
|
+
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
1725
|
+
saveRuntimeConfig(runtime);
|
|
1726
|
+
return runtime;
|
|
1727
|
+
}
|
|
1728
|
+
function deleteAgentRule(agentId, category, ruleFp) {
|
|
1729
|
+
const runtime = loadRuntimeConfig();
|
|
1730
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1731
|
+
if (cat) {
|
|
1732
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1733
|
+
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
1734
|
+
}
|
|
1735
|
+
saveRuntimeConfig(runtime);
|
|
1736
|
+
return runtime;
|
|
1737
|
+
}
|
|
1738
|
+
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
1739
|
+
const runtime = loadRuntimeConfig();
|
|
1740
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1741
|
+
if (cat) cat.category_enabled = enabled;
|
|
1742
|
+
saveRuntimeConfig(runtime);
|
|
1743
|
+
return runtime;
|
|
1744
|
+
}
|
|
1745
|
+
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;
|
|
1746
|
+
var init_runtime_config = __esm({
|
|
1747
|
+
"src/infra/runtime-config.ts"() {
|
|
1748
|
+
"use strict";
|
|
1749
|
+
init_esm_shims();
|
|
1750
|
+
init_heuristic();
|
|
1751
|
+
init_logic_rules();
|
|
1752
|
+
init_skill_loader();
|
|
1753
|
+
__dirname3 = dirname2(fileURLToPath3(import.meta.url));
|
|
1754
|
+
RUNTIME_CONFIG_DIR = join(homedir(), ".config", "agentmoss");
|
|
1755
|
+
RUNTIME_CONFIG_PATH = join(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
1756
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
1757
|
+
LAYER_DISABLE_ENV = {
|
|
1758
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
1759
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
1760
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
1761
|
+
};
|
|
1762
|
+
DENY_MODE_LABELS = {
|
|
1763
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
1764
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
1765
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
1766
|
+
};
|
|
1767
|
+
_AGENTS_DIR_PROD = join(__dirname3, "agents");
|
|
1768
|
+
_AGENTS_DIR_DEV = join(__dirname3, "..", "..", "..", "agent_moss", "agents");
|
|
1769
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
// src/agents/index.ts
|
|
1774
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1775
|
+
import { resolve as resolve2, dirname as dirname3 } from "path";
|
|
1776
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1777
|
+
function resolveAgentsDir() {
|
|
1778
|
+
if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
1779
|
+
if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
1780
|
+
return AGENTS_DIR_PROD;
|
|
1781
|
+
}
|
|
1782
|
+
function getAgentExtensions(agentId) {
|
|
1783
|
+
if (!agentId) return null;
|
|
1784
|
+
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
1785
|
+
let ext = null;
|
|
1786
|
+
try {
|
|
1787
|
+
const { hasRuntimeConfig: hasRuntimeConfig2, loadRuntimeConfig: loadRuntimeConfig2, getEnabledAgentExtensions: getEnabledAgentExtensions2 } = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
1788
|
+
if (hasRuntimeConfig2()) {
|
|
1789
|
+
ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
|
|
1790
|
+
}
|
|
1791
|
+
} catch {
|
|
1792
|
+
}
|
|
1793
|
+
if (ext === null) {
|
|
1794
|
+
const agentsDir = resolveAgentsDir();
|
|
1795
|
+
const rulesFile = resolve2(agentsDir, agentId, "rules.json");
|
|
1796
|
+
if (!existsSync4(rulesFile)) {
|
|
1797
|
+
cache.set(agentId, null);
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
try {
|
|
1801
|
+
const raw = readFileSync3(rulesFile, "utf-8");
|
|
1802
|
+
ext = JSON.parse(raw);
|
|
1803
|
+
if (!ext.agent_id) {
|
|
1804
|
+
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
1805
|
+
cache.set(agentId, null);
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
} catch (e) {
|
|
1809
|
+
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
1810
|
+
cache.set(agentId, null);
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
cache.set(agentId, ext);
|
|
1815
|
+
console.log(
|
|
1816
|
+
`[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`
|
|
1817
|
+
);
|
|
1818
|
+
return ext;
|
|
1819
|
+
}
|
|
1820
|
+
var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
1821
|
+
var init_agents = __esm({
|
|
1822
|
+
"src/agents/index.ts"() {
|
|
1823
|
+
"use strict";
|
|
1824
|
+
init_esm_shims();
|
|
1825
|
+
__filename3 = fileURLToPath4(import.meta.url);
|
|
1826
|
+
__dirname4 = dirname3(__filename3);
|
|
1827
|
+
AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
|
|
1828
|
+
AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
|
|
1829
|
+
cache = /* @__PURE__ */ new Map();
|
|
1830
|
+
}
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
// src/engine/heuristic.ts
|
|
1834
|
+
function runHeuristicCheck(input) {
|
|
1835
|
+
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
1836
|
+
const matched = [];
|
|
1837
|
+
let highestSeverity = "low";
|
|
1838
|
+
let highestRiskType = "";
|
|
1839
|
+
const userRules = getCustomRules();
|
|
1840
|
+
for (const rule of userRules) {
|
|
1841
|
+
try {
|
|
1842
|
+
const re = new RegExp(rule.pattern);
|
|
1843
|
+
if (re.test(command)) {
|
|
1844
|
+
if (rule.action === "Deny") {
|
|
1845
|
+
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
1846
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1847
|
+
highestSeverity = rule.severity;
|
|
1848
|
+
highestRiskType = "custom_rule";
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
} catch {
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
let topRiskType = "";
|
|
1856
|
+
for (const rule of DANGEROUS_PATTERNS) {
|
|
1857
|
+
if (rule.pattern.test(command)) {
|
|
1858
|
+
matched.push(rule.description);
|
|
1859
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1860
|
+
highestSeverity = rule.severity;
|
|
1861
|
+
topRiskType = rule.riskType;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
highestRiskType = topRiskType || highestRiskType;
|
|
1866
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
1867
|
+
if (agentExt?.extra_command_patterns) {
|
|
1868
|
+
for (const rule of agentExt.extra_command_patterns) {
|
|
1869
|
+
try {
|
|
1870
|
+
const re = new RegExp(rule.pattern, "i");
|
|
1871
|
+
if (re.test(command)) {
|
|
1872
|
+
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
1873
|
+
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
1874
|
+
highestSeverity = rule.risk_level;
|
|
1875
|
+
highestRiskType = rule.risk_type;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
} catch {
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
1883
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
1884
|
+
if (injectionHits.length > 0) {
|
|
1885
|
+
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
1886
|
+
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
1887
|
+
if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
|
|
1888
|
+
highestSeverity = topHit.riskLevel;
|
|
1889
|
+
highestRiskType = "prompt_injection";
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const actionTypeL = input.aNext.actionType.toLowerCase();
|
|
1893
|
+
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
1894
|
+
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
1895
|
+
if (!isExcludedType) {
|
|
1896
|
+
const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
1897
|
+
if (pathResult) return pathResult;
|
|
1898
|
+
}
|
|
1899
|
+
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
1900
|
+
if (dangerousResult) return dangerousResult;
|
|
1901
|
+
const lateralResult = checkLateralMovement(detailL);
|
|
1902
|
+
if (lateralResult) return lateralResult;
|
|
1903
|
+
if (matched.length > 0) {
|
|
1904
|
+
return {
|
|
1905
|
+
allowed: false,
|
|
1906
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
1907
|
+
riskLevel: highestSeverity,
|
|
1908
|
+
riskType: highestRiskType,
|
|
1909
|
+
confidence: 95,
|
|
1910
|
+
source: "heuristic",
|
|
1911
|
+
violatedLayers: ["1.1"]
|
|
1912
|
+
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
function checkInjectionKeywords(text, extraKeywords) {
|
|
1918
|
+
const hits = [];
|
|
1919
|
+
for (const entry of INJECTION_KEYWORDS) {
|
|
1920
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1921
|
+
hits.push(entry);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
if (extraKeywords) {
|
|
1925
|
+
for (const entry of extraKeywords) {
|
|
1926
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1927
|
+
hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return hits;
|
|
1932
|
+
}
|
|
1933
|
+
function isInlineScriptCommand(actionDetail) {
|
|
1934
|
+
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;
|
|
1935
|
+
return re.test(actionDetail);
|
|
1936
|
+
}
|
|
1937
|
+
function severityRank(s) {
|
|
1938
|
+
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
1939
|
+
return ranks[s] || 0;
|
|
1940
|
+
}
|
|
1941
|
+
function checkSensitivePath(command, cwd, agentExt) {
|
|
1942
|
+
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
1943
|
+
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/];
|
|
1944
|
+
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
1945
|
+
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"]);
|
|
1946
|
+
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));
|
|
1947
|
+
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
1948
|
+
if (command.includes(sp.path.toLowerCase())) {
|
|
1949
|
+
if (sp.path === "/dev/random") {
|
|
1950
|
+
if (isWriteOpFlag) {
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1953
|
+
return {
|
|
1954
|
+
allowed: false,
|
|
1955
|
+
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1956
|
+
riskLevel: sp.riskLevel,
|
|
1957
|
+
riskType: "file_access",
|
|
1958
|
+
confidence: 90,
|
|
1959
|
+
source: "heuristic",
|
|
1960
|
+
violatedLayers: ["1.1"]
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
return {
|
|
1967
|
+
allowed: false,
|
|
1968
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1969
|
+
riskLevel: sp.riskLevel,
|
|
1970
|
+
riskType: "file_access",
|
|
1971
|
+
confidence: 90,
|
|
1972
|
+
source: "heuristic",
|
|
1973
|
+
violatedLayers: ["1.1"],
|
|
1974
|
+
isCredential: !!sp.credential
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
1979
|
+
const expanded = `${expandedCwd}/${sp.path}`;
|
|
1980
|
+
const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1981
|
+
const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1982
|
+
if (pathRe.test(command) || expandedRe.test(command)) {
|
|
1983
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1986
|
+
return {
|
|
1987
|
+
allowed: false,
|
|
1988
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1989
|
+
riskLevel: sp.riskLevel,
|
|
1990
|
+
riskType: "file_access",
|
|
1991
|
+
confidence: 90,
|
|
1992
|
+
source: "heuristic",
|
|
1993
|
+
violatedLayers: ["1.1"],
|
|
1994
|
+
isCredential: !!sp.credential
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1999
|
+
for (const sp of agentExt.extra_sensitive_paths) {
|
|
2000
|
+
const spPath = sp.path.toLowerCase();
|
|
2001
|
+
const expanded = `${expandedCwd}/${spPath}`;
|
|
2002
|
+
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
2003
|
+
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
2004
|
+
if (denyMode === "deny_read") {
|
|
2005
|
+
if (isWriteOpFlag) continue;
|
|
2006
|
+
} else if (denyMode === "deny_write") {
|
|
2007
|
+
if (!isWriteOpFlag) continue;
|
|
2008
|
+
}
|
|
2009
|
+
return {
|
|
2010
|
+
allowed: false,
|
|
2011
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
2012
|
+
riskLevel: sp.risk_level,
|
|
2013
|
+
riskType: "file_access",
|
|
2014
|
+
confidence: 90,
|
|
2015
|
+
source: "heuristic",
|
|
2016
|
+
violatedLayers: ["1.1"],
|
|
2017
|
+
isCredential: !!sp.credential
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
for (const cp of CREDENTIAL_PATHS) {
|
|
2023
|
+
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2024
|
+
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
2025
|
+
if (re.test(command)) {
|
|
2026
|
+
return {
|
|
2027
|
+
allowed: false,
|
|
2028
|
+
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
2029
|
+
riskLevel: cp.riskLevel,
|
|
2030
|
+
riskType: "file_access",
|
|
2031
|
+
confidence: 90,
|
|
2032
|
+
source: "heuristic",
|
|
2033
|
+
violatedLayers: ["1.1"],
|
|
2034
|
+
isCredential: true
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
2039
|
+
return {
|
|
2040
|
+
allowed: false,
|
|
2041
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
2042
|
+
riskLevel: "critical",
|
|
2043
|
+
riskType: "file_access",
|
|
2044
|
+
confidence: 90,
|
|
2045
|
+
source: "heuristic",
|
|
2046
|
+
violatedLayers: ["1.1"]
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
return null;
|
|
2050
|
+
}
|
|
2051
|
+
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2052
|
+
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2053
|
+
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
2054
|
+
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
2055
|
+
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
2056
|
+
return {
|
|
2057
|
+
allowed: false,
|
|
2058
|
+
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
2059
|
+
riskLevel: "high",
|
|
2060
|
+
riskType: "script_execution",
|
|
2061
|
+
confidence: 90,
|
|
2062
|
+
source: "heuristic",
|
|
2063
|
+
violatedLayers: ["1.1"]
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
2067
|
+
return {
|
|
2068
|
+
allowed: false,
|
|
2069
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
2070
|
+
riskLevel: "critical",
|
|
2071
|
+
riskType: "file_access",
|
|
2072
|
+
confidence: 95,
|
|
2073
|
+
source: "heuristic",
|
|
2074
|
+
violatedLayers: ["1.1"]
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2079
|
+
function extractRemoteHost(command) {
|
|
2080
|
+
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
2081
|
+
return m ? m[1] : null;
|
|
2082
|
+
}
|
|
2083
|
+
function isLocalHost(host) {
|
|
2084
|
+
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
2085
|
+
return localNames.includes(host.replace(/.*@/, ""));
|
|
2086
|
+
}
|
|
2087
|
+
function checkLateralMovement(actionDetail) {
|
|
2088
|
+
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
2089
|
+
const targetHost = extractRemoteHost(actionDetail);
|
|
2090
|
+
if (targetHost && !isLocalHost(targetHost)) {
|
|
2091
|
+
return {
|
|
2092
|
+
allowed: false,
|
|
2093
|
+
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
2094
|
+
riskLevel: "medium",
|
|
2095
|
+
riskType: "lateral_movement",
|
|
2096
|
+
confidence: 80,
|
|
2097
|
+
source: "heuristic",
|
|
2098
|
+
violatedLayers: ["1.1"]
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
return null;
|
|
2103
|
+
}
|
|
2104
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
|
|
2105
|
+
var init_heuristic = __esm({
|
|
2106
|
+
"src/engine/heuristic.ts"() {
|
|
2107
|
+
"use strict";
|
|
2108
|
+
init_esm_shims();
|
|
2109
|
+
init_config();
|
|
2110
|
+
init_agents();
|
|
2111
|
+
init_logic_rules();
|
|
2112
|
+
INJECTION_KEYWORDS = [
|
|
2113
|
+
// ---- Instruction Override ----
|
|
2114
|
+
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
2115
|
+
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
2116
|
+
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
2117
|
+
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
2118
|
+
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
2119
|
+
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
2120
|
+
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
2121
|
+
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
2122
|
+
// ---- Role Hijacking ----
|
|
2123
|
+
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
2124
|
+
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
2125
|
+
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
2126
|
+
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
2127
|
+
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2128
|
+
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2129
|
+
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
2130
|
+
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
2131
|
+
// ---- Command Injection ----
|
|
2132
|
+
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
2133
|
+
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
2134
|
+
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
2135
|
+
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
2136
|
+
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
2137
|
+
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
2138
|
+
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
2139
|
+
// ---- Social Engineering ----
|
|
2140
|
+
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
2141
|
+
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
2142
|
+
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
2143
|
+
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
2144
|
+
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
2145
|
+
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
2146
|
+
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
2147
|
+
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
2148
|
+
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
2149
|
+
// ---- XSS ----
|
|
2150
|
+
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
2151
|
+
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
2152
|
+
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
2153
|
+
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
2154
|
+
// ---- Fake Boundaries ----
|
|
2155
|
+
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
2156
|
+
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
2157
|
+
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
2158
|
+
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
2159
|
+
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
2160
|
+
// ---- Developer Mode ----
|
|
2161
|
+
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
2162
|
+
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
2163
|
+
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
2164
|
+
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
2165
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
2166
|
+
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
2167
|
+
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
2168
|
+
// 由 agent_ext 机制注入,不再污染通用层。
|
|
2169
|
+
];
|
|
2170
|
+
DANGEROUS_PATTERNS = [
|
|
2171
|
+
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
2172
|
+
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
2173
|
+
{ 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" },
|
|
2174
|
+
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
2175
|
+
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
2176
|
+
{ 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" },
|
|
2177
|
+
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
2178
|
+
{ 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" },
|
|
2179
|
+
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
2180
|
+
{ 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" },
|
|
2181
|
+
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
2182
|
+
{ 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" },
|
|
2183
|
+
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
2184
|
+
{ 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" },
|
|
2185
|
+
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2186
|
+
// --- Critical: remote code execution ---
|
|
2187
|
+
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2188
|
+
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2189
|
+
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2190
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2191
|
+
// --- Critical: reverse shell ---
|
|
2192
|
+
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2193
|
+
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
2194
|
+
// --- Critical: sensitive file access ---
|
|
2195
|
+
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
2196
|
+
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
2197
|
+
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
2198
|
+
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2199
|
+
// --- Critical: privilege escalation / container escape ---
|
|
2200
|
+
{ 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" },
|
|
2201
|
+
// --- High: dangerous permissions / ownership ---
|
|
2202
|
+
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
2203
|
+
{ 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" },
|
|
2204
|
+
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2205
|
+
{ 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" },
|
|
2206
|
+
// --- High: system config overwrite ---
|
|
2207
|
+
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2208
|
+
// --- High: download script + execute risk ---
|
|
2209
|
+
{ 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" },
|
|
2210
|
+
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
2211
|
+
// --- High: package manager pipe-to-shell ---
|
|
2212
|
+
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
2213
|
+
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
2214
|
+
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
2215
|
+
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
2216
|
+
{ 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" },
|
|
2217
|
+
// --- High: network port scanning (横向移动侦察) ---
|
|
2218
|
+
{ 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" },
|
|
2219
|
+
{ 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" },
|
|
2220
|
+
// --- Medium: credential export ---
|
|
2221
|
+
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2222
|
+
// --- High: shell rc file append (persistence) ---
|
|
2223
|
+
{ 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" },
|
|
2224
|
+
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2225
|
+
{ 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" },
|
|
2226
|
+
// --- High: non-interactive password modification ---
|
|
2227
|
+
{ 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" },
|
|
2228
|
+
{ 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" },
|
|
2229
|
+
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
2230
|
+
{ 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" },
|
|
2231
|
+
{ 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" },
|
|
2232
|
+
{ 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" },
|
|
2233
|
+
// --- High: non-interactive password modification (extended) ---
|
|
2234
|
+
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
2235
|
+
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
2236
|
+
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
2237
|
+
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
2238
|
+
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
2239
|
+
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
2240
|
+
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
2241
|
+
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
2242
|
+
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
2243
|
+
// --- High: generic command -p parameter password setting ---
|
|
2244
|
+
{ 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" }
|
|
2245
|
+
];
|
|
2246
|
+
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
2247
|
+
}
|
|
2248
|
+
});
|
|
2249
|
+
|
|
2250
|
+
// src/engine/token-stats.ts
|
|
2251
|
+
var token_stats_exports = {};
|
|
2252
|
+
__export(token_stats_exports, {
|
|
2253
|
+
getRecentRecords: () => getRecentRecords,
|
|
2254
|
+
getTokenStats: () => getTokenStats,
|
|
2255
|
+
getTokenTrend: () => getTokenTrend,
|
|
2256
|
+
recordTokenUsage: () => recordTokenUsage,
|
|
2257
|
+
resetTokenStats: () => resetTokenStats
|
|
2258
|
+
});
|
|
2259
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2260
|
+
import { join as join4 } from "path";
|
|
2261
|
+
import { homedir as homedir3 } from "os";
|
|
2262
|
+
function getStatsPath() {
|
|
2263
|
+
const envPath = process.env[ENV_STATS_PATH];
|
|
2264
|
+
return envPath || STATS_FILE;
|
|
2265
|
+
}
|
|
2266
|
+
function loadAllRecords(statsPath) {
|
|
2267
|
+
if (!existsSync7(statsPath)) return [];
|
|
2268
|
+
try {
|
|
2269
|
+
const data = JSON.parse(readFileSync7(statsPath, "utf-8"));
|
|
2270
|
+
if (Array.isArray(data)) return data;
|
|
2271
|
+
if (data && Array.isArray(data.records)) return data.records;
|
|
2272
|
+
return [];
|
|
2273
|
+
} catch (e) {
|
|
2274
|
+
return [];
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
function saveRecords(statsPath, records) {
|
|
2278
|
+
try {
|
|
2279
|
+
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2280
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2281
|
+
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2282
|
+
} catch (e) {
|
|
2102
2283
|
}
|
|
2103
|
-
saveRuntimeConfig(runtime);
|
|
2104
|
-
return runtime;
|
|
2105
2284
|
}
|
|
2106
|
-
function
|
|
2107
|
-
const
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2285
|
+
function recordTokenUsage(record) {
|
|
2286
|
+
const statsPath = getStatsPath();
|
|
2287
|
+
const records = loadAllRecords(statsPath);
|
|
2288
|
+
records.push({
|
|
2289
|
+
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2290
|
+
session_id: record.sessionId,
|
|
2291
|
+
step: record.step,
|
|
2292
|
+
model: record.model,
|
|
2293
|
+
prompt_tokens: record.promptTokens,
|
|
2294
|
+
completion_tokens: record.completionTokens,
|
|
2295
|
+
total_tokens: record.totalTokens
|
|
2296
|
+
});
|
|
2297
|
+
if (records.length > MAX_RECORDS) {
|
|
2298
|
+
records.splice(0, records.length - MAX_RECORDS);
|
|
2114
2299
|
}
|
|
2115
|
-
|
|
2116
|
-
return runtime;
|
|
2300
|
+
saveRecords(statsPath, records);
|
|
2117
2301
|
}
|
|
2118
|
-
function
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2302
|
+
function filterByDate(records, days = 0, startDate, endDate) {
|
|
2303
|
+
if (startDate || endDate) {
|
|
2304
|
+
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
2305
|
+
let endDt = endDate ? new Date(endDate) : void 0;
|
|
2306
|
+
if (endDate) {
|
|
2307
|
+
const e = new Date(endDate);
|
|
2308
|
+
e.setHours(23, 59, 59, 999);
|
|
2309
|
+
endDt = e;
|
|
2124
2310
|
}
|
|
2311
|
+
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
2312
|
+
return records.filter((r) => {
|
|
2313
|
+
try {
|
|
2314
|
+
const ts = new Date(r.timestamp).getTime();
|
|
2315
|
+
return ts >= startDt.getTime() && ts <= endMs;
|
|
2316
|
+
} catch {
|
|
2317
|
+
return true;
|
|
2318
|
+
}
|
|
2319
|
+
});
|
|
2320
|
+
} else if (days > 0) {
|
|
2321
|
+
const cutoff = Date.now() - days * 864e5;
|
|
2322
|
+
return records.filter((r) => {
|
|
2323
|
+
try {
|
|
2324
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
2325
|
+
} catch {
|
|
2326
|
+
return true;
|
|
2327
|
+
}
|
|
2328
|
+
});
|
|
2125
2329
|
}
|
|
2126
|
-
|
|
2127
|
-
return runtime;
|
|
2128
|
-
}
|
|
2129
|
-
function updateCategoryEnabled(layer, category, enabled) {
|
|
2130
|
-
const runtime = loadRuntimeConfig();
|
|
2131
|
-
const cat = runtime[layer]?.[category];
|
|
2132
|
-
if (cat) cat.category_enabled = enabled;
|
|
2133
|
-
saveRuntimeConfig(runtime);
|
|
2134
|
-
return runtime;
|
|
2330
|
+
return records;
|
|
2135
2331
|
}
|
|
2136
|
-
function
|
|
2137
|
-
|
|
2138
|
-
const
|
|
2139
|
-
const
|
|
2140
|
-
const
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
|
|
2332
|
+
function aggregate(records) {
|
|
2333
|
+
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
2334
|
+
const byStep = {};
|
|
2335
|
+
const byModel = {};
|
|
2336
|
+
const byDate = {};
|
|
2337
|
+
const bucket = (b, key, r) => {
|
|
2338
|
+
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
2339
|
+
e.prompt_tokens += r.prompt_tokens || 0;
|
|
2340
|
+
e.completion_tokens += r.completion_tokens || 0;
|
|
2341
|
+
e.total_tokens += r.total_tokens || 0;
|
|
2342
|
+
e.calls += 1;
|
|
2343
|
+
};
|
|
2344
|
+
for (const r of records) {
|
|
2345
|
+
totalPrompt += r.prompt_tokens || 0;
|
|
2346
|
+
totalCompletion += r.completion_tokens || 0;
|
|
2347
|
+
totalTotal += r.total_tokens || 0;
|
|
2348
|
+
bucket(byStep, r.step || "unknown", r);
|
|
2349
|
+
bucket(byModel, r.model || "unknown", r);
|
|
2350
|
+
let dateKey = "unknown";
|
|
2351
|
+
try {
|
|
2352
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2353
|
+
} catch {
|
|
2354
|
+
}
|
|
2355
|
+
bucket(byDate, dateKey, r);
|
|
2144
2356
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
return
|
|
2357
|
+
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
2358
|
+
const sortedByDate = {};
|
|
2359
|
+
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
2360
|
+
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
2149
2361
|
}
|
|
2150
|
-
function
|
|
2151
|
-
const
|
|
2152
|
-
const
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2362
|
+
function getTokenStats(days = 0, startDate, endDate) {
|
|
2363
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2364
|
+
const a = aggregate(records);
|
|
2365
|
+
return {
|
|
2366
|
+
total_prompt_tokens: a.totalPrompt,
|
|
2367
|
+
total_completion_tokens: a.totalCompletion,
|
|
2368
|
+
total_tokens: a.totalTotal,
|
|
2369
|
+
total_calls: records.length,
|
|
2370
|
+
by_step: a.byStep,
|
|
2371
|
+
by_model: a.byModel,
|
|
2372
|
+
by_date: a.byDate
|
|
2373
|
+
};
|
|
2160
2374
|
}
|
|
2161
|
-
function
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2165
|
-
try {
|
|
2166
|
-
mkdirSync2(skillsDir, { recursive: true });
|
|
2167
|
-
} catch {
|
|
2168
|
-
}
|
|
2169
|
-
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2170
|
-
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2171
|
-
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2172
|
-
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
2173
|
-
saveRuntimeConfig(runtime);
|
|
2174
|
-
return runtime;
|
|
2375
|
+
function getRecentRecords(limit = 20) {
|
|
2376
|
+
const records = loadAllRecords(getStatsPath());
|
|
2377
|
+
return records.slice(-limit);
|
|
2175
2378
|
}
|
|
2176
|
-
function
|
|
2177
|
-
|
|
2178
|
-
const
|
|
2179
|
-
const
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
if (skills[idx].builtin) return null;
|
|
2183
|
-
skills.splice(idx, 1);
|
|
2184
|
-
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2379
|
+
function getTokenTrend(days = 0, startDate, endDate) {
|
|
2380
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2381
|
+
const series = {};
|
|
2382
|
+
const datesSet = /* @__PURE__ */ new Set();
|
|
2383
|
+
for (const r of records) {
|
|
2384
|
+
let dateKey;
|
|
2185
2385
|
try {
|
|
2186
|
-
|
|
2386
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2187
2387
|
} catch {
|
|
2388
|
+
continue;
|
|
2188
2389
|
}
|
|
2189
|
-
|
|
2390
|
+
const model = r.model || "unknown";
|
|
2391
|
+
const tokens = r.total_tokens || 0;
|
|
2392
|
+
datesSet.add(dateKey);
|
|
2393
|
+
series[model] = series[model] || {};
|
|
2394
|
+
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
2190
2395
|
}
|
|
2191
|
-
|
|
2396
|
+
const dates = Array.from(datesSet).sort();
|
|
2397
|
+
const models = Object.keys(series).sort();
|
|
2398
|
+
return { dates, models, series };
|
|
2192
2399
|
}
|
|
2193
|
-
function
|
|
2194
|
-
const
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
break;
|
|
2200
|
-
}
|
|
2400
|
+
function resetTokenStats() {
|
|
2401
|
+
const statsPath = getStatsPath();
|
|
2402
|
+
if (existsSync7(statsPath)) {
|
|
2403
|
+
try {
|
|
2404
|
+
unlinkSync2(statsPath);
|
|
2405
|
+
} catch {
|
|
2201
2406
|
}
|
|
2202
2407
|
}
|
|
2203
|
-
saveRuntimeConfig(runtime);
|
|
2204
|
-
return runtime;
|
|
2205
|
-
}
|
|
2206
|
-
function updateSkillCategoryEnabled(category, enabled) {
|
|
2207
|
-
const runtime = loadRuntimeConfig();
|
|
2208
|
-
const cat = runtime.L3_skills?.[category];
|
|
2209
|
-
if (cat) cat.category_enabled = enabled;
|
|
2210
|
-
saveRuntimeConfig(runtime);
|
|
2211
|
-
return runtime;
|
|
2212
|
-
}
|
|
2213
|
-
function getProfileOrDetect() {
|
|
2214
|
-
return null;
|
|
2215
2408
|
}
|
|
2216
|
-
var
|
|
2217
|
-
var
|
|
2218
|
-
"src/
|
|
2409
|
+
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
2410
|
+
var init_token_stats = __esm({
|
|
2411
|
+
"src/engine/token-stats.ts"() {
|
|
2219
2412
|
"use strict";
|
|
2220
2413
|
init_esm_shims();
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2226
|
-
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2227
|
-
LAYER_DISABLE_ENV = {
|
|
2228
|
-
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2229
|
-
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2230
|
-
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2231
|
-
};
|
|
2232
|
-
DENY_MODE_LABELS = {
|
|
2233
|
-
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2234
|
-
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2235
|
-
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2236
|
-
};
|
|
2237
|
-
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2414
|
+
STATS_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2415
|
+
STATS_FILE = join4(STATS_DIR, "agent_moss_token_stats.json");
|
|
2416
|
+
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
2417
|
+
MAX_RECORDS = 1e4;
|
|
2238
2418
|
}
|
|
2239
2419
|
});
|
|
2240
2420
|
|
|
@@ -2320,9 +2500,9 @@ var require_bindings = __commonJS({
|
|
|
2320
2500
|
init_esm_shims();
|
|
2321
2501
|
var fs4 = __require("fs");
|
|
2322
2502
|
var path3 = __require("path");
|
|
2323
|
-
var
|
|
2503
|
+
var fileURLToPath7 = require_file_uri_to_path();
|
|
2324
2504
|
var join6 = path3.join;
|
|
2325
|
-
var
|
|
2505
|
+
var dirname7 = path3.dirname;
|
|
2326
2506
|
var exists = fs4.accessSync && function(path4) {
|
|
2327
2507
|
try {
|
|
2328
2508
|
fs4.accessSync(path4);
|
|
@@ -2432,12 +2612,12 @@ var require_bindings = __commonJS({
|
|
|
2432
2612
|
Error.stackTraceLimit = origSTL;
|
|
2433
2613
|
var fileSchema = "file://";
|
|
2434
2614
|
if (fileName.indexOf(fileSchema) === 0) {
|
|
2435
|
-
fileName =
|
|
2615
|
+
fileName = fileURLToPath7(fileName);
|
|
2436
2616
|
}
|
|
2437
2617
|
return fileName;
|
|
2438
2618
|
};
|
|
2439
2619
|
exports.getRoot = function getRoot(file) {
|
|
2440
|
-
var dir =
|
|
2620
|
+
var dir = dirname7(file), prev;
|
|
2441
2621
|
while (true) {
|
|
2442
2622
|
if (dir === ".") {
|
|
2443
2623
|
dir = process.cwd();
|
|
@@ -3093,28 +3273,28 @@ init_skill_loader();
|
|
|
3093
3273
|
|
|
3094
3274
|
// src/engine/template-loader.ts
|
|
3095
3275
|
init_esm_shims();
|
|
3096
|
-
import { readFileSync as
|
|
3097
|
-
import { resolve as resolve3, dirname as
|
|
3098
|
-
import { fileURLToPath as
|
|
3099
|
-
var __filename4 =
|
|
3100
|
-
var
|
|
3101
|
-
var TEMPLATES_DIR = resolve3(
|
|
3102
|
-
var TEMPLATES_DIR_FALLBACK = resolve3(
|
|
3276
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
3277
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
3278
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3279
|
+
var __filename4 = fileURLToPath5(import.meta.url);
|
|
3280
|
+
var __dirname5 = dirname4(__filename4);
|
|
3281
|
+
var TEMPLATES_DIR = resolve3(__dirname5, "../templates");
|
|
3282
|
+
var TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
|
|
3103
3283
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3104
3284
|
var cachedTemplates = null;
|
|
3105
3285
|
function loadTemplates() {
|
|
3106
3286
|
if (cachedTemplates) return cachedTemplates;
|
|
3107
3287
|
cachedTemplates = {};
|
|
3108
|
-
const dir =
|
|
3109
|
-
if (!
|
|
3288
|
+
const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
3289
|
+
if (!existsSync5(dir)) {
|
|
3110
3290
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
3111
3291
|
return cachedTemplates;
|
|
3112
3292
|
}
|
|
3113
3293
|
for (const file of TEMPLATE_FILES) {
|
|
3114
3294
|
const filePath = resolve3(dir, file);
|
|
3115
|
-
if (
|
|
3295
|
+
if (existsSync5(filePath)) {
|
|
3116
3296
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3117
|
-
cachedTemplates[key] =
|
|
3297
|
+
cachedTemplates[key] = readFileSync4(filePath, "utf-8");
|
|
3118
3298
|
}
|
|
3119
3299
|
}
|
|
3120
3300
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -3123,8 +3303,8 @@ function loadTemplates() {
|
|
|
3123
3303
|
|
|
3124
3304
|
// src/engine/script-analyzer.ts
|
|
3125
3305
|
init_esm_shims();
|
|
3126
|
-
import { readFileSync as
|
|
3127
|
-
import { resolve as resolve4, join } from "path";
|
|
3306
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3307
|
+
import { resolve as resolve4, join as join2 } from "path";
|
|
3128
3308
|
var SUSPICIOUS_PATTERNS = [
|
|
3129
3309
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
3130
3310
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -3257,8 +3437,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3257
3437
|
}
|
|
3258
3438
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3259
3439
|
try {
|
|
3260
|
-
const expandedPath = scriptPath.startsWith("~") ?
|
|
3261
|
-
const raw =
|
|
3440
|
+
const expandedPath = scriptPath.startsWith("~") ? join2(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
|
|
3441
|
+
const raw = readFileSync5(expandedPath, "utf-8");
|
|
3262
3442
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3263
3443
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
3264
3444
|
} catch (e) {
|
|
@@ -3312,9 +3492,9 @@ ${result.scriptContent}
|
|
|
3312
3492
|
init_esm_shims();
|
|
3313
3493
|
init_heuristic();
|
|
3314
3494
|
init_logic_rules();
|
|
3315
|
-
import { readFileSync as
|
|
3316
|
-
import { resolve as resolve5, join as
|
|
3317
|
-
import { homedir } from "os";
|
|
3495
|
+
import { readFileSync as readFileSync6, statSync, existsSync as existsSync6 } from "fs";
|
|
3496
|
+
import { resolve as resolve5, join as join3, isAbsolute, dirname as dirname5 } from "path";
|
|
3497
|
+
import { homedir as homedir2 } from "os";
|
|
3318
3498
|
function emptyChain() {
|
|
3319
3499
|
return {
|
|
3320
3500
|
nodes: [],
|
|
@@ -3542,7 +3722,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
3542
3722
|
let resolved;
|
|
3543
3723
|
try {
|
|
3544
3724
|
resolved = resolvePath(p);
|
|
3545
|
-
if (
|
|
3725
|
+
if (existsSync6(resolved)) {
|
|
3546
3726
|
const stat = statSync(resolved);
|
|
3547
3727
|
if (stat.isFile()) return resolved;
|
|
3548
3728
|
}
|
|
@@ -3554,20 +3734,20 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
3554
3734
|
function resolvePath(p) {
|
|
3555
3735
|
let expanded = p;
|
|
3556
3736
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
3557
|
-
expanded =
|
|
3737
|
+
expanded = join3(homedir2(), expanded.slice(1));
|
|
3558
3738
|
}
|
|
3559
3739
|
return resolve5(expanded);
|
|
3560
3740
|
}
|
|
3561
3741
|
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
3562
3742
|
try {
|
|
3563
3743
|
const path3 = resolvePath(scriptPath);
|
|
3564
|
-
if (!
|
|
3744
|
+
if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
3565
3745
|
const stat = statSync(path3);
|
|
3566
3746
|
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
3567
3747
|
if (stat.size > maxFileSize) {
|
|
3568
3748
|
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
3569
3749
|
}
|
|
3570
|
-
const content =
|
|
3750
|
+
const content = readFileSync6(path3, "utf-8");
|
|
3571
3751
|
const lineCount = content ? content.split("\n").length : 0;
|
|
3572
3752
|
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
3573
3753
|
} catch (e) {
|
|
@@ -3584,9 +3764,9 @@ function extractChildScriptPaths(content, baseDir) {
|
|
|
3584
3764
|
const pathStr = m[1];
|
|
3585
3765
|
if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
|
|
3586
3766
|
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
3587
|
-
const full = isAbsolute(pathStr) ? pathStr :
|
|
3767
|
+
const full = isAbsolute(pathStr) ? pathStr : join3(baseDir, pathStr);
|
|
3588
3768
|
const resolved = resolvePath(full);
|
|
3589
|
-
if (
|
|
3769
|
+
if (existsSync6(resolved)) {
|
|
3590
3770
|
try {
|
|
3591
3771
|
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
3592
3772
|
} catch {
|
|
@@ -3601,7 +3781,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
|
|
|
3601
3781
|
const result = emptyChain();
|
|
3602
3782
|
result.entryPath = entryPath;
|
|
3603
3783
|
const entry = resolvePath(entryPath);
|
|
3604
|
-
const baseDir =
|
|
3784
|
+
const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
|
|
3605
3785
|
resolveRecursive({
|
|
3606
3786
|
path: entry,
|
|
3607
3787
|
depth: 0,
|
|
@@ -3676,7 +3856,7 @@ function resolveRecursive(ctx) {
|
|
|
3676
3856
|
maxTotalSize,
|
|
3677
3857
|
visited,
|
|
3678
3858
|
result,
|
|
3679
|
-
baseDir:
|
|
3859
|
+
baseDir: dirname5(resolvePath(childPath)),
|
|
3680
3860
|
input
|
|
3681
3861
|
});
|
|
3682
3862
|
if (result.hasCritical) return;
|
|
@@ -3938,7 +4118,7 @@ function formatHints(previous) {
|
|
|
3938
4118
|
const hints = [];
|
|
3939
4119
|
if (previous.heuristic) {
|
|
3940
4120
|
hints.push(
|
|
3941
|
-
`\
|
|
4121
|
+
`\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
3942
4122
|
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
3943
4123
|
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
3944
4124
|
);
|
|
@@ -3950,7 +4130,7 @@ function formatHints(previous) {
|
|
|
3950
4130
|
\u539F\u56E0: ${previous.logic.reason}`
|
|
3951
4131
|
);
|
|
3952
4132
|
}
|
|
3953
|
-
return hints.length > 0 ? hints.join("\n\n") : "\
|
|
4133
|
+
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";
|
|
3954
4134
|
}
|
|
3955
4135
|
async function analyzeWithLLM(prompt, config) {
|
|
3956
4136
|
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
@@ -4158,6 +4338,7 @@ async function runAnalysis(input) {
|
|
|
4158
4338
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
4159
4339
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
4160
4340
|
const lowerDetail = rawDetail.toLowerCase();
|
|
4341
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
4161
4342
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
4162
4343
|
return {
|
|
4163
4344
|
allowed: true,
|
|
@@ -4169,7 +4350,23 @@ async function runAnalysis(input) {
|
|
|
4169
4350
|
violatedLayers: []
|
|
4170
4351
|
};
|
|
4171
4352
|
}
|
|
4172
|
-
|
|
4353
|
+
if (agentExt?.builtin_safe_action_types && agentExt.builtin_safe_action_types.length > 0) {
|
|
4354
|
+
const hit = agentExt.builtin_safe_action_types.some((t) => {
|
|
4355
|
+
const tl = t.toLowerCase();
|
|
4356
|
+
return tl.endsWith("-") ? actionType.startsWith(tl) : actionType === tl;
|
|
4357
|
+
});
|
|
4358
|
+
if (hit) {
|
|
4359
|
+
return {
|
|
4360
|
+
allowed: true,
|
|
4361
|
+
reason: `agent \u5185\u90E8\u5B89\u5168\u64CD\u4F5C\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
4362
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
4363
|
+
riskType: "",
|
|
4364
|
+
confidence: 95,
|
|
4365
|
+
source: "whitelist_bypass",
|
|
4366
|
+
violatedLayers: []
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4173
4370
|
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
4174
4371
|
if (agentExt?.builtin_safe_skills) {
|
|
4175
4372
|
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
@@ -4211,9 +4408,11 @@ async function runAnalysis(input) {
|
|
|
4211
4408
|
}
|
|
4212
4409
|
}
|
|
4213
4410
|
const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4411
|
+
const l1FileAccessHit = !!(heuristicResult && !heuristicResult.allowed && heuristicResult.riskType === "file_access");
|
|
4412
|
+
const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
|
|
4413
|
+
if (effectiveResult) {
|
|
4414
|
+
const isCredentialPath = !!effectiveResult.isCredential;
|
|
4415
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && effectiveResult.riskType === "file_access";
|
|
4217
4416
|
if (skipInlineFileAccess) {
|
|
4218
4417
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
4219
4418
|
if (inlineJudgment.actionable) {
|
|
@@ -4239,8 +4438,8 @@ async function runAnalysis(input) {
|
|
|
4239
4438
|
};
|
|
4240
4439
|
}
|
|
4241
4440
|
}
|
|
4242
|
-
if ((
|
|
4243
|
-
return
|
|
4441
|
+
if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
|
|
4442
|
+
return effectiveResult;
|
|
4244
4443
|
}
|
|
4245
4444
|
}
|
|
4246
4445
|
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
@@ -4457,7 +4656,10 @@ async function analyzeRoute(c) {
|
|
|
4457
4656
|
reason: req.reason,
|
|
4458
4657
|
osType,
|
|
4459
4658
|
cwd: req.cwd,
|
|
4460
|
-
|
|
4659
|
+
// agent_id 透传:优先顶层;为空兜底 metadata.agent_type(AgentGate V1
|
|
4660
|
+
// normalizer 历史上只把 agent_type 塞进 metadata,顶层 agent_id 为空 →
|
|
4661
|
+
// 定制规则不生效。AgentGate 已修,此处兜底防御其他只传 metadata 的调用方)。
|
|
4662
|
+
agentId: req.agent_id || req.metadata?.agent_type || "",
|
|
4461
4663
|
llmConfig: req.metadata?.llm_config,
|
|
4462
4664
|
llmLogPath: req.metadata?.llm_log_path
|
|
4463
4665
|
});
|
|
@@ -4513,10 +4715,10 @@ init_runtime_config();
|
|
|
4513
4715
|
init_token_stats();
|
|
4514
4716
|
import { Hono } from "hono";
|
|
4515
4717
|
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4516
|
-
import { join as join5, dirname as
|
|
4517
|
-
import { fileURLToPath as
|
|
4518
|
-
var
|
|
4519
|
-
var STATIC_DIR = join5(
|
|
4718
|
+
import { join as join5, dirname as dirname6 } from "path";
|
|
4719
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
4720
|
+
var __dirname6 = dirname6(fileURLToPath6(import.meta.url));
|
|
4721
|
+
var STATIC_DIR = join5(__dirname6, "static");
|
|
4520
4722
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
4521
4723
|
function getConsoleToken() {
|
|
4522
4724
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -4633,14 +4835,52 @@ function createConsoleRouter() {
|
|
|
4633
4835
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
4634
4836
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
4635
4837
|
}
|
|
4636
|
-
const skillPath = join5(
|
|
4838
|
+
const skillPath = join5(dirname6(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
4637
4839
|
if (!existsSync9(skillPath)) {
|
|
4638
|
-
const builtinPath = join5(
|
|
4840
|
+
const builtinPath = join5(__dirname6, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
4639
4841
|
if (existsSync9(builtinPath)) return c.text(readFileSync8(builtinPath, "utf-8"));
|
|
4640
4842
|
return c.json({ error: "skill not found" }, 404);
|
|
4641
4843
|
}
|
|
4642
4844
|
return c.text(readFileSync8(skillPath, "utf-8"));
|
|
4643
4845
|
});
|
|
4846
|
+
console2.get("/api/agents", (c) => {
|
|
4847
|
+
const rc = loadRuntimeConfig();
|
|
4848
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4849
|
+
});
|
|
4850
|
+
console2.put("/api/agents/rules/enabled", async (c) => {
|
|
4851
|
+
const b = await c.req.json();
|
|
4852
|
+
const fp = {};
|
|
4853
|
+
if (b.value) fp.value = b.value;
|
|
4854
|
+
if (b.pattern) fp.pattern = b.pattern;
|
|
4855
|
+
if (b.keyword) fp.keyword = b.keyword;
|
|
4856
|
+
if (b.path) fp.path = b.path;
|
|
4857
|
+
const rc = updateAgentRuleEnabled(b.agent_id, b.category, fp, b.enabled);
|
|
4858
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4859
|
+
});
|
|
4860
|
+
console2.post("/api/agents/rules", async (c) => {
|
|
4861
|
+
const b = await c.req.json();
|
|
4862
|
+
const rule = {};
|
|
4863
|
+
for (const [k, v] of Object.entries(b)) {
|
|
4864
|
+
if (!["agent_id", "category"].includes(k) && v !== void 0 && v !== null) rule[k] = v;
|
|
4865
|
+
}
|
|
4866
|
+
const rc = addAgentRule(b.agent_id, b.category, rule);
|
|
4867
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4868
|
+
});
|
|
4869
|
+
console2.delete("/api/agents/rules", async (c) => {
|
|
4870
|
+
const b = await c.req.json();
|
|
4871
|
+
const fp = {};
|
|
4872
|
+
if (b.value) fp.value = b.value;
|
|
4873
|
+
if (b.pattern) fp.pattern = b.pattern;
|
|
4874
|
+
if (b.keyword) fp.keyword = b.keyword;
|
|
4875
|
+
if (b.path) fp.path = b.path;
|
|
4876
|
+
const rc = deleteAgentRule(b.agent_id, b.category, fp);
|
|
4877
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4878
|
+
});
|
|
4879
|
+
console2.put("/api/agents/categories/enabled", async (c) => {
|
|
4880
|
+
const b = await c.req.json();
|
|
4881
|
+
const rc = updateAgentCategoryEnabled(b.agent_id, b.category, b.enabled);
|
|
4882
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4883
|
+
});
|
|
4644
4884
|
console2.get("/api/config", (c) => {
|
|
4645
4885
|
const rc = loadRuntimeConfig();
|
|
4646
4886
|
return c.json({ config: rc, config_path: getRuntimeConfigPath() });
|