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