@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.
- package/README.md +3 -3
- package/dist/agents/__init__.py +35 -22
- package/dist/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-312.pyc +0 -0
- package/dist/agents/base.py +5 -0
- package/dist/agents/opendesk/rules.json +29 -0
- package/dist/cli.js +2530 -2255
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.js +1591 -1357
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/server.js +1645 -1370
- package/dist/server.js.map +1 -1
- package/dist/templates/security_judge_template.txt +4 -4
- package/package.json +1 -1
|
@@ -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
|
|
513
|
-
if (
|
|
514
|
-
|
|
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
|
|
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/
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
-
|
|
797
|
-
for (const
|
|
798
|
-
|
|
799
|
-
|
|
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
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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 (
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
|
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
|
|
879
|
-
|
|
880
|
-
return
|
|
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
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
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
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
|
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
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
return
|
|
998
|
-
|
|
999
|
-
|
|
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
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
|
|
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
|
|
1021
|
-
|
|
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
|
|
1025
|
-
|
|
1026
|
-
return localNames.includes(host.replace(/.*@/, ""));
|
|
1029
|
+
function _generatePatternId(pattern) {
|
|
1030
|
+
return `pat_${_slugify(pattern)}`;
|
|
1027
1031
|
}
|
|
1028
|
-
function
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
-
|
|
1046
|
-
|
|
1047
|
-
"
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
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
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
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
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
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
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
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
|
|
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
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
}
|
|
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
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
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
|
-
|
|
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
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
if (
|
|
1347
|
-
|
|
1348
|
-
|
|
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
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
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
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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
|
|
1373
|
-
|
|
1374
|
-
const
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
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
|
|
1420
|
-
const
|
|
1421
|
-
const
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
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
|
|
1431
|
-
const
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
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
|
|
1441
|
-
const
|
|
1442
|
-
if (
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
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
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
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
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
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
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
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
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
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
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
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
|
|
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
|
-
|
|
1558
|
-
|
|
1559
|
-
return
|
|
1525
|
+
runtime.layers[layerKey] = enabled;
|
|
1526
|
+
saveRuntimeConfig(runtime);
|
|
1527
|
+
return runtime;
|
|
1560
1528
|
}
|
|
1561
|
-
function
|
|
1562
|
-
const
|
|
1563
|
-
for (const [
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
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
|
-
|
|
1537
|
+
saveRuntimeConfig(runtime);
|
|
1538
|
+
return runtime;
|
|
1575
1539
|
}
|
|
1576
|
-
function
|
|
1577
|
-
|
|
1578
|
-
|
|
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
|
|
1581
|
-
const
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
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
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
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
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
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
|
|
1612
|
-
|
|
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
|
-
|
|
1601
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
1615
1602
|
} catch {
|
|
1616
|
-
return {};
|
|
1617
1603
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
if (
|
|
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
|
|
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
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
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
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
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
|
|
1721
|
-
return
|
|
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
|
|
1730
|
-
return
|
|
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
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
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
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
enabled
|
|
1764
|
-
|
|
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
|
-
|
|
1780
|
-
return
|
|
1705
|
+
saveRuntimeConfig(runtime);
|
|
1706
|
+
return runtime;
|
|
1781
1707
|
}
|
|
1782
|
-
function
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
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
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
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
|
-
|
|
1834
|
-
|
|
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
|
|
1839
|
-
const
|
|
1840
|
-
const
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
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
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
const
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
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
|
|
1895
|
-
const
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1898
|
-
|
|
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
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
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
|
|
1913
|
-
const
|
|
1914
|
-
|
|
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
|
|
1931
|
-
const
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
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
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
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
|
|
1956
|
-
const
|
|
1957
|
-
return
|
|
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
|
|
1980
|
-
const
|
|
1981
|
-
return
|
|
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
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
if (!
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
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
|
|
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
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
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
|
-
|
|
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
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
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
|
|
2037
|
-
|
|
2038
|
-
|
|
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
|
|
2042
|
-
const
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
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
|
-
|
|
2056
|
-
return runtime;
|
|
2290
|
+
saveRecords(statsPath, records);
|
|
2057
2291
|
}
|
|
2058
|
-
function
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
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
|
-
|
|
2068
|
-
return runtime;
|
|
2320
|
+
return records;
|
|
2069
2321
|
}
|
|
2070
|
-
function
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
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
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
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
|
|
2103
|
-
const
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
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
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
|
2130
|
-
|
|
2131
|
-
const
|
|
2132
|
-
const
|
|
2133
|
-
const
|
|
2134
|
-
|
|
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
|
-
|
|
2376
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2140
2377
|
} catch {
|
|
2378
|
+
continue;
|
|
2141
2379
|
}
|
|
2142
|
-
|
|
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
|
-
|
|
2386
|
+
const dates = Array.from(datesSet).sort();
|
|
2387
|
+
const models = Object.keys(series).sort();
|
|
2388
|
+
return { dates, models, series };
|
|
2145
2389
|
}
|
|
2146
|
-
function
|
|
2147
|
-
const
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
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
|
|
2170
|
-
var
|
|
2171
|
-
"src/
|
|
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
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
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
|
|
2208
|
-
import { resolve as resolve3, dirname as
|
|
2209
|
-
import { fileURLToPath as
|
|
2210
|
-
var __filename4 =
|
|
2211
|
-
var
|
|
2212
|
-
var TEMPLATES_DIR = resolve3(
|
|
2213
|
-
var TEMPLATES_DIR_FALLBACK = resolve3(
|
|
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 =
|
|
2220
|
-
if (!
|
|
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 (
|
|
2443
|
+
if (existsSync5(filePath)) {
|
|
2227
2444
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
2228
|
-
cachedTemplates[key] =
|
|
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
|
|
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("~") ?
|
|
2372
|
-
const raw =
|
|
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
|
|
2427
|
-
import { resolve as resolve5, join as
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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 :
|
|
2915
|
+
const full = isAbsolute(pathStr) ? pathStr : join3(baseDir, pathStr);
|
|
2699
2916
|
const resolved = resolvePath(full);
|
|
2700
|
-
if (
|
|
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 =
|
|
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:
|
|
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
|
-
`\
|
|
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") : "\
|
|
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
|
-
|
|
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);
|