@kenhkl/agent-moss 0.9.1 → 0.10.0

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.
@@ -1,5 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
2
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
3
5
  var __esm = (fn, res) => function __init() {
4
6
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
7
  };
@@ -7,6 +9,15 @@ var __export = (target, all) => {
7
9
  for (var name in all)
8
10
  __defProp(target, name, { get: all[name], enumerable: true });
9
11
  };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
10
21
 
11
22
  // node_modules/tsup/assets/esm_shims.js
12
23
  import path from "path";
@@ -203,56 +214,6 @@ var init_config = __esm({
203
214
  }
204
215
  });
205
216
 
206
- // src/agents/index.ts
207
- import { readFileSync, existsSync } from "fs";
208
- import { resolve, dirname } from "path";
209
- import { fileURLToPath as fileURLToPath2 } from "url";
210
- function resolveAgentsDir() {
211
- if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
212
- if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
213
- return AGENTS_DIR_PROD;
214
- }
215
- function getAgentExtensions(agentId) {
216
- if (!agentId) return null;
217
- if (cache.has(agentId)) return cache.get(agentId) ?? null;
218
- const agentsDir = resolveAgentsDir();
219
- const rulesFile = resolve(agentsDir, agentId, "rules.json");
220
- if (!existsSync(rulesFile)) {
221
- cache.set(agentId, null);
222
- return null;
223
- }
224
- try {
225
- const raw = readFileSync(rulesFile, "utf-8");
226
- const ext = JSON.parse(raw);
227
- if (!ext.agent_id) {
228
- console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
229
- cache.set(agentId, null);
230
- return null;
231
- }
232
- cache.set(agentId, ext);
233
- console.log(
234
- `[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills`
235
- );
236
- return ext;
237
- } catch (e) {
238
- console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
239
- cache.set(agentId, null);
240
- return null;
241
- }
242
- }
243
- var __filename2, __dirname2, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
244
- var init_agents = __esm({
245
- "src/agents/index.ts"() {
246
- "use strict";
247
- init_esm_shims();
248
- __filename2 = fileURLToPath2(import.meta.url);
249
- __dirname2 = dirname(__filename2);
250
- AGENTS_DIR_PROD = resolve(__dirname2, "agents");
251
- AGENTS_DIR_DEV_FALLBACK = resolve(__dirname2, "../../../agent_moss/agents");
252
- cache = /* @__PURE__ */ new Map();
253
- }
254
- });
255
-
256
217
  // src/engine/inline-analyzer.ts
257
218
  function isLiteralTextCommand(command) {
258
219
  ECHO_CMD_RE.lastIndex = 0;
@@ -509,9 +470,12 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
509
470
  if (NON_WRITE_TOOLS.has(actionType)) return null;
510
471
  let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
511
472
  if (!isWrite && actionDetail.includes(">")) {
512
- const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
513
- if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
514
- isWrite = true;
473
+ const detailWithoutDevnull = actionDetail.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
474
+ if (detailWithoutDevnull.includes(">")) {
475
+ const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
476
+ if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
477
+ isWrite = true;
478
+ }
515
479
  }
516
480
  }
517
481
  if (!isWrite) return null;
