@kenhkl/agent-moss 0.7.9 → 0.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -409,6 +409,191 @@ var init_heuristic = __esm({
409
409
  }
410
410
  });
411
411
 
412
+ // src/engine/inline-analyzer.ts
413
+ function isLiteralTextCommand(command) {
414
+ ECHO_CMD_RE.lastIndex = 0;
415
+ return ECHO_CMD_RE.test(command);
416
+ }
417
+ function extractInlineCode(command) {
418
+ const parts = [];
419
+ let m;
420
+ INLINE_CODE_RE.lastIndex = 0;
421
+ while ((m = INLINE_CODE_RE.exec(command)) !== null) {
422
+ const code = m[1] ?? m[2];
423
+ if (code) parts.push(code);
424
+ }
425
+ ECHO_STRING_RE.lastIndex = 0;
426
+ while ((m = ECHO_STRING_RE.exec(command)) !== null) {
427
+ const code = m[1] ?? m[2];
428
+ if (code) parts.push(code);
429
+ }
430
+ return parts.join("\n");
431
+ }
432
+ function hasWriteExecChain(command) {
433
+ return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
434
+ p.lastIndex = 0;
435
+ return p.test(command);
436
+ });
437
+ }
438
+ function hasDirTraversal(code) {
439
+ const lower = code.toLowerCase();
440
+ return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
441
+ }
442
+ function hasReadWriteKeyword(code) {
443
+ const lower = code.toLowerCase();
444
+ return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
445
+ }
446
+ function analyzeInlineScript(command) {
447
+ const code = extractInlineCode(command);
448
+ if (!code) {
449
+ return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
450
+ }
451
+ if (hasWriteExecChain(command)) {
452
+ return {
453
+ actionable: true,
454
+ allowed: false,
455
+ reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
456
+ riskLevel: "critical",
457
+ riskType: "script_execution"
458
+ };
459
+ }
460
+ const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
461
+ if (redirMatch) {
462
+ return {
463
+ actionable: true,
464
+ allowed: false,
465
+ reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
466
+ riskLevel: "critical",
467
+ riskType: "file_access"
468
+ };
469
+ }
470
+ const isEchoOrPrintf1 = isLiteralTextCommand(command);
471
+ if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
472
+ return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
473
+ }
474
+ const isEchoOrPrintf = isLiteralTextCommand(command);
475
+ if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
476
+ return {
477
+ actionable: true,
478
+ allowed: false,
479
+ reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
480
+ riskLevel: "critical",
481
+ riskType: "file_access"
482
+ };
483
+ }
484
+ return {
485
+ actionable: true,
486
+ allowed: true,
487
+ reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
488
+ riskLevel: "low",
489
+ riskType: "file_access"
490
+ };
491
+ }
492
+ var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
493
+ var init_inline_analyzer = __esm({
494
+ "src/engine/inline-analyzer.ts"() {
495
+ "use strict";
496
+ init_esm_shims();
497
+ INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
498
+ ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
499
+ ECHO_CMD_RE = /(?:^|\s|;|&&|\|\|)(?:echo|printf)\s/gi;
500
+ READ_WRITE_KEYWORDS = [
501
+ // 函数调用形态(关键词后跟 '(')
502
+ "open(",
503
+ "read(",
504
+ "write(",
505
+ "fopen(",
506
+ "system(",
507
+ "exec(",
508
+ "eval(",
509
+ "popen(",
510
+ "load(",
511
+ "dump(",
512
+ // 文件读写(组合形态)
513
+ "readline",
514
+ "readlines",
515
+ "writelines",
516
+ "getpass",
517
+ "pickle.load",
518
+ "yaml.load",
519
+ "json.load",
520
+ "json.read",
521
+ "configparser",
522
+ "fread",
523
+ "fwrite",
524
+ "fgets",
525
+ // 子进程执行(组合形态)
526
+ "os.system",
527
+ "os.popen",
528
+ "subprocess",
529
+ "os.exec",
530
+ "os.spawn",
531
+ "import os",
532
+ "import subprocess",
533
+ "shell=true",
534
+ "check_output",
535
+ "check_call",
536
+ "run(",
537
+ // 绕过变体检测
538
+ "getattr(",
539
+ "__builtins__",
540
+ "globals(",
541
+ "locals(",
542
+ // PHP
543
+ "file_get_contents",
544
+ "file_put_contents",
545
+ "passthru",
546
+ "shell_exec",
547
+ "proc_open",
548
+ // Perl-Ruby
549
+ "<stdin>",
550
+ "open my"
551
+ ];
552
+ WRITE_EXEC_CHAIN_PATTERNS = [
553
+ // echo/cat/printf "..." > file && bash/sh/python file
554
+ /(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
555
+ // ... | bash / | sh / | python(管道喂解释器执行)
556
+ /\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
557
+ // eval "$(cat ...)" / eval "$(echo ...)"
558
+ /eval\s+"\$\(/gi,
559
+ // xargs bash / xargs sh
560
+ /\|\s*xargs\s+(?:bash|sh|zsh)/gi
561
+ ];
562
+ SENSITIVE_PATH_FRAGMENTS = [
563
+ "/etc/shadow",
564
+ "/etc/gshadow",
565
+ "/etc/passwd",
566
+ "/etc/sudoers",
567
+ "/etc/hosts",
568
+ "/etc/crontab",
569
+ "/etc/systemd/",
570
+ "/etc/ssh/sshd_config",
571
+ "/boot/",
572
+ "/proc/sys/",
573
+ "/sys/",
574
+ "/dev/mem",
575
+ "/dev/kmsg",
576
+ ".ssh/id_rsa",
577
+ ".ssh/id_ed25519",
578
+ ".ssh/authorized_keys"
579
+ ];
580
+ REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "i");
581
+ DIR_TRAVERSAL_KEYWORDS = [
582
+ // Python 目录遍历
583
+ "os.listdir",
584
+ "os.walk",
585
+ "os.scandir",
586
+ "glob.glob",
587
+ "glob.iglob",
588
+ "pathlib.",
589
+ "iterdir",
590
+ "rglob",
591
+ "scandir(",
592
+ "listdir("
593
+ ];
594
+ }
595
+ });
596
+
412
597
  // src/engine/logic-rules.ts
