@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/server.js CHANGED
@@ -263,247 +263,6 @@ var init_agents = __esm({
263
263
  }
264
264
  });
265
265
 
266
- // src/engine/heuristic.ts
267
- function runHeuristicCheck(input) {
268
- const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
269
- const matched = [];
270
- let highestSeverity = "low";
271
- let highestRiskType = "";
272
- const userRules = getCustomRules();
273
- for (const rule of userRules) {
274
- try {
275
- const re = new RegExp(rule.pattern);
276
- if (re.test(command)) {
277
- if (rule.action === "Deny") {
278
- matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
279
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
280
- highestSeverity = rule.severity;
281
- highestRiskType = "custom_rule";
282
- }
283
- }
284
- }
285
- } catch {
286
- }
287
- }
288
- let topRiskType = "";
289
- for (const rule of DANGEROUS_PATTERNS) {
290
- if (rule.pattern.test(command)) {
291
- matched.push(rule.description);
292
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
293
- highestSeverity = rule.severity;
294
- topRiskType = rule.riskType;
295
- }
296
- }
297
- }
298
- highestRiskType = topRiskType || highestRiskType;
299
- const agentExt = getAgentExtensions(input.agentId);
300
- if (agentExt?.extra_command_patterns) {
301
- for (const rule of agentExt.extra_command_patterns) {
302
- try {
303
- const re = new RegExp(rule.pattern, "i");
304
- if (re.test(command)) {
305
- matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
306
- if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
307
- highestSeverity = rule.risk_level;
308
- highestRiskType = rule.risk_type;
309
- }
310
- }
311
- } catch {
312
- }
313
- }
314
- }
315
- const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
316
- const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
317
- if (injectionHits.length > 0) {
318
- const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
319
- matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
320
- if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
321
- highestSeverity = topHit.riskLevel;
322
- highestRiskType = "prompt_injection";
323
- }
324
- }
325
- if (matched.length > 0) {
326
- return {
327
- allowed: false,
328
- reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
329
- riskLevel: highestSeverity,
330
- riskType: highestRiskType,
331
- confidence: 95,
332
- source: "heuristic",
333
- violatedLayers: ["1.1"]
334
- // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
335
- };
336
- }
337
- return null;
338
- }
339
- function checkInjectionKeywords(text, extraKeywords) {
340
- const hits = [];
341
- for (const entry of INJECTION_KEYWORDS) {
342
- if (text.includes(entry.keyword.toLowerCase())) {
343
- hits.push(entry);
344
- }
345
- }
346
- if (extraKeywords) {
347
- for (const entry of extraKeywords) {
348
- if (text.includes(entry.keyword.toLowerCase())) {
349
- hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
350
- }
351
- }
352
- }
353
- return hits;
354
- }
355
- function isInlineScriptCommand(actionDetail) {
356
- 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;
357
- return re.test(actionDetail);
358
- }
359
- function severityRank(s) {
360
- const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
361
- return ranks[s] || 0;
362
- }
363
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS;
364
- var init_heuristic = __esm({
365
- "src/engine/heuristic.ts"() {
366
- "use strict";
367
- init_esm_shims();
368
- init_config();
369
- init_agents();
370
- INJECTION_KEYWORDS = [
371
- // ---- Instruction Override ----
372
- { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
373
- { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
374
- { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
375
- { keyword: "forget everything", riskLevel: "high", lang: "en" },
376
- { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
377
- { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
378
- { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
379
- { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
380
- // ---- Role Hijacking ----
381
- { keyword: "you are now", riskLevel: "high", lang: "en" },
382
- { keyword: "you are no longer", riskLevel: "high", lang: "en" },
383
- { keyword: "new instructions", riskLevel: "high", lang: "en" },
384
- { keyword: "from now on, you", riskLevel: "high", lang: "en" },
385
- { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
386
- { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
387
- { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
388
- { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
389
- // ---- Command Injection ----
390
- { keyword: "system prompt", riskLevel: "high", lang: "en" },
391
- { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
392
- { keyword: "output your instructions", riskLevel: "high", lang: "en" },
393
- { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
394
- { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
395
- { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
396
- { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
397
- // ---- Social Engineering ----
398
- { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
399
- { keyword: "security audit approved", riskLevel: "high", lang: "en" },
400
- { keyword: "admin override", riskLevel: "critical", lang: "en" },
401
- { keyword: "debug mode", riskLevel: "high", lang: "en" },
402
- { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
403
- { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
404
- { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
405
- { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
406
- { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
407
- // ---- XSS ----
408
- { keyword: "<script>", riskLevel: "high", lang: "en" },
409
- { keyword: "javascript:", riskLevel: "high", lang: "en" },
410
- { keyword: "onerror=", riskLevel: "high", lang: "en" },
411
- { keyword: "onload=", riskLevel: "high", lang: "en" },
412
- // ---- Fake Boundaries ----
413
- { keyword: "=== END OF", riskLevel: "high", lang: "en" },
414
- { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
415
- { keyword: "[END OF", riskLevel: "high", lang: "en" },
416
- { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
417
- { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
418
- // ---- Developer Mode ----
419
- { keyword: "developer mode", riskLevel: "critical", lang: "en" },
420
- { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
421
- { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
422
- { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
423
- { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
424
- // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
425
- // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
426
- // 由 agent_ext 机制注入,不再污染通用层。
427
- ];
428
- DANGEROUS_PATTERNS = [
429
- // --- Critical: rm command graded detection (strict to loose, first match wins) ---
430
- // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
431
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
432
- // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
433
- // 2. rm with -r or -f targeting system dirs
434
- { pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
435
- // 3. rm -r absolute path (no -f)
436
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\//i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
437
- // 4. rm -f absolute path (no -r)
438
- { pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\//i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
439
- // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
440
- { pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
441
- // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
442
- { pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
443
- { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
444
- // --- Critical: remote code execution ---
445
- { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
446
- { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
447
- { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
448
- { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
449
- // --- Critical: reverse shell ---
450
- { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
451
- { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
452
- // --- Critical: sensitive file access ---
453
- { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
454
- { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
455
- // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
456
- { pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
457
- // --- Critical: privilege escalation / container escape ---
458
- { pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
459
- // --- High: dangerous permissions / ownership ---
460
- { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
461
- { pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
462
- // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
463
- { pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
464
- // --- High: system config overwrite ---
465
- { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
466
- // --- High: download script + execute risk ---
467
- { pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
468
- { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
469
- // --- High: package manager pipe-to-shell ---
470
- { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
471
- { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
472
- { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
473
- // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
474
- { pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
475
- // --- High: network port scanning (横向移动侦察) ---
476
- { pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
477
- { pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
478
- // --- Medium: credential export ---
479
- { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
480
- // --- High: shell rc file append (persistence) ---
481
- { pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
482
- // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
483
- { pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
484
- // --- High: non-interactive password modification ---
485
- { pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
486
- { pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
487
- { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
488
- { pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
489
- { pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
490
- { pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
491
- // --- High: non-interactive password modification (extended) ---
492
- { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
493
- { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
494
- { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
495
- // --- High: user/group deletion (irreversible, requires user consent) ---
496
- { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
497
- { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
498
- { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
499
- { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
500
- { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
501
- // --- High: generic command -p parameter password setting ---
502
- { pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
503
- ];
504
- }
505
- });
506
-
507
266
  // src/engine/inline-analyzer.ts
508
267
  function isLiteralTextCommand(command) {
509
268
  ECHO_CMD_RE.lastIndex = 0;
@@ -720,165 +479,26 @@ function runLogicCheck(input) {
720
479
  }
721
480
  }
722
481
  }
723
- if (!isExcludedType) {
724
- const agentExt = getAgentExtensions(input.agentId || "");
725
- const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
726
- if (pathResult) return pathResult;
727
- }
728
- if (actionType !== "file_write" && actionType !== "file_edit") {
729
- const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
730
- const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
731
- if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
732
- return {
733
- allowed: false,
734
- reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
735
- riskLevel: "high",
736
- riskType: "script_execution",
737
- confidence: 90,
738
- source: "logic_rule",
739
- violatedLayers: ["2"]
740
- };
741
- }
742
- if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
743
- return {
744
- allowed: false,
745
- reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
746
- riskLevel: "critical",
747
- riskType: "file_access",
748
- confidence: 95,
749
- source: "logic_rule",
750
- violatedLayers: ["2"]
751
- };
752
- }
753
- }
754
- var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
755
- "cat",
756
- "head",
757
- "tail",
758
- "less",
759
- "more",
760
- "grep",
761
- "find",
762
- "awk",
763
- "sed",
764
- "sort",
765
- "uniq",
766
- "wc",
767
- "cut",
768
- "strings",
769
- "od",
770
- "xxd",
771
- "hexdump",
772
- "tr",
773
- "file",
774
- "stat",
775
- "du",
776
- "df",
777
- "ls",
778
- "dir",
779
- "tree",
780
- "nl",
781
- "tac",
782
- "rev",
783
- "lsblk",
784
- "blockdev",
785
- "smartctl",
786
- "udevadm",
787
- "dmidecode",
788
- "lscpu",
789
- "lspci",
790
- "lsusb",
791
- "lsmem",
792
- "lsns",
793
- "lsof",
794
- "hwinfo",
795
- "inxi",
796
- "fdisk",
797
- "parted",
798
- "dmesg",
799
- "journalctl",
800
- "systemctl",
801
- "hostnamectl",
802
- "localectl",
803
- "timedatectl",
804
- "ps",
805
- "top",
806
- "free",
807
- "vmstat",
808
- "iostat",
809
- "mpstat",
810
- "pidof",
811
- "pgrep",
812
- "ip",
813
- "ifconfig",
814
- "route",
815
- "ss",
816
- "netstat",
817
- "arp",
818
- "ethtool",
819
- "nmcli",
820
- "uname",
821
- "arch",
822
- "nproc",
823
- "getconf",
824
- "getent",
825
- "id",
826
- "whoami",
827
- "who",
828
- "w",
829
- "last",
830
- "uptime",
831
- "lsmod",
832
- "modinfo",
833
- "sysctl"
834
- ]);
835
- var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
836
- var _WRITE_COMMAND_RES = [
837
- /\brm\b/,
838
- /\bunlink\b/,
839
- /\brmdir\b/,
840
- /\bshred\b/,
841
- /\bcp\b/,
842
- /\bmv\b/,
843
- /\binstall\b/,
844
- /\btee\b/,
845
- /\bdd\b/,
846
- /\bmkfs\b/,
847
- /\b(fdisk|parted|cfdisk|sfdisk)\b/,
848
- /\bchmod\b/,
849
- /\bchown\b/,
850
- /\btruncate\b/
851
- ];
852
482
  const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
853
483
  if (indirectResult) return indirectResult;
854
484
  if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
855
- const adminContext = actionHistory.some(
856
- (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")
857
- );
858
- if (!adminContext) {
859
- return {
860
- allowed: false,
861
- reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
862
- riskLevel: "high",
863
- riskType: "privilege_escalation",
864
- confidence: 85,
865
- source: "logic_rule",
866
- violatedLayers: ["2"]
867
- };
868
- }
869
- }
870
- if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
871
- const targetHost = extractRemoteHost(actionDetail);
872
- if (targetHost && !isLocalHost(targetHost)) {
873
- return {
874
- allowed: false,
875
- reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
876
- riskLevel: "medium",
877
- riskType: "lateral_movement",
878
- confidence: 80,
879
- source: "logic_rule",
880
- violatedLayers: ["2"]
881
- };
485
+ const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
486
+ const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
487
+ if (!isAdminCmd) {
488
+ const adminContext = actionHistory.some(
489
+ (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")
490
+ );
491
+ if (!adminContext) {
492
+ return {
493
+ allowed: false,
494
+ reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
495
+ riskLevel: "high",
496
+ riskType: "privilege_escalation",
497
+ confidence: 85,
498
+ source: "logic_rule",
499
+ violatedLayers: ["2"]
500
+ };
501
+ }
882
502
  }
883
503
  }
884
504
  const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
@@ -946,119 +566,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
946
566
  }
947
567
  return null;
948
568
  }
949
- function checkSensitivePath(command, cwd, agentExt) {
950
- const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
951
- const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
952
- const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
953
- const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
954
- const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
955
- for (const sp of ROOT_SENSITIVE_PATHS) {
956
- if (command.includes(sp.path.toLowerCase())) {
957
- if (sp.path === "/dev/random") {
958
- if (isWriteOpFlag) {
959
- continue;
960
- }
961
- return {
962
- allowed: false,
963
- reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
964
- riskLevel: sp.riskLevel,
965
- riskType: "sensitive_path_access",
966
- confidence: 90,
967
- source: "logic_rule",
968
- violatedLayers: ["2"]
969
- };
970
- }
971
- if (!isWriteOpFlag && !sp.credential) {
972
- continue;
973
- }
974
- return {
975
- allowed: false,
976
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
977
- riskLevel: sp.riskLevel,
978
- riskType: "sensitive_path_access",
979
- confidence: 90,
980
- source: "logic_rule",
981
- violatedLayers: ["2"]
982
- };
983
- }
984
- }
985
- for (const sp of HOME_SENSITIVE_PATHS) {
986
- const expanded = `${expandedCwd}/${sp.path}`;
987
- if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
988
- if (!isWriteOpFlag && !sp.credential) {
989
- continue;
990
- }
991
- return {
992
- allowed: false,
993
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
994
- riskLevel: sp.riskLevel,
995
- riskType: "sensitive_path_access",
996
- confidence: 90,
997
- source: "logic_rule",
998
- violatedLayers: ["2"]
999
- };
1000
- }
1001
- }
1002
- if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
1003
- for (const sp of agentExt.extra_sensitive_paths) {
1004
- const spPath = sp.path.toLowerCase();
1005
- const expanded = `${expandedCwd}/${spPath}`;
1006
- if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
1007
- const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
1008
- if (denyMode === "deny_read") {
1009
- if (isWriteOpFlag) continue;
1010
- } else if (denyMode === "deny_write") {
1011
- if (!isWriteOpFlag) continue;
1012
- }
1013
- return {
1014
- allowed: false,
1015
- reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
1016
- riskLevel: sp.risk_level,
1017
- riskType: "sensitive_path_access",
1018
- confidence: 90,
1019
- source: "logic_rule",
1020
- violatedLayers: ["2"]
1021
- };
1022
- }
1023
- }
1024
- }
1025
- for (const cp of CREDENTIAL_PATHS) {
1026
- const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1027
- const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
1028
- if (re.test(command)) {
1029
- return {
1030
- allowed: false,
1031
- reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
1032
- riskLevel: cp.riskLevel,
1033
- riskType: "sensitive_path_access",
1034
- confidence: 90,
1035
- source: "logic_rule",
1036
- violatedLayers: ["2"],
1037
- isCredential: true
1038
- };
1039
- }
1040
- }
1041
- if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
1042
- return {
1043
- allowed: false,
1044
- reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
1045
- riskLevel: "critical",
1046
- riskType: "sensitive_path_access",
1047
- confidence: 90,
1048
- source: "logic_rule",
1049
- violatedLayers: ["2"]
1050
- };
1051
- }
1052
- return null;
1053
- }
1054
- function extractRemoteHost(command) {
1055
- const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1056
- return m ? m[1] : null;
1057
- }
1058
- function isLocalHost(host) {
1059
- const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1060
- return localNames.includes(host.replace(/.*@/, ""));
1061
- }
1062
569
  function checkPasswordConsent(actionType, actionDetail, actionHistory) {
1063
570
  if (actionType !== "bash") return null;
1064
571
  const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
@@ -1161,117 +668,533 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
1161
668
  }
1162
669
  }
1163
670
  }
1164
- return null;
671
+ return null;
672
+ }
673
+ return {
674
+ allowed: false,
675
+ 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",
676
+ riskLevel: "medium",
677
+ riskType: "file_access",
678
+ confidence: 70,
679
+ source: "logic_rule",
680
+ violatedLayers: ["2"]
681
+ };
682
+ }
683
+ 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;
684
+ var init_logic_rules = __esm({
685
+ "src/engine/logic-rules.ts"() {
686
+ "use strict";
687
+ init_esm_shims();
688
+ init_inline_analyzer();
689
+ ROOT_SENSITIVE_PATHS = [
690
+ { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
691
+ { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
692
+ // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
693
+ { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
694
+ { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
695
+ // 系统配置(仅拦截写入)
696
+ { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
697
+ { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
698
+ { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
699
+ { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
700
+ { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
701
+ { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
702
+ { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
703
+ // 危险目录(仅拦截写入)
704
+ { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
705
+ { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
706
+ { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
707
+ // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
708
+ { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
709
+ { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
710
+ { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
711
+ // SSH 主机私钥
712
+ { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
713
+ { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
714
+ { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
715
+ ];
716
+ HOME_SENSITIVE_PATHS = [
717
+ { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
718
+ { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
719
+ { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
720
+ // 持久化与命令劫持(仅拦截写入)
721
+ { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
722
+ { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
723
+ { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
724
+ { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
725
+ { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
726
+ { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
727
+ ];
728
+ CREDENTIAL_PATHS = [
729
+ { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
730
+ { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
731
+ { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
732
+ { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
733
+ { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
734
+ ];
735
+ NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
736
+ READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
737
+ 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"];
738
+ 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"];
739
+ CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
740
+ PASSWORD_MODIFY_PATTERNS = [
741
+ /\|\s*passwd\b/i,
742
+ /\bpasswd\s+--stdin\b/i,
743
+ /\bchpasswd\b/i,
744
+ /\bchgpasswd\b/i,
745
+ /\b(newusers|lnewusers)\b/i,
746
+ /\bpasswd\s+-[a-zA-Z]*d\b/i,
747
+ /\bpasswd\s+-[a-zA-Z]*l\b/i
748
+ ];
749
+ USER_DELETION_PATTERNS = [
750
+ /\buserdel\s/i,
751
+ /\bdeluser\s/i,
752
+ /\bluserdel\s/i,
753
+ /\bgroupdel\s/i,
754
+ /\blgroupdel\s/i
755
+ ];
756
+ INTENT_DEVIATION_PATTERNS = [
757
+ {
758
+ intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
759
+ dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
760
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
761
+ },
762
+ {
763
+ intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
764
+ dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
765
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
766
+ }
767
+ ];
768
+ 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;
769
+ DIR_TRAVERSAL_KW = [
770
+ "os.listdir",
771
+ "os.walk",
772
+ "os.scandir",
773
+ "glob.glob",
774
+ "glob.iglob",
775
+ "pathlib.",
776
+ "iterdir",
777
+ "rglob",
778
+ "scandir(",
779
+ "listdir("
780
+ ];
781
+ }
782
+ });
783
+
784
+ // src/engine/heuristic.ts
785
+ function runHeuristicCheck(input) {
786
+ const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
787
+ const matched = [];
788
+ let highestSeverity = "low";
789
+ let highestRiskType = "";
790
+ const userRules = getCustomRules();
791
+ for (const rule of userRules) {
792
+ try {
793
+ const re = new RegExp(rule.pattern);
794
+ if (re.test(command)) {
795
+ if (rule.action === "Deny") {
796
+ matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
797
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
798
+ highestSeverity = rule.severity;
799
+ highestRiskType = "custom_rule";
800
+ }
801
+ }
802
+ }
803
+ } catch {
804
+ }
805
+ }
806
+ let topRiskType = "";
807
+ for (const rule of DANGEROUS_PATTERNS) {
808
+ if (rule.pattern.test(command)) {
809
+ matched.push(rule.description);
810
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
811
+ highestSeverity = rule.severity;
812
+ topRiskType = rule.riskType;
813
+ }
814
+ }
815
+ }
816
+ highestRiskType = topRiskType || highestRiskType;
817
+ const agentExt = getAgentExtensions(input.agentId);
818
+ if (agentExt?.extra_command_patterns) {
819
+ for (const rule of agentExt.extra_command_patterns) {
820
+ try {
821
+ const re = new RegExp(rule.pattern, "i");
822
+ if (re.test(command)) {
823
+ matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
824
+ if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
825
+ highestSeverity = rule.risk_level;
826
+ highestRiskType = rule.risk_type;
827
+ }
828
+ }
829
+ } catch {
830
+ }
831
+ }
832
+ }
833
+ const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
834
+ const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
835
+ if (injectionHits.length > 0) {
836
+ const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
837
+ matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
838
+ if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
839
+ highestSeverity = topHit.riskLevel;
840
+ highestRiskType = "prompt_injection";
841
+ }
842
+ }
843
+ const actionTypeL = input.aNext.actionType.toLowerCase();
844
+ const detailL = input.aNext.actionDetail.toLowerCase();
845
+ const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
846
+ if (!isExcludedType) {
847
+ const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
848
+ if (pathResult) return pathResult;
849
+ }
850
+ const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
851
+ if (dangerousResult) return dangerousResult;
852
+ const lateralResult = checkLateralMovement(detailL);
853
+ if (lateralResult) return lateralResult;
854
+ if (matched.length > 0) {
855
+ return {
856
+ allowed: false,
857
+ reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
858
+ riskLevel: highestSeverity,
859
+ riskType: highestRiskType,
860
+ confidence: 95,
861
+ source: "heuristic",
862
+ violatedLayers: ["1.1"]
863
+ // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
864
+ };
865
+ }
866
+ return null;
867
+ }
868
+ function checkInjectionKeywords(text, extraKeywords) {
869
+ const hits = [];
870
+ for (const entry of INJECTION_KEYWORDS) {
871
+ if (text.includes(entry.keyword.toLowerCase())) {
872
+ hits.push(entry);
873
+ }
874
+ }
875
+ if (extraKeywords) {
876
+ for (const entry of extraKeywords) {
877
+ if (text.includes(entry.keyword.toLowerCase())) {
878
+ hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
879
+ }
880
+ }
881
+ }
882
+ return hits;
883
+ }
884
+ function isInlineScriptCommand(actionDetail) {
885
+ 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;
886
+ return re.test(actionDetail);
887
+ }
888
+ function severityRank(s) {
889
+ const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
890
+ return ranks[s] || 0;
891
+ }
892
+ function checkSensitivePath(command, cwd, agentExt) {
893
+ const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
894
+ 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/];
895
+ const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
896
+ 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"]);
897
+ 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));
898
+ for (const sp of ROOT_SENSITIVE_PATHS) {
899
+ if (command.includes(sp.path.toLowerCase())) {
900
+ if (sp.path === "/dev/random") {
901
+ if (isWriteOpFlag) {
902
+ continue;
903
+ }
904
+ return {
905
+ allowed: false,
906
+ reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
907
+ riskLevel: sp.riskLevel,
908
+ riskType: "file_access",
909
+ confidence: 90,
910
+ source: "heuristic",
911
+ violatedLayers: ["1.1"]
912
+ };
913
+ }
914
+ if (!isWriteOpFlag && !sp.credential) {
915
+ continue;
916
+ }
917
+ return {
918
+ allowed: false,
919
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
920
+ riskLevel: sp.riskLevel,
921
+ riskType: "file_access",
922
+ confidence: 90,
923
+ source: "heuristic",
924
+ violatedLayers: ["1.1"],
925
+ isCredential: !!sp.credential
926
+ };
927
+ }
928
+ }
929
+ for (const sp of HOME_SENSITIVE_PATHS) {
930
+ const expanded = `${expandedCwd}/${sp.path}`;
931
+ const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
932
+ const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
933
+ if (pathRe.test(command) || expandedRe.test(command)) {
934
+ if (!isWriteOpFlag && !sp.credential) {
935
+ continue;
936
+ }
937
+ return {
938
+ allowed: false,
939
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
940
+ riskLevel: sp.riskLevel,
941
+ riskType: "file_access",
942
+ confidence: 90,
943
+ source: "heuristic",
944
+ violatedLayers: ["1.1"],
945
+ isCredential: !!sp.credential
946
+ };
947
+ }
948
+ }
949
+ if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
950
+ for (const sp of agentExt.extra_sensitive_paths) {
951
+ const spPath = sp.path.toLowerCase();
952
+ const expanded = `${expandedCwd}/${spPath}`;
953
+ if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
954
+ const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
955
+ if (denyMode === "deny_read") {
956
+ if (isWriteOpFlag) continue;
957
+ } else if (denyMode === "deny_write") {
958
+ if (!isWriteOpFlag) continue;
959
+ }
960
+ return {
961
+ allowed: false,
962
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
963
+ riskLevel: sp.risk_level,
964
+ riskType: "file_access",
965
+ confidence: 90,
966
+ source: "heuristic",
967
+ violatedLayers: ["1.1"],
968
+ isCredential: !!sp.credential
969
+ };
970
+ }
971
+ }
972
+ }
973
+ for (const cp of CREDENTIAL_PATHS) {
974
+ const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
975
+ const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
976
+ if (re.test(command)) {
977
+ return {
978
+ allowed: false,
979
+ reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
980
+ riskLevel: cp.riskLevel,
981
+ riskType: "file_access",
982
+ confidence: 90,
983
+ source: "heuristic",
984
+ violatedLayers: ["1.1"],
985
+ isCredential: true
986
+ };
987
+ }
988
+ }
989
+ if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
990
+ return {
991
+ allowed: false,
992
+ reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
993
+ riskLevel: "critical",
994
+ riskType: "file_access",
995
+ confidence: 90,
996
+ source: "heuristic",
997
+ violatedLayers: ["1.1"]
998
+ };
999
+ }
1000
+ return null;
1001
+ }
1002
+ function checkDangerousPatterns(actionType, actionDetail) {
1003
+ if (actionType === "file_write" || actionType === "file_edit") return null;
1004
+ const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
1005
+ const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
1006
+ if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
1007
+ return {
1008
+ allowed: false,
1009
+ reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
1010
+ riskLevel: "high",
1011
+ riskType: "script_execution",
1012
+ confidence: 90,
1013
+ source: "heuristic",
1014
+ violatedLayers: ["1.1"]
1015
+ };
1016
+ }
1017
+ if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
1018
+ return {
1019
+ allowed: false,
1020
+ reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
1021
+ riskLevel: "critical",
1022
+ riskType: "file_access",
1023
+ confidence: 95,
1024
+ source: "heuristic",
1025
+ violatedLayers: ["1.1"]
1026
+ };
1027
+ }
1028
+ return null;
1029
+ }
1030
+ function extractRemoteHost(command) {
1031
+ const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1032
+ return m ? m[1] : null;
1033
+ }
1034
+ function isLocalHost(host) {
1035
+ const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1036
+ return localNames.includes(host.replace(/.*@/, ""));
1037
+ }
1038
+ function checkLateralMovement(actionDetail) {
1039
+ if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
1040
+ const targetHost = extractRemoteHost(actionDetail);
1041
+ if (targetHost && !isLocalHost(targetHost)) {
1042
+ return {
1043
+ allowed: false,
1044
+ reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
1045
+ riskLevel: "medium",
1046
+ riskType: "lateral_movement",
1047
+ confidence: 80,
1048
+ source: "heuristic",
1049
+ violatedLayers: ["1.1"]
1050
+ };
1051
+ }
1165
1052
  }
1166
- return {
1167
- allowed: false,
1168
- 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",
1169
- riskLevel: "medium",
1170
- riskType: "file_access",
1171
- confidence: 70,
1172
- source: "logic_rule",
1173
- violatedLayers: ["2"]
1174
- };
1053
+ return null;
1175
1054
  }
1176
- 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;
1177
- var init_logic_rules = __esm({
1178
- "src/engine/logic-rules.ts"() {
1055
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
1056
+ var init_heuristic = __esm({
1057
+ "src/engine/heuristic.ts"() {
1179
1058
  "use strict";
1180
1059
  init_esm_shims();
1181
- init_inline_analyzer();
1060
+ init_config();
1182
1061
  init_agents();
1183
- ROOT_SENSITIVE_PATHS = [
1184
- { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
1185
- { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
1186
- // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
1187
- { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
1188
- { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
1189
- // 系统配置(仅拦截写入)
1190
- { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
1191
- { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
1192
- { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
1193
- { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
1194
- { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
1195
- { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
1196
- { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
1197
- // 危险目录(仅拦截写入)
1198
- { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
1199
- { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
1200
- { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
1201
- // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
1202
- { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
1203
- { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
1204
- { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
1205
- // SSH 主机私钥
1206
- { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
1207
- { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
1208
- { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
1209
- ];
1210
- HOME_SENSITIVE_PATHS = [
1211
- { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
1212
- { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
1213
- { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
1214
- // 持久化与命令劫持(仅拦截写入)
1215
- { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
1216
- { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
1217
- { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
1218
- { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
1219
- { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
1220
- { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
1221
- ];
1222
- CREDENTIAL_PATHS = [
1223
- { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
1224
- { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
1225
- { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
1226
- { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
1227
- { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
1228
- ];
1229
- NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
1230
- READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
1231
- 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"];
1232
- 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"];
1233
- CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
1234
- PASSWORD_MODIFY_PATTERNS = [
1235
- /\|\s*passwd\b/i,
1236
- /\bpasswd\s+--stdin\b/i,
1237
- /\bchpasswd\b/i,
1238
- /\bchgpasswd\b/i,
1239
- /\b(newusers|lnewusers)\b/i,
1240
- /\bpasswd\s+-[a-zA-Z]*d\b/i,
1241
- /\bpasswd\s+-[a-zA-Z]*l\b/i
1242
- ];
1243
- USER_DELETION_PATTERNS = [
1244
- /\buserdel\s/i,
1245
- /\bdeluser\s/i,
1246
- /\bluserdel\s/i,
1247
- /\bgroupdel\s/i,
1248
- /\blgroupdel\s/i
1249
- ];
1250
- INTENT_DEVIATION_PATTERNS = [
1251
- {
1252
- intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
1253
- dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
1254
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
1255
- },
1256
- {
1257
- intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
1258
- dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
1259
- reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
1260
- }
1062
+ init_logic_rules();
1063
+ INJECTION_KEYWORDS = [
1064
+ // ---- Instruction Override ----
1065
+ { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
1066
+ { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
1067
+ { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
1068
+ { keyword: "forget everything", riskLevel: "high", lang: "en" },
1069
+ { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
1070
+ { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
1071
+ { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
1072
+ { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
1073
+ // ---- Role Hijacking ----
1074
+ { keyword: "you are now", riskLevel: "high", lang: "en" },
1075
+ { keyword: "you are no longer", riskLevel: "high", lang: "en" },
1076
+ { keyword: "new instructions", riskLevel: "high", lang: "en" },
1077
+ { keyword: "from now on, you", riskLevel: "high", lang: "en" },
1078
+ { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1079
+ { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1080
+ { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
1081
+ { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
1082
+ // ---- Command Injection ----
1083
+ { keyword: "system prompt", riskLevel: "high", lang: "en" },
1084
+ { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
1085
+ { keyword: "output your instructions", riskLevel: "high", lang: "en" },
1086
+ { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
1087
+ { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
1088
+ { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
1089
+ { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
1090
+ // ---- Social Engineering ----
1091
+ { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
1092
+ { keyword: "security audit approved", riskLevel: "high", lang: "en" },
1093
+ { keyword: "admin override", riskLevel: "critical", lang: "en" },
1094
+ { keyword: "debug mode", riskLevel: "high", lang: "en" },
1095
+ { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
1096
+ { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
1097
+ { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
1098
+ { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
1099
+ { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
1100
+ // ---- XSS ----
1101
+ { keyword: "<script>", riskLevel: "high", lang: "en" },
1102
+ { keyword: "javascript:", riskLevel: "high", lang: "en" },
1103
+ { keyword: "onerror=", riskLevel: "high", lang: "en" },
1104
+ { keyword: "onload=", riskLevel: "high", lang: "en" },
1105
+ // ---- Fake Boundaries ----
1106
+ { keyword: "=== END OF", riskLevel: "high", lang: "en" },
1107
+ { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
1108
+ { keyword: "[END OF", riskLevel: "high", lang: "en" },
1109
+ { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
1110
+ { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
1111
+ // ---- Developer Mode ----
1112
+ { keyword: "developer mode", riskLevel: "critical", lang: "en" },
1113
+ { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
1114
+ { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
1115
+ { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
1116
+ { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
1117
+ // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
1118
+ // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
1119
+ // 由 agent_ext 机制注入,不再污染通用层。
1261
1120
  ];
1262
- 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;
1263
- DIR_TRAVERSAL_KW = [
1264
- "os.listdir",
1265
- "os.walk",
1266
- "os.scandir",
1267
- "glob.glob",
1268
- "glob.iglob",
1269
- "pathlib.",
1270
- "iterdir",
1271
- "rglob",
1272
- "scandir(",
1273
- "listdir("
1121
+ DANGEROUS_PATTERNS = [
1122
+ // --- Critical: rm command graded detection (strict to loose, first match wins) ---
1123
+ // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
1124
+ { 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" },
1125
+ // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
1126
+ // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
1127
+ { 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" },
1128
+ // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
1129
+ { 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" },
1130
+ // 4. rm -f absolute path (no -r) — 同样排除临时目录
1131
+ { 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" },
1132
+ // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
1133
+ { 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" },
1134
+ // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
1135
+ { 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" },
1136
+ { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
1137
+ // --- Critical: remote code execution ---
1138
+ { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
1139
+ { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
1140
+ { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
1141
+ { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
1142
+ // --- Critical: reverse shell ---
1143
+ { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
1144
+ { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
1145
+ // --- Critical: sensitive file access ---
1146
+ { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
1147
+ { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
1148
+ // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
1149
+ { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
1150
+ // --- Critical: privilege escalation / container escape ---
1151
+ { 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" },
1152
+ // --- High: dangerous permissions / ownership ---
1153
+ { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
1154
+ { 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" },
1155
+ // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
1156
+ { 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" },
1157
+ // --- High: system config overwrite ---
1158
+ { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
1159
+ // --- High: download script + execute risk ---
1160
+ { 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" },
1161
+ { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
1162
+ // --- High: package manager pipe-to-shell ---
1163
+ { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
1164
+ { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
1165
+ { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
1166
+ // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
1167
+ { 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" },
1168
+ // --- High: network port scanning (横向移动侦察) ---
1169
+ { 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" },
1170
+ { 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" },
1171
+ // --- Medium: credential export ---
1172
+ { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
1173
+ // --- High: shell rc file append (persistence) ---
1174
+ { 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" },
1175
+ // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
1176
+ { 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" },
1177
+ // --- High: non-interactive password modification ---
1178
+ { 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" },
1179
+ { 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" },
1180
+ { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
1181
+ { 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" },
1182
+ { 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" },
1183
+ { 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" },
1184
+ // --- High: non-interactive password modification (extended) ---
1185
+ { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
1186
+ { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
1187
+ { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
1188
+ // --- High: user/group deletion (irreversible, requires user consent) ---
1189
+ { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
1190
+ { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
1191
+ { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
1192
+ { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
1193
+ { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
1194
+ // --- High: generic command -p parameter password setting ---
1195
+ { 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" }
1274
1196
  ];
1197
+ SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
1275
1198
  }
1276
1199
  });
1277
1200
 
@@ -1674,7 +1597,10 @@ function getEnabledL1CommandPatterns(runtime) {
1674
1597
  }));
1675
1598
  }
1676
1599
  function getEnabledL2SensitivePaths(runtime) {
1677
- const cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
1600
+ let cat = runtime.L1_rules?.sensitive_path_access;
1601
+ if (!cat || !cat.rules || cat.rules.length === 0) {
1602
+ cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
1603
+ }
1678
1604
  return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
1679
1605
  const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
1680
1606
  if (r.credential) out.credential = true;
@@ -1700,22 +1626,24 @@ function getDisabledL2RulesWithSkipL3() {
1700
1626
  return {};
1701
1627
  }
1702
1628
  const result = {};
1703
- const l2 = runtime.L2_rules || {};
1704
- for (const [catName, cat] of Object.entries(l2)) {
1705
- if (!cat.category_enabled) continue;
1706
- const items = [];
1707
- for (const r of cat.rules || []) {
1708
- if (r.enabled === false && r.skip_l3_on_disabled) {
1709
- if (catName === "sensitive_path_access") {
1710
- if (r.path) items.push(r.path);
1711
- } else if (catName === "intent_consistency") {
1712
- items.push(...r.intent_keywords || []);
1713
- } else if (r.pattern) {
1714
- items.push(r.pattern);
1629
+ for (const layerKey of ["L1_rules", "L2_rules"]) {
1630
+ const layer = runtime[layerKey] || {};
1631
+ for (const [catName, cat] of Object.entries(layer)) {
1632
+ if (!cat.category_enabled) continue;
1633
+ const items = [];
1634
+ for (const r of cat.rules || []) {
1635
+ if (r.enabled === false && r.skip_l3_on_disabled) {
1636
+ if (catName === "sensitive_path_access") {
1637
+ if (r.path) items.push(r.path);
1638
+ } else if (catName === "intent_consistency") {
1639
+ items.push(...r.intent_keywords || []);
1640
+ } else if (r.pattern) {
1641
+ items.push(r.pattern);
1642
+ }
1715
1643
  }
1716
1644
  }
1645
+ if (items.length > 0) result[catName] = items;
1717
1646
  }
1718
- if (items.length > 0) result[catName] = items;
1719
1647
  }
1720
1648
  return result;
1721
1649
  }
@@ -1876,15 +1804,14 @@ function generateSourceDefaults() {
1876
1804
  category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
1877
1805
  rules: _l1InjectionKeywordsToRules()
1878
1806
  },
1879
- user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
1880
- user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
1881
- },
1882
- L2_rules: {
1883
1807
  sensitive_path_access: {
1884
1808
  category_enabled: true,
1885
- category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
1809
+ category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
1886
1810
  rules: _l2SensitivePathsToRules()
1887
1811
  },
1812
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
1813
+ },
1814
+ L2_rules: {
1888
1815
  intent_consistency: {
1889
1816
  category_enabled: true,
1890
1817
  category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
@@ -1901,7 +1828,7 @@ function generateSourceDefaults() {
1901
1828
  rules: _l2UserDeletionPatternsToRules()
1902
1829
  },
1903
1830
  read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
1904
- dangerous_patterns: { category_enabled: true, category_desc: "\u5371\u9669\u64CD\u4F5C\u6A21\u5F0F\u68C0\u6D4B", rules: [] }
1831
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
1905
1832
  },
1906
1833
  L3_skills: _l3SkillsToRules()
1907
1834
  };
@@ -1969,6 +1896,10 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
1969
1896
  }
1970
1897
  }
1971
1898
  }
1899
+ for (const catName of Object.keys(userLayer)) {
1900
+ if (catName === "user_custom") continue;
1901
+ if (!(catName in srcLayer)) delete userLayer[catName];
1902
+ }
1972
1903
  }
1973
1904
  function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
1974
1905
  const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
@@ -1995,11 +1926,42 @@ function mergeConfig(userCfg, srcDefaults) {
1995
1926
  for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
1996
1927
  if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
1997
1928
  }
1929
+ _migrateCategoriesAcrossLayers(
1930
+ cfg,
1931
+ "L2_rules",
1932
+ "L1_rules",
1933
+ ["sensitive_path_access"]
1934
+ );
1998
1935
  _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
1999
1936
  _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
2000
1937
  _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
2001
1938
  return cfg;
2002
1939
  }
1940
+ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
1941
+ const src = cfg[srcLayer] || {};
1942
+ const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
1943
+ for (const catName of catNames) {
1944
+ if (!(catName in src)) continue;
1945
+ const srcCat = src[catName];
1946
+ if (!dst[catName]) {
1947
+ dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
1948
+ }
1949
+ const dstCat = dst[catName];
1950
+ if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
1951
+ if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
1952
+ if (srcCat.rules && srcCat.rules.length > 0) {
1953
+ const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
1954
+ for (const r of srcCat.rules) {
1955
+ const fp = _ruleFingerprint(r);
1956
+ if (!dstFps.has(fp)) {
1957
+ (dstCat.rules = dstCat.rules || []).push(r);
1958
+ dstFps.add(fp);
1959
+ }
1960
+ }
1961
+ }
1962
+ delete src[catName];
1963
+ }
1964
+ }
2003
1965
  function getEnabledL1InjectionKeywords(runtime) {
2004
1966
  const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
2005
1967
  return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
@@ -2152,7 +2114,8 @@ function deleteCustomRule(layer, category, ruleId) {
2152
2114
  const rules = runtime[layer]?.[category]?.rules || [];
2153
2115
  const idx = rules.findIndex((r) => r.id === ruleId);
2154
2116
  if (idx >= 0) {
2155
- if (rules[idx].builtin) return null;
2117
+ const r = rules[idx];
2118
+ if (r.builtin && !r.source_removed) return null;
2156
2119
  rules.splice(idx, 1);
2157
2120
  saveRuntimeConfig(runtime);
2158
2121
  }
@@ -4211,9 +4174,11 @@ async function runAnalysis(input) {
4211
4174
  }
4212
4175
  }
4213
4176
  const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
4214
- if (logicResult && !logicResult.allowed) {
4215
- const isCredentialPath = !!logicResult.isCredential;
4216
- const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && logicResult.riskType === "sensitive_path_access";
4177
+ const l1FileAccessHit = !!(heuristicResult && !heuristicResult.allowed && heuristicResult.riskType === "file_access");
4178
+ const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
4179
+ if (effectiveResult) {
4180
+ const isCredentialPath = !!effectiveResult.isCredential;
4181
+ const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && effectiveResult.riskType === "file_access";
4217
4182
  if (skipInlineFileAccess) {
4218
4183
  const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
4219
4184
  if (inlineJudgment.actionable) {
@@ -4239,8 +4204,8 @@ async function runAnalysis(input) {
4239
4204
  };
4240
4205
  }
4241
4206
  }
4242
- if ((logicResult.riskLevel === "high" || logicResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
4243
- return logicResult;
4207
+ if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
4208
+ return effectiveResult;
4244
4209
  }
4245
4210
  }
4246
4211
  if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {