@kenhkl/agent-moss 0.7.8 → 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 +461 -177
- package/dist/cli.js.map +1 -1
- package/dist/skills/indirect_access_guard.md +40 -0
- package/package.json +1 -1
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
|
-
|
|
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: "/
|
|
686
|
-
|
|
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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 =
|
|
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
|
}
|
|
@@ -1340,18 +1609,26 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1340
1609
|
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
1341
1610
|
});
|
|
1342
1611
|
const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || String(retryCfg.maxRetries), 10);
|
|
1612
|
+
const totalTimeoutMs = timeoutCfg.totalTimeout * 1e3;
|
|
1613
|
+
const abortController = new AbortController();
|
|
1614
|
+
const totalTimeoutTimer = setTimeout(() => {
|
|
1615
|
+
console.error(`[AgentMoss] totalTimeout (${timeoutCfg.totalTimeout}s) exceeded, aborting all LLM calls`);
|
|
1616
|
+
abortController.abort();
|
|
1617
|
+
}, totalTimeoutMs);
|
|
1343
1618
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1619
|
+
if (abortController.signal.aborted) {
|
|
1620
|
+
break;
|
|
1621
|
+
}
|
|
1344
1622
|
try {
|
|
1345
1623
|
const response = await client.chat.completions.create({
|
|
1346
1624
|
model: config.model,
|
|
1347
1625
|
temperature: config.temperature || 0.1,
|
|
1348
1626
|
max_tokens: config.maxTokens || 1024,
|
|
1349
1627
|
messages: [
|
|
1350
|
-
// security_judge 模板自带角色定义与判断规则,system 仅做输出格式约束,
|
|
1351
|
-
// 避免与模板里的"你是 xiaoO Audit Agent 安全审计员"角色定义冲突(对齐 Python:无独立 system)。
|
|
1352
1628
|
{ role: "system", content: "\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002" },
|
|
1353
1629
|
{ role: "user", content: prompt }
|
|
1354
|
-
]
|
|
1630
|
+
],
|
|
1631
|
+
signal: abortController.signal
|
|
1355
1632
|
});
|
|
1356
1633
|
const content = response.choices[0]?.message?.content || "{}";
|
|
1357
1634
|
console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
|
|
@@ -1360,6 +1637,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1360
1637
|
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
1361
1638
|
}
|
|
1362
1639
|
const parsed = JSON.parse(jsonStr);
|
|
1640
|
+
clearTimeout(totalTimeoutTimer);
|
|
1363
1641
|
return {
|
|
1364
1642
|
allowed: parsed.allowed !== false,
|
|
1365
1643
|
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
@@ -1370,10 +1648,15 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1370
1648
|
violatedLayers: ["3"]
|
|
1371
1649
|
};
|
|
1372
1650
|
} catch (err) {
|
|
1651
|
+
if (abortController.signal.aborted) {
|
|
1652
|
+
console.error(`[AgentMoss] LLM analysis aborted by totalTimeout (${timeoutCfg.totalTimeout}s)`);
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1373
1655
|
if (attempt < maxRetries) {
|
|
1374
1656
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
1375
1657
|
await new Promise((resolve4) => setTimeout(resolve4, retryCfg.retryInterval * 1e3));
|
|
1376
1658
|
} else {
|
|
1659
|
+
clearTimeout(totalTimeoutTimer);
|
|
1377
1660
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
1378
1661
|
return {
|
|
1379
1662
|
allowed: false,
|
|
@@ -1387,13 +1670,14 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
1387
1670
|
}
|
|
1388
1671
|
}
|
|
1389
1672
|
}
|
|
1673
|
+
clearTimeout(totalTimeoutTimer);
|
|
1390
1674
|
return {
|
|
1391
1675
|
allowed: false,
|
|
1392
|
-
reason:
|
|
1676
|
+
reason: `LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09\uFF0C\u6309 fail-closed \u539F\u5219\u62D2\u7EDD`,
|
|
1393
1677
|
riskLevel: "medium",
|
|
1394
|
-
riskType: "",
|
|
1678
|
+
riskType: "analysis_timeout",
|
|
1395
1679
|
confidence: 60,
|
|
1396
|
-
source: "
|
|
1680
|
+
source: "llm_timeout",
|
|
1397
1681
|
violatedLayers: ["3"]
|
|
1398
1682
|
};
|
|
1399
1683
|
}
|
|
@@ -1523,7 +1807,7 @@ async function runAnalysis(input) {
|
|
|
1523
1807
|
};
|
|
1524
1808
|
}
|
|
1525
1809
|
}
|
|
1526
|
-
if (!skipInlineFileAccess || isCredentialPath) {
|
|
1810
|
+
if ((logicResult.riskLevel === "high" || logicResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
|
|
1527
1811
|
return logicResult;
|
|
1528
1812
|
}
|
|
1529
1813
|
}
|