413
598
  import * as fs2 from "fs";
414
599
  function runLogicCheck(input) {
@@ -470,6 +655,127 @@ function runLogicCheck(input) {
470
655
  };
471
656
  }
472
657
  }
658
+ const READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
659
+ "cat",
660
+ "head",
661
+ "tail",
662
+ "less",
663
+ "more",
664
+ "grep",
665
+ "find",
666
+ "awk",
667
+ "sed",
668
+ "sort",
669
+ "uniq",
670
+ "wc",
671
+ "cut",
672
+ "strings",
673
+ "od",
674
+ "xxd",
675
+ "hexdump",
676
+ "tr",
677
+ "file",
678
+ "stat",
679
+ "du",
680
+ "df",
681
+ "ls",
682
+ "dir",
683
+ "tree",
684
+ "nl",
685
+ "tac",
686
+ "rev",
687
+ "lsblk",
688
+ "blockdev",
689
+ "smartctl",
690
+ "udevadm",
691
+ "dmidecode",
692
+ "lscpu",
693
+ "lspci",
694
+ "lsusb",
695
+ "lsmem",
696
+ "lsns",
697
+ "lsof",
698
+ "hwinfo",
699
+ "inxi",
700
+ "fdisk",
701
+ "parted",
702
+ "dmesg",
703
+ "journalctl",
704
+ "systemctl",
705
+ "hostnamectl",
706
+ "localectl",
707
+ "timedatectl",
708
+ "ps",
709
+ "top",
710
+ "free",
711
+ "vmstat",
712
+ "iostat",
713
+ "mpstat",
714
+ "pidof",
715
+ "pgrep",
716
+ "ip",
717
+ "ifconfig",
718
+ "route",
719
+ "ss",
720
+ "netstat",
721
+ "arp",
722
+ "ethtool",
723
+ "nmcli",
724
+ "uname",
725
+ "arch",
726
+ "nproc",
727
+ "getconf",
728
+ "getent",
729
+ "id",
730
+ "whoami",
731
+ "who",
732
+ "w",
733
+ "last",
734
+ "uptime",
735
+ "lsmod",
736
+ "modinfo",
737
+ "sysctl"
738
+ ]);
739
+ const DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
740
+ const WRITE_COMMAND_RES = [
741
+ /\brm\b/,
742
+ /\bunlink\b/,
743
+ /\brmdir\b/,
744
+ /\bshred\b/,
745
+ /\bcp\b/,
746
+ /\bmv\b/,
747
+ /\binstall\b/,
748
+ /\btee\b/,
749
+ /\bdd\b/,
750
+ /\bmkfs\b/,
751
+ /\b(fdisk|parted|cfdisk|sfdisk)\b/,
752
+ /\bchmod\b/,
753
+ /\bchown\b/,
754
+ /\btruncate\b/
755
+ ];
756
+ function isWriteOperation2(actionType2, actionDetail2) {
757
+ if (WRITE_KEYWORDS.some((kw) => actionType2.includes(kw) || actionDetail2.includes(kw))) {
758
+ return true;
759
+ }
760
+ if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail2))) {
761
+ return true;
762
+ }
763
+ if (/\bsed\s+-[a-zA-Z]*i/.test(actionDetail2)) {
764
+ return true;
765
+ }
766
+ if (actionDetail2.includes(">")) {
767
+ const withoutDevNull = actionDetail2.replace(DEVNULL_REDIRECT_RE, "");
768
+ if (withoutDevNull.includes(">")) {
769
+ const firstWord = actionDetail2.trim().split(/\s+/)[0]?.toLowerCase() || "";
770
+ if (!READ_ONLY_COMMANDS.has(firstWord)) {
771
+ return true;
772
+ }
773
+ }
774
+ }
775
+ return false;
776
+ }
777
+ const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
778
+ if (indirectResult) return indirectResult;
473
779
  if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