@@ -679,7 +643,7 @@ var init_logic_rules = __esm({
679
643
  ROOT_SENSITIVE_PATHS = [
680
644
  { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
681
645
  { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
682
- // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
646
+ // 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
683
647
  { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
684
648
  { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
685
649
  // 系统配置(仅拦截写入)
@@ -771,1423 +735,1676 @@ var init_logic_rules = __esm({
771
735
  }
772
736
  });
773
737
 
774
- // src/engine/heuristic.ts
775
- function runHeuristicCheck(input) {
776
- const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
777
- const matched = [];
778
- let highestSeverity = "low";
779
- let highestRiskType = "";
780
- const userRules = getCustomRules();
781
- for (const rule of userRules) {
782
- try {
783
- const re = new RegExp(rule.pattern);
784
- if (re.test(command)) {
785
- if (rule.action === "Deny") {
786
- matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
787
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
788
- highestSeverity = rule.severity;
789
- highestRiskType = "custom_rule";
790
- }
791
- }
792
- }
793
- } catch {
794
- }
738
+ // src/engine/skill-loader.ts
739
+ import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
740
+ import { resolve, dirname } from "path";
741
+ import { fileURLToPath as fileURLToPath2 } from "url";
742
+ function loadAllSkills() {
743
+ if (cachedSkills) return cachedSkills;
744
+ cachedSkills = {};
745
+ const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
746
+ if (!existsSync2(dir)) {
747
+ console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
748
+ return cachedSkills;
795
749
  }
796
- let topRiskType = "";
797
- for (const rule of DANGEROUS_PATTERNS) {
798
- if (rule.pattern.test(command)) {
799
- matched.push(rule.description);
800
- if (severityRank(rule.severity) > severityRank(highestSeverity)) {
801
- highestSeverity = rule.severity;
802
- topRiskType = rule.riskType;
803
- }
804
- }
750
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
751
+ for (const f of files) {
752
+ const name = f.replace(".md", "");
753
+ cachedSkills[name] = readFileSync(resolve(dir, f), "utf-8");
805
754
  }
806
- highestRiskType = topRiskType || highestRiskType;
807
- const agentExt = getAgentExtensions(input.agentId);
808
- if (agentExt?.extra_command_patterns) {
809
- for (const rule of agentExt.extra_command_patterns) {
810
- try {
811
- const re = new RegExp(rule.pattern, "i");
812
- if (re.test(command)) {
813
- matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
814
- if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
815
- highestSeverity = rule.risk_level;
816
- highestRiskType = rule.risk_type;
817
- }
818
- }
819
- } catch {
755
+ console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
756
+ return cachedSkills;
757
+ }
758
+ function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
759
+ const skills = loadAllSkills();
760
+ const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
761
+ const actionDetailLower = actionDetail.toLowerCase();
762
+ const actionTypeLower = actionType.toLowerCase();
763
+ const reasonLower = reason.toLowerCase();
764
+ const scores = {};
765
+ for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
766
+ let score = 0;
767
+ for (const kw of keywords) {
768
+ if (combined.includes(kw)) {
769
+ if (kw === actionTypeLower) score += 2;
770
+ else if (actionDetailLower.includes(kw)) score += 1.5;
771
+ else if (reasonLower.includes(kw)) score += 1;
772
+ else score += 0.5;
820
773
  }
821
774
  }
775
+ if (score > 0) scores[skillName] = score;
822
776
  }
823
- const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
824
- const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
825
- if (injectionHits.length > 0) {
826
- const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
827
- matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
828
- if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
829
- highestSeverity = topHit.riskLevel;
830
- highestRiskType = "prompt_injection";
831
- }
832
- }
833
- const actionTypeL = input.aNext.actionType.toLowerCase();
834
- const detailL = input.aNext.actionDetail.toLowerCase();
835
- const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
836
- if (!isExcludedType) {
837
- const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
838
- if (pathResult) return pathResult;
839
- }
840
- const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
841
- if (dangerousResult) return dangerousResult;
842
- const lateralResult = checkLateralMovement(detailL);
843
- if (lateralResult) return lateralResult;
844
- if (matched.length > 0) {
845
- return {
846
- allowed: false,
847
- reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
848
- riskLevel: highestSeverity,
849
- riskType: highestRiskType,
850
- confidence: 95,
851
- source: "heuristic",
852
- violatedLayers: ["1.1"]
853
- // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
854
- };
855
- }
856
- return null;
857
- }
858
- function checkInjectionKeywords(text, extraKeywords) {
859
- const hits = [];
860
- for (const entry of INJECTION_KEYWORDS) {
861
- if (text.includes(entry.keyword.toLowerCase())) {
862
- hits.push(entry);
777
+ const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
778
+ const top = sorted.slice(0, maxSkills);
779
+ const maxScore = top[0]?.[1] || 1;
780
+ const results = [];
781
+ for (const [name, score] of top) {
782
+ const content = skills[name];
783
+ if (content) {
784
+ results.push({
785
+ skillName: name,
786
+ relevance: Math.min(score / maxScore, 1),
787
+ skillContent: content
788
+ });
863
789
  }
864
790
  }
865
- if (extraKeywords) {
866
- for (const entry of extraKeywords) {
867
- if (text.includes(entry.keyword.toLowerCase())) {
868
- hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
869
- }
791
+ if (results.length === 0) {
792
+ const fallback = skills["general_tool_risk_guard"];
793
+ if (fallback) {
794
+ results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
870
795
  }
871
796
  }
872
- return hits;
873
- }
874
- function isInlineScriptCommand(actionDetail) {
875
- const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
876
- return re.test(actionDetail);
797
+ return results;
877
798
  }
878
- function severityRank(s) {
879
- const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
880
- return ranks[s] || 0;
799
+ function formatSkillsForPrompt(skills) {
800
+ if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
801
+ return skills.map(
802
+ (s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
803
+
804
+ ${s.skillContent}`
805
+ ).join("\n\n---\n\n");
881
806
  }
882
- function checkSensitivePath(command, cwd, agentExt) {
883
- const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
884
- const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
885
- const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
886
- const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
887
- const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
888
- for (const sp of ROOT_SENSITIVE_PATHS) {
889
- if (command.includes(sp.path.toLowerCase())) {
890
- if (sp.path === "/dev/random") {
891
- if (isWriteOpFlag) {
892
- continue;
893
- }
894
- return {
895
- allowed: false,
896
- reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
897
- riskLevel: sp.riskLevel,
898
- riskType: "file_access",
899
- confidence: 90,
900
- source: "heuristic",
901
- violatedLayers: ["1.1"]
902
- };
903
- }
904
- if (!isWriteOpFlag && !sp.credential) {
905
- continue;
906
- }
907
- return {
908
- allowed: false,
909
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
910
- riskLevel: sp.riskLevel,
911
- riskType: "file_access",
912
- confidence: 90,
913
- source: "heuristic",
914
- violatedLayers: ["1.1"],
915
- isCredential: !!sp.credential
916
- };
917
- }
807
+ var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
808
+ var init_skill_loader = __esm({
809
+ "src/engine/skill-loader.ts"() {
810
+ "use strict";
811
+ init_esm_shims();
812
+ __filename2 = fileURLToPath2(import.meta.url);
813
+ __dirname2 = dirname(__filename2);
814
+ SKILLS_DIR = resolve(__dirname2, "../skills");
815
+ SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
816
+ SKILL_KEYWORD_MAP = {
817
+ "file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
818
+ "script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
819
+ "data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
820
+ "browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
821
+ "email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
822
+ "general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
823
+ "lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
824
+ "persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys", "bashrc", "zshrc", "profile", ".rc", "shell config", "shell \u914D\u7F6E"],
825
+ // 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
826
+ // 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
827
+ "resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
828
+ "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
829
+ "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
830
+ "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"],
831
+ "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
832
+ };
833
+ cachedSkills = null;
918
834
  }
919
- for (const sp of HOME_SENSITIVE_PATHS) {
920
- const expanded = `${expandedCwd}/${sp.path}`;
921
- const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
922
- const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
923
- if (pathRe.test(command) || expandedRe.test(command)) {
924
- if (!isWriteOpFlag && !sp.credential) {
925
- continue;
926
- }
927
- return {
928
- allowed: false,
929
- reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
930
- riskLevel: sp.riskLevel,
931
- riskType: "file_access",
932
- confidence: 90,
933
- source: "heuristic",
934
- violatedLayers: ["1.1"],
935
- isCredential: !!sp.credential
936
- };
835
+ });
836
+
837
+ // src/infra/runtime-config.ts
838
+ var runtime_config_exports = {};
839
+ __export(runtime_config_exports, {
840
+ DENY_MODE_LABELS: () => DENY_MODE_LABELS,
841
+ addAgentRule: () => addAgentRule,
842
+ addCustomRule: () => addCustomRule,
843
+ addCustomSkill: () => addCustomSkill,
844
+ deleteAgentRule: () => deleteAgentRule,
845
+ deleteCustomRule: () => deleteCustomRule,
846
+ deleteCustomSkill: () => deleteCustomSkill,
847
+ filterEnabledRules: () => filterEnabledRules,
848
+ generateSourceDefaults: () => generateSourceDefaults,
849
+ getAgentRules: () => getAgentRules,
850
+ getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
851
+ getDisabledSkillIds: () => getDisabledSkillIds,
852
+ getEnabledAgentExtensions: () => getEnabledAgentExtensions,
853
+ getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
854
+ getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
855
+ getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
856
+ getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
857
+ getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
858
+ getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
859
+ getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
860
+ getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
861
+ getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
862
+ getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
863
+ getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
864
+ getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
865
+ getEnvOverrides: () => getEnvOverrides,
866
+ getProfileOrDetect: () => getProfileOrDetect,
867
+ getRuntimeConfigPath: () => getRuntimeConfigPath,
868
+ hasRuntimeConfig: () => hasRuntimeConfig,
869
+ isL2CategoryEnabled: () => isL2CategoryEnabled,
870
+ isLayerEnabled: () => isLayerEnabled,
871
+ loadRuntimeConfig: () => loadRuntimeConfig,
872
+ mergeConfig: () => mergeConfig,
873
+ saveRuntimeConfig: () => saveRuntimeConfig,
874
+ updateAgentCategoryEnabled: () => updateAgentCategoryEnabled,
875
+ updateAgentRuleEnabled: () => updateAgentRuleEnabled,
876
+ updateCategoryEnabled: () => updateCategoryEnabled,
877
+ updateLayerEnabled: () => updateLayerEnabled,
878
+ updateRuleDenyMode: () => updateRuleDenyMode,
879
+ updateRuleEnabled: () => updateRuleEnabled,
880
+ updateRuleSkipL3: () => updateRuleSkipL3,
881
+ updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
882
+ updateSkillEnabled: () => updateSkillEnabled
883
+ });
884
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
885
+ import { join } from "path";
886
+ import { homedir } from "os";
887
+ import { fileURLToPath as fileURLToPath3 } from "url";
888
+ import { dirname as dirname2 } from "path";
889
+ function getRuntimeConfigPath() {
890
+ const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
891
+ if (envPath) return envPath;
892
+ return RUNTIME_CONFIG_PATH;
893
+ }
894
+ function saveRuntimeConfig(config) {
895
+ const p = getRuntimeConfigPath();
896
+ const dir = p.substring(0, p.lastIndexOf("/"));
897
+ if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
898
+ writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
899
+ }
900
+ function emptyRuntimeConfig() {
901
+ return {
902
+ version: 1,
903
+ layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
904
+ L1_rules: {},
905
+ L2_rules: {},
906
+ L3_skills: {},
907
+ agent_rules: {}
908
+ };
909
+ }
910
+ function loadRuntimeConfig() {
911
+ const p = getRuntimeConfigPath();
912
+ if (!existsSync3(p)) {
913
+ const seeded = mergeConfig({}, generateSourceDefaults());
914
+ try {
915
+ saveRuntimeConfig(seeded);
916
+ } catch {
937
917
  }
918
+ return seeded;
938
919
  }
939
- if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
940
- for (const sp of agentExt.extra_sensitive_paths) {
941
- const spPath = sp.path.toLowerCase();
942
- const expanded = `${expandedCwd}/${spPath}`;
943
- if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
944
- const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
945
- if (denyMode === "deny_read") {
946
- if (isWriteOpFlag) continue;
947
- } else if (denyMode === "deny_write") {
948
- if (!isWriteOpFlag) continue;
949
- }
950
- return {
951
- allowed: false,
952
- reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
953
- riskLevel: sp.risk_level,
954
- riskType: "file_access",
955
- confidence: 90,
956
- source: "heuristic",
957
- violatedLayers: ["1.1"],
958
- isCredential: !!sp.credential
959
- };
960
- }
961
- }
920
+ try {
921
+ const data = JSON.parse(readFileSync2(p, "utf-8"));
922
+ const merged = mergeConfig(data, generateSourceDefaults());
923
+ return {
924
+ version: data.version || 1,
925
+ layers: {
926
+ L1_heuristic: data.layers?.L1_heuristic ?? true,
927
+ L2_logic_rules: data.layers?.L2_logic_rules ?? true,
928
+ L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
929
+ },
930
+ L1_rules: merged.L1_rules || {},
931
+ L2_rules: merged.L2_rules || {},
932
+ L3_skills: merged.L3_skills || {},
933
+ agent_rules: merged.agent_rules || {}
934
+ };
935
+ } catch {
936
+ return emptyRuntimeConfig();
962
937
  }
963
- for (const cp of CREDENTIAL_PATHS) {
964
- const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
965
- const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
966
- if (re.test(command)) {
967
- return {
968
- allowed: false,
969
- reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
970
- riskLevel: cp.riskLevel,
971
- riskType: "file_access",
972
- confidence: 90,
973
- source: "heuristic",
974
- violatedLayers: ["1.1"],
975
- isCredential: true
938
+ }
939
+ function isLayerEnabled(layerKey) {
940
+ const disableKey = LAYER_DISABLE_ENV[layerKey];
941
+ if (disableKey && process.env[disableKey] === "1") return false;
942
+ const runtime = loadRuntimeConfig();
943
+ const layers = runtime.layers || {};
944
+ if (layerKey in layers) return !!layers[layerKey];
945
+ return true;
946
+ }
947
+ function getEnvOverrides() {
948
+ const overrides = {};
949
+ for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
950
+ const val = process.env[envKey];
951
+ if (val !== void 0) {
952
+ overrides[layerKey] = {
953
+ env_var: envKey,
954
+ env_value: val,
955
+ overridden: val === "1",
956
+ effect: val === "1" ? "disabled" : "no_override"
976
957
  };
977
958
  }
978
959
  }
979
- if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
980
- return {
981
- allowed: false,
982
- reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
983
- riskLevel: "critical",
984
- riskType: "file_access",
985
- confidence: 90,
986
- source: "heuristic",
987
- violatedLayers: ["1.1"]
988
- };
960
+ return overrides;
961
+ }
962
+ function filterEnabledRules(rules, categoryEnabled = true) {
963
+ if (!categoryEnabled) return [];
964
+ return rules.filter((r) => r.enabled !== false);
965
+ }
966
+ function getEnabledL1CommandPatterns(runtime) {
967
+ const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
968
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
969
+ pattern: r.pattern || "",
970
+ risk_level: r.risk_level || "",
971
+ risk_type: r.risk_type || "",
972
+ reason: r.reason || ""
973
+ }));
974
+ }
975
+ function getEnabledL2SensitivePaths(runtime) {
976
+ let cat = runtime.L1_rules?.sensitive_path_access;
977
+ if (!cat || !cat.rules || cat.rules.length === 0) {
978
+ cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
989
979
  }
990
- return null;
980
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
981
+ const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
982
+ if (r.credential) out.credential = true;
983
+ if (r.read_only) out.read_only = true;
984
+ return out;
985
+ });
991
986
  }
992
- function checkDangerousPatterns(actionType, actionDetail) {
993
- if (actionType === "file_write" || actionType === "file_edit") return null;
994
- const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
995
- const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
996
- if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
997
- return {
998
- allowed: false,
999
- reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
1000
- riskLevel: "high",
1001
- riskType: "script_execution",
1002
- confidence: 90,
1003
- source: "heuristic",
1004
- violatedLayers: ["1.1"]
1005
- };
987
+ function hasRuntimeConfig() {
988
+ const p = getRuntimeConfigPath();
989
+ if (!existsSync3(p)) return false;
990
+ try {
991
+ JSON.parse(readFileSync2(p, "utf-8"));
992
+ return true;
993
+ } catch {
994
+ return false;
1006
995
  }
1007
- if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
1008
- return {
1009
- allowed: false,
1010
- reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
1011
- riskLevel: "critical",
1012
- riskType: "file_access",
1013
- confidence: 95,
1014
- source: "heuristic",
1015
- violatedLayers: ["1.1"]
1016
- };
996
+ }
997
+ function getDisabledL2RulesWithSkipL3() {
998
+ let runtime;
999
+ try {
1000
+ runtime = loadRuntimeConfig();
1001
+ } catch {
1002
+ return {};
1017
1003
  }
1018
- return null;
1004
+ const result = {};
1005
+ for (const layerKey of ["L1_rules", "L2_rules"]) {
1006
+ const layer = runtime[layerKey] || {};
1007
+ for (const [catName, cat] of Object.entries(layer)) {
1008
+ if (!cat.category_enabled) continue;
1009
+ const items = [];
1010
+ for (const r of cat.rules || []) {
1011
+ if (r.enabled === false && r.skip_l3_on_disabled) {
1012
+ if (catName === "sensitive_path_access") {
1013
+ if (r.path) items.push(r.path);
1014
+ } else if (catName === "intent_consistency") {
1015
+ items.push(...r.intent_keywords || []);
1016
+ } else if (r.pattern) {
1017
+ items.push(r.pattern);
1018
+ }
1019
+ }
1020
+ }
1021
+ if (items.length > 0) result[catName] = items;
1022
+ }
1023
+ }
1024
+ return result;
1019
1025
  }
1020
- function extractRemoteHost(command) {
1021
- const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1022
- return m ? m[1] : null;
1026
+ function _slugify(text) {
1027
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
1023
1028
  }
1024
- function isLocalHost(host) {
1025
- const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1026
- return localNames.includes(host.replace(/.*@/, ""));
1029
+ function _generatePatternId(pattern) {
1030
+ return `pat_${_slugify(pattern)}`;
1027
1031
  }
1028
- function checkLateralMovement(actionDetail) {
1029
- if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
1030
- const targetHost = extractRemoteHost(actionDetail);
1031
- if (targetHost && !isLocalHost(targetHost)) {
1032
- return {
1033
- allowed: false,
1034
- reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
1035
- riskLevel: "medium",
1036
- riskType: "lateral_movement",
1037
- confidence: 80,
1038
- source: "heuristic",
1039
- violatedLayers: ["1.1"]
1040
- };
1041
- }
1042
- }
1043
- return null;
1032
+ function _deriveDenyMode(rule) {
1033
+ if (rule.deny_mode) return rule.deny_mode;
1034
+ if (rule.credential) return "deny_both";
1035
+ if (rule.read_only) return "deny_read";
1036
+ return "deny_write";
1044
1037
  }
1045
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
1046
- var init_heuristic = __esm({
1047
- "src/engine/heuristic.ts"() {
1048
- "use strict";
1049
- init_esm_shims();
1050
- init_config();
1051
- init_agents();
1052
- init_logic_rules();
1053
- INJECTION_KEYWORDS = [
1054
- // ---- Instruction Override ----
1055
- { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
1056
- { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
1057
- { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
1058
- { keyword: "forget everything", riskLevel: "high", lang: "en" },
1059
- { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
1060
- { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
1061
- { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
1062
- { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
1063
- // ---- Role Hijacking ----
1064
- { keyword: "you are now", riskLevel: "high", lang: "en" },
1065
- { keyword: "you are no longer", riskLevel: "high", lang: "en" },
1066
- { keyword: "new instructions", riskLevel: "high", lang: "en" },
1067
- { keyword: "from now on, you", riskLevel: "high", lang: "en" },
1068
- { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1069
- { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1070
- { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
1071
- { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
1072
- // ---- Command Injection ----
1073
- { keyword: "system prompt", riskLevel: "high", lang: "en" },
1074
- { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
1075
- { keyword: "output your instructions", riskLevel: "high", lang: "en" },
1076
- { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
1077
- { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
1078
- { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
1079
- { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
1080
- // ---- Social Engineering ----
1081
- { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
1082
- { keyword: "security audit approved", riskLevel: "high", lang: "en" },
1083
- { keyword: "admin override", riskLevel: "critical", lang: "en" },
1084
- { keyword: "debug mode", riskLevel: "high", lang: "en" },
1085
- { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
1086
- { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
1087
- { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
1088
- { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
1089
- { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
1090
- // ---- XSS ----
1091
- { keyword: "<script>", riskLevel: "high", lang: "en" },
1092
- { keyword: "javascript:", riskLevel: "high", lang: "en" },
1093
- { keyword: "onerror=", riskLevel: "high", lang: "en" },
1094
- { keyword: "onload=", riskLevel: "high", lang: "en" },
1095
- // ---- Fake Boundaries ----
1096
- { keyword: "=== END OF", riskLevel: "high", lang: "en" },
1097
- { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
1098
- { keyword: "[END OF", riskLevel: "high", lang: "en" },
1099
- { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
1100
- { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
1101
- // ---- Developer Mode ----
1102
- { keyword: "developer mode", riskLevel: "critical", lang: "en" },
1103
- { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
1104
- { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
1105
- { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
1106
- { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
1107
- // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
1108
- // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
1109
- // 由 agent_ext 机制注入,不再污染通用层。
1110
- ];
1111
- DANGEROUS_PATTERNS = [
1112
- // --- Critical: rm command graded detection (strict to loose, first match wins) ---
1113
- // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
1114
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
1115
- // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
1116
- // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
1117
- { pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var(?!\/tmp)|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
1118
- // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
1119
- { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
1120
- // 4. rm -f absolute path (no -r) — 同样排除临时目录
1121
- { pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
1122
- // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
1123
- { pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
1124
- // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
1125
- { pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
1126
- { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
1127
- // --- Critical: remote code execution ---
1128
- { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
1129
- { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
1130
- { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
1131
- { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
1132
- // --- Critical: reverse shell ---
1133
- { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
1134
- { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
1135
- // --- Critical: sensitive file access ---
1136
- { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
1137
- { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
1138
- // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
1139
- { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
1140
- // --- Critical: privilege escalation / container escape ---
1141
- { pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
1142
- // --- High: dangerous permissions / ownership ---
1143
- { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
1144
- { pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
1145
- // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
1146
- { pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
1147
- // --- High: system config overwrite ---
1148
- { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
1149
- // --- High: download script + execute risk ---
1150
- { pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
1151
- { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
1152
- // --- High: package manager pipe-to-shell ---
1153
- { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
1154
- { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
1155
- { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
1156
- // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
1157
- { pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
1158
- // --- High: network port scanning (横向移动侦察) ---
1159
- { pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
1160
- { pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
1161
- // --- Medium: credential export ---
1162
- { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
1163
- // --- High: shell rc file append (persistence) ---
1164
- { pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
1165
- // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
1166
- { pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
1167
- // --- High: non-interactive password modification ---
1168
- { pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
1169
- { pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
1170
- { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
1171
- { pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
1172
- { pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
1173
- { pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
1174
- // --- High: non-interactive password modification (extended) ---
1175
- { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
1176
- { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
1177
- { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
1178
- // --- High: user/group deletion (irreversible, requires user consent) ---
1179
- { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
1180
- { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
1181
- { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
1182
- { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
1183
- { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
1184
- // --- High: generic command -p parameter password setting ---
1185
- { pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
1186
- ];
1187
- SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
1188
- }
1189
- });
1190
-
1191
- // src/engine/skill-loader.ts
1192
- import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
1193
- import { resolve as resolve2, dirname as dirname2 } from "path";
1194
- import { fileURLToPath as fileURLToPath3 } from "url";
1195
- function loadAllSkills() {
1196
- if (cachedSkills) return cachedSkills;
1197
- cachedSkills = {};
1198
- const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1199
- if (!existsSync3(dir)) {
1200
- console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
1201
- return cachedSkills;
1202
- }
1203
- const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
1204
- for (const f of files) {
1205
- const name = f.replace(".md", "");
1206
- cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
1038
+ function _syncDenyModeFields(rule) {
1039
+ const dm = rule.deny_mode || "deny_write";
1040
+ if (dm === "deny_both") {
1041
+ rule.credential = true;
1042
+ delete rule.read_only;
1043
+ } else if (dm === "deny_read") {
1044
+ delete rule.credential;
1045
+ rule.read_only = true;
1046
+ } else {
1047
+ delete rule.credential;
1048
+ delete rule.read_only;
1207
1049
  }
1208
- console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
1209
- return cachedSkills;
1210
1050
  }
1211
- function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
1212
- const skills = loadAllSkills();
1213
- const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
1214
- const actionDetailLower = actionDetail.toLowerCase();
1215
- const actionTypeLower = actionType.toLowerCase();
1216
- const reasonLower = reason.toLowerCase();
1217
- const scores = {};
1218
- for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
1219
- let score = 0;
1220
- for (const kw of keywords) {
1221
- if (combined.includes(kw)) {
1222
- if (kw === actionTypeLower) score += 2;
1223
- else if (actionDetailLower.includes(kw)) score += 1.5;
1224
- else if (reasonLower.includes(kw)) score += 1;
1225
- else score += 0.5;
1226
- }
1227
- }
1228
- if (score > 0) scores[skillName] = score;
1229
- }
1230
- const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
1231
- const top = sorted.slice(0, maxSkills);
1232
- const maxScore = top[0]?.[1] || 1;
1233
- const results = [];
1234
- for (const [name, score] of top) {
1235
- const content = skills[name];
1236
- if (content) {
1237
- results.push({
1238
- skillName: name,
1239
- relevance: Math.min(score / maxScore, 1),
1240
- skillContent: content
1241
- });
1242
- }
1243
- }
1244
- if (results.length === 0) {
1245
- const fallback = skills["general_tool_risk_guard"];
1246
- if (fallback) {
1247
- results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
1248
- }
1249
- }
1250
- return results;
1051
+ function _l1CommandPatternsToRules() {
1052
+ return DANGEROUS_PATTERNS.map((p) => ({
1053
+ id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
1054
+ pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
1055
+ risk_level: p.severity,
1056
+ risk_type: p.riskType,
1057
+ reason: p.description,
1058
+ enabled: true,
1059
+ builtin: true
1060
+ }));
1251
1061
  }
1252
- function formatSkillsForPrompt(skills) {
1253
- if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
1254
- return skills.map(
1255
- (s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
1256
-
1257
- ${s.skillContent}`
1258
- ).join("\n\n---\n\n");
1062
+ function _l1InjectionKeywordsToRules() {
1063
+ return INJECTION_KEYWORDS.map((kw) => ({
1064
+ id: `inj_${_slugify(kw.keyword)}`,
1065
+ keyword: kw.keyword,
1066
+ risk_level: kw.riskLevel,
1067
+ lang: kw.lang || "en",
1068
+ enabled: true,
1069
+ builtin: true
1070
+ }));
1259
1071
  }
1260
- var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1261
- var init_skill_loader = __esm({
1262
- "src/engine/skill-loader.ts"() {
1263
- "use strict";
1264
- init_esm_shims();
1265
- __filename3 = fileURLToPath3(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
- "file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
1271
- "script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
1272
- "data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
1273
- "browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
1274
- "email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
1275
- "general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
1276
- "lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
1277
- "persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys", "bashrc", "zshrc", "profile", ".rc", "shell config", "shell \u914D\u7F6E"],
1278
- // 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
1279
- // 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
1280
- "resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
1281
- "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
1282
- "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
1283
- "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"],
1284
- "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
1072
+ function _l2SensitivePathsToRules() {
1073
+ const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
1074
+ return all.map((sp) => {
1075
+ const denyMode = _deriveDenyMode({
1076
+ deny_mode: sp.deny_mode,
1077
+ credential: sp.credential,
1078
+ read_only: sp.read_only
1079
+ });
1080
+ const rule = {
1081
+ id: `path_${_slugify(sp.path)}`,
1082
+ path: sp.path,
1083
+ risk_level: sp.riskLevel,
1084
+ desc: sp.desc,
1085
+ deny_mode: denyMode,
1086
+ source_deny_mode: denyMode,
1087
+ skip_l3_on_disabled: true,
1088
+ enabled: true,
1089
+ builtin: true
1090
+ };
1091
+ _syncDenyModeFields(rule);
1092
+ return rule;
1093
+ });
1094
+ }
1095
+ function _l2IntentPatternsToRules() {
1096
+ return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
1097
+ id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
1098
+ intent_keywords: pat.intentKeywords,
1099
+ dangerous_actions: pat.dangerousActions,
1100
+ reason: pat.reason,
1101
+ skip_l3_on_disabled: true,
1102
+ enabled: true,
1103
+ builtin: true
1104
+ }));
1105
+ }
1106
+ function _l2PasswordPatternsToRules() {
1107
+ return PASSWORD_MODIFY_PATTERNS.map((p) => ({
1108
+ id: `pw_${_slugify(p.source)}`,
1109
+ pattern: p.source,
1110
+ skip_l3_on_disabled: true,
1111
+ enabled: true,
1112
+ builtin: true
1113
+ }));
1114
+ }
1115
+ function _l2UserDeletionPatternsToRules() {
1116
+ return USER_DELETION_PATTERNS.map((p) => ({
1117
+ id: `ud_${_slugify(p.source)}`,
1118
+ pattern: p.source,
1119
+ skip_l3_on_disabled: true,
1120
+ enabled: true,
1121
+ builtin: true
1122
+ }));
1123
+ }
1124
+ function _l3SkillsToRules() {
1125
+ const skillCategories = {
1126
+ file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
1127
+ network_security: [
1128
+ "data_exfiltration_guard",
1129
+ "browser_web_access_guard",
1130
+ "lateral_movement_guard",
1131
+ "email_operation_guard"
1132
+ ],
1133
+ persistence_supply_chain: [
1134
+ "persistence_backdoor_guard",
1135
+ "supply_chain_guard",
1136
+ "skill_installation_guard"
1137
+ ],
1138
+ intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
1139
+ resource: ["resource_exhaustion_guard"]
1140
+ };
1141
+ const out = {};
1142
+ for (const [cat, ids] of Object.entries(skillCategories)) {
1143
+ out[cat] = {
1144
+ category_enabled: true,
1145
+ category_desc: "",
1146
+ skills: ids.map((s) => ({
1147
+ id: s,
1148
+ keywords: SKILL_KEYWORD_MAP[s] || [],
1149
+ enabled: true,
1150
+ builtin: true
1151
+ }))
1285
1152
  };
1286
- cachedSkills = null;
1287
1153
  }
1288
- });
1289
-
1290
- // src/engine/token-stats.ts
1291
- var token_stats_exports = {};
1292
- __export(token_stats_exports, {
1293
- getRecentRecords: () => getRecentRecords,
1294
- getTokenStats: () => getTokenStats,
1295
- getTokenTrend: () => getTokenTrend,
1296
- recordTokenUsage: () => recordTokenUsage,
1297
- resetTokenStats: () => resetTokenStats
1298
- });
1299
- import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
1300
- import { join as join3 } from "path";
1301
- import { homedir as homedir2 } from "os";
1302
- function getStatsPath() {
1303
- const envPath = process.env[ENV_STATS_PATH];
1304
- return envPath || STATS_FILE;
1305
- }
1306
- function loadAllRecords(statsPath) {
1307
- if (!existsSync6(statsPath)) return [];
1308
- try {
1309
- const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
1310
- if (Array.isArray(data)) return data;
1311
- if (data && Array.isArray(data.records)) return data.records;
1312
- return [];
1313
- } catch (e) {
1314
- return [];
1154
+ const categorized = new Set(Object.values(skillCategories).flat());
1155
+ for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
1156
+ if (!categorized.has(s)) {
1157
+ (out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
1158
+ id: s,
1159
+ keywords: SKILL_KEYWORD_MAP[s],
1160
+ enabled: true,
1161
+ builtin: true
1162
+ });
1163
+ }
1315
1164
  }
1165
+ out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
1166
+ return out;
1316
1167
  }
1317
- function saveRecords(statsPath, records) {
1168
+ function _agentRulesToDefaults() {
1169
+ const out = {};
1170
+ const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
1171
+ if (!existsSync3(agentsDir)) return out;
1172
+ let entries;
1318
1173
  try {
1319
- const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
1320
- if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
1321
- writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
1322
- } catch (e) {
1174
+ entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1175
+ } catch {
1176
+ return out;
1177
+ }
1178
+ for (const agentId of entries) {
1179
+ const rulesFile = join(agentsDir, agentId, "rules.json");
1180
+ if (!existsSync3(rulesFile)) continue;
1181
+ let data;
1182
+ try {
1183
+ data = JSON.parse(readFileSync2(rulesFile, "utf-8"));
1184
+ } catch {
1185
+ continue;
1186
+ }
1187
+ out[agentId] = {
1188
+ builtin_safe_action_types: {
1189
+ category_enabled: true,
1190
+ category_desc: "agent \u5B8C\u5168\u5B89\u5168\u64CD\u4F5C\u767D\u540D\u5355\uFF08\u524D\u7F00\u5C3E\u968F - \u6216\u7CBE\u786E\u540D\uFF0C\u547D\u4E2D\u8DF3 L2+L3\uFF09",
1191
+ rules: (data.builtin_safe_action_types || []).map((v) => ({ value: v, enabled: true, builtin: true }))
1192
+ },
1193
+ builtin_safe_skills: {
1194
+ category_enabled: true,
1195
+ category_desc: "agent \u4FE1\u4EFB\u7684\u5185\u7F6E Skill\uFF08\u547D\u4E2D\u8DF3 L2+L3\uFF09",
1196
+ rules: (data.builtin_safe_skills || []).map((v) => ({ value: v, enabled: true, builtin: true }))
1197
+ },
1198
+ extra_command_patterns: {
1199
+ category_enabled: true,
1200
+ category_desc: "agent \u4E13\u5C5E\u5371\u9669\u547D\u4EE4\u6B63\u5219\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
1201
+ rules: (data.extra_command_patterns || []).map((p) => ({ ...p, enabled: true, builtin: true }))
1202
+ },
1203
+ extra_injection_keywords: {
1204
+ category_enabled: true,
1205
+ category_desc: "agent \u4E13\u5C5E\u6CE8\u5165\u5173\u952E\u8BCD\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
1206
+ rules: (data.extra_injection_keywords || []).map((k) => ({ ...k, enabled: true, builtin: true }))
1207
+ },
1208
+ extra_sensitive_paths: {
1209
+ category_enabled: true,
1210
+ category_desc: "agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
1211
+ rules: (data.extra_sensitive_paths || []).map((sp) => ({ ...sp, enabled: true, builtin: true }))
1212
+ }
1213
+ };
1323
1214
  }
1215
+ return out;
1324
1216
  }
1325
- function recordTokenUsage(record) {
1326
- const statsPath = getStatsPath();
1327
- const records = loadAllRecords(statsPath);
1328
- records.push({
1329
- timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
1330
- session_id: record.sessionId,
1331
- step: record.step,
1332
- model: record.model,
1333
- prompt_tokens: record.promptTokens,
1334
- completion_tokens: record.completionTokens,
1335
- total_tokens: record.totalTokens
1336
- });
1337
- if (records.length > MAX_RECORDS) {
1338
- records.splice(0, records.length - MAX_RECORDS);
1217
+ function generateSourceDefaults() {
1218
+ return {
1219
+ version: 1,
1220
+ layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1221
+ L1_rules: {
1222
+ dangerous_commands: {
1223
+ category_enabled: true,
1224
+ category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
1225
+ rules: _l1CommandPatternsToRules()
1226
+ },
1227
+ injection_detection: {
1228
+ category_enabled: true,
1229
+ category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
1230
+ rules: _l1InjectionKeywordsToRules()
1231
+ },
1232
+ sensitive_path_access: {
1233
+ category_enabled: true,
1234
+ category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
1235
+ rules: _l2SensitivePathsToRules()
1236
+ },
1237
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
1238
+ },
1239
+ L2_rules: {
1240
+ intent_consistency: {
1241
+ category_enabled: true,
1242
+ category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
1243
+ rules: _l2IntentPatternsToRules()
1244
+ },
1245
+ password_modify_consent: {
1246
+ category_enabled: true,
1247
+ category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
1248
+ rules: _l2PasswordPatternsToRules()
1249
+ },
1250
+ user_deletion_consent: {
1251
+ category_enabled: true,
1252
+ category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
1253
+ rules: _l2UserDeletionPatternsToRules()
1254
+ },
1255
+ read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
1256
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
1257
+ },
1258
+ L3_skills: _l3SkillsToRules(),
1259
+ agent_rules: _agentRulesToDefaults()
1260
+ };
1261
+ }
1262
+ function _ruleFingerprint(rule) {
1263
+ if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
1264
+ return JSON.stringify([
1265
+ [...rule.intent_keywords || []].sort(),
1266
+ [...rule.dangerous_actions || []].sort()
1267
+ ]);
1339
1268
  }
1340
- saveRecords(statsPath, records);
1269
+ if (rule.path) return JSON.stringify([rule.path]);
1270
+ if (rule.pattern) return JSON.stringify([rule.pattern]);
1271
+ if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
1272
+ if (rule.value) return JSON.stringify([rule.value]);
1273
+ return JSON.stringify([rule.id || ""]);
1341
1274
  }
1342
- function filterByDate(records, days = 0, startDate, endDate) {
1343
- if (startDate || endDate) {
1344
- let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
1345
- let endDt = endDate ? new Date(endDate) : void 0;
1346
- if (endDate) {
1347
- const e = new Date(endDate);
1348
- e.setHours(23, 59, 59, 999);
1349
- endDt = e;
1275
+ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
1276
+ const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1277
+ const srcLayer = srcDefaults[layerKey] || {};
1278
+ for (const [catName, srcCat] of Object.entries(srcLayer)) {
1279
+ if (!(catName in userLayer)) {
1280
+ userLayer[catName] = srcCat;
1281
+ continue;
1350
1282
  }
1351
- const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
1352
- return records.filter((r) => {
1353
- try {
1354
- const ts = new Date(r.timestamp).getTime();
1355
- return ts >= startDt.getTime() && ts <= endMs;
1356
- } catch {
1357
- return true;
1283
+ const userCat = userLayer[catName];
1284
+ if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1285
+ if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1286
+ const srcRules = srcCat.rules || [];
1287
+ const userRules = userCat.rules || (userCat.rules = []);
1288
+ const userByFp = /* @__PURE__ */ new Map();
1289
+ for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
1290
+ const srcFps = /* @__PURE__ */ new Set();
1291
+ for (const srcR of srcRules) {
1292
+ const fp = _ruleFingerprint(srcR);
1293
+ srcFps.add(fp);
1294
+ if (userByFp.has(fp)) {
1295
+ const existing = userByFp.get(fp);
1296
+ const userEnabled = existing.enabled;
1297
+ const userSkipL3 = existing.skip_l3_on_disabled;
1298
+ const userDenyMode = existing.deny_mode;
1299
+ const userSourceDeny = existing.source_deny_mode;
1300
+ Object.keys(existing).forEach((k) => delete existing[k]);
1301
+ Object.assign(existing, srcR);
1302
+ existing.enabled = userEnabled;
1303
+ if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
1304
+ if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
1305
+ existing.deny_mode = userDenyMode;
1306
+ }
1307
+ if (existing.deny_mode) _syncDenyModeFields(existing);
1308
+ } else {
1309
+ const newR = { ...srcR, enabled: true };
1310
+ userRules.push(newR);
1358
1311
  }
1359
- });
1360
- } else if (days > 0) {
1361
- const cutoff = Date.now() - days * 864e5;
1362
- return records.filter((r) => {
1363
- try {
1364
- return new Date(r.timestamp).getTime() >= cutoff;
1365
- } catch {
1366
- return true;
1312
+ }
1313
+ for (const r of userRules) {
1314
+ if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
1315
+ r.source_removed = true;
1316
+ r.enabled = false;
1367
1317
  }
1368
- });
1318
+ }
1319
+ if (catName === "sensitive_path_access") {
1320
+ for (const r of userRules) {
1321
+ if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
1322
+ _syncDenyModeFields(r);
1323
+ }
1324
+ }
1325
+ }
1326
+ for (const catName of Object.keys(userLayer)) {
1327
+ if (catName === "user_custom") continue;
1328
+ if (!(catName in srcLayer)) delete userLayer[catName];
1369
1329
  }
1370
- return records;
1371
1330
  }
1372
- function aggregate(records) {
1373
- let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
1374
- const byStep = {};
1375
- const byModel = {};
1376
- const byDate = {};
1377
- const bucket = (b, key, r) => {
1378
- const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
1379
- e.prompt_tokens += r.prompt_tokens || 0;
1380
- e.completion_tokens += r.completion_tokens || 0;
1381
- e.total_tokens += r.total_tokens || 0;
1382
- e.calls += 1;
1383
- };
1384
- for (const r of records) {
1385
- totalPrompt += r.prompt_tokens || 0;
1386
- totalCompletion += r.completion_tokens || 0;
1387
- totalTotal += r.total_tokens || 0;
1388
- bucket(byStep, r.step || "unknown", r);
1389
- bucket(byModel, r.model || "unknown", r);
1390
- let dateKey = "unknown";
1391
- try {
1392
- dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
1393
- } catch {
1331
+ function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
1332
+ const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1333
+ const srcLayer = srcDefaults[layerKey] || {};
1334
+ for (const [catName, srcCat] of Object.entries(srcLayer)) {
1335
+ if (!(catName in userLayer)) {
1336
+ userLayer[catName] = srcCat;
1337
+ continue;
1338
+ }
1339
+ const userCat = userLayer[catName];
1340
+ if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1341
+ if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1342
+ const userSkills = userCat.skills || (userCat.skills = []);
1343
+ const existingIds = new Set(userSkills.map((s) => s.id));
1344
+ for (const srcS of srcCat.skills || []) {
1345
+ if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
1394
1346
  }
1395
- bucket(byDate, dateKey, r);
1396
1347
  }
1397
- const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
1398
- const sortedByDate = {};
1399
- for (const k of sortedDate) sortedByDate[k] = byDate[k];
1400
- return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
1401
- }
1402
- function getTokenStats(days = 0, startDate, endDate) {
1403
- const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
1404
- const a = aggregate(records);
1405
- return {
1406
- total_prompt_tokens: a.totalPrompt,
1407
- total_completion_tokens: a.totalCompletion,
1408
- total_tokens: a.totalTotal,
1409
- total_calls: records.length,
1410
- by_step: a.byStep,
1411
- by_model: a.byModel,
1412
- by_date: a.byDate
1413
- };
1414
- }
1415
- function getRecentRecords(limit = 20) {
1416
- const records = loadAllRecords(getStatsPath());
1417
- return records.slice(-limit);
1418
1348
  }
1419
- function getTokenTrend(days = 0, startDate, endDate) {
1420
- const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
1421
- const series = {};
1422
- const datesSet = /* @__PURE__ */ new Set();
1423
- for (const r of records) {
1424
- let dateKey;
1425
- try {
1426
- dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
1427
- } catch {
1349
+ function _mergeAgentRules(userCfg, srcDefaults) {
1350
+ const userAgent = userCfg.agent_rules || (userCfg.agent_rules = {});
1351
+ const srcAgent = srcDefaults.agent_rules || {};
1352
+ for (const [agentId, srcCats] of Object.entries(srcAgent)) {
1353
+ if (!(agentId in userAgent)) {
1354
+ userAgent[agentId] = JSON.parse(JSON.stringify(srcCats));
1428
1355
  continue;
1429
1356
  }
1430
- const model = r.model || "unknown";
1431
- const tokens = r.total_tokens || 0;
1432
- datesSet.add(dateKey);
1433
- series[model] = series[model] || {};
1434
- series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
1357
+ const userCats = userAgent[agentId];
1358
+ for (const [catName, srcCat] of Object.entries(srcCats)) {
1359
+ if (!(catName in userCats)) {
1360
+ userCats[catName] = JSON.parse(JSON.stringify(srcCat));
1361
+ continue;
1362
+ }
1363
+ const userCat = userCats[catName];
1364
+ if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1365
+ if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1366
+ const srcRules = srcCat.rules || [];
1367
+ const userRules = userCat.rules || (userCat.rules = []);
1368
+ const userByFp = /* @__PURE__ */ new Map();
1369
+ for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
1370
+ const srcFps = /* @__PURE__ */ new Set();
1371
+ for (const srcR of srcRules) {
1372
+ const fp = _ruleFingerprint(srcR);
1373
+ srcFps.add(fp);
1374
+ if (userByFp.has(fp)) {
1375
+ const existing = userByFp.get(fp);
1376
+ const userEnabled = existing.enabled;
1377
+ Object.keys(existing).forEach((k) => delete existing[k]);
1378
+ Object.assign(existing, srcR);
1379
+ existing.enabled = userEnabled;
1380
+ } else {
1381
+ userRules.push({ ...srcR, enabled: true });
1382
+ }
1383
+ }
1384
+ for (const r of userRules) {
1385
+ if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
1386
+ r.source_removed = true;
1387
+ r.enabled = false;
1388
+ }
1389
+ }
1390
+ }
1435
1391
  }
1436
- const dates = Array.from(datesSet).sort();
1437
- const models = Object.keys(series).sort();
1438
- return { dates, models, series };
1439
1392
  }
1440
- function resetTokenStats() {
1441
- const statsPath = getStatsPath();
1442
- if (existsSync6(statsPath)) {
1443
- try {
1444
- unlinkSync(statsPath);
1445
- } catch {
1446
- }
1393
+ function mergeConfig(userCfg, srcDefaults) {
1394
+ const cfg = userCfg;
1395
+ if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
1396
+ if (!cfg.layers) cfg.layers = {};
1397
+ for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
1398
+ if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
1447
1399
  }
1400
+ _migrateCategoriesAcrossLayers(
1401
+ cfg,
1402
+ "L2_rules",
1403
+ "L1_rules",
1404
+ ["sensitive_path_access"]
1405
+ );
1406
+ _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
1407
+ _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
1408
+ _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
1409
+ _mergeAgentRules(cfg, srcDefaults);
1410
+ return cfg;
1448
1411
  }
1449
- var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
1450
- var init_token_stats = __esm({
1451
- "src/engine/token-stats.ts"() {
1452
- "use strict";
1453
- init_esm_shims();
1454
- STATS_DIR = join3(homedir2(), ".config", "agentmoss");
1455
- STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
1456
- ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
1457
- MAX_RECORDS = 1e4;
1412
+ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
1413
+ const src = cfg[srcLayer] || {};
1414
+ const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
1415
+ for (const catName of catNames) {
1416
+ if (!(catName in src)) continue;
1417
+ const srcCat = src[catName];
1418
+ if (!dst[catName]) {
1419
+ dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
1420
+ }
1421
+ const dstCat = dst[catName];
1422
+ if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
1423
+ if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
1424
+ if (srcCat.rules && srcCat.rules.length > 0) {
1425
+ const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
1426
+ for (const r of srcCat.rules) {
1427
+ const fp = _ruleFingerprint(r);
1428
+ if (!dstFps.has(fp)) {
1429
+ (dstCat.rules = dstCat.rules || []).push(r);
1430
+ dstFps.add(fp);
1431
+ }
1432
+ }
1433
+ }
1434
+ delete src[catName];
1458
1435
  }
1459
- });
1460
-
1461
- // src/infra/runtime-config.ts
1462
- var runtime_config_exports = {};
1463
- __export(runtime_config_exports, {
1464
- DENY_MODE_LABELS: () => DENY_MODE_LABELS,
1465
- addCustomRule: () => addCustomRule,
1466
- addCustomSkill: () => addCustomSkill,
1467
- deleteCustomRule: () => deleteCustomRule,
1468
- deleteCustomSkill: () => deleteCustomSkill,
1469
- filterEnabledRules: () => filterEnabledRules,
1470
- generateSourceDefaults: () => generateSourceDefaults,
1471
- getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
1472
- getDisabledSkillIds: () => getDisabledSkillIds,
1473
- getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
1474
- getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
1475
- getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
1476
- getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
1477
- getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
1478
- getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
1479
- getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
1480
- getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
1481
- getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
1482
- getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
1483
- getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
1484
- getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
1485
- getEnvOverrides: () => getEnvOverrides,
1486
- getProfileOrDetect: () => getProfileOrDetect,
1487
- getRuntimeConfigPath: () => getRuntimeConfigPath,
1488
- hasRuntimeConfig: () => hasRuntimeConfig,
1489
- isL2CategoryEnabled: () => isL2CategoryEnabled,
1490
- isLayerEnabled: () => isLayerEnabled,
1491
- loadRuntimeConfig: () => loadRuntimeConfig,
1492
- mergeConfig: () => mergeConfig,
1493
- saveRuntimeConfig: () => saveRuntimeConfig,
1494
- updateCategoryEnabled: () => updateCategoryEnabled,
1495
- updateLayerEnabled: () => updateLayerEnabled,
1496
- updateRuleDenyMode: () => updateRuleDenyMode,
1497
- updateRuleEnabled: () => updateRuleEnabled,
1498
- updateRuleSkipL3: () => updateRuleSkipL3,
1499
- updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1500
- updateSkillEnabled: () => updateSkillEnabled
1501
- });
1502
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
1503
- import { join as join4 } from "path";
1504
- import { homedir as homedir3 } from "os";
1505
- function getRuntimeConfigPath() {
1506
- const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
1507
- if (envPath) return envPath;
1508
- return RUNTIME_CONFIG_PATH;
1509
1436
  }
1510
- function saveRuntimeConfig(config) {
1511
- const p = getRuntimeConfigPath();
1512
- const dir = p.substring(0, p.lastIndexOf("/"));
1513
- if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
1514
- writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
1437
+ function getEnabledL1InjectionKeywords(runtime) {
1438
+ const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
1439
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1440
+ keyword: r.keyword || "",
1441
+ risk_level: r.risk_level || "",
1442
+ lang: r.lang || "en"
1443
+ }));
1515
1444
  }
1516
- function emptyRuntimeConfig() {
1517
- return {
1518
- version: 1,
1519
- layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1520
- L1_rules: {},
1521
- L2_rules: {},
1522
- L3_skills: {}
1523
- };
1445
+ function getEnabledL2IntentPatterns(runtime) {
1446
+ const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
1447
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1448
+ intent_keywords: r.intent_keywords || [],
1449
+ dangerous_actions: r.dangerous_actions || [],
1450
+ reason: r.reason || ""
1451
+ }));
1524
1452
  }
1525
- function loadRuntimeConfig() {
1526
- const p = getRuntimeConfigPath();
1527
- if (!existsSync7(p)) {
1528
- const seeded = mergeConfig({}, generateSourceDefaults());
1529
- try {
1530
- saveRuntimeConfig(seeded);
1531
- } catch {
1453
+ function getEnabledL2PasswordPatterns(runtime) {
1454
+ const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
1455
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
1456
+ }
1457
+ function getEnabledL2UserDeletionPatterns(runtime) {
1458
+ const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
1459
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
1460
+ }
1461
+ function isL2CategoryEnabled(runtime, categoryKey) {
1462
+ const cat = runtime.L2_rules?.[categoryKey];
1463
+ return cat?.category_enabled ?? true;
1464
+ }
1465
+ function getDisabledSkillIds(runtime) {
1466
+ const disabled = /* @__PURE__ */ new Set();
1467
+ for (const catData of Object.values(runtime.L3_skills || {})) {
1468
+ if (!catData.category_enabled) {
1469
+ for (const s of catData.skills || []) disabled.add(s.id);
1470
+ } else {
1471
+ for (const s of catData.skills || []) {
1472
+ if (s.enabled === false) disabled.add(s.id);
1473
+ }
1532
1474
  }
1533
- return seeded;
1534
1475
  }
1535
- try {
1536
- const data = JSON.parse(readFileSync7(p, "utf-8"));
1537
- const merged = mergeConfig(data, generateSourceDefaults());
1538
- return {
1539
- version: data.version || 1,
1540
- layers: {
1541
- L1_heuristic: data.layers?.L1_heuristic ?? true,
1542
- L2_logic_rules: data.layers?.L2_logic_rules ?? true,
1543
- L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
1544
- },
1545
- L1_rules: merged.L1_rules || {},
1546
- L2_rules: merged.L2_rules || {},
1547
- L3_skills: merged.L3_skills || {}
1548
- };
1549
- } catch {
1550
- return emptyRuntimeConfig();
1476
+ return disabled;
1477
+ }
1478
+ function getEnabledL1CommandPatternsOrDefault(runtime) {
1479
+ const enabled = getEnabledL1CommandPatterns(runtime);
1480
+ if (enabled.length) return enabled;
1481
+ if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
1482
+ return [];
1483
+ }
1484
+ function getEnabledL1InjectionKeywordsOrDefault(runtime) {
1485
+ const enabled = getEnabledL1InjectionKeywords(runtime);
1486
+ if (enabled.length) return enabled;
1487
+ if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
1488
+ return [];
1489
+ }
1490
+ function getEnabledL2SensitivePathsOrDefault(runtime) {
1491
+ const enabled = getEnabledL2SensitivePaths(runtime);
1492
+ if (enabled.length) return enabled;
1493
+ if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
1494
+ return [];
1495
+ }
1496
+ function getEnabledL2IntentPatternsOrDefault(runtime) {
1497
+ const enabled = getEnabledL2IntentPatterns(runtime);
1498
+ if (enabled.length) return enabled;
1499
+ if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
1500
+ return [];
1501
+ }
1502
+ function getEnabledL2PasswordPatternsOrDefault(runtime) {
1503
+ const enabled = getEnabledL2PasswordPatterns(runtime);
1504
+ if (enabled.length) return enabled;
1505
+ if (!hasRuntimeConfig()) {
1506
+ return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
1507
+ }
1508
+ return [];
1509
+ }
1510
+ function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
1511
+ const enabled = getEnabledL2UserDeletionPatterns(runtime);
1512
+ if (enabled.length) return enabled;
1513
+ if (!hasRuntimeConfig()) {
1514
+ return USER_DELETION_PATTERNS.map((p) => p.source);
1515
+ }
1516
+ return [];
1517
+ }
1518
+ function _validateSkillId(skillId) {
1519
+ if (!skillId || !SKILL_ID_RE.test(skillId)) {
1520
+ throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
1551
1521
  }
1552
1522
  }
1553
- function isLayerEnabled(layerKey) {
1554
- const disableKey = LAYER_DISABLE_ENV[layerKey];
1555
- if (disableKey && process.env[disableKey] === "1") return false;
1523
+ function updateLayerEnabled(layerKey, enabled) {
1556
1524
  const runtime = loadRuntimeConfig();
1557
- const layers = runtime.layers || {};
1558
- if (layerKey in layers) return !!layers[layerKey];
1559
- return true;
1525
+ runtime.layers[layerKey] = enabled;
1526
+ saveRuntimeConfig(runtime);
1527
+ return runtime;
1560
1528
  }
1561
- function getEnvOverrides() {
1562
- const overrides = {};
1563
- for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
1564
- const val = process.env[envKey];
1565
- if (val !== void 0) {
1566
- overrides[layerKey] = {
1567
- env_var: envKey,
1568
- env_value: val,
1569
- overridden: val === "1",
1570
- effect: val === "1" ? "disabled" : "no_override"
1571
- };
1529
+ function updateRuleEnabled(layer, category, ruleId, enabled) {
1530
+ const runtime = loadRuntimeConfig();
1531
+ for (const r of runtime[layer]?.[category]?.rules || []) {
1532
+ if (r.id === ruleId) {
1533
+ r.enabled = enabled;
1534
+ break;
1572
1535
  }
1573
1536
  }
1574
- return overrides;
1537
+ saveRuntimeConfig(runtime);
1538
+ return runtime;
1575
1539
  }
1576
- function filterEnabledRules(rules, categoryEnabled = true) {
1577
- if (!categoryEnabled) return [];
1578
- return rules.filter((r) => r.enabled !== false);
1540
+ function updateRuleDenyMode(layer, category, ruleId, denyMode) {
1541
+ const runtime = loadRuntimeConfig();
1542
+ for (const r of runtime[layer]?.[category]?.rules || []) {
1543
+ if (r.id === ruleId) {
1544
+ r.deny_mode = denyMode;
1545
+ _syncDenyModeFields(r);
1546
+ break;
1547
+ }
1548
+ }
1549
+ saveRuntimeConfig(runtime);
1550
+ return runtime;
1579
1551
  }
1580
- function getEnabledL1CommandPatterns(runtime) {
1581
- const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
1582
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1583
- pattern: r.pattern || "",
1584
- risk_level: r.risk_level || "",
1585
- risk_type: r.risk_type || "",
1586
- reason: r.reason || ""
1587
- }));
1552
+ function updateRuleSkipL3(layer, category, ruleId, skipL3) {
1553
+ const runtime = loadRuntimeConfig();
1554
+ for (const r of runtime[layer]?.[category]?.rules || []) {
1555
+ if (r.id === ruleId) {
1556
+ r.skip_l3_on_disabled = skipL3;
1557
+ break;
1558
+ }
1559
+ }
1560
+ saveRuntimeConfig(runtime);
1561
+ return runtime;
1588
1562
  }
1589
- function getEnabledL2SensitivePaths(runtime) {
1590
- let cat = runtime.L1_rules?.sensitive_path_access;
1591
- if (!cat || !cat.rules || cat.rules.length === 0) {
1592
- cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
1563
+ function updateCategoryEnabled(layer, category, enabled) {
1564
+ const runtime = loadRuntimeConfig();
1565
+ const cat = runtime[layer]?.[category];
1566
+ if (cat) cat.category_enabled = enabled;
1567
+ saveRuntimeConfig(runtime);
1568
+ return runtime;
1569
+ }
1570
+ function addCustomRule(layer, category, rule) {
1571
+ const runtime = loadRuntimeConfig();
1572
+ const layerObj = runtime[layer] || (runtime[layer] = {});
1573
+ const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
1574
+ const rules = cat.rules || (cat.rules = []);
1575
+ const newRule = { ...rule, builtin: false, enabled: true };
1576
+ if (!newRule.id) {
1577
+ newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
1593
1578
  }
1594
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
1595
- const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
1596
- if (r.credential) out.credential = true;
1597
- if (r.read_only) out.read_only = true;
1598
- return out;
1599
- });
1579
+ if (newRule.deny_mode) _syncDenyModeFields(newRule);
1580
+ rules.push(newRule);
1581
+ saveRuntimeConfig(runtime);
1582
+ return runtime;
1600
1583
  }
1601
- function hasRuntimeConfig() {
1602
- const p = getRuntimeConfigPath();
1603
- if (!existsSync7(p)) return false;
1604
- try {
1605
- JSON.parse(readFileSync7(p, "utf-8"));
1606
- return true;
1607
- } catch {
1608
- return false;
1584
+ function deleteCustomRule(layer, category, ruleId) {
1585
+ const runtime = loadRuntimeConfig();
1586
+ const rules = runtime[layer]?.[category]?.rules || [];
1587
+ const idx = rules.findIndex((r) => r.id === ruleId);
1588
+ if (idx >= 0) {
1589
+ const r = rules[idx];
1590
+ if (r.builtin && !r.source_removed) return null;
1591
+ rules.splice(idx, 1);
1592
+ saveRuntimeConfig(runtime);
1609
1593
  }
1594
+ return runtime;
1610
1595
  }
1611
- function getDisabledL2RulesWithSkipL3() {
1612
- let runtime;
1596
+ function addCustomSkill(skillId, category, keywords, content) {
1597
+ _validateSkillId(skillId);
1598
+ const runtime = loadRuntimeConfig();
1599
+ const skillsDir = join(RUNTIME_CONFIG_DIR, "agent_moss_skills");
1613
1600
  try {
1614
- runtime = loadRuntimeConfig();
1601
+ mkdirSync(skillsDir, { recursive: true });
1615
1602
  } catch {
1616
- return {};
1617
1603
  }
1618
- const result = {};
1619
- for (const layerKey of ["L1_rules", "L2_rules"]) {
1620
- const layer = runtime[layerKey] || {};
1621
- for (const [catName, cat] of Object.entries(layer)) {
1622
- if (!cat.category_enabled) continue;
1623
- const items = [];
1624
- for (const r of cat.rules || []) {
1625
- if (r.enabled === false && r.skip_l3_on_disabled) {
1626
- if (catName === "sensitive_path_access") {
1627
- if (r.path) items.push(r.path);
1628
- } else if (catName === "intent_consistency") {
1629
- items.push(...r.intent_keywords || []);
1630
- } else if (r.pattern) {
1631
- items.push(r.pattern);
1632
- }
1633
- }
1634
- }
1635
- if (items.length > 0) result[catName] = items;
1604
+ writeFileSync(join(skillsDir, `${skillId}.md`), content, "utf-8");
1605
+ const l3 = runtime.L3_skills || (runtime.L3_skills = {});
1606
+ const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
1607
+ (cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
1608
+ saveRuntimeConfig(runtime);
1609
+ return runtime;
1610
+ }
1611
+ function deleteCustomSkill(skillId, category) {
1612
+ _validateSkillId(skillId);
1613
+ const runtime = loadRuntimeConfig();
1614
+ const skills = runtime.L3_skills?.[category]?.skills || [];
1615
+ const idx = skills.findIndex((s) => s.id === skillId);
1616
+ if (idx >= 0) {
1617
+ if (skills[idx].builtin) return null;
1618
+ skills.splice(idx, 1);
1619
+ const skillPath = join(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
1620
+ try {
1621
+ if (existsSync3(skillPath)) unlinkSync(skillPath);
1622
+ } catch {
1636
1623
  }
1624
+ saveRuntimeConfig(runtime);
1637
1625
  }
1638
- return result;
1639
- }
1640
- function _slugify(text) {
1641
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
1642
- }
1643
- function _generatePatternId(pattern) {
1644
- return `pat_${_slugify(pattern)}`;
1645
- }
1646
- function _deriveDenyMode(rule) {
1647
- if (rule.deny_mode) return rule.deny_mode;
1648
- if (rule.credential) return "deny_both";
1649
- if (rule.read_only) return "deny_read";
1650
- return "deny_write";
1626
+ return runtime;
1651
1627
  }
1652
- function _syncDenyModeFields(rule) {
1653
- const dm = rule.deny_mode || "deny_write";
1654
- if (dm === "deny_both") {
1655
- rule.credential = true;
1656
- delete rule.read_only;
1657
- } else if (dm === "deny_read") {
1658
- delete rule.credential;
1659
- rule.read_only = true;
1660
- } else {
1661
- delete rule.credential;
1662
- delete rule.read_only;
1628
+ function updateSkillEnabled(skillId, enabled) {
1629
+ const runtime = loadRuntimeConfig();
1630
+ for (const catData of Object.values(runtime.L3_skills || {})) {
1631
+ for (const s of catData.skills || []) {
1632
+ if (s.id === skillId) {
1633
+ s.enabled = enabled;
1634
+ break;
1635
+ }
1636
+ }
1663
1637
  }
1638
+ saveRuntimeConfig(runtime);
1639
+ return runtime;
1664
1640
  }
1665
- function _l1CommandPatternsToRules() {
1666
- return DANGEROUS_PATTERNS.map((p) => ({
1667
- id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
1668
- pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
1669
- risk_level: p.severity,
1670
- risk_type: p.riskType,
1671
- reason: p.description,
1672
- enabled: true,
1673
- builtin: true
1674
- }));
1675
- }
1676
- function _l1InjectionKeywordsToRules() {
1677
- return INJECTION_KEYWORDS.map((kw) => ({
1678
- id: `inj_${_slugify(kw.keyword)}`,
1679
- keyword: kw.keyword,
1680
- risk_level: kw.riskLevel,
1681
- lang: kw.lang || "en",
1682
- enabled: true,
1683
- builtin: true
1684
- }));
1685
- }
1686
- function _l2SensitivePathsToRules() {
1687
- const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
1688
- return all.map((sp) => {
1689
- const denyMode = _deriveDenyMode({
1690
- deny_mode: sp.deny_mode,
1691
- credential: sp.credential,
1692
- read_only: sp.read_only
1693
- });
1694
- const rule = {
1695
- id: `path_${_slugify(sp.path)}`,
1696
- path: sp.path,
1697
- risk_level: sp.riskLevel,
1698
- desc: sp.desc,
1699
- deny_mode: denyMode,
1700
- source_deny_mode: denyMode,
1701
- skip_l3_on_disabled: true,
1702
- enabled: true,
1703
- builtin: true
1704
- };
1705
- _syncDenyModeFields(rule);
1706
- return rule;
1707
- });
1708
- }
1709
- function _l2IntentPatternsToRules() {
1710
- return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
1711
- id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
1712
- intent_keywords: pat.intentKeywords,
1713
- dangerous_actions: pat.dangerousActions,
1714
- reason: pat.reason,
1715
- skip_l3_on_disabled: true,
1716
- enabled: true,
1717
- builtin: true
1718
- }));
1641
+ function updateSkillCategoryEnabled(category, enabled) {
1642
+ const runtime = loadRuntimeConfig();
1643
+ const cat = runtime.L3_skills?.[category];
1644
+ if (cat) cat.category_enabled = enabled;
1645
+ saveRuntimeConfig(runtime);
1646
+ return runtime;
1719
1647
  }
1720
- function _l2PasswordPatternsToRules() {
1721
- return PASSWORD_MODIFY_PATTERNS.map((p) => ({
1722
- id: `pw_${_slugify(p.source)}`,
1723
- pattern: p.source,
1724
- skip_l3_on_disabled: true,
1725
- enabled: true,
1726
- builtin: true
1727
- }));
1648
+ function getProfileOrDetect() {
1649
+ return null;
1728
1650
  }
1729
- function _l2UserDeletionPatternsToRules() {
1730
- return USER_DELETION_PATTERNS.map((p) => ({
1731
- id: `ud_${_slugify(p.source)}`,
1732
- pattern: p.source,
1733
- skip_l3_on_disabled: true,
1734
- enabled: true,
1735
- builtin: true
1736
- }));
1651
+ function getAgentRules(runtime) {
1652
+ return runtime.agent_rules || {};
1737
1653
  }
1738
- function _l3SkillsToRules() {
1739
- const skillCategories = {
1740
- file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
1741
- network_security: [
1742
- "data_exfiltration_guard",
1743
- "browser_web_access_guard",
1744
- "lateral_movement_guard",
1745
- "email_operation_guard"
1746
- ],
1747
- persistence_supply_chain: [
1748
- "persistence_backdoor_guard",
1749
- "supply_chain_guard",
1750
- "skill_installation_guard"
1751
- ],
1752
- intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
1753
- resource: ["resource_exhaustion_guard"]
1654
+ function _agentCategoryEnabledRules(cat) {
1655
+ if (!cat || cat.category_enabled === false) return [];
1656
+ return (cat.rules || []).filter((r) => r.enabled !== false);
1657
+ }
1658
+ function getEnabledAgentExtensions(runtime, agentId) {
1659
+ const agentData = runtime.agent_rules?.[agentId];
1660
+ if (!agentData) return null;
1661
+ const safeAction = _agentCategoryEnabledRules(agentData.builtin_safe_action_types);
1662
+ const safeSkills = _agentCategoryEnabledRules(agentData.builtin_safe_skills);
1663
+ const cmdPatterns = _agentCategoryEnabledRules(agentData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
1664
+ pattern: r.pattern,
1665
+ risk_level: r.risk_level || "",
1666
+ risk_type: r.risk_type || "",
1667
+ reason: r.reason || ""
1668
+ }));
1669
+ const injKeywords = _agentCategoryEnabledRules(agentData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
1670
+ keyword: r.keyword,
1671
+ risk_level: r.risk_level || "",
1672
+ lang: r.lang || "en"
1673
+ }));
1674
+ const sensitivePaths = _agentCategoryEnabledRules(agentData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
1675
+ path: r.path,
1676
+ risk_level: r.risk_level || "",
1677
+ desc: r.desc || "",
1678
+ credential: r.credential,
1679
+ read_only: r.read_only,
1680
+ deny_mode: r.deny_mode
1681
+ }));
1682
+ return {
1683
+ agent_id: agentId,
1684
+ description: "",
1685
+ extra_command_patterns: cmdPatterns,
1686
+ extra_injection_keywords: injKeywords,
1687
+ extra_sensitive_paths: sensitivePaths,
1688
+ builtin_safe_skills: safeSkills.filter((r) => r.value).map((r) => r.value),
1689
+ builtin_safe_action_types: safeAction.filter((r) => r.value).map((r) => r.value),
1690
+ extra_skills: []
1754
1691
  };
1755
- const out = {};
1756
- for (const [cat, ids] of Object.entries(skillCategories)) {
1757
- out[cat] = {
1758
- category_enabled: true,
1759
- category_desc: "",
1760
- skills: ids.map((s) => ({
1761
- id: s,
1762
- keywords: SKILL_KEYWORD_MAP[s] || [],
1763
- enabled: true,
1764
- builtin: true
1765
- }))
1766
- };
1767
- }
1768
- const categorized = new Set(Object.values(skillCategories).flat());
1769
- for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
1770
- if (!categorized.has(s)) {
1771
- (out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
1772
- id: s,
1773
- keywords: SKILL_KEYWORD_MAP[s],
1774
- enabled: true,
1775
- builtin: true
1776
- });
1692
+ }
1693
+ function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
1694
+ const runtime = loadRuntimeConfig();
1695
+ const cat = runtime.agent_rules?.[agentId]?.[category];
1696
+ if (cat) {
1697
+ const targetFp = _ruleFingerprint(ruleFp);
1698
+ for (const r of cat.rules || []) {
1699
+ if (_ruleFingerprint(r) === targetFp) {
1700
+ r.enabled = enabled;
1701
+ break;
1702
+ }
1777
1703
  }
1778
1704
  }
1779
- out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
1780
- return out;
1705
+ saveRuntimeConfig(runtime);
1706
+ return runtime;
1781
1707
  }
1782
- function generateSourceDefaults() {
1783
- return {
1784
- version: 1,
1785
- layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1786
- L1_rules: {
1787
- dangerous_commands: {
1788
- category_enabled: true,
1789
- category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
1790
- rules: _l1CommandPatternsToRules()
1791
- },
1792
- injection_detection: {
1793
- category_enabled: true,
1794
- category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
1795
- rules: _l1InjectionKeywordsToRules()
1796
- },
1797
- sensitive_path_access: {
1798
- category_enabled: true,
1799
- category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
1800
- rules: _l2SensitivePathsToRules()
1801
- },
1802
- user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
1803
- },
1804
- L2_rules: {
1805
- intent_consistency: {
1806
- category_enabled: true,
1807
- category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
1808
- rules: _l2IntentPatternsToRules()
1809
- },
1810
- password_modify_consent: {
1811
- category_enabled: true,
1812
- category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
1813
- rules: _l2PasswordPatternsToRules()
1814
- },
1815
- user_deletion_consent: {
1816
- category_enabled: true,
1817
- category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
1818
- rules: _l2UserDeletionPatternsToRules()
1819
- },
1820
- read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
1821
- user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
1822
- },
1823
- L3_skills: _l3SkillsToRules()
1824
- };
1708
+ function addAgentRule(agentId, category, rule) {
1709
+ const runtime = loadRuntimeConfig();
1710
+ if (!runtime.agent_rules) runtime.agent_rules = {};
1711
+ if (!runtime.agent_rules[agentId]) runtime.agent_rules[agentId] = {};
1712
+ const cats = runtime.agent_rules[agentId];
1713
+ if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
1714
+ (cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
1715
+ saveRuntimeConfig(runtime);
1716
+ return runtime;
1825
1717
  }
1826
- function _ruleFingerprint(rule) {
1827
- if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
1828
- return JSON.stringify([
1829
- [...rule.intent_keywords || []].sort(),
1830
- [...rule.dangerous_actions || []].sort()
1831
- ]);
1718
+ function deleteAgentRule(agentId, category, ruleFp) {
1719
+ const runtime = loadRuntimeConfig();
1720
+ const cat = runtime.agent_rules?.[agentId]?.[category];
1721
+ if (cat) {
1722
+ const targetFp = _ruleFingerprint(ruleFp);
1723
+ cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
1832
1724
  }
1833
- if (rule.path) return JSON.stringify([rule.path]);
1834
- if (rule.pattern) return JSON.stringify([rule.pattern]);
1835
- if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
1836
- return JSON.stringify([rule.id || ""]);
1725
+ saveRuntimeConfig(runtime);
1726
+ return runtime;
1837
1727
  }
1838
- function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
1839
- const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1840
- const srcLayer = srcDefaults[layerKey] || {};
1841
- for (const [catName, srcCat] of Object.entries(srcLayer)) {
1842
- if (!(catName in userLayer)) {
1843
- userLayer[catName] = srcCat;
1844
- continue;
1728
+ function updateAgentCategoryEnabled(agentId, category, enabled) {
1729
+ const runtime = loadRuntimeConfig();
1730
+ const cat = runtime.agent_rules?.[agentId]?.[category];
1731
+ if (cat) cat.category_enabled = enabled;
1732
+ saveRuntimeConfig(runtime);
1733
+ return runtime;
1734
+ }
1735
+ var __dirname3, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, SKILL_ID_RE;
1736
+ var init_runtime_config = __esm({
1737
+ "src/infra/runtime-config.ts"() {
1738
+ "use strict";
1739
+ init_esm_shims();
1740
+ init_heuristic();
1741
+ init_logic_rules();
1742
+ init_skill_loader();
1743
+ __dirname3 = dirname2(fileURLToPath3(import.meta.url));
1744
+ RUNTIME_CONFIG_DIR = join(homedir(), ".config", "agentmoss");
1745
+ RUNTIME_CONFIG_PATH = join(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
1746
+ ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
1747
+ LAYER_DISABLE_ENV = {
1748
+ L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
1749
+ L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
1750
+ L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
1751
+ };
1752
+ DENY_MODE_LABELS = {
1753
+ deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
1754
+ deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
1755
+ deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
1756
+ };
1757
+ _AGENTS_DIR_PROD = join(__dirname3, "agents");
1758
+ _AGENTS_DIR_DEV = join(__dirname3, "..", "..", "..", "agent_moss", "agents");
1759
+ SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
1760
+ }
1761
+ });
1762
+
1763
+ // src/agents/index.ts
1764
+ import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
1765
+ import { resolve as resolve2, dirname as dirname3 } from "path";
1766
+ import { fileURLToPath as fileURLToPath4 } from "url";
1767
+ function resolveAgentsDir() {
1768
+ if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
1769
+ if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
1770
+ return AGENTS_DIR_PROD;
1771
+ }
1772
+ function getAgentExtensions(agentId) {
1773
+ if (!agentId) return null;
1774
+ if (cache.has(agentId)) return cache.get(agentId) ?? null;
1775
+ let ext = null;
1776
+ try {
1777
+ const { hasRuntimeConfig: hasRuntimeConfig2, loadRuntimeConfig: loadRuntimeConfig2, getEnabledAgentExtensions: getEnabledAgentExtensions2 } = (init_runtime_config(), __toCommonJS(runtime_config_exports));
1778
+ if (hasRuntimeConfig2()) {
1779
+ ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
1845
1780
  }
1846
- const userCat = userLayer[catName];
1847
- if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1848
- if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1849
- const srcRules = srcCat.rules || [];
1850
- const userRules = userCat.rules || (userCat.rules = []);
1851
- const userByFp = /* @__PURE__ */ new Map();
1852
- for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
1853
- const srcFps = /* @__PURE__ */ new Set();
1854
- for (const srcR of srcRules) {
1855
- const fp = _ruleFingerprint(srcR);
1856
- srcFps.add(fp);
1857
- if (userByFp.has(fp)) {
1858
- const existing = userByFp.get(fp);
1859
- const userEnabled = existing.enabled;
1860
- const userSkipL3 = existing.skip_l3_on_disabled;
1861
- const userDenyMode = existing.deny_mode;
1862
- const userSourceDeny = existing.source_deny_mode;
1863
- Object.keys(existing).forEach((k) => delete existing[k]);
1864
- Object.assign(existing, srcR);
1865
- existing.enabled = userEnabled;
1866
- if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
1867
- if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
1868
- existing.deny_mode = userDenyMode;
1781
+ } catch {
1782
+ }
1783
+ if (ext === null) {
1784
+ const agentsDir = resolveAgentsDir();
1785
+ const rulesFile = resolve2(agentsDir, agentId, "rules.json");
1786
+ if (!existsSync4(rulesFile)) {
1787
+ cache.set(agentId, null);
1788
+ return null;
1789
+ }
1790
+ try {
1791
+ const raw = readFileSync3(rulesFile, "utf-8");
1792
+ ext = JSON.parse(raw);
1793
+ if (!ext.agent_id) {
1794
+ console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
1795
+ cache.set(agentId, null);
1796
+ return null;
1797
+ }
1798
+ } catch (e) {
1799
+ console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
1800
+ cache.set(agentId, null);
1801
+ return null;
1802
+ }
1803
+ }
1804
+ cache.set(agentId, ext);
1805
+ console.log(
1806
+ `[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
1807
+ );
1808
+ return ext;
1809
+ }
1810
+ var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
1811
+ var init_agents = __esm({
1812
+ "src/agents/index.ts"() {
1813
+ "use strict";
1814
+ init_esm_shims();
1815
+ __filename3 = fileURLToPath4(import.meta.url);
1816
+ __dirname4 = dirname3(__filename3);
1817
+ AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
1818
+ AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
1819
+ cache = /* @__PURE__ */ new Map();
1820
+ }
1821
+ });
1822
+
1823
+ // src/engine/heuristic.ts
1824
+ function runHeuristicCheck(input) {
1825
+ const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
1826
+ const matched = [];
1827
+ let highestSeverity = "low";
1828
+ let highestRiskType = "";
1829
+ const userRules = getCustomRules();
1830
+ for (const rule of userRules) {
1831
+ try {
1832
+ const re = new RegExp(rule.pattern);
1833
+ if (re.test(command)) {
1834
+ if (rule.action === "Deny") {
1835
+ matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
1836
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
1837
+ highestSeverity = rule.severity;
1838
+ highestRiskType = "custom_rule";
1839
+ }
1869
1840
  }
1870
- if (existing.deny_mode) _syncDenyModeFields(existing);
1871
- } else {
1872
- const newR = { ...srcR, enabled: true };
1873
- userRules.push(newR);
1874
1841
  }
1842
+ } catch {
1875
1843
  }
1876
- for (const r of userRules) {
1877
- if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
1878
- r.source_removed = true;
1879
- r.enabled = false;
1844
+ }
1845
+ let topRiskType = "";
1846
+ for (const rule of DANGEROUS_PATTERNS) {
1847
+ if (rule.pattern.test(command)) {
1848
+ matched.push(rule.description);
1849
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
1850
+ highestSeverity = rule.severity;
1851
+ topRiskType = rule.riskType;
1880
1852
  }
1881
1853
  }
1882
- if (catName === "sensitive_path_access") {
1883
- for (const r of userRules) {
1884
- if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
1885
- _syncDenyModeFields(r);
1854
+ }
1855
+ highestRiskType = topRiskType || highestRiskType;
1856
+ const agentExt = getAgentExtensions(input.agentId);
1857
+ if (agentExt?.extra_command_patterns) {
1858
+ for (const rule of agentExt.extra_command_patterns) {
1859
+ try {
1860
+ const re = new RegExp(rule.pattern, "i");
1861
+ if (re.test(command)) {
1862
+ matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
1863
+ if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
1864
+ highestSeverity = rule.risk_level;
1865
+ highestRiskType = rule.risk_type;
1866
+ }
1867
+ }
1868
+ } catch {
1886
1869
  }
1887
1870
  }
1888
1871
  }
1889
- for (const catName of Object.keys(userLayer)) {
1890
- if (catName === "user_custom") continue;
1891
- if (!(catName in srcLayer)) delete userLayer[catName];
1872
+ const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
1873
+ const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
1874
+ if (injectionHits.length > 0) {
1875
+ const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
1876
+ matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
1877
+ if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
1878
+ highestSeverity = topHit.riskLevel;
1879
+ highestRiskType = "prompt_injection";
1880
+ }
1892
1881
  }
1882
+ const actionTypeL = input.aNext.actionType.toLowerCase();
1883
+ const detailL = input.aNext.actionDetail.toLowerCase();
1884
+ const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
1885
+ if (!isExcludedType) {
1886
+ const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
1887
+ if (pathResult) return pathResult;
1888
+ }
1889
+ const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
1890
+ if (dangerousResult) return dangerousResult;
1891
+ const lateralResult = checkLateralMovement(detailL);
1892
+ if (lateralResult) return lateralResult;
1893
+ if (matched.length > 0) {
1894
+ return {
1895
+ allowed: false,
1896
+ reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
1897
+ riskLevel: highestSeverity,
1898
+ riskType: highestRiskType,
1899
+ confidence: 95,
1900
+ source: "heuristic",
1901
+ violatedLayers: ["1.1"]
1902
+ // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
1903
+ };
1904
+ }
1905
+ return null;
1893
1906
  }
1894
- function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
1895
- const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1896
- const srcLayer = srcDefaults[layerKey] || {};
1897
- for (const [catName, srcCat] of Object.entries(srcLayer)) {
1898
- if (!(catName in userLayer)) {
1899
- userLayer[catName] = srcCat;
1900
- continue;
1907
+ function checkInjectionKeywords(text, extraKeywords) {
1908
+ const hits = [];
1909
+ for (const entry of INJECTION_KEYWORDS) {
1910
+ if (text.includes(entry.keyword.toLowerCase())) {
1911
+ hits.push(entry);
1901
1912
  }
1902
- const userCat = userLayer[catName];
1903
- if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1904
- if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1905
- const userSkills = userCat.skills || (userCat.skills = []);
1906
- const existingIds = new Set(userSkills.map((s) => s.id));
1907
- for (const srcS of srcCat.skills || []) {
1908
- if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
1913
+ }
1914
+ if (extraKeywords) {
1915
+ for (const entry of extraKeywords) {
1916
+ if (text.includes(entry.keyword.toLowerCase())) {
1917
+ hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
1918
+ }
1909
1919
  }
1910
1920
  }
1921
+ return hits;
1911
1922
  }
1912
- function mergeConfig(userCfg, srcDefaults) {
1913
- const cfg = userCfg;
1914
- if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
1915
- if (!cfg.layers) cfg.layers = {};
1916
- for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
1917
- if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
1918
- }
1919
- _migrateCategoriesAcrossLayers(
1920
- cfg,
1921
- "L2_rules",
1922
- "L1_rules",
1923
- ["sensitive_path_access"]
1924
- );
1925
- _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
1926
- _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
1927
- _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
1928
- return cfg;
1923
+ function isInlineScriptCommand(actionDetail) {
1924
+ const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1925
+ return re.test(actionDetail);
1929
1926
  }
1930
- function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
1931
- const src = cfg[srcLayer] || {};
1932
- const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
1933
- for (const catName of catNames) {
1934
- if (!(catName in src)) continue;
1935
- const srcCat = src[catName];
1936
- if (!dst[catName]) {
1937
- dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
1927
+ function severityRank(s) {
1928
+ const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
1929
+ return ranks[s] || 0;
1930
+ }
1931
+ function checkSensitivePath(command, cwd, agentExt) {
1932
+ const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
1933
+ const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
1934
+ const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
1935
+ const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
1936
+ const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
1937
+ for (const sp of ROOT_SENSITIVE_PATHS) {
1938
+ if (command.includes(sp.path.toLowerCase())) {
1939
+ if (sp.path === "/dev/random") {
1940
+ if (isWriteOpFlag) {
1941
+ continue;
1942
+ }
1943
+ return {
1944
+ allowed: false,
1945
+ reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
1946
+ riskLevel: sp.riskLevel,
1947
+ riskType: "file_access",
1948
+ confidence: 90,
1949
+ source: "heuristic",
1950
+ violatedLayers: ["1.1"]
1951
+ };
1952
+ }
1953
+ if (!isWriteOpFlag && !sp.credential) {
1954
+ continue;
1955
+ }
1956
+ return {
1957
+ allowed: false,
1958
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
1959
+ riskLevel: sp.riskLevel,
1960
+ riskType: "file_access",
1961
+ confidence: 90,
1962
+ source: "heuristic",
1963
+ violatedLayers: ["1.1"],
1964
+ isCredential: !!sp.credential
1965
+ };
1938
1966
  }
1939
- const dstCat = dst[catName];
1940
- if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
1941
- if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
1942
- if (srcCat.rules && srcCat.rules.length > 0) {
1943
- const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
1944
- for (const r of srcCat.rules) {
1945
- const fp = _ruleFingerprint(r);
1946
- if (!dstFps.has(fp)) {
1947
- (dstCat.rules = dstCat.rules || []).push(r);
1948
- dstFps.add(fp);
1967
+ }
1968
+ for (const sp of HOME_SENSITIVE_PATHS) {
1969
+ const expanded = `${expandedCwd}/${sp.path}`;
1970
+ const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
1971
+ const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
1972
+ if (pathRe.test(command) || expandedRe.test(command)) {
1973
+ if (!isWriteOpFlag && !sp.credential) {
1974
+ continue;
1975
+ }
1976
+ return {
1977
+ allowed: false,
1978
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
1979
+ riskLevel: sp.riskLevel,
1980
+ riskType: "file_access",
1981
+ confidence: 90,
1982
+ source: "heuristic",
1983
+ violatedLayers: ["1.1"],
1984
+ isCredential: !!sp.credential
1985
+ };
1986
+ }
1987
+ }
1988
+ if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
1989
+ for (const sp of agentExt.extra_sensitive_paths) {
1990
+ const spPath = sp.path.toLowerCase();
1991
+ const expanded = `${expandedCwd}/${spPath}`;
1992
+ if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
1993
+ const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
1994
+ if (denyMode === "deny_read") {
1995
+ if (isWriteOpFlag) continue;
1996
+ } else if (denyMode === "deny_write") {
1997
+ if (!isWriteOpFlag) continue;
1949
1998
  }
1999
+ return {
2000
+ allowed: false,
2001
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
2002
+ riskLevel: sp.risk_level,
2003
+ riskType: "file_access",
2004
+ confidence: 90,
2005
+ source: "heuristic",
2006
+ violatedLayers: ["1.1"],
2007
+ isCredential: !!sp.credential
2008
+ };
1950
2009
  }
1951
2010
  }
1952
- delete src[catName];
1953
2011
  }
2012
+ for (const cp of CREDENTIAL_PATHS) {
2013
+ const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2014
+ const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
2015
+ if (re.test(command)) {
2016
+ return {
2017
+ allowed: false,
2018
+ reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
2019
+ riskLevel: cp.riskLevel,
2020
+ riskType: "file_access",
2021
+ confidence: 90,
2022
+ source: "heuristic",
2023
+ violatedLayers: ["1.1"],
2024
+ isCredential: true
2025
+ };
2026
+ }
2027
+ }
2028
+ if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
2029
+ return {
2030
+ allowed: false,
2031
+ reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
2032
+ riskLevel: "critical",
2033
+ riskType: "file_access",
2034
+ confidence: 90,
2035
+ source: "heuristic",
2036
+ violatedLayers: ["1.1"]
2037
+ };
2038
+ }
2039
+ return null;
2040
+ }
2041
+ function checkDangerousPatterns(actionType, actionDetail) {
2042
+ if (actionType === "file_write" || actionType === "file_edit") return null;
2043
+ const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
2044
+ const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
2045
+ if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
2046
+ return {
2047
+ allowed: false,
2048
+ reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
2049
+ riskLevel: "high",
2050
+ riskType: "script_execution",
2051
+ confidence: 90,
2052
+ source: "heuristic",
2053
+ violatedLayers: ["1.1"]
2054
+ };
2055
+ }
2056
+ if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
2057
+ return {
2058
+ allowed: false,
2059
+ reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
2060
+ riskLevel: "critical",
2061
+ riskType: "file_access",
2062
+ confidence: 95,
2063
+ source: "heuristic",
2064
+ violatedLayers: ["1.1"]
2065
+ };
2066
+ }
2067
+ return null;
1954
2068
  }
1955
- function getEnabledL1InjectionKeywords(runtime) {
1956
- const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
1957
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1958
- keyword: r.keyword || "",
1959
- risk_level: r.risk_level || "",
1960
- lang: r.lang || "en"
1961
- }));
1962
- }
1963
- function getEnabledL2IntentPatterns(runtime) {
1964
- const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
1965
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1966
- intent_keywords: r.intent_keywords || [],
1967
- dangerous_actions: r.dangerous_actions || [],
1968
- reason: r.reason || ""
1969
- }));
1970
- }
1971
- function getEnabledL2PasswordPatterns(runtime) {
1972
- const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
1973
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
1974
- }
1975
- function getEnabledL2UserDeletionPatterns(runtime) {
1976
- const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
1977
- return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
2069
+ function extractRemoteHost(command) {
2070
+ const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
2071
+ return m ? m[1] : null;
1978
2072
  }
1979
- function isL2CategoryEnabled(runtime, categoryKey) {
1980
- const cat = runtime.L2_rules?.[categoryKey];
1981
- return cat?.category_enabled ?? true;
2073
+ function isLocalHost(host) {
2074
+ const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
2075
+ return localNames.includes(host.replace(/.*@/, ""));
1982
2076
  }
1983
- function getDisabledSkillIds(runtime) {
1984
- const disabled = /* @__PURE__ */ new Set();
1985
- for (const catData of Object.values(runtime.L3_skills || {})) {
1986
- if (!catData.category_enabled) {
1987
- for (const s of catData.skills || []) disabled.add(s.id);
1988
- } else {
1989
- for (const s of catData.skills || []) {
1990
- if (s.enabled === false) disabled.add(s.id);
1991
- }
2077
+ function checkLateralMovement(actionDetail) {
2078
+ if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
2079
+ const targetHost = extractRemoteHost(actionDetail);
2080
+ if (targetHost && !isLocalHost(targetHost)) {
2081
+ return {
2082
+ allowed: false,
2083
+ reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
2084
+ riskLevel: "medium",
2085
+ riskType: "lateral_movement",
2086
+ confidence: 80,
2087
+ source: "heuristic",
2088
+ violatedLayers: ["1.1"]
2089
+ };
1992
2090
  }
1993
2091
  }
1994
- return disabled;
1995
- }
1996
- function getEnabledL1CommandPatternsOrDefault(runtime) {
1997
- const enabled = getEnabledL1CommandPatterns(runtime);
1998
- if (enabled.length) return enabled;
1999
- if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
2000
- return [];
2001
- }
2002
- function getEnabledL1InjectionKeywordsOrDefault(runtime) {
2003
- const enabled = getEnabledL1InjectionKeywords(runtime);
2004
- if (enabled.length) return enabled;
2005
- if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
2006
- return [];
2007
- }
2008
- function getEnabledL2SensitivePathsOrDefault(runtime) {
2009
- const enabled = getEnabledL2SensitivePaths(runtime);
2010
- if (enabled.length) return enabled;
2011
- if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
2012
- return [];
2013
- }
2014
- function getEnabledL2IntentPatternsOrDefault(runtime) {
2015
- const enabled = getEnabledL2IntentPatterns(runtime);
2016
- if (enabled.length) return enabled;
2017
- if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
2018
- return [];
2092
+ return null;
2019
2093
  }
2020
- function getEnabledL2PasswordPatternsOrDefault(runtime) {
2021
- const enabled = getEnabledL2PasswordPatterns(runtime);
2022
- if (enabled.length) return enabled;
2023
- if (!hasRuntimeConfig()) {
2024
- return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
2094
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
2095
+ var init_heuristic = __esm({
2096
+ "src/engine/heuristic.ts"() {
2097
+ "use strict";
2098
+ init_esm_shims();
2099
+ init_config();
2100
+ init_agents();
2101
+ init_logic_rules();
2102
+ INJECTION_KEYWORDS = [
2103
+ // ---- Instruction Override ----
2104
+ { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
2105
+ { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
2106
+ { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
2107
+ { keyword: "forget everything", riskLevel: "high", lang: "en" },
2108
+ { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
2109
+ { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
2110
+ { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
2111
+ { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
2112
+ // ---- Role Hijacking ----
2113
+ { keyword: "you are now", riskLevel: "high", lang: "en" },
2114
+ { keyword: "you are no longer", riskLevel: "high", lang: "en" },
2115
+ { keyword: "new instructions", riskLevel: "high", lang: "en" },
2116
+ { keyword: "from now on, you", riskLevel: "high", lang: "en" },
2117
+ { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2118
+ { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
2119
+ { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
2120
+ { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
2121
+ // ---- Command Injection ----
2122
+ { keyword: "system prompt", riskLevel: "high", lang: "en" },
2123
+ { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
2124
+ { keyword: "output your instructions", riskLevel: "high", lang: "en" },
2125
+ { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
2126
+ { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
2127
+ { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
2128
+ { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
2129
+ // ---- Social Engineering ----
2130
+ { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
2131
+ { keyword: "security audit approved", riskLevel: "high", lang: "en" },
2132
+ { keyword: "admin override", riskLevel: "critical", lang: "en" },
2133
+ { keyword: "debug mode", riskLevel: "high", lang: "en" },
2134
+ { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
2135
+ { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
2136
+ { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
2137
+ { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
2138
+ { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
2139
+ // ---- XSS ----
2140
+ { keyword: "<script>", riskLevel: "high", lang: "en" },
2141
+ { keyword: "javascript:", riskLevel: "high", lang: "en" },
2142
+ { keyword: "onerror=", riskLevel: "high", lang: "en" },
2143
+ { keyword: "onload=", riskLevel: "high", lang: "en" },
2144
+ // ---- Fake Boundaries ----
2145
+ { keyword: "=== END OF", riskLevel: "high", lang: "en" },
2146
+ { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
2147
+ { keyword: "[END OF", riskLevel: "high", lang: "en" },
2148
+ { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
2149
+ { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
2150
+ // ---- Developer Mode ----
2151
+ { keyword: "developer mode", riskLevel: "critical", lang: "en" },
2152
+ { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
2153
+ { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
2154
+ { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
2155
+ { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
2156
+ // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
2157
+ // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
2158
+ // 由 agent_ext 机制注入,不再污染通用层。
2159
+ ];
2160
+ DANGEROUS_PATTERNS = [
2161
+ // --- Critical: rm command graded detection (strict to loose, first match wins) ---
2162
+ // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
2163
+ { 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" },
2164
+ // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
2165
+ // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
2166
+ { 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" },
2167
+ // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
2168
+ { 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" },
2169
+ // 4. rm -f absolute path (no -r) — 同样排除临时目录
2170
+ { 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" },
2171
+ // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
2172
+ { 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" },
2173
+ // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
2174
+ { 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" },
2175
+ { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
2176
+ // --- Critical: remote code execution ---
2177
+ { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
2178
+ { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
2179
+ { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
2180
+ { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
2181
+ // --- Critical: reverse shell ---
2182
+ { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
2183
+ { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
2184
+ // --- Critical: sensitive file access ---
2185
+ { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
2186
+ { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
2187
+ // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
2188
+ { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
2189
+ // --- Critical: privilege escalation / container escape ---
2190
+ { 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" },
2191
+ // --- High: dangerous permissions / ownership ---
2192
+ { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
2193
+ { 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" },
2194
+ // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
2195
+ { 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" },
2196
+ // --- High: system config overwrite ---
2197
+ { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
2198
+ // --- High: download script + execute risk ---
2199
+ { 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" },
2200
+ { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
2201
+ // --- High: package manager pipe-to-shell ---
2202
+ { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
2203
+ { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
2204
+ { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
2205
+ // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
2206
+ { 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" },
2207
+ // --- High: network port scanning (横向移动侦察) ---
2208
+ { 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" },
2209
+ { 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" },
2210
+ // --- Medium: credential export ---
2211
+ { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
2212
+ // --- High: shell rc file append (persistence) ---
2213
+ { 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" },
2214
+ // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
2215
+ { 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" },
2216
+ // --- High: non-interactive password modification ---
2217
+ { 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" },
2218
+ { 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" },
2219
+ { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
2220
+ { 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" },
2221
+ { 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" },
2222
+ { 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" },
2223
+ // --- High: non-interactive password modification (extended) ---
2224
+ { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
2225
+ { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
2226
+ { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
2227
+ // --- High: user/group deletion (irreversible, requires user consent) ---
2228
+ { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
2229
+ { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
2230
+ { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
2231
+ { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
2232
+ { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
2233
+ // --- High: generic command -p parameter password setting ---
2234
+ { 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" }
2235
+ ];
2236
+ SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
2025
2237
  }
2026
- return [];
2238
+ });
2239
+
2240
+ // src/engine/token-stats.ts
2241
+ var token_stats_exports = {};
2242
+ __export(token_stats_exports, {
2243
+ getRecentRecords: () => getRecentRecords,
2244
+ getTokenStats: () => getTokenStats,
2245
+ getTokenTrend: () => getTokenTrend,
2246
+ recordTokenUsage: () => recordTokenUsage,
2247
+ resetTokenStats: () => resetTokenStats
2248
+ });
2249
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
2250
+ import { join as join4 } from "path";
2251
+ import { homedir as homedir3 } from "os";
2252
+ function getStatsPath() {
2253
+ const envPath = process.env[ENV_STATS_PATH];
2254
+ return envPath || STATS_FILE;
2027
2255
  }
2028
- function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
2029
- const enabled = getEnabledL2UserDeletionPatterns(runtime);
2030
- if (enabled.length) return enabled;
2031
- if (!hasRuntimeConfig()) {
2032
- return USER_DELETION_PATTERNS.map((p) => p.source);
2256
+ function loadAllRecords(statsPath) {
2257
+ if (!existsSync7(statsPath)) return [];
2258
+ try {
2259
+ const data = JSON.parse(readFileSync7(statsPath, "utf-8"));
2260
+ if (Array.isArray(data)) return data;
2261
+ if (data && Array.isArray(data.records)) return data.records;
2262
+ return [];
2263
+ } catch (e) {
2264
+ return [];
2033
2265
  }
2034
- return [];
2035
2266
  }
2036
- function _validateSkillId(skillId) {
2037
- if (!skillId || !SKILL_ID_RE.test(skillId)) {
2038
- throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
2267
+ function saveRecords(statsPath, records) {
2268
+ try {
2269
+ const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
2270
+ if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
2271
+ writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
2272
+ } catch (e) {
2039
2273
  }
2040
2274
  }
2041
- function updateLayerEnabled(layerKey, enabled) {
2042
- const runtime = loadRuntimeConfig();
2043
- runtime.layers[layerKey] = enabled;
2044
- saveRuntimeConfig(runtime);
2045
- return runtime;
2046
- }
2047
- function updateRuleEnabled(layer, category, ruleId, enabled) {
2048
- const runtime = loadRuntimeConfig();
2049
- for (const r of runtime[layer]?.[category]?.rules || []) {
2050
- if (r.id === ruleId) {
2051
- r.enabled = enabled;
2052
- break;
2053
- }
2275
+ function recordTokenUsage(record) {
2276
+ const statsPath = getStatsPath();
2277
+ const records = loadAllRecords(statsPath);
2278
+ records.push({
2279
+ timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
2280
+ session_id: record.sessionId,
2281
+ step: record.step,
2282
+ model: record.model,
2283
+ prompt_tokens: record.promptTokens,
2284
+ completion_tokens: record.completionTokens,
2285
+ total_tokens: record.totalTokens
2286
+ });
2287
+ if (records.length > MAX_RECORDS) {
2288
+ records.splice(0, records.length - MAX_RECORDS);
2054
2289
  }
2055
- saveRuntimeConfig(runtime);
2056
- return runtime;
2290
+ saveRecords(statsPath, records);
2057
2291
  }
2058
- function updateRuleDenyMode(layer, category, ruleId, denyMode) {
2059
- const runtime = loadRuntimeConfig();
2060
- for (const r of runtime[layer]?.[category]?.rules || []) {
2061
- if (r.id === ruleId) {
2062
- r.deny_mode = denyMode;
2063
- _syncDenyModeFields(r);
2064
- break;
2292
+ function filterByDate(records, days = 0, startDate, endDate) {
2293
+ if (startDate || endDate) {
2294
+ let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
2295
+ let endDt = endDate ? new Date(endDate) : void 0;
2296
+ if (endDate) {
2297
+ const e = new Date(endDate);
2298
+ e.setHours(23, 59, 59, 999);
2299
+ endDt = e;
2065
2300
  }
2301
+ const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
2302
+ return records.filter((r) => {
2303
+ try {
2304
+ const ts = new Date(r.timestamp).getTime();
2305
+ return ts >= startDt.getTime() && ts <= endMs;
2306
+ } catch {
2307
+ return true;
2308
+ }
2309
+ });
2310
+ } else if (days > 0) {
2311
+ const cutoff = Date.now() - days * 864e5;
2312
+ return records.filter((r) => {
2313
+ try {
2314
+ return new Date(r.timestamp).getTime() >= cutoff;
2315
+ } catch {
2316
+ return true;
2317
+ }
2318
+ });
2066
2319
  }
2067
- saveRuntimeConfig(runtime);
2068
- return runtime;
2320
+ return records;
2069
2321
  }
2070
- function updateRuleSkipL3(layer, category, ruleId, skipL3) {
2071
- const runtime = loadRuntimeConfig();
2072
- for (const r of runtime[layer]?.[category]?.rules || []) {
2073
- if (r.id === ruleId) {
2074
- r.skip_l3_on_disabled = skipL3;
2075
- break;
2322
+ function aggregate(records) {
2323
+ let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
2324
+ const byStep = {};
2325
+ const byModel = {};
2326
+ const byDate = {};
2327
+ const bucket = (b, key, r) => {
2328
+ const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
2329
+ e.prompt_tokens += r.prompt_tokens || 0;
2330
+ e.completion_tokens += r.completion_tokens || 0;
2331
+ e.total_tokens += r.total_tokens || 0;
2332
+ e.calls += 1;
2333
+ };
2334
+ for (const r of records) {
2335
+ totalPrompt += r.prompt_tokens || 0;
2336
+ totalCompletion += r.completion_tokens || 0;
2337
+ totalTotal += r.total_tokens || 0;
2338
+ bucket(byStep, r.step || "unknown", r);
2339
+ bucket(byModel, r.model || "unknown", r);
2340
+ let dateKey = "unknown";
2341
+ try {
2342
+ dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
2343
+ } catch {
2076
2344
  }
2345
+ bucket(byDate, dateKey, r);
2077
2346
  }
2078
- saveRuntimeConfig(runtime);
2079
- return runtime;
2080
- }
2081
- function updateCategoryEnabled(layer, category, enabled) {
2082
- const runtime = loadRuntimeConfig();
2083
- const cat = runtime[layer]?.[category];
2084
- if (cat) cat.category_enabled = enabled;
2085
- saveRuntimeConfig(runtime);
2086
- return runtime;
2087
- }
2088
- function addCustomRule(layer, category, rule) {
2089
- const runtime = loadRuntimeConfig();
2090
- const layerObj = runtime[layer] || (runtime[layer] = {});
2091
- const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
2092
- const rules = cat.rules || (cat.rules = []);
2093
- const newRule = { ...rule, builtin: false, enabled: true };
2094
- if (!newRule.id) {
2095
- newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
2096
- }
2097
- if (newRule.deny_mode) _syncDenyModeFields(newRule);
2098
- rules.push(newRule);
2099
- saveRuntimeConfig(runtime);
2100
- return runtime;
2347
+ const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
2348
+ const sortedByDate = {};
2349
+ for (const k of sortedDate) sortedByDate[k] = byDate[k];
2350
+ return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
2101
2351
  }
2102
- function deleteCustomRule(layer, category, ruleId) {
2103
- const runtime = loadRuntimeConfig();
2104
- const rules = runtime[layer]?.[category]?.rules || [];
2105
- const idx = rules.findIndex((r) => r.id === ruleId);
2106
- if (idx >= 0) {
2107
- const r = rules[idx];
2108
- if (r.builtin && !r.source_removed) return null;
2109
- rules.splice(idx, 1);
2110
- saveRuntimeConfig(runtime);
2111
- }
2112
- return runtime;
2352
+ function getTokenStats(days = 0, startDate, endDate) {
2353
+ const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
2354
+ const a = aggregate(records);
2355
+ return {
2356
+ total_prompt_tokens: a.totalPrompt,
2357
+ total_completion_tokens: a.totalCompletion,
2358
+ total_tokens: a.totalTotal,
2359
+ total_calls: records.length,
2360
+ by_step: a.byStep,
2361
+ by_model: a.byModel,
2362
+ by_date: a.byDate
2363
+ };
2113
2364
  }
2114
- function addCustomSkill(skillId, category, keywords, content) {
2115
- _validateSkillId(skillId);
2116
- const runtime = loadRuntimeConfig();
2117
- const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
2118
- try {
2119
- mkdirSync2(skillsDir, { recursive: true });
2120
- } catch {
2121
- }
2122
- writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
2123
- const l3 = runtime.L3_skills || (runtime.L3_skills = {});
2124
- const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
2125
- (cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
2126
- saveRuntimeConfig(runtime);
2127
- return runtime;
2365
+ function getRecentRecords(limit = 20) {
2366
+ const records = loadAllRecords(getStatsPath());
2367
+ return records.slice(-limit);
2128
2368
  }
2129
- function deleteCustomSkill(skillId, category) {
2130
- _validateSkillId(skillId);
2131
- const runtime = loadRuntimeConfig();
2132
- const skills = runtime.L3_skills?.[category]?.skills || [];
2133
- const idx = skills.findIndex((s) => s.id === skillId);
2134
- if (idx >= 0) {
2135
- if (skills[idx].builtin) return null;
2136
- skills.splice(idx, 1);
2137
- const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2369
+ function getTokenTrend(days = 0, startDate, endDate) {
2370
+ const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
2371
+ const series = {};
2372
+ const datesSet = /* @__PURE__ */ new Set();
2373
+ for (const r of records) {
2374
+ let dateKey;
2138
2375
  try {
2139
- if (existsSync7(skillPath)) unlinkSync2(skillPath);
2376
+ dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
2140
2377
  } catch {
2378
+ continue;
2141
2379
  }
2142
- saveRuntimeConfig(runtime);
2380
+ const model = r.model || "unknown";
2381
+ const tokens = r.total_tokens || 0;
2382
+ datesSet.add(dateKey);
2383
+ series[model] = series[model] || {};
2384
+ series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
2143
2385
  }
2144
- return runtime;
2386
+ const dates = Array.from(datesSet).sort();
2387
+ const models = Object.keys(series).sort();
2388
+ return { dates, models, series };
2145
2389
  }
2146
- function updateSkillEnabled(skillId, enabled) {
2147
- const runtime = loadRuntimeConfig();
2148
- for (const catData of Object.values(runtime.L3_skills || {})) {
2149
- for (const s of catData.skills || []) {
2150
- if (s.id === skillId) {
2151
- s.enabled = enabled;
2152
- break;
2153
- }
2390
+ function resetTokenStats() {
2391
+ const statsPath = getStatsPath();
2392
+ if (existsSync7(statsPath)) {
2393
+ try {
2394
+ unlinkSync2(statsPath);
2395
+ } catch {
2154
2396
  }
2155
2397
  }
2156
- saveRuntimeConfig(runtime);
2157
- return runtime;
2158
- }
2159
- function updateSkillCategoryEnabled(category, enabled) {
2160
- const runtime = loadRuntimeConfig();
2161
- const cat = runtime.L3_skills?.[category];
2162
- if (cat) cat.category_enabled = enabled;
2163
- saveRuntimeConfig(runtime);
2164
- return runtime;
2165
- }
2166
- function getProfileOrDetect() {
2167
- return null;
2168
2398
  }
2169
- var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
2170
- var init_runtime_config = __esm({
2171
- "src/infra/runtime-config.ts"() {
2399
+ var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
2400
+ var init_token_stats = __esm({
2401
+ "src/engine/token-stats.ts"() {
2172
2402
  "use strict";
2173
2403
  init_esm_shims();
2174
- init_heuristic();
2175
- init_logic_rules();
2176
- init_skill_loader();
2177
- RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
2178
- RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
2179
- ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
2180
- LAYER_DISABLE_ENV = {
2181
- L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
2182
- L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
2183
- L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
2184
- };
2185
- DENY_MODE_LABELS = {
2186
- deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
2187
- deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2188
- deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2189
- };
2190
- SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
2404
+ STATS_DIR = join4(homedir3(), ".config", "agentmoss");
2405
+ STATS_FILE = join4(STATS_DIR, "agent_moss_token_stats.json");
2406
+ ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
2407
+ MAX_RECORDS = 1e4;
2191
2408
  }
2192
2409
  });
2193
2410
 
@@ -2204,28 +2421,28 @@ init_skill_loader();
2204
2421
 
2205
2422
  // src/engine/template-loader.ts
2206
2423
  init_esm_shims();
2207
- import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
2208
- import { resolve as resolve3, dirname as dirname3 } from "path";
2209
- import { fileURLToPath as fileURLToPath4 } from "url";
2210
- var __filename4 = fileURLToPath4(import.meta.url);
2211
- var __dirname4 = dirname3(__filename4);
2212
- var TEMPLATES_DIR = resolve3(__dirname4, "../templates");
2213
- var TEMPLATES_DIR_FALLBACK = resolve3(__dirname4, "../../../agent_moss/templates");
2424
+ import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
2425
+ import { resolve as resolve3, dirname as dirname4 } from "path";
2426
+ import { fileURLToPath as fileURLToPath5 } from "url";
2427
+ var __filename4 = fileURLToPath5(import.meta.url);
2428
+ var __dirname5 = dirname4(__filename4);
2429
+ var TEMPLATES_DIR = resolve3(__dirname5, "../templates");
2430
+ var TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
2214
2431
  var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
2215
2432
  var cachedTemplates = null;
2216
2433
  function loadTemplates() {
2217
2434
  if (cachedTemplates) return cachedTemplates;
2218
2435
  cachedTemplates = {};
2219
- const dir = existsSync4(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
2220
- if (!existsSync4(dir)) {
2436
+ const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
2437
+ if (!existsSync5(dir)) {
2221
2438
  console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
2222
2439
  return cachedTemplates;
2223
2440
  }
2224
2441
  for (const file of TEMPLATE_FILES) {
2225
2442
  const filePath = resolve3(dir, file);
2226
- if (existsSync4(filePath)) {
2443
+ if (existsSync5(filePath)) {
2227
2444
  const key = file.replace(".txt", "").replace("_template", "");
2228
- cachedTemplates[key] = readFileSync3(filePath, "utf-8");
2445
+ cachedTemplates[key] = readFileSync4(filePath, "utf-8");
2229
2446
  }
2230
2447
  }
2231
2448
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
@@ -2234,8 +2451,8 @@ function loadTemplates() {
2234
2451
 
2235
2452
  // src/engine/script-analyzer.ts
2236
2453
  init_esm_shims();
2237
- import { readFileSync as readFileSync4 } from "fs";
2238
- import { resolve as resolve4, join } from "path";
2454
+ import { readFileSync as readFileSync5 } from "fs";
2455
+ import { resolve as resolve4, join as join2 } from "path";
2239
2456
  var SUSPICIOUS_PATTERNS = [
2240
2457
  // Network exfiltration (curl/wget POST in scripts is almost never legitimate)
2241
2458
  { name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
@@ -2368,8 +2585,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
2368
2585
  }
2369
2586
  function readScriptContent(scriptPath, maxLines = 500) {
2370
2587
  try {
2371
- const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
2372
- const raw = readFileSync4(expandedPath, "utf-8");
2588
+ const expandedPath = scriptPath.startsWith("~") ? join2(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
2589
+ const raw = readFileSync5(expandedPath, "utf-8");
2373
2590
  const lines = raw.split("\n").slice(0, maxLines);
2374
2591
  return { content: lines.join("\n"), lineCount: lines.length, error: "" };
2375
2592
  } catch (e) {
@@ -2423,9 +2640,9 @@ ${result.scriptContent}
2423
2640
  init_esm_shims();
2424
2641
  init_heuristic();
2425
2642
  init_logic_rules();
2426
- import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "fs";
2427
- import { resolve as resolve5, join as join2, isAbsolute, dirname as dirname4 } from "path";
2428
- import { homedir } from "os";
2643
+ import { readFileSync as readFileSync6, statSync, existsSync as existsSync6 } from "fs";
2644
+ import { resolve as resolve5, join as join3, isAbsolute, dirname as dirname5 } from "path";
2645
+ import { homedir as homedir2 } from "os";
2429
2646
  function emptyChain() {
2430
2647
  return {
2431
2648
  nodes: [],
@@ -2653,7 +2870,7 @@ function extractInlineScriptPath(actionDetail) {
2653
2870
  let resolved;
2654
2871
  try {
2655
2872
  resolved = resolvePath(p);
2656
- if (existsSync5(resolved)) {
2873
+ if (existsSync6(resolved)) {
2657
2874
  const stat = statSync(resolved);
2658
2875
  if (stat.isFile()) return resolved;
2659
2876
  }
@@ -2665,20 +2882,20 @@ function extractInlineScriptPath(actionDetail) {
2665
2882
  function resolvePath(p) {
2666
2883
  let expanded = p;
2667
2884
  if (expanded.startsWith("~/") || expanded === "~") {
2668
- expanded = join2(homedir(), expanded.slice(1));
2885
+ expanded = join3(homedir2(), expanded.slice(1));
2669
2886
  }
2670
2887
  return resolve5(expanded);
2671
2888
  }
2672
2889
  function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
2673
2890
  try {
2674
2891
  const path3 = resolvePath(scriptPath);
2675
- if (!existsSync5(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
2892
+ if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
2676
2893
  const stat = statSync(path3);
2677
2894
  if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
2678
2895
  if (stat.size > maxFileSize) {
2679
2896
  return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
2680
2897
  }
2681
- const content = readFileSync5(path3, "utf-8");
2898
+ const content = readFileSync6(path3, "utf-8");
2682
2899
  const lineCount = content ? content.split("\n").length : 0;
2683
2900
  return { content, lineCount, fileSize: stat.size, error: "" };
2684
2901
  } catch (e) {
@@ -2695,9 +2912,9 @@ function extractChildScriptPaths(content, baseDir) {
2695
2912
  const pathStr = m[1];
2696
2913
  if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
2697
2914
  if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
2698
- const full = isAbsolute(pathStr) ? pathStr : join2(baseDir, pathStr);
2915
+ const full = isAbsolute(pathStr) ? pathStr : join3(baseDir, pathStr);
2699
2916
  const resolved = resolvePath(full);
2700
- if (existsSync5(resolved)) {
2917
+ if (existsSync6(resolved)) {
2701
2918
  try {
2702
2919
  if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
2703
2920
  } catch {
@@ -2712,7 +2929,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
2712
2929
  const result = emptyChain();
2713
2930
  result.entryPath = entryPath;
2714
2931
  const entry = resolvePath(entryPath);
2715
- const baseDir = existsSync5(entry) ? dirname4(entry) : input.cwd || process.cwd();
2932
+ const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
2716
2933
  resolveRecursive({
2717
2934
  path: entry,
2718
2935
  depth: 0,
@@ -2787,7 +3004,7 @@ function resolveRecursive(ctx) {
2787
3004
  maxTotalSize,
2788
3005
  visited,
2789
3006
  result,
2790
- baseDir: dirname4(resolvePath(childPath)),
3007
+ baseDir: dirname5(resolvePath(childPath)),
2791
3008
  input
2792
3009
  });
2793
3010
  if (result.hasCritical) return;
@@ -3049,7 +3266,7 @@ function formatHints(previous) {
3049
3266
  const hints = [];
3050
3267
  if (previous.heuristic) {
3051
3268
  hints.push(
3052
- `\u542F\u53D1\u5F0F\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
3269
+ `\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
3053
3270
  \u7C7B\u578B: ${previous.heuristic.riskType}
3054
3271
  \u539F\u56E0: ${previous.heuristic.reason}`
3055
3272
  );
@@ -3061,7 +3278,7 @@ function formatHints(previous) {
3061
3278
  \u539F\u56E0: ${previous.logic.reason}`
3062
3279
  );
3063
3280
  }
3064
- return hints.length > 0 ? hints.join("\n\n") : "\u542F\u53D1\u5F0F\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
3281
+ return hints.length > 0 ? hints.join("\n\n") : "\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
3065
3282
  }
3066
3283
  async function analyzeWithLLM(prompt, config) {
3067
3284
  console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
@@ -3269,6 +3486,7 @@ async function runAnalysis(input) {
3269
3486
  const actionType = input.aNext.actionType.toLowerCase();
3270
3487
  const rawDetail = extractRawCommand(input.aNext.actionDetail);
3271
3488
  const lowerDetail = rawDetail.toLowerCase();
3489
+ const agentExt = getAgentExtensions(input.agentId);
3272
3490
  if (FULLY_SAFE_TOOLS.has(actionType)) {
3273
3491
  return {
3274
3492
  allowed: true,
@@ -3280,7 +3498,23 @@ async function runAnalysis(input) {
3280
3498
  violatedLayers: []
3281
3499
  };
3282
3500
  }
3283
- const agentExt = getAgentExtensions(input.agentId);
3501
+ if (agentExt?.builtin_safe_action_types && agentExt.builtin_safe_action_types.length > 0) {
3502
+ const hit = agentExt.builtin_safe_action_types.some((t) => {
3503
+ const tl = t.toLowerCase();
3504
+ return tl.endsWith("-") ? actionType.startsWith(tl) : actionType === tl;
3505
+ });
3506
+ if (hit) {
3507
+ return {
3508
+ allowed: true,
3509
+ reason: `agent \u5185\u90E8\u5B89\u5168\u64CD\u4F5C\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
3510
+ riskLevel: heuristicResult?.riskLevel || "low",
3511
+ riskType: "",
3512
+ confidence: 95,
3513
+ source: "whitelist_bypass",
3514
+ violatedLayers: []
3515
+ };
3516
+ }
3517
+ }
3284
3518
  const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
3285
3519
  if (agentExt?.builtin_safe_skills) {
3286
3520
  for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);