@kenhkl/agent-moss 0.9.0 → 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));
@@ -1162,117 +669,533 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
1162
669
  }
1163
670
  }
1164
671
  }
1165
- return null;
672
+ return null;
673
+ }
674
+ return {
675
+ allowed: false,
676
+ reason: "\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u51FD\u6570\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0C\u65E0\u6CD5\u83B7\u53D6 cwd \u5B9E\u9645\u6587\u4EF6\u5217\u8868\uFF0C\u8F6C\u5C423 LLM \u5206\u6790",
677
+ riskLevel: "medium",
678
+ riskType: "file_access",
679
+ confidence: 70,
680
+ source: "logic_rule",
681
+ violatedLayers: ["2"]
682
+ };
683
+ }
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
+ }
1166
1053
  }
1167
- return {
1168
- allowed: false,
1169
- reason: "\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u51FD\u6570\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0C\u65E0\u6CD5\u83B7\u53D6 cwd \u5B9E\u9645\u6587\u4EF6\u5217\u8868\uFF0C\u8F6C\u5C423 LLM \u5206\u6790",
1170
- riskLevel: "medium",
1171
- riskType: "file_access",
1172
- confidence: 70,
1173
- source: "logic_rule",
1174
- violatedLayers: ["2"]
1175
- };
1054
+ return null;
1176
1055
  }
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"() {
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()) {