474
780
  const adminContext = actionHistory.some(
475
781
  (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")
@@ -567,8 +873,26 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
567
873
  }
568
874
  function checkSensitivePath(command, cwd) {
569
875
  const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
876
+ const isWriteOp = isWriteOperation("", command);
570
877
  for (const sp of ROOT_SENSITIVE_PATHS) {
571
878
  if (command.includes(sp.path.toLowerCase())) {
879
+ if (sp.path === "/dev/random") {
880
+ if (isWriteOp) {
881
+ continue;
882
+ }
883
+ return {
884
+ allowed: false,
885
+ reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
886
+ riskLevel: sp.riskLevel,
887
+ riskType: "sensitive_path_access",
888
+ confidence: 90,
889
+ source: "logic_rule",
890
+ violatedLayers: ["2"]
891
+ };
892
+ }
893
+ if (!isWriteOp) {
894
+ continue;
895
+ }
572
896
  return {
573
897
  allowed: false,
574
898
  reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
@@ -583,6 +907,9 @@ function checkSensitivePath(command, cwd) {
583
907
  for (const sp of HOME_SENSITIVE_PATHS) {
584
908
  const expanded = `${expandedCwd}/${sp.path}`;
585
909
  if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
910
+ if (!isWriteOp) {
911
+ continue;
912
+ }
586
913
  return {
587
914
  allowed: false,
588
915
  reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
@@ -667,33 +994,125 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
667
994
  violatedLayers: ["2"]
668
995
  };
669
996
  }
670
- 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;
997
+ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
998
+ if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
999
+ return null;
1000
+ }
1001
+ INLINE_SCRIPT_RE.lastIndex = 0;
1002
+ if (!INLINE_SCRIPT_RE.test(actionDetail)) {
1003
+ return null;
1004
+ }
1005
+ const lowerDetail = actionDetail.toLowerCase();
1006
+ const hasTraversal = DIR_TRAVERSAL_KEYWORDS2.some((kw) => lowerDetail.includes(kw));
1007
+ if (!hasTraversal) {
1008
+ return null;
1009
+ }
1010
+ const READ_KEYWORDS2 = ["read(", "readline", "readlines", "fread", "fgets", "load(", "getpass", "file_get_contents"];
1011
+ const WRITE_KEYWORDS2 = ["write(", "writelines", "fwrite", "file_put_contents"];
1012
+ const EXEC_KEYWORDS = ["system(", "exec(", "eval(", "popen(", "os.system", "os.popen", "subprocess", "os.exec", "os.spawn", "shell=true", "check_output", "check_call", "run(", "passthru", "shell_exec", "proc_open"];
1013
+ const OPEN_KEYWORDS = ["open(", "fopen("];
1014
+ const code = extractInlineCode(actionDetail);
1015
+ if (!code) return null;
1016
+ const codeLower = code.toLowerCase();
1017
+ const hasAnyOp = READ_KEYWORDS2.some((kw) => codeLower.includes(kw)) || WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw)) || EXEC_KEYWORDS.some((kw) => codeLower.includes(kw)) || OPEN_KEYWORDS.some((kw) => codeLower.includes(kw));
1018
+ if (!hasAnyOp) {
1019
+ return null;
1020
+ }
1021
+ const hasWrite = WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw));
1022
+ const hasExec = EXEC_KEYWORDS.some((kw) => codeLower.includes(kw));
1023
+ const blockAllSensitive = hasWrite || hasExec;
1024
+ let actualFiles = [];
1025
+ if (cwd && cwd !== "/") {
1026
+ try {
1027
+ actualFiles = fs2.readdirSync(cwd);
1028
+ } catch {
1029
+ }
1030
+ }
1031
+ if (actualFiles.length > 0) {
1032
+ for (const cp of CREDENTIAL_PATHS) {
1033
+ const fname = cp.path.split("/").pop() || cp.path;
1034
+ if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
1035
+ return {
1036
+ allowed: false,
1037
+ reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u51ED\u636E\u6587\u4EF6: ${fname}`,
1038
+ riskLevel: "high",
1039
+ riskType: "file_access",
1040
+ confidence: 95,
1041
+ source: "logic_rule",
1042
+ violatedLayers: ["2"],
1043
+ isCredential: true
1044
+ };
1045
+ }
1046
+ }
1047
+ if (blockAllSensitive) {
1048
+ for (const sp of [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS]) {
1049
+ const fname = sp.path.split("/").pop() || sp.path;
1050
+ if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
1051
+ return {
1052
+ allowed: false,
1053
+ reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u654F\u611F\u6587\u4EF6: ${fname}`,
1054
+ riskLevel: "high",
1055
+ riskType: "file_access",
1056
+ confidence: 95,
1057
+ source: "logic_rule",
1058
+ violatedLayers: ["2"]
1059
+ };
1060
+ }
1061
+ }
1062
+ }
1063
+ return null;
1064
+ }
1065
+ return {
1066
+ allowed: false,
1067
+ 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",
1068
+ riskLevel: "medium",
1069
+ riskType: "file_access",
1070
+ confidence: 70,
1071
+ source: "logic_rule",
1072
+ violatedLayers: ["2"]
1073
+ };
1074
+ }
1075
+ 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_KEYWORDS2;
671
1076
  var init_logic_rules = __esm({
672
1077
  "src/engine/logic-rules.ts"() {
673
1078
  "use strict";
674
1079
  init_esm_shims();
1080
+ init_inline_analyzer();
675
1081
  ROOT_SENSITIVE_PATHS = [
676
1082
  { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6" },
677
1083
  { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6" },
678
1084
  // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
679
1085
  { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
680
1086
  { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E" },
1087
+ // 系统配置(仅拦截写入)
681
1088
  { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
682
1089
  { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
1090
+ { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
1091
+ { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
683
1092
  { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
684
1093
  { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
685
- { path: "/root/.ssh", riskLevel: "critical", desc: "root SSH \u76EE\u5F55" },
686
- { path: "/root/.bashrc", riskLevel: "high", desc: "root shell \u914D\u7F6E" },
1094
+ { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
1095
+ // 危险目录(仅拦截写入)
687
1096
  { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
688
1097
  { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
689
1098
  { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
1099
+ // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
1100
+ { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
690
1101
  { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
691
- { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" }
1102
+ { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
1103
+ // SSH 主机私钥
1104
+ { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5" },
1105
+ { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5" },
1106
+ { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5" }
692
1107
  ];
693
1108
  HOME_SENSITIVE_PATHS = [
694
1109
  { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5" },
695
1110
  { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)" },
696
1111
  { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
1112
+ // 持久化与命令劫持(仅拦截写入)
1113
+ { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
1114
+ { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
1115
+ { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
697
1116
  { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1" },
698
1117
  { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
699
1118
  { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
@@ -738,175 +1157,24 @@ var init_logic_rules = __esm({
738
1157
  reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
739
1158
  }
740
1159
  ];
741
- }
742
- });
743
-
744
- // src/engine/inline-analyzer.ts
745
- function isLiteralTextCommand(command) {
746
- ECHO_CMD_RE.lastIndex = 0;
747
- return ECHO_CMD_RE.test(command);
748
- }
749
- function extractInlineCode(command) {
750
- const parts = [];
751
- let m;
752
- INLINE_CODE_RE.lastIndex = 0;
753
- while ((m = INLINE_CODE_RE.exec(command)) !== null) {
754
- const code = m[1] ?? m[2];
755
- if (code) parts.push(code);
756
- }
757
- ECHO_STRING_RE.lastIndex = 0;
758
- while ((m = ECHO_STRING_RE.exec(command)) !== null) {
759
- const code = m[1] ?? m[2];
760
- if (code) parts.push(code);
761
- }
762
- return parts.join("\n");
763
- }
764
- function hasWriteExecChain(command) {
765
- return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
766
- p.lastIndex = 0;
767
- return p.test(command);
768
- });
769
- }
770
- function hasReadWriteKeyword(code) {
771
- const lower = code.toLowerCase();
772
- return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
773
- }
774
- function analyzeInlineScript(command) {
775
- const code = extractInlineCode(command);
776
- if (!code) {
777
- return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
778
- }
779
- if (hasWriteExecChain(command)) {
780
- return {
781
- actionable: true,
782
- allowed: false,
783
- reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
784
- riskLevel: "critical",
785
- riskType: "script_execution"
786
- };
787
- }
788
- const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
789
- if (redirMatch) {
790
- return {
791
- actionable: true,
792
- allowed: false,
793
- reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
794
- riskLevel: "critical",
795
- riskType: "file_access"
796
- };
797
- }
798
- const isEchoOrPrintf = isLiteralTextCommand(command);
799
- if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
800
- return {
801
- actionable: true,
802
- allowed: false,
803
- reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
804
- riskLevel: "critical",
805
- riskType: "file_access"
806
- };
807
- }
808
- return {
809
- actionable: true,
810
- allowed: true,
811
- reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
812
- riskLevel: "low",
813
- riskType: "file_access"
814
- };
815
- }
816
- var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE;
817
- var init_inline_analyzer = __esm({
818
- "src/engine/inline-analyzer.ts"() {
819
- "use strict";
820
- init_esm_shims();
821
- INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
822
- ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
823
- ECHO_CMD_RE = /(?:^|\s|;|&&|\|\|)(?:echo|printf)\s/gi;
824
- READ_WRITE_KEYWORDS = [
825
- // 函数调用形态(关键词后跟 '(')
826
- "open(",
827
- "read(",
828
- "write(",
829
- "fopen(",
830
- "system(",
831
- "exec(",
832
- "eval(",
833
- "popen(",
834
- "load(",
835
- "dump(",
836
- // 文件读写(组合形态)
837
- "readline",
838
- "readlines",
839
- "writelines",
840
- "getpass",
841
- "pickle.load",
842
- "yaml.load",
843
- "json.load",
844
- "json.read",
845
- "configparser",
846
- "fread",
847
- "fwrite",
848
- "fgets",
849
- // 子进程执行(组合形态)
850
- "os.system",
851
- "os.popen",
852
- "subprocess",
853
- "os.exec",
854
- "os.spawn",
855
- "import os",
856
- "import subprocess",
857
- "shell=true",
858
- "check_output",
859
- "check_call",
860
- "run(",
861
- // 绕过变体检测
862
- "getattr(",
863
- "__builtins__",
864
- "globals(",
865
- "locals(",
866
- // PHP
867
- "file_get_contents",
868
- "file_put_contents",
869
- "passthru",
870
- "shell_exec",
871
- "proc_open",
872
- // Perl-Ruby
873
- "<stdin>",
874
- "open my"
875
- ];
876
- WRITE_EXEC_CHAIN_PATTERNS = [
877
- // echo/cat/printf "..." > file && bash/sh/python file
878
- /(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
879
- // ... | bash / | sh / | python(管道喂解释器执行)
880
- /\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
881
- // eval "$(cat ...)" / eval "$(echo ...)"
882
- /eval\s+"\$\(/gi,
883
- // xargs bash / xargs sh
884
- /\|\s*xargs\s+(?:bash|sh|zsh)/gi
885
- ];
886
- SENSITIVE_PATH_FRAGMENTS = [
887
- "/etc/shadow",
888
- "/etc/gshadow",
889
- "/etc/passwd",
890
- "/etc/sudoers",
891
- "/etc/hosts",
892
- "/etc/crontab",
893
- "/etc/systemd/",
894
- "/etc/ssh/sshd_config",
895
- "/boot/",
896
- "/proc/sys/",
897
- "/sys/",
898
- "/dev/mem",
899
- "/dev/kmsg",
900
- ".ssh/id_rsa",
901
- ".ssh/id_ed25519",
902
- ".ssh/authorized_keys"
1160
+ 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;
1161
+ DIR_TRAVERSAL_KEYWORDS2 = [
1162
+ "os.listdir",
1163
+ "os.walk",
1164
+ "os.scandir",
1165
+ "glob.glob",
1166
+ "glob.iglob",
1167
+ "pathlib.",
1168
+ "iterdir",
1169
+ "rglob",
1170
+ "scandir(",
1171
+ "listdir("
903
1172
  ];
904
- REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "i");
905
1173
  }
906
1174
  });
907
1175
 
908
1176
  // src/engine/skill-loader.ts
909
- import { readFileSync, readdirSync, existsSync as existsSync2 } from "fs";
1177
+ import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
910
1178
  import { resolve, dirname } from "path";
911
1179
  import { fileURLToPath as fileURLToPath2 } from "url";
912
1180
  function loadAllSkills() {
@@ -917,7 +1185,7 @@ function loadAllSkills() {
917
1185
  console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
918
1186
  return cachedSkills;
919
1187
  }
920
- const files = readdirSync(dir).filter((f) => f.endsWith(".md"));
1188
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
921
1189
  for (const f of files) {
922
1190
  const name = f.replace(".md", "");
923
1191
  cachedSkills[name] = readFileSync(resolve(dir, f), "utf-8");
@@ -997,7 +1265,8 @@ var init_skill_loader = __esm({
997
1265
  "resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
998
1266
  "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
999
1267
  "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
1000
- "intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"]
1268
+ "intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"],
1269
+ "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
1001
1270
  };
1002
1271
  cachedSkills = null;
1003
1272
  }
@@ -1538,7 +1807,7 @@ async function runAnalysis(input) {
1538
1807
  };
1539
1808
  }
1540
1809
  }
1541
- if (!skipInlineFileAccess || isCredentialPath) {
1810
+ if ((logicResult.riskLevel === "high" || logicResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
1542
1811
  return logicResult;
1543
1812
  }
1544
1813
  }