@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
package/dist/server.js
CHANGED
|
@@ -574,8 +574,70 @@ var init_inline_analyzer = __esm({
|
|
|
574
574
|
}
|
|
575
575
|
});
|
|
576
576
|
|
|
577
|
+
// src/infra/rules-loader.ts
|
|
578
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
579
|
+
import { resolve, dirname } from "path";
|
|
580
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
581
|
+
function findRulesFile(name) {
|
|
582
|
+
for (const dir of RULES_DIR_CANDS) {
|
|
583
|
+
const p = resolve(dir, name);
|
|
584
|
+
if (existsSync(p)) return p;
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
function loadJson(name) {
|
|
589
|
+
const p = findRulesFile(name);
|
|
590
|
+
if (!p) return {};
|
|
591
|
+
try {
|
|
592
|
+
return JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
593
|
+
} catch {
|
|
594
|
+
return {};
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function loadL1() {
|
|
598
|
+
return loadJson("l1_rules.json");
|
|
599
|
+
}
|
|
600
|
+
function loadL2() {
|
|
601
|
+
return loadJson("l2_rules.json");
|
|
602
|
+
}
|
|
603
|
+
function loadL3() {
|
|
604
|
+
return loadJson("l3_rules.json");
|
|
605
|
+
}
|
|
606
|
+
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
607
|
+
var init_rules_loader = __esm({
|
|
608
|
+
"src/infra/rules-loader.ts"() {
|
|
609
|
+
"use strict";
|
|
610
|
+
init_esm_shims();
|
|
611
|
+
__filename2 = fileURLToPath3(import.meta.url);
|
|
612
|
+
__dirname2 = dirname(__filename2);
|
|
613
|
+
RULES_DIR_CANDS = (() => {
|
|
614
|
+
const cands = [
|
|
615
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
616
|
+
resolve(__dirname2, "../rules"),
|
|
617
|
+
resolve(__dirname2, "rules"),
|
|
618
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
619
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
620
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
621
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
622
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
623
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
624
|
+
];
|
|
625
|
+
return cands.filter((c) => existsSync(c));
|
|
626
|
+
})();
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
577
630
|
// src/engine/logic-rules.ts
|
|
578
631
|
import * as fs2 from "fs";
|
|
632
|
+
function _loadSensitivePathScope(scope) {
|
|
633
|
+
const sps = loadL1().sensitive_path_access || [];
|
|
634
|
+
return sps.filter((sp) => sp.scope === scope).map((sp) => ({
|
|
635
|
+
path: sp.path,
|
|
636
|
+
riskLevel: sp.risk_level,
|
|
637
|
+
desc: sp.desc,
|
|
638
|
+
credential: sp.deny_mode === "deny_both" ? true : void 0
|
|
639
|
+
}));
|
|
640
|
+
}
|
|
579
641
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
580
642
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
581
643
|
if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail))) return true;
|
|
@@ -620,6 +682,32 @@ function matchDangerousAction(actionDetail, action) {
|
|
|
620
682
|
const re = new RegExp(`(?<![-\\w])${escaped}\\b`, "i");
|
|
621
683
|
return re.test(actionDetail);
|
|
622
684
|
}
|
|
685
|
+
function collectIntentTexts(promptSession, promptHistory) {
|
|
686
|
+
const texts = [];
|
|
687
|
+
if (promptHistory) {
|
|
688
|
+
for (const turn of promptHistory) {
|
|
689
|
+
if (typeof turn === "string") {
|
|
690
|
+
const t2 = turn.trim();
|
|
691
|
+
if (t2) texts.push(t2.toLowerCase());
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const t = (turn?.text || "").trim();
|
|
695
|
+
if (t) texts.push(t.toLowerCase());
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (promptSession && promptSession.trim()) {
|
|
699
|
+
texts.push(promptSession.toLowerCase());
|
|
700
|
+
}
|
|
701
|
+
const seen = /* @__PURE__ */ new Set();
|
|
702
|
+
const deduped = [];
|
|
703
|
+
for (const t of texts) {
|
|
704
|
+
if (!seen.has(t)) {
|
|
705
|
+
seen.add(t);
|
|
706
|
+
deduped.push(t);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return deduped;
|
|
710
|
+
}
|
|
623
711
|
function runLogicCheck(input) {
|
|
624
712
|
const { actionHistory, aNext, cwd } = input;
|
|
625
713
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
@@ -629,25 +717,24 @@ function runLogicCheck(input) {
|
|
|
629
717
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
630
718
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
|
|
631
719
|
if (rbwResult) return rbwResult;
|
|
632
|
-
if (
|
|
633
|
-
if (!
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
720
|
+
if (!isExcludedType) {
|
|
721
|
+
if (!INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
722
|
+
const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
|
|
723
|
+
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
724
|
+
const intentMatched = intentTexts.some(
|
|
725
|
+
(text) => pattern.intentKeywords.some((kw) => text.includes(kw))
|
|
726
|
+
);
|
|
727
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
728
|
+
if (intentMatched && dangerousMatched) {
|
|
729
|
+
return {
|
|
730
|
+
allowed: false,
|
|
731
|
+
reason: pattern.reason,
|
|
732
|
+
riskLevel: "high",
|
|
733
|
+
riskType: "intent_deviation",
|
|
734
|
+
confidence: 85,
|
|
735
|
+
source: "logic_rule",
|
|
736
|
+
violatedLayers: ["2"]
|
|
737
|
+
};
|
|
651
738
|
}
|
|
652
739
|
}
|
|
653
740
|
}
|
|
@@ -937,57 +1024,10 @@ var init_logic_rules = __esm({
|
|
|
937
1024
|
"use strict";
|
|
938
1025
|
init_esm_shims();
|
|
939
1026
|
init_inline_analyzer();
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
945
|
-
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
946
|
-
// 系统配置(仅拦截写入)
|
|
947
|
-
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
948
|
-
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
949
|
-
{ path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
|
|
950
|
-
{ path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
|
|
951
|
-
{ path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
|
|
952
|
-
{ path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
|
|
953
|
-
{ path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
|
|
954
|
-
// 危险目录(仅拦截写入)
|
|
955
|
-
{ path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
|
|
956
|
-
{ path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
|
|
957
|
-
{ path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
|
|
958
|
-
// 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
|
|
959
|
-
{ path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
|
|
960
|
-
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907\uFF08\u8BFB\u5199\u5747\u5371\u9669\uFF09", credential: true },
|
|
961
|
-
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
962
|
-
// SSH 主机私钥
|
|
963
|
-
// SSH 主机私钥(排除 .pub 公钥变体,对齐 Python .ssh/id_rsa(?!\.pub) 范式)
|
|
964
|
-
{ path: "/etc/ssh/ssh_host_rsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
|
|
965
|
-
{ path: "/etc/ssh/ssh_host_ed25519_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
|
|
966
|
-
{ path: "/etc/ssh/ssh_host_ecdsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true },
|
|
967
|
-
// Nginx 配置(仅拦截写入,对齐 Python linux.py sensitive_paths)
|
|
968
|
-
{ path: "/etc/nginx/nginx.conf", riskLevel: "high", desc: "Nginx \u4E3B\u914D\u7F6E" },
|
|
969
|
-
{ path: "/etc/nginx/conf.d/", riskLevel: "high", desc: "Nginx \u5B50\u914D\u7F6E\u76EE\u5F55" },
|
|
970
|
-
{ path: "/etc/nginx/sites-enabled/", riskLevel: "high", desc: "Nginx \u542F\u7528\u7AD9\u70B9\u914D\u7F6E\u76EE\u5F55" }
|
|
971
|
-
];
|
|
972
|
-
HOME_SENSITIVE_PATHS = [
|
|
973
|
-
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
|
|
974
|
-
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
|
|
975
|
-
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
976
|
-
// 持久化与命令劫持(仅拦截写入)
|
|
977
|
-
{ path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
|
|
978
|
-
{ path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
|
|
979
|
-
{ path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
|
|
980
|
-
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
|
|
981
|
-
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
982
|
-
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
983
|
-
];
|
|
984
|
-
CREDENTIAL_PATHS = [
|
|
985
|
-
{ path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
986
|
-
{ path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
987
|
-
{ path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
988
|
-
{ path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
989
|
-
{ path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
|
|
990
|
-
];
|
|
1027
|
+
init_rules_loader();
|
|
1028
|
+
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1029
|
+
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1030
|
+
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path3, riskLevel, desc }) => ({ path: path3, riskLevel, desc, credential: true }));
|
|
991
1031
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
992
1032
|
"ask_user_question",
|
|
993
1033
|
"glob",
|
|
@@ -1115,34 +1155,22 @@ var init_logic_rules = __esm({
|
|
|
1115
1155
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
1116
1156
|
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"];
|
|
1117
1157
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1118
|
-
PASSWORD_MODIFY_PATTERNS =
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
INTENT_DEVIATION_PATTERNS = [
|
|
1135
|
-
{
|
|
1136
|
-
intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
|
|
1137
|
-
dangerousActions: ["\u5220\u9664", "remove", "delete", "rm", "\u683C\u5F0F\u5316", "format"],
|
|
1138
|
-
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
|
|
1139
|
-
},
|
|
1140
|
-
{
|
|
1141
|
-
intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
|
|
1142
|
-
dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
|
|
1143
|
-
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
|
|
1144
|
-
}
|
|
1145
|
-
];
|
|
1158
|
+
PASSWORD_MODIFY_PATTERNS = (() => {
|
|
1159
|
+
const pats = loadL2().password_modify_consent || [];
|
|
1160
|
+
return pats.map((p) => new RegExp(p.pattern, "i"));
|
|
1161
|
+
})();
|
|
1162
|
+
USER_DELETION_PATTERNS = (() => {
|
|
1163
|
+
const pats = loadL2().user_deletion_consent || [];
|
|
1164
|
+
return pats.map((p) => new RegExp(p.pattern, "i"));
|
|
1165
|
+
})();
|
|
1166
|
+
INTENT_DEVIATION_PATTERNS = (() => {
|
|
1167
|
+
const pats = loadL2().intent_consistency || [];
|
|
1168
|
+
return pats.map((p) => ({
|
|
1169
|
+
intentKeywords: p.intent_keywords,
|
|
1170
|
+
dangerousActions: p.dangerous_actions,
|
|
1171
|
+
reason: p.reason
|
|
1172
|
+
}));
|
|
1173
|
+
})();
|
|
1146
1174
|
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;
|
|
1147
1175
|
DIR_TRAVERSAL_KW = [
|
|
1148
1176
|
"os.listdir",
|
|
@@ -1160,21 +1188,21 @@ var init_logic_rules = __esm({
|
|
|
1160
1188
|
});
|
|
1161
1189
|
|
|
1162
1190
|
// src/engine/skill-loader.ts
|
|
1163
|
-
import { readFileSync as
|
|
1164
|
-
import { resolve, dirname } from "path";
|
|
1165
|
-
import { fileURLToPath as
|
|
1191
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
1192
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
1193
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1166
1194
|
function loadAllSkills() {
|
|
1167
1195
|
if (cachedSkills) return cachedSkills;
|
|
1168
1196
|
cachedSkills = {};
|
|
1169
|
-
const dir =
|
|
1170
|
-
if (!
|
|
1197
|
+
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1198
|
+
if (!existsSync3(dir)) {
|
|
1171
1199
|
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1172
1200
|
return cachedSkills;
|
|
1173
1201
|
}
|
|
1174
1202
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1175
1203
|
for (const f of files) {
|
|
1176
1204
|
const name = f.replace(".md", "");
|
|
1177
|
-
cachedSkills[name] =
|
|
1205
|
+
cachedSkills[name] = readFileSync3(resolve2(dir, f), "utf-8");
|
|
1178
1206
|
}
|
|
1179
1207
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1180
1208
|
return cachedSkills;
|
|
@@ -1228,32 +1256,27 @@ function formatSkillsForPrompt(skills) {
|
|
|
1228
1256
|
${s.skillContent}`
|
|
1229
1257
|
).join("\n\n---\n\n");
|
|
1230
1258
|
}
|
|
1231
|
-
var
|
|
1259
|
+
var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1232
1260
|
var init_skill_loader = __esm({
|
|
1233
1261
|
"src/engine/skill-loader.ts"() {
|
|
1234
1262
|
"use strict";
|
|
1235
1263
|
init_esm_shims();
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
"skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
|
|
1253
|
-
"supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
|
|
1254
|
-
"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"],
|
|
1255
|
-
"indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
|
|
1256
|
-
};
|
|
1264
|
+
init_rules_loader();
|
|
1265
|
+
__filename3 = fileURLToPath4(import.meta.url);
|
|
1266
|
+
__dirname3 = dirname2(__filename3);
|
|
1267
|
+
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1268
|
+
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1269
|
+
SKILL_KEYWORD_MAP = (() => {
|
|
1270
|
+
const map = {};
|
|
1271
|
+
const cats = loadL3();
|
|
1272
|
+
for (const cat of Object.values(cats)) {
|
|
1273
|
+
if (!cat || typeof cat !== "object") continue;
|
|
1274
|
+
for (const [skillId, keywords] of Object.entries(cat)) {
|
|
1275
|
+
map[skillId] = keywords;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return map;
|
|
1279
|
+
})();
|
|
1257
1280
|
cachedSkills = null;
|
|
1258
1281
|
}
|
|
1259
1282
|
});
|
|
@@ -1305,10 +1328,10 @@ __export(runtime_config_exports, {
|
|
|
1305
1328
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1306
1329
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1307
1330
|
});
|
|
1308
|
-
import { readFileSync as
|
|
1331
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1309
1332
|
import { join as join2 } from "path";
|
|
1310
|
-
import { fileURLToPath as
|
|
1311
|
-
import { dirname as
|
|
1333
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1334
|
+
import { dirname as dirname3 } from "path";
|
|
1312
1335
|
function runtimeDir() {
|
|
1313
1336
|
return getRuntimeConfigDir();
|
|
1314
1337
|
}
|
|
@@ -1320,7 +1343,7 @@ function getRuntimeConfigPath() {
|
|
|
1320
1343
|
function saveRuntimeConfig(config) {
|
|
1321
1344
|
const p = getRuntimeConfigPath();
|
|
1322
1345
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1323
|
-
if (dir && !
|
|
1346
|
+
if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
|
|
1324
1347
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1325
1348
|
}
|
|
1326
1349
|
function emptyRuntimeConfig() {
|
|
@@ -1335,7 +1358,7 @@ function emptyRuntimeConfig() {
|
|
|
1335
1358
|
}
|
|
1336
1359
|
function loadRuntimeConfig() {
|
|
1337
1360
|
const p = getRuntimeConfigPath();
|
|
1338
|
-
if (!
|
|
1361
|
+
if (!existsSync4(p)) {
|
|
1339
1362
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1340
1363
|
try {
|
|
1341
1364
|
saveRuntimeConfig(seeded);
|
|
@@ -1344,7 +1367,7 @@ function loadRuntimeConfig() {
|
|
|
1344
1367
|
return seeded;
|
|
1345
1368
|
}
|
|
1346
1369
|
try {
|
|
1347
|
-
const data = JSON.parse(
|
|
1370
|
+
const data = JSON.parse(readFileSync4(p, "utf-8"));
|
|
1348
1371
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1349
1372
|
return {
|
|
1350
1373
|
version: data.version || 1,
|
|
@@ -1412,9 +1435,9 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1412
1435
|
}
|
|
1413
1436
|
function hasRuntimeConfig() {
|
|
1414
1437
|
const p = getRuntimeConfigPath();
|
|
1415
|
-
if (!
|
|
1438
|
+
if (!existsSync4(p)) return false;
|
|
1416
1439
|
try {
|
|
1417
|
-
JSON.parse(
|
|
1440
|
+
JSON.parse(readFileSync4(p, "utf-8"));
|
|
1418
1441
|
return true;
|
|
1419
1442
|
} catch {
|
|
1420
1443
|
return false;
|
|
@@ -1593,8 +1616,8 @@ function _l3SkillsToRules() {
|
|
|
1593
1616
|
}
|
|
1594
1617
|
function _agentRulesToDefaults() {
|
|
1595
1618
|
const out = {};
|
|
1596
|
-
const agentsDir =
|
|
1597
|
-
if (!
|
|
1619
|
+
const agentsDir = existsSync4(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync4(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
1620
|
+
if (!existsSync4(agentsDir)) return out;
|
|
1598
1621
|
let entries;
|
|
1599
1622
|
try {
|
|
1600
1623
|
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1603,10 +1626,10 @@ function _agentRulesToDefaults() {
|
|
|
1603
1626
|
}
|
|
1604
1627
|
for (const agentId of entries) {
|
|
1605
1628
|
const rulesFile = join2(agentsDir, agentId, "rules.json");
|
|
1606
|
-
if (!
|
|
1629
|
+
if (!existsSync4(rulesFile)) continue;
|
|
1607
1630
|
let data;
|
|
1608
1631
|
try {
|
|
1609
|
-
data = JSON.parse(
|
|
1632
|
+
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
1610
1633
|
} catch {
|
|
1611
1634
|
continue;
|
|
1612
1635
|
}
|
|
@@ -1685,6 +1708,9 @@ function generateSourceDefaults() {
|
|
|
1685
1708
|
agent_rules: _agentRulesToDefaults()
|
|
1686
1709
|
};
|
|
1687
1710
|
}
|
|
1711
|
+
function normalizePatternForFingerprint(pattern) {
|
|
1712
|
+
return pattern.replace(/\\\//g, "/");
|
|
1713
|
+
}
|
|
1688
1714
|
function _ruleFingerprint(rule) {
|
|
1689
1715
|
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1690
1716
|
return JSON.stringify([
|
|
@@ -1692,8 +1718,8 @@ function _ruleFingerprint(rule) {
|
|
|
1692
1718
|
[...rule.dangerous_actions || []].sort()
|
|
1693
1719
|
]);
|
|
1694
1720
|
}
|
|
1695
|
-
if (rule.path) return JSON.stringify([rule.path]);
|
|
1696
|
-
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1721
|
+
if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
|
|
1722
|
+
if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
|
|
1697
1723
|
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1698
1724
|
if (rule.value) return JSON.stringify([rule.value]);
|
|
1699
1725
|
return JSON.stringify([rule.id || ""]);
|
|
@@ -2044,7 +2070,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2044
2070
|
skills.splice(idx, 1);
|
|
2045
2071
|
const skillPath = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2046
2072
|
try {
|
|
2047
|
-
if (
|
|
2073
|
+
if (existsSync4(skillPath)) unlinkSync(skillPath);
|
|
2048
2074
|
} catch {
|
|
2049
2075
|
}
|
|
2050
2076
|
saveRuntimeConfig(runtime);
|
|
@@ -2158,7 +2184,7 @@ function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
|
2158
2184
|
saveRuntimeConfig(runtime);
|
|
2159
2185
|
return runtime;
|
|
2160
2186
|
}
|
|
2161
|
-
var
|
|
2187
|
+
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;
|
|
2162
2188
|
var init_runtime_config = __esm({
|
|
2163
2189
|
"src/infra/runtime-config.ts"() {
|
|
2164
2190
|
"use strict";
|
|
@@ -2167,7 +2193,7 @@ var init_runtime_config = __esm({
|
|
|
2167
2193
|
init_heuristic();
|
|
2168
2194
|
init_logic_rules();
|
|
2169
2195
|
init_skill_loader();
|
|
2170
|
-
|
|
2196
|
+
__dirname4 = dirname3(fileURLToPath5(import.meta.url));
|
|
2171
2197
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2172
2198
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
2173
2199
|
RUNTIME_CONFIG_PATH = join2(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
@@ -2181,19 +2207,19 @@ var init_runtime_config = __esm({
|
|
|
2181
2207
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2182
2208
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2183
2209
|
};
|
|
2184
|
-
_AGENTS_DIR_PROD = join2(
|
|
2185
|
-
_AGENTS_DIR_DEV = join2(
|
|
2210
|
+
_AGENTS_DIR_PROD = join2(__dirname4, "agents");
|
|
2211
|
+
_AGENTS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2186
2212
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2187
2213
|
}
|
|
2188
2214
|
});
|
|
2189
2215
|
|
|
2190
2216
|
// src/agents/index.ts
|
|
2191
|
-
import { readFileSync as
|
|
2192
|
-
import { resolve as
|
|
2193
|
-
import { fileURLToPath as
|
|
2217
|
+
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
|
|
2218
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
2219
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2194
2220
|
function resolveAgentsDir() {
|
|
2195
|
-
if (
|
|
2196
|
-
if (
|
|
2221
|
+
if (existsSync5(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
2222
|
+
if (existsSync5(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
2197
2223
|
return AGENTS_DIR_PROD;
|
|
2198
2224
|
}
|
|
2199
2225
|
function getAgentExtensions(agentId) {
|
|
@@ -2209,13 +2235,13 @@ function getAgentExtensions(agentId) {
|
|
|
2209
2235
|
}
|
|
2210
2236
|
if (ext === null) {
|
|
2211
2237
|
const agentsDir = resolveAgentsDir();
|
|
2212
|
-
const rulesFile =
|
|
2213
|
-
if (!
|
|
2238
|
+
const rulesFile = resolve3(agentsDir, agentId, "rules.json");
|
|
2239
|
+
if (!existsSync5(rulesFile)) {
|
|
2214
2240
|
cache.set(agentId, null);
|
|
2215
2241
|
return null;
|
|
2216
2242
|
}
|
|
2217
2243
|
try {
|
|
2218
|
-
const raw =
|
|
2244
|
+
const raw = readFileSync5(rulesFile, "utf-8");
|
|
2219
2245
|
ext = JSON.parse(raw);
|
|
2220
2246
|
if (!ext.agent_id) {
|
|
2221
2247
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
@@ -2234,15 +2260,15 @@ function getAgentExtensions(agentId) {
|
|
|
2234
2260
|
);
|
|
2235
2261
|
return ext;
|
|
2236
2262
|
}
|
|
2237
|
-
var
|
|
2263
|
+
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
2238
2264
|
var init_agents = __esm({
|
|
2239
2265
|
"src/agents/index.ts"() {
|
|
2240
2266
|
"use strict";
|
|
2241
2267
|
init_esm_shims();
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
AGENTS_DIR_PROD =
|
|
2245
|
-
AGENTS_DIR_DEV_FALLBACK =
|
|
2268
|
+
__filename4 = fileURLToPath6(import.meta.url);
|
|
2269
|
+
__dirname5 = dirname4(__filename4);
|
|
2270
|
+
AGENTS_DIR_PROD = resolve3(__dirname5, "agents");
|
|
2271
|
+
AGENTS_DIR_DEV_FALLBACK = resolve3(__dirname5, "../../../agent_moss/agents");
|
|
2246
2272
|
cache = /* @__PURE__ */ new Map();
|
|
2247
2273
|
}
|
|
2248
2274
|
});
|
|
@@ -2273,7 +2299,7 @@ function runHeuristicCheck(input) {
|
|
|
2273
2299
|
} catch {
|
|
2274
2300
|
}
|
|
2275
2301
|
}
|
|
2276
|
-
const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
|
|
2302
|
+
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2277
2303
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2278
2304
|
let topRiskType = "";
|
|
2279
2305
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
@@ -2384,6 +2410,28 @@ function stripHeredocContent(actionDetail) {
|
|
|
2384
2410
|
"<<heredoc>>\n"
|
|
2385
2411
|
);
|
|
2386
2412
|
}
|
|
2413
|
+
function normalizePathSegments(text) {
|
|
2414
|
+
const protTokens = [];
|
|
2415
|
+
let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
|
|
2416
|
+
const scheme = m.slice(0, m.indexOf(":"));
|
|
2417
|
+
protTokens.push("://");
|
|
2418
|
+
return `${scheme}__PROT${protTokens.length - 1}__`;
|
|
2419
|
+
});
|
|
2420
|
+
const quoteTokens = [];
|
|
2421
|
+
t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
|
|
2422
|
+
quoteTokens.push(m);
|
|
2423
|
+
return `__Q${quoteTokens.length - 1}__`;
|
|
2424
|
+
});
|
|
2425
|
+
t = t.replace(/\/{2,}/g, "/");
|
|
2426
|
+
t = t.replace(/\/\.\//g, "/");
|
|
2427
|
+
for (let i = 0; i < quoteTokens.length; i++) {
|
|
2428
|
+
t = t.replace(`__Q${i}__`, quoteTokens[i]);
|
|
2429
|
+
}
|
|
2430
|
+
for (let i = 0; i < protTokens.length; i++) {
|
|
2431
|
+
t = t.replace(`__PROT${i}__`, protTokens[i]);
|
|
2432
|
+
}
|
|
2433
|
+
return t;
|
|
2434
|
+
}
|
|
2387
2435
|
function splitCommandSegments(actionDetail) {
|
|
2388
2436
|
const segments = [];
|
|
2389
2437
|
let cur = "";
|
|
@@ -2436,7 +2484,7 @@ function isSearchActionType(actionType) {
|
|
|
2436
2484
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2437
2485
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2438
2486
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2439
|
-
const pathScanText = stripLiteralTextSegments(command);
|
|
2487
|
+
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2440
2488
|
const pathMatches = (text, path3) => {
|
|
2441
2489
|
const parts = path3.split(/(\(\?!.*?\))/);
|
|
2442
2490
|
const escaped = parts.map((part) => {
|
|
@@ -2707,164 +2755,25 @@ var init_heuristic = __esm({
|
|
|
2707
2755
|
init_esm_shims();
|
|
2708
2756
|
init_config();
|
|
2709
2757
|
init_agents();
|
|
2758
|
+
init_rules_loader();
|
|
2710
2759
|
init_logic_rules();
|
|
2711
|
-
INJECTION_KEYWORDS =
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
2729
|
-
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
2730
|
-
// ---- Command Injection ----
|
|
2731
|
-
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
2732
|
-
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
2733
|
-
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
2734
|
-
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
2735
|
-
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
2736
|
-
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
2737
|
-
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
2738
|
-
// ---- Social Engineering ----
|
|
2739
|
-
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
2740
|
-
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
2741
|
-
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
2742
|
-
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
2743
|
-
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
2744
|
-
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
2745
|
-
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
2746
|
-
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
2747
|
-
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
2748
|
-
// ---- XSS ----
|
|
2749
|
-
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
2750
|
-
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
2751
|
-
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
2752
|
-
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
2753
|
-
// ---- Fake Boundaries ----
|
|
2754
|
-
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
2755
|
-
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
2756
|
-
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
2757
|
-
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
2758
|
-
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
2759
|
-
// ---- Developer Mode ----
|
|
2760
|
-
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
2761
|
-
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
2762
|
-
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
2763
|
-
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
2764
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
2765
|
-
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
2766
|
-
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
2767
|
-
// 由 agent_ext 机制注入,不再污染通用层。
|
|
2768
|
-
];
|
|
2769
|
-
DANGEROUS_PATTERNS = [
|
|
2770
|
-
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
2771
|
-
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
2772
|
-
{ 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" },
|
|
2773
|
-
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
2774
|
-
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
2775
|
-
{ 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" },
|
|
2776
|
-
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
2777
|
-
{ 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" },
|
|
2778
|
-
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
2779
|
-
{ 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" },
|
|
2780
|
-
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
2781
|
-
{ 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" },
|
|
2782
|
-
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
2783
|
-
{ 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" },
|
|
2784
|
-
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2785
|
-
// --- Critical: remote code execution ---
|
|
2786
|
-
// curl|bash 优先匹配 user_rules(high/sensitive_action),见下方 UserRuleMatcher 段;
|
|
2787
|
-
// 非 bash 的 curl|sh/zsh/dash 走 CommandPatternScanner critical。两条分流由
|
|
2788
|
-
// runHeuristicCheck 的规则顺序保证(bash 在前 high,其余 fall-through 到 critical)。
|
|
2789
|
-
{ pattern: /curl\s+.*\|\s*bash/i, severity: "high", description: "curl pipe bash \u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2790
|
-
{ pattern: /curl\s+.*\|\s*(sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2791
|
-
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2792
|
-
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2793
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "high", description: "base64 \u89E3\u7801\u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2794
|
-
// --- Critical: reverse shell ---
|
|
2795
|
-
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2796
|
-
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
2797
|
-
// --- Critical: sensitive file access ---
|
|
2798
|
-
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
2799
|
-
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
2800
|
-
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
2801
|
-
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2802
|
-
// --- Critical: privilege escalation / container escape ---
|
|
2803
|
-
{ 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" },
|
|
2804
|
-
// --- Critical: dangerous permissions / ownership (提权 / 权限敞口) ---
|
|
2805
|
-
// 修正「只拦 777、xx7 漏放」的缺口:
|
|
2806
|
-
// 1) 八进制末位 7:other 位含写+执行敞口(chmod 777/647/007/4777 等);644/755/1700 不命中
|
|
2807
|
-
// 2) 八进制 setuid/setgid 位:首位 4(setuid)/2(setgid) 的 4 位八进制(chmod 4755/2755)
|
|
2808
|
-
// 3) 符号 setuid/setgid:chmod u+s / g+s / a+s
|
|
2809
|
-
// 普通权限修改(chmod 644)不在此列——L2 read_before_write 已豁免 chmod/chown(不写内容),
|
|
2810
|
-
// 危险形态在此 L1 专门拦,以「提权」正确理由拦截,不再靠 RBW 误打误撞。
|
|
2811
|
-
{ 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" },
|
|
2812
|
-
{ 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" },
|
|
2813
|
-
{ 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" },
|
|
2814
|
-
{ 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" },
|
|
2815
|
-
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2816
|
-
{ 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" },
|
|
2817
|
-
// --- High: system config overwrite ---
|
|
2818
|
-
// > /etc/ 重定向覆盖:对齐 Python CommandPatternScanner `>\s*/etc/`(不兼容引号路径),
|
|
2819
|
-
// 引号包裹的重定向目标(cat > '/etc/ssh/sshd_config')走 checkSensitivePath 敏感路径 high。
|
|
2820
|
-
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2821
|
-
{ pattern: />\s*\/boot\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55", riskType: "file_access" },
|
|
2822
|
-
// 注:/proc/ 重定向覆盖走 checkSensitivePath 的 high(内核参数 /proc/sys/),此处不拦 /proc/ 避免遮蔽(对齐 Python)。
|
|
2823
|
-
// --- High: download script + execute risk ---
|
|
2824
|
-
// curl -X POST 外传敏感信息(对齐 Python linux.py: curl POST env/key/token)
|
|
2825
|
-
{ 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" },
|
|
2826
|
-
{ 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" },
|
|
2827
|
-
{ 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" },
|
|
2828
|
-
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
2829
|
-
// --- High: package manager pipe-to-shell ---
|
|
2830
|
-
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
2831
|
-
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
2832
|
-
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
2833
|
-
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
2834
|
-
{ 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" },
|
|
2835
|
-
// --- High: network port scanning (横向移动侦察) ---
|
|
2836
|
-
{ 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" },
|
|
2837
|
-
{ 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" },
|
|
2838
|
-
// --- Medium: credential export ---
|
|
2839
|
-
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2840
|
-
// --- High: shell rc file append (persistence) ---
|
|
2841
|
-
// 两条规则:第一条匹配 ~ 与 /home/<user>/ 形态;第二条按文件名兜底,
|
|
2842
|
-
// 覆盖非标准 home 路径(如鸿蒙 /storage/Users/currentUser/.bashrc、
|
|
2843
|
-
// /root/.bashrc),避免按 home 前缀枚举导致漏检持久化后门。
|
|
2844
|
-
{ 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" },
|
|
2845
|
-
{ 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" },
|
|
2846
|
-
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2847
|
-
{ 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" },
|
|
2848
|
-
// --- High: non-interactive password modification ---
|
|
2849
|
-
{ 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" },
|
|
2850
|
-
{ 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" },
|
|
2851
|
-
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
2852
|
-
{ 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" },
|
|
2853
|
-
{ 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" },
|
|
2854
|
-
{ 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" },
|
|
2855
|
-
// --- High: non-interactive password modification (extended) ---
|
|
2856
|
-
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
2857
|
-
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
2858
|
-
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
2859
|
-
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
2860
|
-
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
2861
|
-
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
2862
|
-
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
2863
|
-
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
2864
|
-
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
2865
|
-
// --- High: generic command -p parameter password setting ---
|
|
2866
|
-
{ 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" }
|
|
2867
|
-
];
|
|
2760
|
+
INJECTION_KEYWORDS = (() => {
|
|
2761
|
+
const kws = loadL1().injection_detection || [];
|
|
2762
|
+
return kws.map((k) => ({
|
|
2763
|
+
keyword: k.keyword,
|
|
2764
|
+
riskLevel: k.risk_level,
|
|
2765
|
+
lang: k.lang || "en"
|
|
2766
|
+
}));
|
|
2767
|
+
})();
|
|
2768
|
+
DANGEROUS_PATTERNS = (() => {
|
|
2769
|
+
const cmds = loadL1().dangerous_commands || [];
|
|
2770
|
+
return cmds.map((c) => ({
|
|
2771
|
+
pattern: new RegExp(c.pattern, "i"),
|
|
2772
|
+
severity: c.risk_level,
|
|
2773
|
+
riskType: c.risk_type,
|
|
2774
|
+
description: c.reason
|
|
2775
|
+
}));
|
|
2776
|
+
})();
|
|
2868
2777
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
2869
2778
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
2870
2779
|
}
|
|
@@ -2879,16 +2788,16 @@ __export(token_stats_exports, {
|
|
|
2879
2788
|
recordTokenUsage: () => recordTokenUsage,
|
|
2880
2789
|
resetTokenStats: () => resetTokenStats
|
|
2881
2790
|
});
|
|
2882
|
-
import { readFileSync as
|
|
2791
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2883
2792
|
function getStatsPath() {
|
|
2884
2793
|
const envPath = process.env[ENV_STATS_PATH];
|
|
2885
2794
|
if (envPath) return envPath;
|
|
2886
2795
|
return getTokenStatsPath();
|
|
2887
2796
|
}
|
|
2888
2797
|
function loadAllRecords(statsPath) {
|
|
2889
|
-
if (!
|
|
2798
|
+
if (!existsSync8(statsPath)) return [];
|
|
2890
2799
|
try {
|
|
2891
|
-
const data = JSON.parse(
|
|
2800
|
+
const data = JSON.parse(readFileSync9(statsPath, "utf-8"));
|
|
2892
2801
|
if (Array.isArray(data)) return data;
|
|
2893
2802
|
if (data && Array.isArray(data.records)) return data.records;
|
|
2894
2803
|
return [];
|
|
@@ -2899,7 +2808,7 @@ function loadAllRecords(statsPath) {
|
|
|
2899
2808
|
function saveRecords(statsPath, records) {
|
|
2900
2809
|
try {
|
|
2901
2810
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2902
|
-
if (dir && !
|
|
2811
|
+
if (dir && !existsSync8(dir)) mkdirSync2(dir, { recursive: true });
|
|
2903
2812
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2904
2813
|
} catch (e) {
|
|
2905
2814
|
}
|
|
@@ -3021,7 +2930,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
3021
2930
|
}
|
|
3022
2931
|
function resetTokenStats() {
|
|
3023
2932
|
const statsPath = getStatsPath();
|
|
3024
|
-
if (
|
|
2933
|
+
if (existsSync8(statsPath)) {
|
|
3025
2934
|
try {
|
|
3026
2935
|
unlinkSync2(statsPath);
|
|
3027
2936
|
} catch {
|
|
@@ -3121,9 +3030,9 @@ var require_bindings = __commonJS({
|
|
|
3121
3030
|
init_esm_shims();
|
|
3122
3031
|
var fs4 = __require("fs");
|
|
3123
3032
|
var path3 = __require("path");
|
|
3124
|
-
var
|
|
3033
|
+
var fileURLToPath9 = require_file_uri_to_path();
|
|
3125
3034
|
var join6 = path3.join;
|
|
3126
|
-
var
|
|
3035
|
+
var dirname8 = path3.dirname;
|
|
3127
3036
|
var exists = fs4.accessSync && function(path4) {
|
|
3128
3037
|
try {
|
|
3129
3038
|
fs4.accessSync(path4);
|
|
@@ -3233,12 +3142,12 @@ var require_bindings = __commonJS({
|
|
|
3233
3142
|
Error.stackTraceLimit = origSTL;
|
|
3234
3143
|
var fileSchema = "file://";
|
|
3235
3144
|
if (fileName.indexOf(fileSchema) === 0) {
|
|
3236
|
-
fileName =
|
|
3145
|
+
fileName = fileURLToPath9(fileName);
|
|
3237
3146
|
}
|
|
3238
3147
|
return fileName;
|
|
3239
3148
|
};
|
|
3240
3149
|
exports.getRoot = function getRoot(file) {
|
|
3241
|
-
var dir =
|
|
3150
|
+
var dir = dirname8(file), prev;
|
|
3242
3151
|
while (true) {
|
|
3243
3152
|
if (dir === ".") {
|
|
3244
3153
|
dir = process.cwd();
|
|
@@ -3444,13 +3353,13 @@ var require_backup = __commonJS({
|
|
|
3444
3353
|
var runBackup = (backup, handler) => {
|
|
3445
3354
|
let rate = 0;
|
|
3446
3355
|
let useDefault = true;
|
|
3447
|
-
return new Promise((
|
|
3356
|
+
return new Promise((resolve7, reject) => {
|
|
3448
3357
|
setImmediate(function step() {
|
|
3449
3358
|
try {
|
|
3450
3359
|
const progress = backup.transfer(rate);
|
|
3451
3360
|
if (!progress.remainingPages) {
|
|
3452
3361
|
backup.close();
|
|
3453
|
-
|
|
3362
|
+
resolve7(progress);
|
|
3454
3363
|
return;
|
|
3455
3364
|
}
|
|
3456
3365
|
if (useDefault) {
|
|
@@ -3854,9 +3763,14 @@ var ActionItemSchema = z.object({
|
|
|
3854
3763
|
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
3855
3764
|
action_detail: z.string().describe("Action detail / command content")
|
|
3856
3765
|
});
|
|
3766
|
+
var PromptTurnSchema = z.object({
|
|
3767
|
+
text: z.string().default("").describe("User prompt text at this turn"),
|
|
3768
|
+
actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
|
|
3769
|
+
});
|
|
3857
3770
|
var AnalyzeRequestSchema = z.object({
|
|
3858
3771
|
session_id: z.string().describe("Session / agent unique identifier"),
|
|
3859
|
-
prompt_session: z.string().default("").describe("User original prompt"),
|
|
3772
|
+
prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
|
|
3773
|
+
prompt_history: z.array(PromptTurnSchema).default([]).describe("Full interleaved prompt history (ordered turns, each with its triggered actions). Optional; empty = fall back to prompt_session only."),
|
|
3860
3774
|
action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
|
|
3861
3775
|
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
3862
3776
|
reason: z.string().default("").describe("Reason for executing this action"),
|
|
@@ -3898,28 +3812,28 @@ init_skill_loader();
|
|
|
3898
3812
|
|
|
3899
3813
|
// src/engine/template-loader.ts
|
|
3900
3814
|
init_esm_shims();
|
|
3901
|
-
import { readFileSync as
|
|
3902
|
-
import { resolve as
|
|
3903
|
-
import { fileURLToPath as
|
|
3904
|
-
var
|
|
3905
|
-
var
|
|
3906
|
-
var TEMPLATES_DIR =
|
|
3907
|
-
var TEMPLATES_DIR_FALLBACK =
|
|
3815
|
+
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
3816
|
+
import { resolve as resolve4, dirname as dirname5 } from "path";
|
|
3817
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3818
|
+
var __filename5 = fileURLToPath7(import.meta.url);
|
|
3819
|
+
var __dirname6 = dirname5(__filename5);
|
|
3820
|
+
var TEMPLATES_DIR = resolve4(__dirname6, "../templates");
|
|
3821
|
+
var TEMPLATES_DIR_FALLBACK = resolve4(__dirname6, "../../../agent_moss/templates");
|
|
3908
3822
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3909
3823
|
var cachedTemplates = null;
|
|
3910
3824
|
function loadTemplates() {
|
|
3911
3825
|
if (cachedTemplates) return cachedTemplates;
|
|
3912
3826
|
cachedTemplates = {};
|
|
3913
|
-
const dir =
|
|
3914
|
-
if (!
|
|
3827
|
+
const dir = existsSync6(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
3828
|
+
if (!existsSync6(dir)) {
|
|
3915
3829
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
3916
3830
|
return cachedTemplates;
|
|
3917
3831
|
}
|
|
3918
3832
|
for (const file of TEMPLATE_FILES) {
|
|
3919
|
-
const filePath =
|
|
3920
|
-
if (
|
|
3833
|
+
const filePath = resolve4(dir, file);
|
|
3834
|
+
if (existsSync6(filePath)) {
|
|
3921
3835
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3922
|
-
cachedTemplates[key] =
|
|
3836
|
+
cachedTemplates[key] = readFileSync6(filePath, "utf-8");
|
|
3923
3837
|
}
|
|
3924
3838
|
}
|
|
3925
3839
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -3928,8 +3842,8 @@ function loadTemplates() {
|
|
|
3928
3842
|
|
|
3929
3843
|
// src/engine/script-analyzer.ts
|
|
3930
3844
|
init_esm_shims();
|
|
3931
|
-
import { readFileSync as
|
|
3932
|
-
import { resolve as
|
|
3845
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
3846
|
+
import { resolve as resolve5, join as join3 } from "path";
|
|
3933
3847
|
var SUSPICIOUS_PATTERNS = [
|
|
3934
3848
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
3935
3849
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -4062,8 +3976,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4062
3976
|
}
|
|
4063
3977
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4064
3978
|
try {
|
|
4065
|
-
const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) :
|
|
4066
|
-
const raw =
|
|
3979
|
+
const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve5(scriptPath);
|
|
3980
|
+
const raw = readFileSync7(expandedPath, "utf-8");
|
|
4067
3981
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4068
3982
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
4069
3983
|
} catch (e) {
|
|
@@ -4117,8 +4031,8 @@ ${result.scriptContent}
|
|
|
4117
4031
|
init_esm_shims();
|
|
4118
4032
|
init_heuristic();
|
|
4119
4033
|
init_logic_rules();
|
|
4120
|
-
import { readFileSync as
|
|
4121
|
-
import { resolve as
|
|
4034
|
+
import { readFileSync as readFileSync8, statSync, existsSync as existsSync7 } from "fs";
|
|
4035
|
+
import { resolve as resolve6, join as join4, isAbsolute, dirname as dirname6 } from "path";
|
|
4122
4036
|
import { homedir as homedir2 } from "os";
|
|
4123
4037
|
function emptyChain() {
|
|
4124
4038
|
return {
|
|
@@ -4347,7 +4261,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
4347
4261
|
let resolved;
|
|
4348
4262
|
try {
|
|
4349
4263
|
resolved = resolvePath(p);
|
|
4350
|
-
if (
|
|
4264
|
+
if (existsSync7(resolved)) {
|
|
4351
4265
|
const stat = statSync(resolved);
|
|
4352
4266
|
if (stat.isFile()) return resolved;
|
|
4353
4267
|
}
|
|
@@ -4361,18 +4275,18 @@ function resolvePath(p) {
|
|
|
4361
4275
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
4362
4276
|
expanded = join4(homedir2(), expanded.slice(1));
|
|
4363
4277
|
}
|
|
4364
|
-
return
|
|
4278
|
+
return resolve6(expanded);
|
|
4365
4279
|
}
|
|
4366
4280
|
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
4367
4281
|
try {
|
|
4368
4282
|
const path3 = resolvePath(scriptPath);
|
|
4369
|
-
if (!
|
|
4283
|
+
if (!existsSync7(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
4370
4284
|
const stat = statSync(path3);
|
|
4371
4285
|
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
4372
4286
|
if (stat.size > maxFileSize) {
|
|
4373
4287
|
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
4374
4288
|
}
|
|
4375
|
-
const content =
|
|
4289
|
+
const content = readFileSync8(path3, "utf-8");
|
|
4376
4290
|
const lineCount = content ? content.split("\n").length : 0;
|
|
4377
4291
|
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
4378
4292
|
} catch (e) {
|
|
@@ -4391,7 +4305,7 @@ function extractChildScriptPaths(content, baseDir) {
|
|
|
4391
4305
|
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
4392
4306
|
const full = isAbsolute(pathStr) ? pathStr : join4(baseDir, pathStr);
|
|
4393
4307
|
const resolved = resolvePath(full);
|
|
4394
|
-
if (
|
|
4308
|
+
if (existsSync7(resolved)) {
|
|
4395
4309
|
try {
|
|
4396
4310
|
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
4397
4311
|
} catch {
|
|
@@ -4406,7 +4320,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
|
|
|
4406
4320
|
const result = emptyChain();
|
|
4407
4321
|
result.entryPath = entryPath;
|
|
4408
4322
|
const entry = resolvePath(entryPath);
|
|
4409
|
-
const baseDir =
|
|
4323
|
+
const baseDir = existsSync7(entry) ? dirname6(entry) : input.cwd || process.cwd();
|
|
4410
4324
|
resolveRecursive({
|
|
4411
4325
|
path: entry,
|
|
4412
4326
|
depth: 0,
|
|
@@ -4481,7 +4395,7 @@ function resolveRecursive(ctx) {
|
|
|
4481
4395
|
maxTotalSize,
|
|
4482
4396
|
visited,
|
|
4483
4397
|
result,
|
|
4484
|
-
baseDir:
|
|
4398
|
+
baseDir: dirname6(resolvePath(childPath)),
|
|
4485
4399
|
input
|
|
4486
4400
|
});
|
|
4487
4401
|
if (result.hasCritical) return;
|
|
@@ -4705,11 +4619,12 @@ ${prompt}
|
|
|
4705
4619
|
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
4706
4620
|
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
4707
4621
|
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
4622
|
+
const promptHistoryText = formatPromptHistory(input.promptHistory);
|
|
4708
4623
|
const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
4709
4624
|
const hintsText = formatHints(previous);
|
|
4710
4625
|
const securityJudgeTemplate = templates["security_judge"];
|
|
4711
4626
|
if (securityJudgeTemplate) {
|
|
4712
|
-
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("}}", "}");
|
|
4627
|
+
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("}}", "}");
|
|
4713
4628
|
}
|
|
4714
4629
|
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
4715
4630
|
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
@@ -4739,6 +4654,27 @@ ${scriptAnalysisText}` : ""}
|
|
|
4739
4654
|
\u8F93\u51FA\u683C\u5F0F:
|
|
4740
4655
|
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
4741
4656
|
}
|
|
4657
|
+
function formatPromptHistory(promptHistory, maxTextChars = 500) {
|
|
4658
|
+
if (!promptHistory || promptHistory.length === 0) {
|
|
4659
|
+
return "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
|
|
4660
|
+
}
|
|
4661
|
+
const lines = [];
|
|
4662
|
+
for (let i = 0; i < promptHistory.length; i++) {
|
|
4663
|
+
const turn = promptHistory[i];
|
|
4664
|
+
const text = (turn?.text || "").trim();
|
|
4665
|
+
if (!text) continue;
|
|
4666
|
+
let line = ` \u7B2C${i + 1}\u8F6E\u7528\u6237\u610F\u56FE: ${text.slice(0, maxTextChars)}`;
|
|
4667
|
+
const actions = turn?.actions;
|
|
4668
|
+
if (actions && actions.length > 0) {
|
|
4669
|
+
const types = actions.map((a) => a?.actionType).filter((t) => typeof t === "string" && t.length > 0);
|
|
4670
|
+
if (types.length > 0) {
|
|
4671
|
+
line += ` \u2192 \u52A8\u4F5C: ${types.join(", ")}`;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
lines.push(line);
|
|
4675
|
+
}
|
|
4676
|
+
return lines.length > 0 ? lines.join("\n") : "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
|
|
4677
|
+
}
|
|
4742
4678
|
function formatHints(previous) {
|
|
4743
4679
|
const hints = [];
|
|
4744
4680
|
if (previous.heuristic) {
|
|
@@ -4834,7 +4770,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
4834
4770
|
}
|
|
4835
4771
|
if (attempt < maxRetries) {
|
|
4836
4772
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
4837
|
-
await new Promise((
|
|
4773
|
+
await new Promise((resolve7) => setTimeout(resolve7, retryCfg.retryInterval * 1e3));
|
|
4838
4774
|
} else {
|
|
4839
4775
|
clearTimeout(totalTimeoutTimer);
|
|
4840
4776
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -5347,11 +5283,11 @@ init_esm_shims();
|
|
|
5347
5283
|
init_runtime_config();
|
|
5348
5284
|
init_token_stats();
|
|
5349
5285
|
import { Hono } from "hono";
|
|
5350
|
-
import { readFileSync as
|
|
5351
|
-
import { join as join5, dirname as
|
|
5352
|
-
import { fileURLToPath as
|
|
5353
|
-
var
|
|
5354
|
-
var STATIC_DIR = join5(
|
|
5286
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
5287
|
+
import { join as join5, dirname as dirname7 } from "path";
|
|
5288
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
5289
|
+
var __dirname7 = dirname7(fileURLToPath8(import.meta.url));
|
|
5290
|
+
var STATIC_DIR = join5(__dirname7, "static");
|
|
5355
5291
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
5356
5292
|
function getConsoleToken() {
|
|
5357
5293
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -5376,8 +5312,8 @@ function createConsoleRouter() {
|
|
|
5376
5312
|
console2.use("/api/*", authMiddleware());
|
|
5377
5313
|
const serveIndex = (c) => {
|
|
5378
5314
|
const indexFile = join5(STATIC_DIR, "index.html");
|
|
5379
|
-
if (
|
|
5380
|
-
return c.html(
|
|
5315
|
+
if (existsSync10(indexFile)) {
|
|
5316
|
+
return c.html(readFileSync10(indexFile, "utf-8"));
|
|
5381
5317
|
}
|
|
5382
5318
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
5383
5319
|
};
|
|
@@ -5468,13 +5404,13 @@ function createConsoleRouter() {
|
|
|
5468
5404
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
5469
5405
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
5470
5406
|
}
|
|
5471
|
-
const skillPath = join5(
|
|
5472
|
-
if (!
|
|
5473
|
-
const builtinPath = join5(
|
|
5474
|
-
if (
|
|
5407
|
+
const skillPath = join5(dirname7(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
5408
|
+
if (!existsSync10(skillPath)) {
|
|
5409
|
+
const builtinPath = join5(__dirname7, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
5410
|
+
if (existsSync10(builtinPath)) return c.text(readFileSync10(builtinPath, "utf-8"));
|
|
5475
5411
|
return c.json({ error: "skill not found" }, 404);
|
|
5476
5412
|
}
|
|
5477
|
-
return c.text(
|
|
5413
|
+
return c.text(readFileSync10(skillPath, "utf-8"));
|
|
5478
5414
|
});
|
|
5479
5415
|
console2.get("/api/agents", (c) => {
|
|
5480
5416
|
const rc = loadRuntimeConfig();
|
|
@@ -5558,12 +5494,12 @@ function createApp() {
|
|
|
5558
5494
|
}
|
|
5559
5495
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
5560
5496
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
5561
|
-
const free = await new Promise((
|
|
5497
|
+
const free = await new Promise((resolve7) => {
|
|
5562
5498
|
const server = net.createServer();
|
|
5563
5499
|
server.listen(port, host, () => {
|
|
5564
|
-
server.close(() =>
|
|
5500
|
+
server.close(() => resolve7(true));
|
|
5565
5501
|
});
|
|
5566
|
-
server.on("error", () =>
|
|
5502
|
+
server.on("error", () => resolve7(false));
|
|
5567
5503
|
});
|
|
5568
5504
|
if (free) {
|
|
5569
5505
|
if (port !== startPort) {
|