@kenhkl/agent-moss 0.11.5 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +321 -385
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +9 -0
- package/dist/engine/coordinator.js +280 -349
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/l1_rules.json +821 -0
- package/dist/rules/l2_rules.json +82 -0
- package/dist/rules/l3_rules.json +245 -0
- package/dist/server.js +307 -371
- package/dist/server.js.map +1 -1
- package/dist/templates/security_judge_template.txt +7 -1
- package/package.json +1 -1
|
@@ -564,8 +564,70 @@ var init_inline_analyzer = __esm({
|
|
|
564
564
|
}
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
+
// src/infra/rules-loader.ts
|
|
568
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
569
|
+
import { resolve, dirname } from "path";
|
|
570
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
571
|
+
function findRulesFile(name) {
|
|
572
|
+
for (const dir of RULES_DIR_CANDS) {
|
|
573
|
+
const p = resolve(dir, name);
|
|
574
|
+
if (existsSync(p)) return p;
|
|
575
|
+
}
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
function loadJson(name) {
|
|
579
|
+
const p = findRulesFile(name);
|
|
580
|
+
if (!p) return {};
|
|
581
|
+
try {
|
|
582
|
+
return JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
583
|
+
} catch {
|
|
584
|
+
return {};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function loadL1() {
|
|
588
|
+
return loadJson("l1_rules.json");
|
|
589
|
+
}
|
|
590
|
+
function loadL2() {
|
|
591
|
+
return loadJson("l2_rules.json");
|
|
592
|
+
}
|
|
593
|
+
function loadL3() {
|
|
594
|
+
return loadJson("l3_rules.json");
|
|
595
|
+
}
|
|
596
|
+
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
597
|
+
var init_rules_loader = __esm({
|
|
598
|
+
"src/infra/rules-loader.ts"() {
|
|
599
|
+
"use strict";
|
|
600
|
+
init_esm_shims();
|
|
601
|
+
__filename2 = fileURLToPath3(import.meta.url);
|
|
602
|
+
__dirname2 = dirname(__filename2);
|
|
603
|
+
RULES_DIR_CANDS = (() => {
|
|
604
|
+
const cands = [
|
|
605
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
606
|
+
resolve(__dirname2, "../rules"),
|
|
607
|
+
resolve(__dirname2, "rules"),
|
|
608
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
609
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
610
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
611
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
612
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
613
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
614
|
+
];
|
|
615
|
+
return cands.filter((c) => existsSync(c));
|
|
616
|
+
})();
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
567
620
|
// src/engine/logic-rules.ts
|
|
568
621
|
import * as fs2 from "fs";
|
|
622
|
+
function _loadSensitivePathScope(scope) {
|
|
623
|
+
const sps = loadL1().sensitive_path_access || [];
|
|
624
|
+
return sps.filter((sp) => sp.scope === scope).map((sp) => ({
|
|
625
|
+
path: sp.path,
|
|
626
|
+
riskLevel: sp.risk_level,
|
|
627
|
+
desc: sp.desc,
|
|
628
|
+
credential: sp.deny_mode === "deny_both" ? true : void 0
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
569
631
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
570
632
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
571
633
|
if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail))) return true;
|
|
@@ -610,6 +672,32 @@ function matchDangerousAction(actionDetail, action) {
|
|
|
610
672
|
const re = new RegExp(`(?<![-\\w])${escaped}\\b`, "i");
|
|
611
673
|
return re.test(actionDetail);
|
|
612
674
|
}
|
|
675
|
+
function collectIntentTexts(promptSession, promptHistory) {
|
|
676
|
+
const texts = [];
|
|
677
|
+
if (promptHistory) {
|
|
678
|
+
for (const turn of promptHistory) {
|
|
679
|
+
if (typeof turn === "string") {
|
|
680
|
+
const t2 = turn.trim();
|
|
681
|
+
if (t2) texts.push(t2.toLowerCase());
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
const t = (turn?.text || "").trim();
|
|
685
|
+
if (t) texts.push(t.toLowerCase());
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (promptSession && promptSession.trim()) {
|
|
689
|
+
texts.push(promptSession.toLowerCase());
|
|
690
|
+
}
|
|
691
|
+
const seen = /* @__PURE__ */ new Set();
|
|
692
|
+
const deduped = [];
|
|
693
|
+
for (const t of texts) {
|
|
694
|
+
if (!seen.has(t)) {
|
|
695
|
+
seen.add(t);
|
|
696
|
+
deduped.push(t);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return deduped;
|
|
700
|
+
}
|
|
613
701
|
function runLogicCheck(input) {
|
|
614
702
|
const { actionHistory, aNext, cwd } = input;
|
|
615
703
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
@@ -619,25 +707,24 @@ function runLogicCheck(input) {
|
|
|
619
707
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
620
708
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
|
|
621
709
|
if (rbwResult) return rbwResult;
|
|
622
|
-
if (
|
|
623
|
-
if (!
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
710
|
+
if (!isExcludedType) {
|
|
711
|
+
if (!INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
712
|
+
const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
|
|
713
|
+
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
714
|
+
const intentMatched = intentTexts.some(
|
|
715
|
+
(text) => pattern.intentKeywords.some((kw) => text.includes(kw))
|
|
716
|
+
);
|
|
717
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
718
|
+
if (intentMatched && dangerousMatched) {
|
|
719
|
+
return {
|
|
720
|
+
allowed: false,
|
|
721
|
+
reason: pattern.reason,
|
|
722
|
+
riskLevel: "high",
|
|
723
|
+
riskType: "intent_deviation",
|
|
724
|
+
confidence: 85,
|
|
725
|
+
source: "logic_rule",
|
|
726
|
+
violatedLayers: ["2"]
|
|
727
|
+
};
|
|
641
728
|
}
|
|
642
729
|
}
|
|
643
730
|
}
|
|
@@ -927,57 +1014,10 @@ var init_logic_rules = __esm({
|
|
|
927
1014
|
"use strict";
|
|
928
1015
|
init_esm_shims();
|
|
929
1016
|
init_inline_analyzer();
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
935
|
-
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
936
|
-
// 系统配置(仅拦截写入)
|
|
937
|
-
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
938
|
-
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
939
|
-
{ path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
|
|
940
|
-
{ path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
|
|
941
|
-
{ path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
|
|
942
|
-
{ path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
|
|
943
|
-
{ path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
|
|
944
|
-
// 危险目录(仅拦截写入)
|
|
945
|
-
{ path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
|
|
946
|
-
{ path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
|
|
947
|
-
{ path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
|
|
948
|
-
// 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
|
|
949
|
-
{ path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
|
|
950
|
-
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907\uFF08\u8BFB\u5199\u5747\u5371\u9669\uFF09", credential: true },
|
|
951
|
-
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
952
|
-
// SSH 主机私钥
|
|
953
|
-
// SSH 主机私钥(排除 .pub 公钥变体,对齐 Python .ssh/id_rsa(?!\.pub) 范式)
|
|
954
|
-
{ path: "/etc/ssh/ssh_host_rsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
|
|
955
|
-
{ path: "/etc/ssh/ssh_host_ed25519_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
|
|
956
|
-
{ path: "/etc/ssh/ssh_host_ecdsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true },
|
|
957
|
-
// Nginx 配置(仅拦截写入,对齐 Python linux.py sensitive_paths)
|
|
958
|
-
{ path: "/etc/nginx/nginx.conf", riskLevel: "high", desc: "Nginx \u4E3B\u914D\u7F6E" },
|
|
959
|
-
{ path: "/etc/nginx/conf.d/", riskLevel: "high", desc: "Nginx \u5B50\u914D\u7F6E\u76EE\u5F55" },
|
|
960
|
-
{ path: "/etc/nginx/sites-enabled/", riskLevel: "high", desc: "Nginx \u542F\u7528\u7AD9\u70B9\u914D\u7F6E\u76EE\u5F55" }
|
|
961
|
-
];
|
|
962
|
-
HOME_SENSITIVE_PATHS = [
|
|
963
|
-
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
|
|
964
|
-
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
|
|
965
|
-
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
966
|
-
// 持久化与命令劫持(仅拦截写入)
|
|
967
|
-
{ path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
|
|
968
|
-
{ path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
|
|
969
|
-
{ path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
|
|
970
|
-
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
|
|
971
|
-
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
972
|
-
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
973
|
-
];
|
|
974
|
-
CREDENTIAL_PATHS = [
|
|
975
|
-
{ path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
976
|
-
{ path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
977
|
-
{ path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
978
|
-
{ path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
979
|
-
{ path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
|
|
980
|
-
];
|
|
1017
|
+
init_rules_loader();
|
|
1018
|
+
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1019
|
+
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1020
|
+
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path3, riskLevel, desc }) => ({ path: path3, riskLevel, desc, credential: true }));
|
|
981
1021
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
982
1022
|
"ask_user_question",
|
|
983
1023
|
"glob",
|
|
@@ -1105,34 +1145,22 @@ var init_logic_rules = __esm({
|
|
|
1105
1145
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
1106
1146
|
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"];
|
|
1107
1147
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1108
|
-
PASSWORD_MODIFY_PATTERNS =
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
INTENT_DEVIATION_PATTERNS = [
|
|
1125
|
-
{
|
|
1126
|
-
intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
|
|
1127
|
-
dangerousActions: ["\u5220\u9664", "remove", "delete", "rm", "\u683C\u5F0F\u5316", "format"],
|
|
1128
|
-
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
|
|
1129
|
-
},
|
|
1130
|
-
{
|
|
1131
|
-
intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
|
|
1132
|
-
dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
|
|
1133
|
-
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
|
|
1134
|
-
}
|
|
1135
|
-
];
|
|
1148
|
+
PASSWORD_MODIFY_PATTERNS = (() => {
|
|
1149
|
+
const pats = loadL2().password_modify_consent || [];
|
|
1150
|
+
return pats.map((p) => new RegExp(p.pattern, "i"));
|
|
1151
|
+
})();
|
|
1152
|
+
USER_DELETION_PATTERNS = (() => {
|
|
1153
|
+
const pats = loadL2().user_deletion_consent || [];
|
|
1154
|
+
return pats.map((p) => new RegExp(p.pattern, "i"));
|
|
1155
|
+
})();
|
|
1156
|
+
INTENT_DEVIATION_PATTERNS = (() => {
|
|
1157
|
+
const pats = loadL2().intent_consistency || [];
|
|
1158
|
+
return pats.map((p) => ({
|
|
1159
|
+
intentKeywords: p.intent_keywords,
|
|
1160
|
+
dangerousActions: p.dangerous_actions,
|
|
1161
|
+
reason: p.reason
|
|
1162
|
+
}));
|
|
1163
|
+
})();
|
|
1136
1164
|
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;
|
|
1137
1165
|
DIR_TRAVERSAL_KW = [
|
|
1138
1166
|
"os.listdir",
|
|
@@ -1150,21 +1178,21 @@ var init_logic_rules = __esm({
|
|
|
1150
1178
|
});
|
|
1151
1179
|
|
|
1152
1180
|
// src/engine/skill-loader.ts
|
|
1153
|
-
import { readFileSync as
|
|
1154
|
-
import { resolve, dirname } from "path";
|
|
1155
|
-
import { fileURLToPath as
|
|
1181
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
1182
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
1183
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1156
1184
|
function loadAllSkills() {
|
|
1157
1185
|
if (cachedSkills) return cachedSkills;
|
|
1158
1186
|
cachedSkills = {};
|
|
1159
|
-
const dir =
|
|
1160
|
-
if (!
|
|
1187
|
+
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1188
|
+
if (!existsSync3(dir)) {
|
|
1161
1189
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1162
1190
|
return cachedSkills;
|
|
1163
1191
|
}
|
|
1164
1192
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1165
1193
|
for (const f of files) {
|
|
1166
1194
|
const name = f.replace(".md", "");
|
|
1167
|
-
cachedSkills[name] =
|
|
1195
|
+
cachedSkills[name] = readFileSync3(resolve2(dir, f), "utf-8");
|
|
1168
1196
|
}
|
|
1169
1197
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1170
1198
|
return cachedSkills;
|
|
@@ -1218,32 +1246,27 @@ function formatSkillsForPrompt(skills) {
|
|
|
1218
1246
|
${s.skillContent}`
|
|
1219
1247
|
).join("\n\n---\n\n");
|
|
1220
1248
|
}
|
|
1221
|
-
var
|
|
1249
|
+
var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1222
1250
|
var init_skill_loader = __esm({
|
|
1223
1251
|
"src/engine/skill-loader.ts"() {
|
|
1224
1252
|
"use strict";
|
|
1225
1253
|
init_esm_shims();
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
"skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
|
|
1243
|
-
"supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
|
|
1244
|
-
"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"],
|
|
1245
|
-
"indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
|
|
1246
|
-
};
|
|
1254
|
+
init_rules_loader();
|
|
1255
|
+
__filename3 = fileURLToPath4(import.meta.url);
|
|
1256
|
+
__dirname3 = dirname2(__filename3);
|
|
1257
|
+
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1258
|
+
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1259
|
+
SKILL_KEYWORD_MAP = (() => {
|
|
1260
|
+
const map = {};
|
|
1261
|
+
const cats = loadL3();
|
|
1262
|
+
for (const cat of Object.values(cats)) {
|
|
1263
|
+
if (!cat || typeof cat !== "object") continue;
|
|
1264
|
+
for (const [skillId, keywords] of Object.entries(cat)) {
|
|
1265
|
+
map[skillId] = keywords;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return map;
|
|
1269
|
+
})();
|
|
1247
1270
|
cachedSkills = null;
|
|
1248
1271
|
}
|
|
1249
1272
|
});
|
|
@@ -1295,10 +1318,10 @@ __export(runtime_config_exports, {
|
|
|
1295
1318
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1296
1319
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1297
1320
|
});
|
|
1298
|
-
import { readFileSync as
|
|
1321
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1299
1322
|
import { join as join2 } from "path";
|
|
1300
|
-
import { fileURLToPath as
|
|
1301
|
-
import { dirname as
|
|
1323
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1324
|
+
import { dirname as dirname3 } from "path";
|
|
1302
1325
|
function runtimeDir() {
|
|
1303
1326
|
return getRuntimeConfigDir();
|
|
1304
1327
|
}
|
|
@@ -1310,7 +1333,7 @@ function getRuntimeConfigPath() {
|
|
|
1310
1333
|
function saveRuntimeConfig(config) {
|
|
1311
1334
|
const p = getRuntimeConfigPath();
|
|
1312
1335
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1313
|
-
if (dir && !
|
|
1336
|
+
if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
|
|
1314
1337
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1315
1338
|
}
|
|
1316
1339
|
function emptyRuntimeConfig() {
|
|
@@ -1325,7 +1348,7 @@ function emptyRuntimeConfig() {
|
|
|
1325
1348
|
}
|
|
1326
1349
|
function loadRuntimeConfig() {
|
|
1327
1350
|
const p = getRuntimeConfigPath();
|
|
1328
|
-
if (!
|
|
1351
|
+
if (!existsSync4(p)) {
|
|
1329
1352
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1330
1353
|
try {
|
|
1331
1354
|
saveRuntimeConfig(seeded);
|
|
@@ -1334,7 +1357,7 @@ function loadRuntimeConfig() {
|
|
|
1334
1357
|
return seeded;
|
|
1335
1358
|
}
|
|
1336
1359
|
try {
|
|
1337
|
-
const data = JSON.parse(
|
|
1360
|
+
const data = JSON.parse(readFileSync4(p, "utf-8"));
|
|
1338
1361
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1339
1362
|
return {
|
|
1340
1363
|
version: data.version || 1,
|
|
@@ -1402,9 +1425,9 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1402
1425
|
}
|
|
1403
1426
|
function hasRuntimeConfig() {
|
|
1404
1427
|
const p = getRuntimeConfigPath();
|
|
1405
|
-
if (!
|
|
1428
|
+
if (!existsSync4(p)) return false;
|
|
1406
1429
|
try {
|
|
1407
|
-
JSON.parse(
|
|
1430
|
+
JSON.parse(readFileSync4(p, "utf-8"));
|
|
1408
1431
|
return true;
|
|
1409
1432
|
} catch {
|
|
1410
1433
|
return false;
|
|
@@ -1583,8 +1606,8 @@ function _l3SkillsToRules() {
|
|
|
1583
1606
|
}
|
|
1584
1607
|
function _agentRulesToDefaults() {
|
|
1585
1608
|
const out = {};
|
|
1586
|
-
const agentsDir =
|
|
1587
|
-
if (!
|
|
1609
|
+
const agentsDir = existsSync4(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync4(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
1610
|
+
if (!existsSync4(agentsDir)) return out;
|
|
1588
1611
|
let entries;
|
|
1589
1612
|
try {
|
|
1590
1613
|
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1593,10 +1616,10 @@ function _agentRulesToDefaults() {
|
|
|
1593
1616
|
}
|
|
1594
1617
|
for (const agentId of entries) {
|
|
1595
1618
|
const rulesFile = join2(agentsDir, agentId, "rules.json");
|
|
1596
|
-
if (!
|
|
1619
|
+
if (!existsSync4(rulesFile)) continue;
|
|
1597
1620
|
let data;
|
|
1598
1621
|
try {
|
|
1599
|
-
data = JSON.parse(
|
|
1622
|
+
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
1600
1623
|
} catch {
|
|
1601
1624
|
continue;
|
|
1602
1625
|
}
|
|
@@ -1675,6 +1698,9 @@ function generateSourceDefaults() {
|
|
|
1675
1698
|
agent_rules: _agentRulesToDefaults()
|
|
1676
1699
|
};
|
|
1677
1700
|
}
|
|
1701
|
+
function normalizePatternForFingerprint(pattern) {
|
|
1702
|
+
return pattern.replace(/\\\//g, "/");
|
|
1703
|
+
}
|
|
1678
1704
|
function _ruleFingerprint(rule) {
|
|
1679
1705
|
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1680
1706
|
return JSON.stringify([
|
|
@@ -1682,8 +1708,8 @@ function _ruleFingerprint(rule) {
|
|
|
1682
1708
|
[...rule.dangerous_actions || []].sort()
|
|
1683
1709
|
]);
|
|
1684
1710
|
}
|
|
1685
|
-
if (rule.path) return JSON.stringify([rule.path]);
|
|
1686
|
-
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1711
|
+
if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
|
|
1712
|
+
if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
|
|
1687
1713
|
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1688
1714
|
if (rule.value) return JSON.stringify([rule.value]);
|
|
1689
1715
|
return JSON.stringify([rule.id || ""]);
|
|
@@ -2034,7 +2060,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2034
2060
|
skills.splice(idx, 1);
|
|
2035
2061
|
const skillPath = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2036
2062
|
try {
|
|
2037
|
-
if (
|
|
2063
|
+
if (existsSync4(skillPath)) unlinkSync(skillPath);
|
|
2038
2064
|
} catch {
|
|
2039
2065
|
}
|
|
2040
2066
|
saveRuntimeConfig(runtime);
|
|
@@ -2148,7 +2174,7 @@ function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
|
2148
2174
|
saveRuntimeConfig(runtime);
|
|
2149
2175
|
return runtime;
|
|
2150
2176
|
}
|
|
2151
|
-
var
|
|
2177
|
+
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, SKILL_ID_RE;
|
|
2152
2178
|
var init_runtime_config = __esm({
|
|
2153
2179
|
"src/infra/runtime-config.ts"() {
|
|
2154
2180
|
"use strict";
|
|
@@ -2157,7 +2183,7 @@ var init_runtime_config = __esm({
|
|
|
2157
2183
|
init_heuristic();
|
|
2158
2184
|
init_logic_rules();
|
|
2159
2185
|
init_skill_loader();
|
|
2160
|
-
|
|
2186
|
+
__dirname4 = dirname3(fileURLToPath5(import.meta.url));
|
|
2161
2187
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2162
2188
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
2163
2189
|
RUNTIME_CONFIG_PATH = join2(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
@@ -2171,19 +2197,19 @@ var init_runtime_config = __esm({
|
|
|
2171
2197
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2172
2198
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2173
2199
|
};
|
|
2174
|
-
_AGENTS_DIR_PROD = join2(
|
|
2175
|
-
_AGENTS_DIR_DEV = join2(
|
|
2200
|
+
_AGENTS_DIR_PROD = join2(__dirname4, "agents");
|
|
2201
|
+
_AGENTS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2176
2202
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2177
2203
|
}
|
|
2178
2204
|
});
|
|
2179
2205
|
|
|
2180
2206
|
// src/agents/index.ts
|
|
2181
|
-
import { readFileSync as
|
|
2182
|
-
import { resolve as
|
|
2183
|
-
import { fileURLToPath as
|
|
2207
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
|
|
2208
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
2209
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2184
2210
|
function resolveAgentsDir() {
|
|
2185
|
-
if (
|
|
2186
|
-
if (
|
|
2211
|
+
if (existsSync5(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
2212
|
+
if (existsSync5(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
2187
2213
|
return AGENTS_DIR_PROD;
|
|
2188
2214
|
}
|
|
2189
2215
|
function getAgentExtensions(agentId) {
|
|
@@ -2199,13 +2225,13 @@ function getAgentExtensions(agentId) {
|
|
|
2199
2225
|
}
|
|
2200
2226
|
if (ext === null) {
|
|
2201
2227
|
const agentsDir = resolveAgentsDir();
|
|
2202
|
-
const rulesFile =
|
|
2203
|
-
if (!
|
|
2228
|
+
const rulesFile = resolve3(agentsDir, agentId, "rules.json");
|
|
2229
|
+
if (!existsSync5(rulesFile)) {
|
|
2204
2230
|
cache.set(agentId, null);
|
|
2205
2231
|
return null;
|
|
2206
2232
|
}
|
|
2207
2233
|
try {
|
|
2208
|
-
const raw =
|
|
2234
|
+
const raw = readFileSync5(rulesFile, "utf-8");
|
|
2209
2235
|
ext = JSON.parse(raw);
|
|
2210
2236
|
if (!ext.agent_id) {
|
|
2211
2237
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
@@ -2224,15 +2250,15 @@ function getAgentExtensions(agentId) {
|
|
|
2224
2250
|
);
|
|
2225
2251
|
return ext;
|
|
2226
2252
|
}
|
|
2227
|
-
var
|
|
2253
|
+
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
2228
2254
|
var init_agents = __esm({
|
|
2229
2255
|
"src/agents/index.ts"() {
|
|
2230
2256
|
"use strict";
|
|
2231
2257
|
init_esm_shims();
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
AGENTS_DIR_PROD =
|
|
2235
|
-
AGENTS_DIR_DEV_FALLBACK =
|
|
2258
|
+
__filename4 = fileURLToPath6(import.meta.url);
|
|
2259
|
+
__dirname5 = dirname4(__filename4);
|
|
2260
|
+
AGENTS_DIR_PROD = resolve3(__dirname5, "agents");
|
|
2261
|
+
AGENTS_DIR_DEV_FALLBACK = resolve3(__dirname5, "../../../agent_moss/agents");
|
|
2236
2262
|
cache = /* @__PURE__ */ new Map();
|
|
2237
2263
|
}
|
|
2238
2264
|
});
|
|
@@ -2263,7 +2289,7 @@ function runHeuristicCheck(input) {
|
|
|
2263
2289
|
} catch {
|
|
2264
2290
|
}
|
|
2265
2291
|
}
|
|
2266
|
-
const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
|
|
2292
|
+
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2267
2293
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2268
2294
|
let topRiskType = "";
|
|
2269
2295
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
@@ -2374,6 +2400,28 @@ function stripHeredocContent(actionDetail) {
|
|
|
2374
2400
|
"<<heredoc>>\n"
|
|
2375
2401
|
);
|
|
2376
2402
|
}
|
|
2403
|
+
function normalizePathSegments(text) {
|
|
2404
|
+
const protTokens = [];
|
|
2405
|
+
let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
|
|
2406
|
+
const scheme = m.slice(0, m.indexOf(":"));
|
|
2407
|
+
protTokens.push("://");
|
|
2408
|
+
return `${scheme}__PROT${protTokens.length - 1}__`;
|
|
2409
|
+
});
|
|
2410
|
+
const quoteTokens = [];
|
|
2411
|
+
t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
|
|
2412
|
+
quoteTokens.push(m);
|
|
2413
|
+
return `__Q${quoteTokens.length - 1}__`;
|
|
2414
|
+
});
|
|
2415
|
+
t = t.replace(/\/{2,}/g, "/");
|
|
2416
|
+
t = t.replace(/\/\.\//g, "/");
|
|
2417
|
+
for (let i = 0; i < quoteTokens.length; i++) {
|
|
2418
|
+
t = t.replace(`__Q${i}__`, quoteTokens[i]);
|
|
2419
|
+
}
|
|
2420
|
+
for (let i = 0; i < protTokens.length; i++) {
|
|
2421
|
+
t = t.replace(`__PROT${i}__`, protTokens[i]);
|
|
2422
|
+
}
|
|
2423
|
+
return t;
|
|
2424
|
+
}
|
|
2377
2425
|
function splitCommandSegments(actionDetail) {
|
|
2378
2426
|
const segments = [];
|
|
2379
2427
|
let cur = "";
|
|
@@ -2426,7 +2474,7 @@ function isSearchActionType(actionType) {
|
|
|
2426
2474
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2427
2475
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2428
2476
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2429
|
-
const pathScanText = stripLiteralTextSegments(command);
|
|
2477
|
+
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2430
2478
|
const pathMatches = (text, path3) => {
|
|
2431
2479
|
const parts = path3.split(/(\(\?!.*?\))/);
|
|
2432
2480
|
const escaped = parts.map((part) => {
|
|
@@ -2697,164 +2745,25 @@ var init_heuristic = __esm({
|
|
|
2697
2745
|
init_esm_shims();
|
|
2698
2746
|
init_config();
|
|
2699
2747
|
init_agents();
|
|
2748
|
+
init_rules_loader();
|
|
2700
2749
|
init_logic_rules();
|
|
2701
|
-
INJECTION_KEYWORDS =
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
2719
|
-
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
2720
|
-
// ---- Command Injection ----
|
|
2721
|
-
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
2722
|
-
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
2723
|
-
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
2724
|
-
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
2725
|
-
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
2726
|
-
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
2727
|
-
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
2728
|
-
// ---- Social Engineering ----
|
|
2729
|
-
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
2730
|
-
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
2731
|
-
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
2732
|
-
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
2733
|
-
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
2734
|
-
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
2735
|
-
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
2736
|
-
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
2737
|
-
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
2738
|
-
// ---- XSS ----
|
|
2739
|
-
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
2740
|
-
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
2741
|
-
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
2742
|
-
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
2743
|
-
// ---- Fake Boundaries ----
|
|
2744
|
-
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
2745
|
-
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
2746
|
-
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
2747
|
-
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
2748
|
-
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
2749
|
-
// ---- Developer Mode ----
|
|
2750
|
-
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
2751
|
-
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
2752
|
-
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
2753
|
-
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
2754
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
2755
|
-
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
2756
|
-
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
2757
|
-
// 由 agent_ext 机制注入,不再污染通用层。
|
|
2758
|
-
];
|
|
2759
|
-
DANGEROUS_PATTERNS = [
|
|
2760
|
-
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
2761
|
-
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
2762
|
-
{ 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" },
|
|
2763
|
-
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
2764
|
-
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
2765
|
-
{ 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" },
|
|
2766
|
-
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
2767
|
-
{ 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" },
|
|
2768
|
-
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
2769
|
-
{ 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" },
|
|
2770
|
-
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
2771
|
-
{ 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" },
|
|
2772
|
-
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
2773
|
-
{ 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" },
|
|
2774
|
-
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2775
|
-
// --- Critical: remote code execution ---
|
|
2776
|
-
// curl|bash 优先匹配 user_rules(high/sensitive_action),见下方 UserRuleMatcher 段;
|
|
2777
|
-
// 非 bash 的 curl|sh/zsh/dash 走 CommandPatternScanner critical。两条分流由
|
|
2778
|
-
// runHeuristicCheck 的规则顺序保证(bash 在前 high,其余 fall-through 到 critical)。
|
|
2779
|
-
{ pattern: /curl\s+.*\|\s*bash/i, severity: "high", description: "curl pipe bash \u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2780
|
-
{ pattern: /curl\s+.*\|\s*(sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2781
|
-
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2782
|
-
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2783
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "high", description: "base64 \u89E3\u7801\u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2784
|
-
// --- Critical: reverse shell ---
|
|
2785
|
-
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2786
|
-
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
2787
|
-
// --- Critical: sensitive file access ---
|
|
2788
|
-
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
2789
|
-
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
2790
|
-
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
2791
|
-
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2792
|
-
// --- Critical: privilege escalation / container escape ---
|
|
2793
|
-
{ 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" },
|
|
2794
|
-
// --- Critical: dangerous permissions / ownership (提权 / 权限敞口) ---
|
|
2795
|
-
// 修正「只拦 777、xx7 漏放」的缺口:
|
|
2796
|
-
// 1) 八进制末位 7:other 位含写+执行敞口(chmod 777/647/007/4777 等);644/755/1700 不命中
|
|
2797
|
-
// 2) 八进制 setuid/setgid 位:首位 4(setuid)/2(setgid) 的 4 位八进制(chmod 4755/2755)
|
|
2798
|
-
// 3) 符号 setuid/setgid:chmod u+s / g+s / a+s
|
|
2799
|
-
// 普通权限修改(chmod 644)不在此列——L2 read_before_write 已豁免 chmod/chown(不写内容),
|
|
2800
|
-
// 危险形态在此 L1 专门拦,以「提权」正确理由拦截,不再靠 RBW 误打误撞。
|
|
2801
|
-
{ pattern: /chmod\s+[0-7]{0,3}7\b/i, severity: "critical", description: "\u5371\u9669\u6743\u9650\u8BBE\u7F6E (chmod ...7, other \u4F4D\u542B\u5199+\u6267\u884C\u655E\u53E3)", riskType: "privilege_escalation" },
|
|
2802
|
-
{ pattern: /chmod\s+[24][0-7]{3}\b/i, severity: "critical", description: "setuid/setgid \u4F4D\u8BBE\u7F6E (chmod 4xxx/2xxx, \u63D0\u6743)", riskType: "privilege_escalation" },
|
|
2803
|
-
{ pattern: /chmod\s+[ugoa]\+s\b/i, severity: "critical", description: "\u7B26\u53F7 setuid/setgid \u8BBE\u7F6E (chmod u+s/g+s/a+s, \u63D0\u6743)", riskType: "privilege_escalation" },
|
|
2804
|
-
{ 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" },
|
|
2805
|
-
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2806
|
-
{ 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" },
|
|
2807
|
-
// --- High: system config overwrite ---
|
|
2808
|
-
// > /etc/ 重定向覆盖:对齐 Python CommandPatternScanner `>\s*/etc/`(不兼容引号路径),
|
|
2809
|
-
// 引号包裹的重定向目标(cat > '/etc/ssh/sshd_config')走 checkSensitivePath 敏感路径 high。
|
|
2810
|
-
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2811
|
-
{ pattern: />\s*\/boot\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55", riskType: "file_access" },
|
|
2812
|
-
// 注:/proc/ 重定向覆盖走 checkSensitivePath 的 high(内核参数 /proc/sys/),此处不拦 /proc/ 避免遮蔽(对齐 Python)。
|
|
2813
|
-
// --- High: download script + execute risk ---
|
|
2814
|
-
// curl -X POST 外传敏感信息(对齐 Python linux.py: curl POST env/key/token)
|
|
2815
|
-
{ pattern: /curl\s+.*-X\s+POST.*\b(env|environment|secret|key|token|password)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 curl POST \u5916\u4F20\u53EF\u80FD\u5305\u542B\u654F\u611F\u4FE1\u606F\u7684\u8BF7\u6C42", riskType: "data_exfiltration" },
|
|
2816
|
-
{ 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" },
|
|
2817
|
-
{ pattern: /(?:curl|wget)\s+\S+.*\b(?:chmod|chown)\s+\+x\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u540E\u4FEE\u6539\u53EF\u6267\u884C\u6743\u9650\uFF0C\u5B58\u5728\u8FDC\u7A0B\u4EE3\u7801\u6267\u884C\u98CE\u9669", riskType: "script_execution" },
|
|
2818
|
-
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
2819
|
-
// --- High: package manager pipe-to-shell ---
|
|
2820
|
-
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
2821
|
-
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
2822
|
-
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
2823
|
-
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
2824
|
-
{ 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" },
|
|
2825
|
-
// --- High: network port scanning (横向移动侦察) ---
|
|
2826
|
-
{ 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" },
|
|
2827
|
-
{ 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" },
|
|
2828
|
-
// --- Medium: credential export ---
|
|
2829
|
-
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2830
|
-
// --- High: shell rc file append (persistence) ---
|
|
2831
|
-
// 两条规则:第一条匹配 ~ 与 /home/<user>/ 形态;第二条按文件名兜底,
|
|
2832
|
-
// 覆盖非标准 home 路径(如鸿蒙 /storage/Users/currentUser/.bashrc、
|
|
2833
|
-
// /root/.bashrc),避免按 home 前缀枚举导致漏检持久化后门。
|
|
2834
|
-
{ 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" },
|
|
2835
|
-
{ pattern: />>\s*\S*\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)\b/, 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" },
|
|
2836
|
-
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2837
|
-
{ 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" },
|
|
2838
|
-
// --- High: non-interactive password modification ---
|
|
2839
|
-
{ 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" },
|
|
2840
|
-
{ 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" },
|
|
2841
|
-
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
2842
|
-
{ 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" },
|
|
2843
|
-
{ 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" },
|
|
2844
|
-
{ 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" },
|
|
2845
|
-
// --- High: non-interactive password modification (extended) ---
|
|
2846
|
-
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
2847
|
-
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
2848
|
-
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
2849
|
-
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
2850
|
-
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
2851
|
-
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
2852
|
-
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
2853
|
-
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
2854
|
-
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
2855
|
-
// --- High: generic command -p parameter password setting ---
|
|
2856
|
-
{ 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" }
|
|
2857
|
-
];
|
|
2750
|
+
INJECTION_KEYWORDS = (() => {
|
|
2751
|
+
const kws = loadL1().injection_detection || [];
|
|
2752
|
+
return kws.map((k) => ({
|
|
2753
|
+
keyword: k.keyword,
|
|
2754
|
+
riskLevel: k.risk_level,
|
|
2755
|
+
lang: k.lang || "en"
|
|
2756
|
+
}));
|
|
2757
|
+
})();
|
|
2758
|
+
DANGEROUS_PATTERNS = (() => {
|
|
2759
|
+
const cmds = loadL1().dangerous_commands || [];
|
|
2760
|
+
return cmds.map((c) => ({
|
|
2761
|
+
pattern: new RegExp(c.pattern, "i"),
|
|
2762
|
+
severity: c.risk_level,
|
|
2763
|
+
riskType: c.risk_type,
|
|
2764
|
+
description: c.reason
|
|
2765
|
+
}));
|
|
2766
|
+
})();
|
|
2858
2767
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
2859
2768
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
2860
2769
|
}
|
|
@@ -2869,16 +2778,16 @@ __export(token_stats_exports, {
|
|
|
2869
2778
|
recordTokenUsage: () => recordTokenUsage,
|
|
2870
2779
|
resetTokenStats: () => resetTokenStats
|
|
2871
2780
|
});
|
|
2872
|
-
import { readFileSync as
|
|
2781
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2873
2782
|
function getStatsPath() {
|
|
2874
2783
|
const envPath = process.env[ENV_STATS_PATH];
|
|
2875
2784
|
if (envPath) return envPath;
|
|
2876
2785
|
return getTokenStatsPath();
|
|
2877
2786
|
}
|
|
2878
2787
|
function loadAllRecords(statsPath) {
|
|
2879
|
-
if (!
|
|
2788
|
+
if (!existsSync8(statsPath)) return [];
|
|
2880
2789
|
try {
|
|
2881
|
-
const data = JSON.parse(
|
|
2790
|
+
const data = JSON.parse(readFileSync9(statsPath, "utf-8"));
|
|
2882
2791
|
if (Array.isArray(data)) return data;
|
|
2883
2792
|
if (data && Array.isArray(data.records)) return data.records;
|
|
2884
2793
|
return [];
|
|
@@ -2889,7 +2798,7 @@ function loadAllRecords(statsPath) {
|
|
|
2889
2798
|
function saveRecords(statsPath, records) {
|
|
2890
2799
|
try {
|
|
2891
2800
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2892
|
-
if (dir && !
|
|
2801
|
+
if (dir && !existsSync8(dir)) mkdirSync2(dir, { recursive: true });
|
|
2893
2802
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2894
2803
|
} catch (e) {
|
|
2895
2804
|
}
|
|
@@ -3011,7 +2920,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
3011
2920
|
}
|
|
3012
2921
|
function resetTokenStats() {
|
|
3013
2922
|
const statsPath = getStatsPath();
|
|
3014
|
-
if (
|
|
2923
|
+
if (existsSync8(statsPath)) {
|
|
3015
2924
|
try {
|
|
3016
2925
|
unlinkSync2(statsPath);
|
|
3017
2926
|
} catch {
|
|
@@ -3042,28 +2951,28 @@ init_skill_loader();
|
|
|
3042
2951
|
|
|
3043
2952
|
// src/engine/template-loader.ts
|
|
3044
2953
|
init_esm_shims();
|
|
3045
|
-
import { readFileSync as
|
|
3046
|
-
import { resolve as
|
|
3047
|
-
import { fileURLToPath as
|
|
3048
|
-
var
|
|
3049
|
-
var
|
|
3050
|
-
var TEMPLATES_DIR =
|
|
3051
|
-
var TEMPLATES_DIR_FALLBACK =
|
|
2954
|
+
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
2955
|
+
import { resolve as resolve4, dirname as dirname5 } from "path";
|
|
2956
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
2957
|
+
var __filename5 = fileURLToPath7(import.meta.url);
|
|
2958
|
+
var __dirname6 = dirname5(__filename5);
|
|
2959
|
+
var TEMPLATES_DIR = resolve4(__dirname6, "../templates");
|
|
2960
|
+
var TEMPLATES_DIR_FALLBACK = resolve4(__dirname6, "../../../agent_moss/templates");
|
|
3052
2961
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3053
2962
|
var cachedTemplates = null;
|
|
3054
2963
|
function loadTemplates() {
|
|
3055
2964
|
if (cachedTemplates) return cachedTemplates;
|
|
3056
2965
|
cachedTemplates = {};
|
|
3057
|
-
const dir =
|
|
3058
|
-
if (!
|
|
2966
|
+
const dir = existsSync6(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
2967
|
+
if (!existsSync6(dir)) {
|
|
3059
2968
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
3060
2969
|
return cachedTemplates;
|
|
3061
2970
|
}
|
|
3062
2971
|
for (const file of TEMPLATE_FILES) {
|
|
3063
|
-
const filePath =
|
|
3064
|
-
if (
|
|
2972
|
+
const filePath = resolve4(dir, file);
|
|
2973
|
+
if (existsSync6(filePath)) {
|
|
3065
2974
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3066
|
-
cachedTemplates[key] =
|
|
2975
|
+
cachedTemplates[key] = readFileSync6(filePath, "utf-8");
|
|
3067
2976
|
}
|
|
3068
2977
|
}
|
|
3069
2978
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -3072,8 +2981,8 @@ function loadTemplates() {
|
|
|
3072
2981
|
|
|
3073
2982
|
// src/engine/script-analyzer.ts
|
|
3074
2983
|
init_esm_shims();
|
|
3075
|
-
import { readFileSync as
|
|
3076
|
-
import { resolve as
|
|
2984
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2985
|
+
import { resolve as resolve5, join as join3 } from "path";
|
|
3077
2986
|
var SUSPICIOUS_PATTERNS = [
|
|
3078
2987
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
3079
2988
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -3206,8 +3115,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3206
3115
|
}
|
|
3207
3116
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3208
3117
|
try {
|
|
3209
|
-
const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) :
|
|
3210
|
-
const raw =
|
|
3118
|
+
const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve5(scriptPath);
|
|
3119
|
+
const raw = readFileSync7(expandedPath, "utf-8");
|
|
3211
3120
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3212
3121
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
3213
3122
|
} catch (e) {
|
|
@@ -3261,8 +3170,8 @@ ${result.scriptContent}
|
|
|
3261
3170
|
init_esm_shims();
|
|
3262
3171
|
init_heuristic();
|
|
3263
3172
|
init_logic_rules();
|
|
3264
|
-
import { readFileSync as
|
|
3265
|
-
import { resolve as
|
|
3173
|
+
import { readFileSync as readFileSync8, statSync, existsSync as existsSync7 } from "fs";
|
|
3174
|
+
import { resolve as resolve6, join as join4, isAbsolute, dirname as dirname6 } from "path";
|
|
3266
3175
|
import { homedir as homedir2 } from "os";
|
|
3267
3176
|
function emptyChain() {
|
|
3268
3177
|
return {
|
|
@@ -3491,7 +3400,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
3491
3400
|
let resolved;
|
|
3492
3401
|
try {
|
|
3493
3402
|
resolved = resolvePath(p);
|
|
3494
|
-
if (
|
|
3403
|
+
if (existsSync7(resolved)) {
|
|
3495
3404
|
const stat = statSync(resolved);
|
|
3496
3405
|
if (stat.isFile()) return resolved;
|
|
3497
3406
|
}
|
|
@@ -3505,18 +3414,18 @@ function resolvePath(p) {
|
|
|
3505
3414
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
3506
3415
|
expanded = join4(homedir2(), expanded.slice(1));
|
|
3507
3416
|
}
|
|
3508
|
-
return
|
|
3417
|
+
return resolve6(expanded);
|
|
3509
3418
|
}
|
|
3510
3419
|
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
3511
3420
|
try {
|
|
3512
3421
|
const path3 = resolvePath(scriptPath);
|
|
3513
|
-
if (!
|
|
3422
|
+
if (!existsSync7(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
3514
3423
|
const stat = statSync(path3);
|
|
3515
3424
|
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
3516
3425
|
if (stat.size > maxFileSize) {
|
|
3517
3426
|
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
3518
3427
|
}
|
|
3519
|
-
const content =
|
|
3428
|
+
const content = readFileSync8(path3, "utf-8");
|
|
3520
3429
|
const lineCount = content ? content.split("\n").length : 0;
|
|
3521
3430
|
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
3522
3431
|
} catch (e) {
|
|
@@ -3535,7 +3444,7 @@ function extractChildScriptPaths(content, baseDir) {
|
|
|
3535
3444
|
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
3536
3445
|
const full = isAbsolute(pathStr) ? pathStr : join4(baseDir, pathStr);
|
|
3537
3446
|
const resolved = resolvePath(full);
|
|
3538
|
-
if (
|
|
3447
|
+
if (existsSync7(resolved)) {
|
|
3539
3448
|
try {
|
|
3540
3449
|
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
3541
3450
|
} catch {
|
|
@@ -3550,7 +3459,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
|
|
|
3550
3459
|
const result = emptyChain();
|
|
3551
3460
|
result.entryPath = entryPath;
|
|
3552
3461
|
const entry = resolvePath(entryPath);
|
|
3553
|
-
const baseDir =
|
|
3462
|
+
const baseDir = existsSync7(entry) ? dirname6(entry) : input.cwd || process.cwd();
|
|
3554
3463
|
resolveRecursive({
|
|
3555
3464
|
path: entry,
|
|
3556
3465
|
depth: 0,
|
|
@@ -3625,7 +3534,7 @@ function resolveRecursive(ctx) {
|
|
|
3625
3534
|
maxTotalSize,
|
|
3626
3535
|
visited,
|
|
3627
3536
|
result,
|
|
3628
|
-
baseDir:
|
|
3537
|
+
baseDir: dirname6(resolvePath(childPath)),
|
|
3629
3538
|
input
|
|
3630
3539
|
});
|
|
3631
3540
|
if (result.hasCritical) return;
|
|
@@ -3849,11 +3758,12 @@ ${prompt}
|
|
|
3849
3758
|
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
3850
3759
|
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
3851
3760
|
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
3761
|
+
const promptHistoryText = formatPromptHistory(input.promptHistory);
|
|
3852
3762
|
const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
3853
3763
|
const hintsText = formatHints(previous);
|
|
3854
3764
|
const securityJudgeTemplate = templates["security_judge"];
|
|
3855
3765
|
if (securityJudgeTemplate) {
|
|
3856
|
-
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
|
|
3766
|
+
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
|
|
3857
3767
|
}
|
|
3858
3768
|
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
3859
3769
|
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
@@ -3883,6 +3793,27 @@ ${scriptAnalysisText}` : ""}
|
|
|
3883
3793
|
\u8F93\u51FA\u683C\u5F0F:
|
|
3884
3794
|
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
3885
3795
|
}
|
|
3796
|
+
function formatPromptHistory(promptHistory, maxTextChars = 500) {
|
|
3797
|
+
if (!promptHistory || promptHistory.length === 0) {
|
|
3798
|
+
return "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
|
|
3799
|
+
}
|
|
3800
|
+
const lines = [];
|
|
3801
|
+
for (let i = 0; i < promptHistory.length; i++) {
|
|
3802
|
+
const turn = promptHistory[i];
|
|
3803
|
+
const text = (turn?.text || "").trim();
|
|
3804
|
+
if (!text) continue;
|
|
3805
|
+
let line = ` \u7B2C${i + 1}\u8F6E\u7528\u6237\u610F\u56FE: ${text.slice(0, maxTextChars)}`;
|
|
3806
|
+
const actions = turn?.actions;
|
|
3807
|
+
if (actions && actions.length > 0) {
|
|
3808
|
+
const types = actions.map((a) => a?.actionType).filter((t) => typeof t === "string" && t.length > 0);
|
|
3809
|
+
if (types.length > 0) {
|
|
3810
|
+
line += ` \u2192 \u52A8\u4F5C: ${types.join(", ")}`;
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
lines.push(line);
|
|
3814
|
+
}
|
|
3815
|
+
return lines.length > 0 ? lines.join("\n") : "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
|
|
3816
|
+
}
|
|
3886
3817
|
function formatHints(previous) {
|
|
3887
3818
|
const hints = [];
|
|
3888
3819
|
if (previous.heuristic) {
|
|
@@ -3978,7 +3909,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
3978
3909
|
}
|
|
3979
3910
|
if (attempt < maxRetries) {
|
|
3980
3911
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
3981
|
-
await new Promise((
|
|
3912
|
+
await new Promise((resolve7) => setTimeout(resolve7, retryCfg.retryInterval * 1e3));
|
|
3982
3913
|
} else {
|
|
3983
3914
|
clearTimeout(totalTimeoutTimer);
|
|
3984
3915
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|