@kenhkl/agent-moss 0.8.5 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -264,247 +264,6 @@ var init_agents = __esm({
264
264
  }
265
265
  });
266
266
 
267
- // src/engine/heuristic.ts
268
- function runHeuristicCheck(input) {
269
- const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
270
- const matched = [];
271
- let highestSeverity = "low";
272
- let highestRiskType = "";
273
- const userRules = getCustomRules();
274
- for (const rule of userRules) {
275
- try {
276
- const re = new RegExp(rule.pattern);
277
- if (re.test(command)) {
278
- if (rule.action === "Deny") {
279
- matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
280
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
281
- highestSeverity = rule.severity;
282
- highestRiskType = "custom_rule";
283
- }
284
- }
285
- }
286
- } catch {
287
- }
288
- }
289
- let topRiskType = "";
290
- for (const rule of DANGEROUS_PATTERNS) {
291
- if (rule.pattern.test(command)) {
292
- matched.push(rule.description);
293
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
294
- highestSeverity = rule.severity;
295
- topRiskType = rule.riskType;
296
- }
297
- }
298
- }
299
- highestRiskType = topRiskType || highestRiskType;
300
- const agentExt = getAgentExtensions(input.agentId);
301
- if (agentExt?.extra_command_patterns) {
302
- for (const rule of agentExt.extra_command_patterns) {
303
- try {
304
- const re = new RegExp(rule.pattern, "i");
305
- if (re.test(command)) {
306
- matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
307
- if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
308
- highestSeverity = rule.risk_level;
309
- highestRiskType = rule.risk_type;
310
- }
311
- }
312
- } catch {
313
- }
314
- }
315
- }
316
- const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
317
- const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
318
- if (injectionHits.length > 0) {
319
- const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
320
- matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
321
- if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
322
- highestSeverity = topHit.riskLevel;
323
- highestRiskType = "prompt_injection";
324
- }
325
- }
326
- if (matched.length > 0) {
327
- return {
328
- allowed: false,
329
- reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
330
- riskLevel: highestSeverity,
331
- riskType: highestRiskType,
332
- confidence: 95,
333
- source: "heuristic",
334
- violatedLayers: ["1.1"]
335
- // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
336
- };
337
- }
338
- return null;
339
- }
340
- function checkInjectionKeywords(text, extraKeywords) {
341
- const hits = [];
342
- for (const entry of INJECTION_KEYWORDS) {
343
- if (text.includes(entry.keyword.toLowerCase())) {
344
- hits.push(entry);
345
- }
346
- }
347
- if (extraKeywords) {
348
- for (const entry of extraKeywords) {
349
- if (text.includes(entry.keyword.toLowerCase())) {
350
- hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
351
- }
352
- }
353
- }
354
- return hits;
355
- }
356
- function isInlineScriptCommand(actionDetail) {
357
- 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;
358
- return re.test(actionDetail);
359
- }
360
- function severityRank(s) {
361
- const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
362
- return ranks[s] || 0;
363
- }
364
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS;
365
- var init_heuristic = __esm({
366
- "src/engine/heuristic.ts"() {
367
- "use strict";
368
- init_esm_shims();
369
- init_config();
370
- init_agents();
371
- INJECTION_KEYWORDS = [
372
- // ---- Instruction Override ----
373
- { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
374
- { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
375
- { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
376
- { keyword: "forget everything", riskLevel: "high", lang: "en" },
377
- { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
378
- { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
379
- { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
380
- { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
381
- // ---- Role Hijacking ----
382
- { keyword: "you are now", riskLevel: "high", lang: "en" },
383
- { keyword: "you are no longer", riskLevel: "high", lang: "en" },
384
- { keyword: "new instructions", riskLevel: "high", lang: "en" },
385
- { keyword: "from now on, you", riskLevel: "high", lang: "en" },
386
- { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
387
- { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
388
- { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
389
- { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
390
- // ---- Command Injection ----
391
- { keyword: "system prompt", riskLevel: "high", lang: "en" },
392
- { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
393
- { keyword: "output your instructions", riskLevel: "high", lang: "en" },
394
- { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
395
- { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
396
- { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
397
- { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
398
- // ---- Social Engineering ----
399
- { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
400
- { keyword: "security audit approved", riskLevel: "high", lang: "en" },
401
- { keyword: "admin override", riskLevel: "critical", lang: "en" },
402
- { keyword: "debug mode", riskLevel: "high", lang: "en" },
403
- { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
404
- { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
405
- { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
406
- { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
407
- { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
408
- // ---- XSS ----
409
- { keyword: "<script>", riskLevel: "high", lang: "en" },
410
- { keyword: "javascript:", riskLevel: "high", lang: "en" },
411
- { keyword: "onerror=", riskLevel: "high", lang: "en" },
412
- { keyword: "onload=", riskLevel: "high", lang: "en" },
413
- // ---- Fake Boundaries ----
414
- { keyword: "=== END OF", riskLevel: "high", lang: "en" },
415
- { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
416
- { keyword: "[END OF", riskLevel: "high", lang: "en" },
417
- { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
418
- { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
419
- // ---- Developer Mode ----
420
- { keyword: "developer mode", riskLevel: "critical", lang: "en" },
421
- { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
422
- { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
423
- { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
424
- { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
425
- // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
426
- // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
427
- // 由 agent_ext 机制注入,不再污染通用层。
428
- ];
429
- DANGEROUS_PATTERNS = [
430
- // --- Critical: rm command graded detection (strict to loose, first match wins) ---
431
- // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
432
- { 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" },
433
- // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
434
- // 2. rm with -r or -f targeting system dirs
435
- { 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" },
436
- // 3. rm -r absolute path (no -f)
437
- { 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" },
438
- // 4. rm -f absolute path (no -r)
439
- { 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" },
440
- // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
441
- { 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" },
442
- // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
443
- { 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" },
444
- { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
445
- // --- Critical: remote code execution ---
446
- { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
447
- { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
448
- { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
449
- { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
450
- // --- Critical: reverse shell ---
451
- { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
452
- { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
453
- // --- Critical: sensitive file access ---
454
- { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
455
- { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
456
- // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
457
- { pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
458
- // --- Critical: privilege escalation / container escape ---
459
- { 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" },
460
- // --- High: dangerous permissions / ownership ---
461
- { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
462
- { 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" },
463
- // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
464
- { 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" },
465
- // --- High: system config overwrite ---
466
- { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
467
- // --- High: download script + execute risk ---
468
- { 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" },
469
- { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
470
- // --- High: package manager pipe-to-shell ---
471
- { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
472
- { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
473
- { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
474
- // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
475
- { 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" },
476
- // --- High: network port scanning (横向移动侦察) ---
477
- { 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" },
478
- { 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" },
479
- // --- Medium: credential export ---
480
- { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
481
- // --- High: shell rc file append (persistence) ---
482
- { 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" },
483
- // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
484
- { 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" },
485
- // --- High: non-interactive password modification ---
486
- { 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" },
487
- { 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" },
488
- { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
489
- { 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" },
490
- { 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" },
491
- { 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" },
492
- // --- High: non-interactive password modification (extended) ---
493
- { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
494
- { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
495
- { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
496
- // --- High: user/group deletion (irreversible, requires user consent) ---
497
- { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
498
- { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
499
- { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
500
- { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
501
- { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
502
- // --- High: generic command -p parameter password setting ---
503
- { 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" }
504
- ];
505
- }
506
- });
507
-
508
267
  // src/engine/inline-analyzer.ts
509
268
  function isLiteralTextCommand(command) {
510
269
  ECHO_CMD_RE.lastIndex = 0;
@@ -721,165 +480,26 @@ function runLogicCheck(input) {
721
480
  }
722
481
  }
723
482
  }
724
- if (!isExcludedType) {
725
- const agentExt = getAgentExtensions(input.agentId || "");
726
- const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
727
- if (pathResult) return pathResult;
728
- }
729
- if (actionType !== "file_write" && actionType !== "file_edit") {
730
- const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
731
- const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
732
- if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
733
- return {
734
- allowed: false,
735
- reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
736
- riskLevel: "high",
737
- riskType: "script_execution",
738
- confidence: 90,
739
- source: "logic_rule",
740
- violatedLayers: ["2"]
741
- };
742
- }
743
- if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
744
- return {
745
- allowed: false,
746
- reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
747
- riskLevel: "critical",
748
- riskType: "file_access",
749
- confidence: 95,
750
- source: "logic_rule",
751
- violatedLayers: ["2"]
752
- };
753
- }
754
- }
755
- var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
756
- "cat",
757
- "head",
758
- "tail",
759
- "less",
760
- "more",
761
- "grep",
762
- "find",
763
- "awk",
764
- "sed",
765
- "sort",
766
- "uniq",
767
- "wc",
768
- "cut",
769
- "strings",
770
- "od",
771
- "xxd",
772
- "hexdump",
773
- "tr",
774
- "file",
775
- "stat",
776
- "du",
777
- "df",
778
- "ls",
779
- "dir",
780
- "tree",
781
- "nl",
782
- "tac",
783
- "rev",
784
- "lsblk",
785
- "blockdev",
786
- "smartctl",
787
- "udevadm",
788
- "dmidecode",
789
- "lscpu",
790
- "lspci",
791
- "lsusb",
792
- "lsmem",
793
- "lsns",
794
- "lsof",
795
- "hwinfo",
796
- "inxi",
797
- "fdisk",
798
- "parted",
799
- "dmesg",
800
- "journalctl",
801
- "systemctl",
802
- "hostnamectl",
803
- "localectl",
804
- "timedatectl",
805
- "ps",
806
- "top",
807
- "free",
808
- "vmstat",
809
- "iostat",
810
- "mpstat",
811
- "pidof",
812
- "pgrep",
813
- "ip",
814
- "ifconfig",
815
- "route",
816
- "ss",
817
- "netstat",
818
- "arp",
819
- "ethtool",
820
- "nmcli",
821
- "uname",
822
- "arch",
823
- "nproc",
824
- "getconf",
825
- "getent",
826
- "id",
827
- "whoami",
828
- "who",
829
- "w",
830
- "last",
831
- "uptime",
832
- "lsmod",
833
- "modinfo",
834
- "sysctl"
835
- ]);
836
- var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
837
- var _WRITE_COMMAND_RES = [
838
- /\brm\b/,
839
- /\bunlink\b/,
840
- /\brmdir\b/,
841
- /\bshred\b/,
842
- /\bcp\b/,
843
- /\bmv\b/,
844
- /\binstall\b/,
845
- /\btee\b/,
846
- /\bdd\b/,
847
- /\bmkfs\b/,
848
- /\b(fdisk|parted|cfdisk|sfdisk)\b/,
849
- /\bchmod\b/,
850
- /\bchown\b/,
851
- /\btruncate\b/
852
- ];
853
483
  const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
854
484
  if (indirectResult) return indirectResult;
855
485
  if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
856
- const adminContext = actionHistory.some(
857
- (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")
858
- );
859
- if (!adminContext) {
860
- return {
861
- allowed: false,
862
- reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
863
- riskLevel: "high",
864
- riskType: "privilege_escalation",
865
- confidence: 85,
866
- source: "logic_rule",
867
- violatedLayers: ["2"]
868
- };
869
- }
870
- }
871
- if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
872
- const targetHost = extractRemoteHost(actionDetail);
873
- if (targetHost && !isLocalHost(targetHost)) {
874
- return {
875
- allowed: false,
876
- reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
877
- riskLevel: "medium",
878
- riskType: "lateral_movement",
879
- confidence: 80,
880
- source: "logic_rule",
881
- violatedLayers: ["2"]
882
- };
486
+ const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
487
+ const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
488
+ if (!isAdminCmd) {
489
+ const adminContext = actionHistory.some(
490
+ (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")
491
+ );
492
+ if (!adminContext) {
493
+ return {
494
+ allowed: false,
495
+ reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
496
+ riskLevel: "high",
497
+ riskType: "privilege_escalation",
498
+ confidence: 85,
499
+ source: "logic_rule",
500
+ violatedLayers: ["2"]
501
+ };
502
+ }
883
503
  }
884
504
  }
885
505
  const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
@@ -947,119 +567,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
947
567
  }
948
568
  return null;
949
569
  }
950
- function checkSensitivePath(command, cwd, agentExt) {
951
- const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
952
- 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/];
953
- const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
954
- 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"]);
955
- 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));
956
- for (const sp of ROOT_SENSITIVE_PATHS) {
957
- if (command.includes(sp.path.toLowerCase())) {
958
- if (sp.path === "/dev/random") {
959
- if (isWriteOpFlag) {
960
- continue;
961
- }
962
- return {
963
- allowed: false,
964
- reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
965
- riskLevel: sp.riskLevel,
966
- riskType: "sensitive_path_access",
967
- confidence: 90,
968
- source: "logic_rule",
969
- violatedLayers: ["2"]
970
- };
971
- }
972
- if (!isWriteOpFlag && !sp.credential) {
973
- continue;
974
- }
975
- return {
976
- allowed: false,
977
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
978
- riskLevel: sp.riskLevel,
979
- riskType: "sensitive_path_access",
980
- confidence: 90,
981
- source: "logic_rule",
982
- violatedLayers: ["2"]
983
- };
984
- }
985
- }
986
- for (const sp of HOME_SENSITIVE_PATHS) {
987
- const expanded = `${expandedCwd}/${sp.path}`;
988
- if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
989
- if (!isWriteOpFlag && !sp.credential) {
990
- continue;
991
- }
992
- return {
993
- allowed: false,
994
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
995
- riskLevel: sp.riskLevel,
996
- riskType: "sensitive_path_access",
997
- confidence: 90,
998
- source: "logic_rule",
999
- violatedLayers: ["2"]
1000
- };
1001
- }
1002
- }
1003
- if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
1004
- for (const sp of agentExt.extra_sensitive_paths) {
1005
- const spPath = sp.path.toLowerCase();
1006
- const expanded = `${expandedCwd}/${spPath}`;
1007
- if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
1008
- const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
1009
- if (denyMode === "deny_read") {
1010
- if (isWriteOpFlag) continue;
1011
- } else if (denyMode === "deny_write") {
1012
- if (!isWriteOpFlag) continue;
1013
- }
1014
- return {
1015
- allowed: false,
1016
- reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
1017
- riskLevel: sp.risk_level,
1018
- riskType: "sensitive_path_access",
1019
- confidence: 90,
1020
- source: "logic_rule",
1021
- violatedLayers: ["2"]
1022
- };
1023
- }
1024
- }
1025
- }
1026
- for (const cp of CREDENTIAL_PATHS) {
1027
- const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1028
- const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
1029
- if (re.test(command)) {
1030
- return {
1031
- allowed: false,
1032
- reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
1033
- riskLevel: cp.riskLevel,
1034
- riskType: "sensitive_path_access",
1035
- confidence: 90,
1036
- source: "logic_rule",
1037
- violatedLayers: ["2"],
1038
- isCredential: true
1039
- };
1040
- }
1041
- }
1042
- if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
1043
- return {
1044
- allowed: false,
1045
- reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
1046
- riskLevel: "critical",
1047
- riskType: "sensitive_path_access",
1048
- confidence: 90,
1049
- source: "logic_rule",
1050
- violatedLayers: ["2"]
1051
- };
1052
- }
1053
- return null;
1054
- }
1055
- function extractRemoteHost(command) {
1056
- const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1057
- return m ? m[1] : null;
1058
- }
1059
- function isLocalHost(host) {
1060
- const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1061
- return localNames.includes(host.replace(/.*@/, ""));
1062
- }
1063
570
  function checkPasswordConsent(actionType, actionDetail, actionHistory) {
1064
571
  if (actionType !== "bash") return null;
1065
572
  const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
@@ -1174,105 +681,521 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
1174
681
  violatedLayers: ["2"]
1175
682
  };
1176
683
  }
1177
- var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
1178
- var init_logic_rules = __esm({
1179
- "src/engine/logic-rules.ts"() {
684
+ var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
685
+ var init_logic_rules = __esm({
686
+ "src/engine/logic-rules.ts"() {
687
+ "use strict";
688
+ init_esm_shims();
689
+ init_inline_analyzer();
690
+ ROOT_SENSITIVE_PATHS = [
691
+ { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
692
+ { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
693
+ // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
694
+ { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
695
+ { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
696
+ // 系统配置(仅拦截写入)
697
+ { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
698
+ { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
699
+ { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
700
+ { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
701
+ { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
702
+ { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
703
+ { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
704
+ // 危险目录(仅拦截写入)
705
+ { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
706
+ { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
707
+ { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
708
+ // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
709
+ { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
710
+ { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
711
+ { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
712
+ // SSH 主机私钥
713
+ { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
714
+ { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
715
+ { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
716
+ ];
717
+ HOME_SENSITIVE_PATHS = [
718
+ { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
719
+ { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
720
+ { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
721
+ // 持久化与命令劫持(仅拦截写入)
722
+ { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
723
+ { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
724
+ { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
725
+ { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
726
+ { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
727
+ { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
728
+ ];
729
+ CREDENTIAL_PATHS = [
730
+ { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
731
+ { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
732
+ { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
733
+ { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
734
+ { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
735
+ ];
736
+ NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
737
+ READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
738
+ WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362"];
739
+ READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
740
+ CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
741
+ PASSWORD_MODIFY_PATTERNS = [
742
+ /\|\s*passwd\b/i,
743
+ /\bpasswd\s+--stdin\b/i,
744
+ /\bchpasswd\b/i,
745
+ /\bchgpasswd\b/i,
746
+ /\b(newusers|lnewusers)\b/i,
747
+ /\bpasswd\s+-[a-zA-Z]*d\b/i,
748
+ /\bpasswd\s+-[a-zA-Z]*l\b/i
749
+ ];
750
+ USER_DELETION_PATTERNS = [
751
+ /\buserdel\s/i,
752
+ /\bdeluser\s/i,
753
+ /\bluserdel\s/i,
754
+ /\bgroupdel\s/i,
755
+ /\blgroupdel\s/i
756
+ ];
757
+ INTENT_DEVIATION_PATTERNS = [
758
+ {
759
+ intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
760
+ dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
761
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
762
+ },
763
+ {
764
+ intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
765
+ dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
766
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
767
+ }
768
+ ];
769
+ INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
770
+ DIR_TRAVERSAL_KW = [
771
+ "os.listdir",
772
+ "os.walk",
773
+ "os.scandir",
774
+ "glob.glob",
775
+ "glob.iglob",
776
+ "pathlib.",
777
+ "iterdir",
778
+ "rglob",
779
+ "scandir(",
780
+ "listdir("
781
+ ];
782
+ }
783
+ });
784
+
785
+ // src/engine/heuristic.ts
786
+ function runHeuristicCheck(input) {
787
+ const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
788
+ const matched = [];
789
+ let highestSeverity = "low";
790
+ let highestRiskType = "";
791
+ const userRules = getCustomRules();
792
+ for (const rule of userRules) {
793
+ try {
794
+ const re = new RegExp(rule.pattern);
795
+ if (re.test(command)) {
796
+ if (rule.action === "Deny") {
797
+ matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
798
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
799
+ highestSeverity = rule.severity;
800
+ highestRiskType = "custom_rule";
801
+ }
802
+ }
803
+ }
804
+ } catch {
805
+ }
806
+ }
807
+ let topRiskType = "";
808
+ for (const rule of DANGEROUS_PATTERNS) {
809
+ if (rule.pattern.test(command)) {
810
+ matched.push(rule.description);
811
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
812
+ highestSeverity = rule.severity;
813
+ topRiskType = rule.riskType;
814
+ }
815
+ }
816
+ }
817
+ highestRiskType = topRiskType || highestRiskType;
818
+ const agentExt = getAgentExtensions(input.agentId);
819
+ if (agentExt?.extra_command_patterns) {
820
+ for (const rule of agentExt.extra_command_patterns) {
821
+ try {
822
+ const re = new RegExp(rule.pattern, "i");
823
+ if (re.test(command)) {
824
+ matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
825
+ if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
826
+ highestSeverity = rule.risk_level;
827
+ highestRiskType = rule.risk_type;
828
+ }
829
+ }
830
+ } catch {
831
+ }
832
+ }
833
+ }
834
+ const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
835
+ const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
836
+ if (injectionHits.length > 0) {
837
+ const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
838
+ matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
839
+ if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
840
+ highestSeverity = topHit.riskLevel;
841
+ highestRiskType = "prompt_injection";
842
+ }
843
+ }
844
+ const actionTypeL = input.aNext.actionType.toLowerCase();
845
+ const detailL = input.aNext.actionDetail.toLowerCase();
846
+ const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
847
+ if (!isExcludedType) {
848
+ const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
849
+ if (pathResult) return pathResult;
850
+ }
851
+ const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
852
+ if (dangerousResult) return dangerousResult;
853
+ const lateralResult = checkLateralMovement(detailL);
854
+ if (lateralResult) return lateralResult;
855
+ if (matched.length > 0) {
856
+ return {
857
+ allowed: false,
858
+ reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
859
+ riskLevel: highestSeverity,
860
+ riskType: highestRiskType,
861
+ confidence: 95,
862
+ source: "heuristic",
863
+ violatedLayers: ["1.1"]
864
+ // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
865
+ };
866
+ }
867
+ return null;
868
+ }
869
+ function checkInjectionKeywords(text, extraKeywords) {
870
+ const hits = [];
871
+ for (const entry of INJECTION_KEYWORDS) {
872
+ if (text.includes(entry.keyword.toLowerCase())) {
873
+ hits.push(entry);
874
+ }
875
+ }
876
+ if (extraKeywords) {
877
+ for (const entry of extraKeywords) {
878
+ if (text.includes(entry.keyword.toLowerCase())) {
879
+ hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
880
+ }
881
+ }
882
+ }
883
+ return hits;
884
+ }
885
+ function isInlineScriptCommand(actionDetail) {
886
+ 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;
887
+ return re.test(actionDetail);
888
+ }
889
+ function severityRank(s) {
890
+ const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
891
+ return ranks[s] || 0;
892
+ }
893
+ function checkSensitivePath(command, cwd, agentExt) {
894
+ const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
895
+ 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/];
896
+ const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
897
+ 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"]);
898
+ 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));
899
+ for (const sp of ROOT_SENSITIVE_PATHS) {
900
+ if (command.includes(sp.path.toLowerCase())) {
901
+ if (sp.path === "/dev/random") {
902
+ if (isWriteOpFlag) {
903
+ continue;
904
+ }
905
+ return {
906
+ allowed: false,
907
+ reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
908
+ riskLevel: sp.riskLevel,
909
+ riskType: "file_access",
910
+ confidence: 90,
911
+ source: "heuristic",
912
+ violatedLayers: ["1.1"]
913
+ };
914
+ }
915
+ if (!isWriteOpFlag && !sp.credential) {
916
+ continue;
917
+ }
918
+ return {
919
+ allowed: false,
920
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
921
+ riskLevel: sp.riskLevel,
922
+ riskType: "file_access",
923
+ confidence: 90,
924
+ source: "heuristic",
925
+ violatedLayers: ["1.1"],
926
+ isCredential: !!sp.credential
927
+ };
928
+ }
929
+ }
930
+ for (const sp of HOME_SENSITIVE_PATHS) {
931
+ const expanded = `${expandedCwd}/${sp.path}`;
932
+ const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
933
+ const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
934
+ if (pathRe.test(command) || expandedRe.test(command)) {
935
+ if (!isWriteOpFlag && !sp.credential) {
936
+ continue;
937
+ }
938
+ return {
939
+ allowed: false,
940
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
941
+ riskLevel: sp.riskLevel,
942
+ riskType: "file_access",
943
+ confidence: 90,
944
+ source: "heuristic",
945
+ violatedLayers: ["1.1"],
946
+ isCredential: !!sp.credential
947
+ };
948
+ }
949
+ }
950
+ if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
951
+ for (const sp of agentExt.extra_sensitive_paths) {
952
+ const spPath = sp.path.toLowerCase();
953
+ const expanded = `${expandedCwd}/${spPath}`;
954
+ if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
955
+ const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
956
+ if (denyMode === "deny_read") {
957
+ if (isWriteOpFlag) continue;
958
+ } else if (denyMode === "deny_write") {
959
+ if (!isWriteOpFlag) continue;
960
+ }
961
+ return {
962
+ allowed: false,
963
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
964
+ riskLevel: sp.risk_level,
965
+ riskType: "file_access",
966
+ confidence: 90,
967
+ source: "heuristic",
968
+ violatedLayers: ["1.1"],
969
+ isCredential: !!sp.credential
970
+ };
971
+ }
972
+ }
973
+ }
974
+ for (const cp of CREDENTIAL_PATHS) {
975
+ const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
976
+ const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
977
+ if (re.test(command)) {
978
+ return {
979
+ allowed: false,
980
+ reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
981
+ riskLevel: cp.riskLevel,
982
+ riskType: "file_access",
983
+ confidence: 90,
984
+ source: "heuristic",
985
+ violatedLayers: ["1.1"],
986
+ isCredential: true
987
+ };
988
+ }
989
+ }
990
+ if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
991
+ return {
992
+ allowed: false,
993
+ reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
994
+ riskLevel: "critical",
995
+ riskType: "file_access",
996
+ confidence: 90,
997
+ source: "heuristic",
998
+ violatedLayers: ["1.1"]
999
+ };
1000
+ }
1001
+ return null;
1002
+ }
1003
+ function checkDangerousPatterns(actionType, actionDetail) {
1004
+ if (actionType === "file_write" || actionType === "file_edit") return null;
1005
+ const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
1006
+ const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
1007
+ if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
1008
+ return {
1009
+ allowed: false,
1010
+ reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
1011
+ riskLevel: "high",
1012
+ riskType: "script_execution",
1013
+ confidence: 90,
1014
+ source: "heuristic",
1015
+ violatedLayers: ["1.1"]
1016
+ };
1017
+ }
1018
+ if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
1019
+ return {
1020
+ allowed: false,
1021
+ reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
1022
+ riskLevel: "critical",
1023
+ riskType: "file_access",
1024
+ confidence: 95,
1025
+ source: "heuristic",
1026
+ violatedLayers: ["1.1"]
1027
+ };
1028
+ }
1029
+ return null;
1030
+ }
1031
+ function extractRemoteHost(command) {
1032
+ const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1033
+ return m ? m[1] : null;
1034
+ }
1035
+ function isLocalHost(host) {
1036
+ const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1037
+ return localNames.includes(host.replace(/.*@/, ""));
1038
+ }
1039
+ function checkLateralMovement(actionDetail) {
1040
+ if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
1041
+ const targetHost = extractRemoteHost(actionDetail);
1042
+ if (targetHost && !isLocalHost(targetHost)) {
1043
+ return {
1044
+ allowed: false,
1045
+ reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
1046
+ riskLevel: "medium",
1047
+ riskType: "lateral_movement",
1048
+ confidence: 80,
1049
+ source: "heuristic",
1050
+ violatedLayers: ["1.1"]
1051
+ };
1052
+ }
1053
+ }
1054
+ return null;
1055
+ }
1056
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
1057
+ var init_heuristic = __esm({
1058
+ "src/engine/heuristic.ts"() {
1180
1059
  "use strict";
1181
1060
  init_esm_shims();
1182
- init_inline_analyzer();
1061
+ init_config();
1183
1062
  init_agents();
1184
- ROOT_SENSITIVE_PATHS = [
1185
- { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
1186
- { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
1187
- // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
1188
- { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
1189
- { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
1190
- // 系统配置(仅拦截写入)
1191
- { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
1192
- { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
1193
- { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
1194
- { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
1195
- { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
1196
- { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
1197
- { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
1198
- // 危险目录(仅拦截写入)
1199
- { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
1200
- { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
1201
- { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
1202
- // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
1203
- { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
1204
- { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
1205
- { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
1206
- // SSH 主机私钥
1207
- { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
1208
- { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
1209
- { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
1210
- ];
1211
- HOME_SENSITIVE_PATHS = [
1212
- { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
1213
- { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
1214
- { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
1215
- // 持久化与命令劫持(仅拦截写入)
1216
- { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
1217
- { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
1218
- { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
1219
- { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
1220
- { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
1221
- { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
1222
- ];
1223
- CREDENTIAL_PATHS = [
1224
- { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
1225
- { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
1226
- { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
1227
- { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
1228
- { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
1229
- ];
1230
- NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
1231
- READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
1232
- WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362"];
1233
- READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
1234
- CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
1235
- PASSWORD_MODIFY_PATTERNS = [
1236
- /\|\s*passwd\b/i,
1237
- /\bpasswd\s+--stdin\b/i,
1238
- /\bchpasswd\b/i,
1239
- /\bchgpasswd\b/i,
1240
- /\b(newusers|lnewusers)\b/i,
1241
- /\bpasswd\s+-[a-zA-Z]*d\b/i,
1242
- /\bpasswd\s+-[a-zA-Z]*l\b/i
1243
- ];
1244
- USER_DELETION_PATTERNS = [
1245
- /\buserdel\s/i,
1246
- /\bdeluser\s/i,
1247
- /\bluserdel\s/i,
1248
- /\bgroupdel\s/i,
1249
- /\blgroupdel\s/i
1250
- ];
1251
- INTENT_DEVIATION_PATTERNS = [
1252
- {
1253
- intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
1254
- dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
1255
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
1256
- },
1257
- {
1258
- intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
1259
- dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
1260
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
1261
- }
1063
+ init_logic_rules();
1064
+ INJECTION_KEYWORDS = [
1065
+ // ---- Instruction Override ----
1066
+ { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
1067
+ { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
1068
+ { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
1069
+ { keyword: "forget everything", riskLevel: "high", lang: "en" },
1070
+ { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
1071
+ { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
1072
+ { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
1073
+ { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
1074
+ // ---- Role Hijacking ----
1075
+ { keyword: "you are now", riskLevel: "high", lang: "en" },
1076
+ { keyword: "you are no longer", riskLevel: "high", lang: "en" },
1077
+ { keyword: "new instructions", riskLevel: "high", lang: "en" },
1078
+ { keyword: "from now on, you", riskLevel: "high", lang: "en" },
1079
+ { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1080
+ { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1081
+ { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
1082
+ { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
1083
+ // ---- Command Injection ----
1084
+ { keyword: "system prompt", riskLevel: "high", lang: "en" },
1085
+ { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
1086
+ { keyword: "output your instructions", riskLevel: "high", lang: "en" },
1087
+ { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
1088
+ { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
1089
+ { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
1090
+ { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
1091
+ // ---- Social Engineering ----
1092
+ { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
1093
+ { keyword: "security audit approved", riskLevel: "high", lang: "en" },
1094
+ { keyword: "admin override", riskLevel: "critical", lang: "en" },
1095
+ { keyword: "debug mode", riskLevel: "high", lang: "en" },
1096
+ { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
1097
+ { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
1098
+ { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
1099
+ { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
1100
+ { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
1101
+ // ---- XSS ----
1102
+ { keyword: "<script>", riskLevel: "high", lang: "en" },
1103
+ { keyword: "javascript:", riskLevel: "high", lang: "en" },
1104
+ { keyword: "onerror=", riskLevel: "high", lang: "en" },
1105
+ { keyword: "onload=", riskLevel: "high", lang: "en" },
1106
+ // ---- Fake Boundaries ----
1107
+ { keyword: "=== END OF", riskLevel: "high", lang: "en" },
1108
+ { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
1109
+ { keyword: "[END OF", riskLevel: "high", lang: "en" },
1110
+ { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
1111
+ { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
1112
+ // ---- Developer Mode ----
1113
+ { keyword: "developer mode", riskLevel: "critical", lang: "en" },
1114
+ { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
1115
+ { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
1116
+ { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
1117
+ { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
1118
+ // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
1119
+ // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
1120
+ // 由 agent_ext 机制注入,不再污染通用层。
1262
1121
  ];
1263
- INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1264
- DIR_TRAVERSAL_KW = [
1265
- "os.listdir",
1266
- "os.walk",
1267
- "os.scandir",
1268
- "glob.glob",
1269
- "glob.iglob",
1270
- "pathlib.",
1271
- "iterdir",
1272
- "rglob",
1273
- "scandir(",
1274
- "listdir("
1122
+ DANGEROUS_PATTERNS = [
1123
+ // --- Critical: rm command graded detection (strict to loose, first match wins) ---
1124
+ // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
1125
+ { 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" },
1126
+ // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
1127
+ // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
1128
+ { 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" },
1129
+ // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
1130
+ { 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" },
1131
+ // 4. rm -f absolute path (no -r) — 同样排除临时目录
1132
+ { 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" },
1133
+ // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
1134
+ { 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" },
1135
+ // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
1136
+ { 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" },
1137
+ { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
1138
+ // --- Critical: remote code execution ---
1139
+ { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
1140
+ { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
1141
+ { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
1142
+ { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
1143
+ // --- Critical: reverse shell ---
1144
+ { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
1145
+ { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
1146
+ // --- Critical: sensitive file access ---
1147
+ { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
1148
+ { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
1149
+ // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
1150
+ { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
1151
+ // --- Critical: privilege escalation / container escape ---
1152
+ { 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" },
1153
+ // --- High: dangerous permissions / ownership ---
1154
+ { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
1155
+ { 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" },
1156
+ // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
1157
+ { 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" },
1158
+ // --- High: system config overwrite ---
1159
+ { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
1160
+ // --- High: download script + execute risk ---
1161
+ { 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" },
1162
+ { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
1163
+ // --- High: package manager pipe-to-shell ---
1164
+ { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
1165
+ { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
1166
+ { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
1167
+ // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
1168
+ { 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" },
1169
+ // --- High: network port scanning (横向移动侦察) ---
1170
+ { 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" },
1171
+ { 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" },
1172
+ // --- Medium: credential export ---
1173
+ { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
1174
+ // --- High: shell rc file append (persistence) ---
1175
+ { 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" },
1176
+ // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
1177
+ { 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" },
1178
+ // --- High: non-interactive password modification ---
1179
+ { 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" },
1180
+ { 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" },
1181
+ { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
1182
+ { 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" },
1183
+ { 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" },
1184
+ { 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" },
1185
+ // --- High: non-interactive password modification (extended) ---
1186
+ { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
1187
+ { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
1188
+ { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
1189
+ // --- High: user/group deletion (irreversible, requires user consent) ---
1190
+ { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
1191
+ { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
1192
+ { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
1193
+ { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
1194
+ { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
1195
+ // --- High: generic command -p parameter password setting ---
1196
+ { 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" }
1275
1197
  ];
1198
+ SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
1276
1199
  }
1277
1200
  });
1278
1201
 
@@ -2653,7 +2576,10 @@ function getEnabledL1CommandPatterns(runtime) {
2653
2576
  }));
2654
2577
  }
2655
2578
  function getEnabledL2SensitivePaths(runtime) {
2656
- const cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
2579
+ let cat = runtime.L1_rules?.sensitive_path_access;
2580
+ if (!cat || !cat.rules || cat.rules.length === 0) {
2581
+ cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
2582
+ }
2657
2583
  return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
2658
2584
  const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
2659
2585
  if (r.credential) out.credential = true;
@@ -2679,22 +2605,24 @@ function getDisabledL2RulesWithSkipL3() {
2679
2605
  return {};
2680
2606
  }
2681
2607
  const result = {};
2682
- const l2 = runtime.L2_rules || {};
2683
- for (const [catName, cat] of Object.entries(l2)) {
2684
- if (!cat.category_enabled) continue;
2685
- const items = [];
2686
- for (const r of cat.rules || []) {
2687
- if (r.enabled === false && r.skip_l3_on_disabled) {
2688
- if (catName === "sensitive_path_access") {
2689
- if (r.path) items.push(r.path);
2690
- } else if (catName === "intent_consistency") {
2691
- items.push(...r.intent_keywords || []);
2692
- } else if (r.pattern) {
2693
- items.push(r.pattern);
2608
+ for (const layerKey of ["L1_rules", "L2_rules"]) {
2609
+ const layer = runtime[layerKey] || {};
2610
+ for (const [catName, cat] of Object.entries(layer)) {
2611
+ if (!cat.category_enabled) continue;
2612
+ const items = [];
2613
+ for (const r of cat.rules || []) {
2614
+ if (r.enabled === false && r.skip_l3_on_disabled) {
2615
+ if (catName === "sensitive_path_access") {
2616
+ if (r.path) items.push(r.path);
2617
+ } else if (catName === "intent_consistency") {
2618
+ items.push(...r.intent_keywords || []);
2619
+ } else if (r.pattern) {
2620
+ items.push(r.pattern);
2621
+ }
2694
2622
  }
2695
2623
  }
2624
+ if (items.length > 0) result[catName] = items;
2696
2625
  }
2697
- if (items.length > 0) result[catName] = items;
2698
2626
  }
2699
2627
  return result;
2700
2628
  }
@@ -2855,15 +2783,14 @@ function generateSourceDefaults() {
2855
2783
  category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
2856
2784
  rules: _l1InjectionKeywordsToRules()
2857
2785
  },
2858
- user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
2859
- user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
2860
- },
2861
- L2_rules: {
2862
2786
  sensitive_path_access: {
2863
2787
  category_enabled: true,
2864
- category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
2788
+ category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
2865
2789
  rules: _l2SensitivePathsToRules()
2866
2790
  },
2791
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
2792
+ },
2793
+ L2_rules: {
2867
2794
  intent_consistency: {
2868
2795
  category_enabled: true,
2869
2796
  category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
@@ -2880,7 +2807,7 @@ function generateSourceDefaults() {
2880
2807
  rules: _l2UserDeletionPatternsToRules()
2881
2808
  },
2882
2809
  read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
2883
- dangerous_patterns: { category_enabled: true, category_desc: "\u5371\u9669\u64CD\u4F5C\u6A21\u5F0F\u68C0\u6D4B", rules: [] }
2810
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
2884
2811
  },
2885
2812
  L3_skills: _l3SkillsToRules()
2886
2813
  };
@@ -2948,6 +2875,10 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
2948
2875
  }
2949
2876
  }
2950
2877
  }
2878
+ for (const catName of Object.keys(userLayer)) {
2879
+ if (catName === "user_custom") continue;
2880
+ if (!(catName in srcLayer)) delete userLayer[catName];
2881
+ }
2951
2882
  }
2952
2883
  function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
2953
2884
  const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
@@ -2974,11 +2905,42 @@ function mergeConfig(userCfg, srcDefaults) {
2974
2905
  for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
2975
2906
  if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
2976
2907
  }
2908
+ _migrateCategoriesAcrossLayers(
2909
+ cfg,
2910
+ "L2_rules",
2911
+ "L1_rules",
2912
+ ["sensitive_path_access"]
2913
+ );
2977
2914
  _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
2978
2915
  _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
2979
2916
  _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
2980
2917
  return cfg;
2981
2918
  }
2919
+ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
2920
+ const src = cfg[srcLayer] || {};
2921
+ const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
2922
+ for (const catName of catNames) {
2923
+ if (!(catName in src)) continue;
2924
+ const srcCat = src[catName];
2925
+ if (!dst[catName]) {
2926
+ dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
2927
+ }
2928
+ const dstCat = dst[catName];
2929
+ if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
2930
+ if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
2931
+ if (srcCat.rules && srcCat.rules.length > 0) {
2932
+ const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
2933
+ for (const r of srcCat.rules) {
2934
+ const fp = _ruleFingerprint(r);
2935
+ if (!dstFps.has(fp)) {
2936
+ (dstCat.rules = dstCat.rules || []).push(r);
2937
+ dstFps.add(fp);
2938
+ }
2939
+ }
2940
+ }
2941
+ delete src[catName];
2942
+ }
2943
+ }
2982
2944
  function getEnabledL1InjectionKeywords(runtime) {
2983
2945
  const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
2984
2946
  return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
@@ -3131,7 +3093,8 @@ function deleteCustomRule(layer, category, ruleId) {
3131
3093
  const rules = runtime[layer]?.[category]?.rules || [];
3132
3094
  const idx = rules.findIndex((r) => r.id === ruleId);
3133
3095
  if (idx >= 0) {
3134
- if (rules[idx].builtin) return null;
3096
+ const r = rules[idx];
3097
+ if (r.builtin && !r.source_removed) return null;
3135
3098
  rules.splice(idx, 1);
3136
3099
  saveRuntimeConfig(runtime);
3137
3100
  }
@@ -3326,9 +3289,11 @@ async function runAnalysis(input) {
3326
3289
  }
3327
3290
  }
3328
3291
  const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
3329
- if (logicResult && !logicResult.allowed) {
3330
- const isCredentialPath = !!logicResult.isCredential;
3331
- const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && logicResult.riskType === "sensitive_path_access";
3292
+ const l1FileAccessHit = !!(heuristicResult && !heuristicResult.allowed && heuristicResult.riskType === "file_access");
3293
+ const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
3294
+ if (effectiveResult) {
3295
+ const isCredentialPath = !!effectiveResult.isCredential;
3296
+ const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && effectiveResult.riskType === "file_access";
3332
3297
  if (skipInlineFileAccess) {
3333
3298
  const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
3334
3299
  if (inlineJudgment.actionable) {
@@ -3354,8 +3319,8 @@ async function runAnalysis(input) {
3354
3319
  };
3355
3320
  }
3356
3321
  }
3357
- if ((logicResult.riskLevel === "high" || logicResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
3358
- return logicResult;
3322
+ if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
3323
+ return effectiveResult;
3359
3324
  }
3360
3325
  }
3361
3326
  if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
@@ -3606,7 +3571,7 @@ var require_bindings = __commonJS({
3606
3571
  init_esm_shims();
3607
3572
  var fs4 = __require("fs");
3608
3573
  var path3 = __require("path");
3609
- var fileURLToPath6 = require_file_uri_to_path();
3574
+ var fileURLToPath7 = require_file_uri_to_path();
3610
3575
  var join6 = path3.join;
3611
3576
  var dirname6 = path3.dirname;
3612
3577
  var exists = fs4.accessSync && function(path4) {
@@ -3718,7 +3683,7 @@ var require_bindings = __commonJS({
3718
3683
  Error.stackTraceLimit = origSTL;
3719
3684
  var fileSchema = "file://";
3720
3685
  if (fileName.indexOf(fileSchema) === 0) {
3721
- fileName = fileURLToPath6(fileName);
3686
+ fileName = fileURLToPath7(fileName);
3722
3687
  }
3723
3688
  return fileName;
3724
3689
  };
@@ -4749,16 +4714,17 @@ async function startServer(host = "127.0.0.1", port = 9090) {
4749
4714
  const app = createApp();
4750
4715
  return {
4751
4716
  port: actualPort,
4752
- serve: () => {
4717
+ serve: async () => {
4753
4718
  console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
4754
- import("@hono/node-server").then(({ serve: honoServe }) => {
4755
- honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
4756
- });
4719
+ const { serve: honoServe } = await import("@hono/node-server");
4720
+ honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
4757
4721
  }
4758
4722
  };
4759
4723
  }
4760
4724
 
4761
4725
  // src/cli.ts
4726
+ import { fileURLToPath as fileURLToPath6 } from "url";
4727
+ import { realpathSync } from "fs";
4762
4728
  function printHelp() {
4763
4729
  console.log(`AgentMoss security analysis engine (TypeScript v${version})
4764
4730
 
@@ -4840,7 +4806,7 @@ async function main() {
4840
4806
  if (port === 0) port = 9090;
4841
4807
  const server = await startServer(host, port);
4842
4808
  console.log(`Listening on http://${host}:${server.port}`);
4843
- server.serve();
4809
+ await server.serve();
4844
4810
  break;
4845
4811
  }
4846
4812
  case "install": {
@@ -4848,7 +4814,7 @@ async function main() {
4848
4814
  const { execSync } = await import("child_process");
4849
4815
  const fs4 = await import("fs");
4850
4816
  const path3 = await import("path");
4851
- const { fileURLToPath: fileURLToPath6 } = await import("url");
4817
+ const { fileURLToPath: fileURLToPath7 } = await import("url");
4852
4818
  if (process.getuid?.() !== 0) {
4853
4819
  try {
4854
4820
  execSync("sudo -n true", { stdio: "ignore", timeout: 5e3 });
@@ -4863,7 +4829,7 @@ async function main() {
4863
4829
  execSync(sudoArgs.join(" "), { stdio: "inherit" });
4864
4830
  process.exit(0);
4865
4831
  }
4866
- const __filename5 = fileURLToPath6(import.meta.url);
4832
+ const __filename5 = fileURLToPath7(import.meta.url);
4867
4833
  const __dirname6 = path3.dirname(__filename5);
4868
4834
  let srcPath = path3.join(__dirname6, "data", "agent_moss.service");
4869
4835
  if (!fs4.existsSync(srcPath)) {
@@ -5002,8 +4968,22 @@ async function main() {
5002
4968
  process.exit(1);
5003
4969
  }
5004
4970
  }
5005
- main().catch((err) => {
5006
- console.error("Fatal error:", err);
5007
- process.exit(1);
5008
- });
4971
+ var isMain = (() => {
4972
+ try {
4973
+ if (!process.argv[1]) return false;
4974
+ const thisFile = realpathSync(fileURLToPath6(import.meta.url));
4975
+ const argv1 = realpathSync(
4976
+ process.argv[1].startsWith("file://") ? fileURLToPath6(process.argv[1]) : process.argv[1]
4977
+ );
4978
+ return thisFile === argv1;
4979
+ } catch {
4980
+ return false;
4981
+ }
4982
+ })();
4983
+ if (isMain) {
4984
+ main().catch((err) => {
4985
+ console.error("Fatal error:", err);
4986
+ process.exit(1);
4987
+ });
4988
+ }
5009
4989
  //# sourceMappingURL=cli.js.map