@kenhkl/agent-moss 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
package/dist/server.js
CHANGED
|
@@ -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 __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
6
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
7
|
}) : x)(function(x) {
|
|
@@ -16,6 +18,15 @@ var __export = (target, all) => {
|
|
|
16
18
|
for (var name in all)
|
|
17
19
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
20
|
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
30
|
|
|
20
31
|
// node_modules/tsup/assets/esm_shims.js
|
|
21
32
|
import path from "path";
|
|
@@ -213,56 +224,6 @@ var init_config = __esm({
|
|
|
213
224
|
}
|
|
214
225
|
});
|
|
215
226
|
|
|
216
|
-
// src/agents/index.ts
|
|
217
|
-
import { readFileSync, existsSync } from "fs";
|
|
218
|
-
import { resolve, dirname } from "path";
|
|
219
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
220
|
-
function resolveAgentsDir() {
|
|
221
|
-
if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
222
|
-
if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
223
|
-
return AGENTS_DIR_PROD;
|
|
224
|
-
}
|
|
225
|
-
function getAgentExtensions(agentId) {
|
|
226
|
-
if (!agentId) return null;
|
|
227
|
-
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
228
|
-
const agentsDir = resolveAgentsDir();
|
|
229
|
-
const rulesFile = resolve(agentsDir, agentId, "rules.json");
|
|
230
|
-
if (!existsSync(rulesFile)) {
|
|
231
|
-
cache.set(agentId, null);
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
const raw = readFileSync(rulesFile, "utf-8");
|
|
236
|
-
const ext = JSON.parse(raw);
|
|
237
|
-
if (!ext.agent_id) {
|
|
238
|
-
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
239
|
-
cache.set(agentId, null);
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
cache.set(agentId, ext);
|
|
243
|
-
console.log(
|
|
244
|
-
`[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`
|
|
245
|
-
);
|
|
246
|
-
return ext;
|
|
247
|
-
} catch (e) {
|
|
248
|
-
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
249
|
-
cache.set(agentId, null);
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
var __filename2, __dirname2, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
254
|
-
var init_agents = __esm({
|
|
255
|
-
"src/agents/index.ts"() {
|
|
256
|
-
"use strict";
|
|
257
|
-
init_esm_shims();
|
|
258
|
-
__filename2 = fileURLToPath2(import.meta.url);
|
|
259
|
-
__dirname2 = dirname(__filename2);
|
|
260
|
-
AGENTS_DIR_PROD = resolve(__dirname2, "agents");
|
|
261
|
-
AGENTS_DIR_DEV_FALLBACK = resolve(__dirname2, "../../../agent_moss/agents");
|
|
262
|
-
cache = /* @__PURE__ */ new Map();
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
227
|
// src/engine/inline-analyzer.ts
|
|
267
228
|
function isLiteralTextCommand(command) {
|
|
268
229
|
ECHO_CMD_RE.lastIndex = 0;
|
|
@@ -519,9 +480,12 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
519
480
|
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
520
481
|
let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
|
|
521
482
|
if (!isWrite && actionDetail.includes(">")) {
|
|
522
|
-
const
|
|
523
|
-
if (
|
|
524
|
-
|
|
483
|
+
const detailWithoutDevnull = actionDetail.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
484
|
+
if (detailWithoutDevnull.includes(">")) {
|
|
485
|
+
const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
|
|
486
|
+
if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
|
|
487
|
+
isWrite = true;
|
|
488
|
+
}
|
|
525
489
|
}
|
|
526
490
|
}
|
|
527
491
|
if (!isWrite) return null;
|
|
@@ -689,7 +653,7 @@ var init_logic_rules = __esm({
|
|
|
689
653
|
ROOT_SENSITIVE_PATHS = [
|
|
690
654
|
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
691
655
|
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
692
|
-
// 注意:/etc/passwd 仅从 L1
|
|
656
|
+
// 注意:/etc/passwd 仅从 L1 特征匹配规则移除(公开可读),L2 敏感路径仍保留
|
|
693
657
|
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
694
658
|
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
695
659
|
// 系统配置(仅拦截写入)
|
|
@@ -781,1423 +745,1676 @@ var init_logic_rules = __esm({
|
|
|
781
745
|
}
|
|
782
746
|
});
|
|
783
747
|
|
|
784
|
-
// src/engine/
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
if (rule.action === "Deny") {
|
|
796
|
-
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
797
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
798
|
-
highestSeverity = rule.severity;
|
|
799
|
-
highestRiskType = "custom_rule";
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
} catch {
|
|
804
|
-
}
|
|
748
|
+
// src/engine/skill-loader.ts
|
|
749
|
+
import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
750
|
+
import { resolve, dirname } from "path";
|
|
751
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
752
|
+
function loadAllSkills() {
|
|
753
|
+
if (cachedSkills) return cachedSkills;
|
|
754
|
+
cachedSkills = {};
|
|
755
|
+
const dir = existsSync2(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
756
|
+
if (!existsSync2(dir)) {
|
|
757
|
+
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
758
|
+
return cachedSkills;
|
|
805
759
|
}
|
|
806
|
-
|
|
807
|
-
for (const
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
811
|
-
highestSeverity = rule.severity;
|
|
812
|
-
topRiskType = rule.riskType;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
760
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
761
|
+
for (const f of files) {
|
|
762
|
+
const name = f.replace(".md", "");
|
|
763
|
+
cachedSkills[name] = readFileSync(resolve(dir, f), "utf-8");
|
|
815
764
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
765
|
+
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
766
|
+
return cachedSkills;
|
|
767
|
+
}
|
|
768
|
+
function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
769
|
+
const skills = loadAllSkills();
|
|
770
|
+
const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
|
|
771
|
+
const actionDetailLower = actionDetail.toLowerCase();
|
|
772
|
+
const actionTypeLower = actionType.toLowerCase();
|
|
773
|
+
const reasonLower = reason.toLowerCase();
|
|
774
|
+
const scores = {};
|
|
775
|
+
for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
|
|
776
|
+
let score = 0;
|
|
777
|
+
for (const kw of keywords) {
|
|
778
|
+
if (combined.includes(kw)) {
|
|
779
|
+
if (kw === actionTypeLower) score += 2;
|
|
780
|
+
else if (actionDetailLower.includes(kw)) score += 1.5;
|
|
781
|
+
else if (reasonLower.includes(kw)) score += 1;
|
|
782
|
+
else score += 0.5;
|
|
830
783
|
}
|
|
831
784
|
}
|
|
785
|
+
if (score > 0) scores[skillName] = score;
|
|
832
786
|
}
|
|
833
|
-
const
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
846
|
-
if (!isExcludedType) {
|
|
847
|
-
const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
848
|
-
if (pathResult) return pathResult;
|
|
849
|
-
}
|
|
850
|
-
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
851
|
-
if (dangerousResult) return dangerousResult;
|
|
852
|
-
const lateralResult = checkLateralMovement(detailL);
|
|
853
|
-
if (lateralResult) return lateralResult;
|
|
854
|
-
if (matched.length > 0) {
|
|
855
|
-
return {
|
|
856
|
-
allowed: false,
|
|
857
|
-
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
858
|
-
riskLevel: highestSeverity,
|
|
859
|
-
riskType: highestRiskType,
|
|
860
|
-
confidence: 95,
|
|
861
|
-
source: "heuristic",
|
|
862
|
-
violatedLayers: ["1.1"]
|
|
863
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
return null;
|
|
867
|
-
}
|
|
868
|
-
function checkInjectionKeywords(text, extraKeywords) {
|
|
869
|
-
const hits = [];
|
|
870
|
-
for (const entry of INJECTION_KEYWORDS) {
|
|
871
|
-
if (text.includes(entry.keyword.toLowerCase())) {
|
|
872
|
-
hits.push(entry);
|
|
787
|
+
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
788
|
+
const top = sorted.slice(0, maxSkills);
|
|
789
|
+
const maxScore = top[0]?.[1] || 1;
|
|
790
|
+
const results = [];
|
|
791
|
+
for (const [name, score] of top) {
|
|
792
|
+
const content = skills[name];
|
|
793
|
+
if (content) {
|
|
794
|
+
results.push({
|
|
795
|
+
skillName: name,
|
|
796
|
+
relevance: Math.min(score / maxScore, 1),
|
|
797
|
+
skillContent: content
|
|
798
|
+
});
|
|
873
799
|
}
|
|
874
800
|
}
|
|
875
|
-
if (
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
801
|
+
if (results.length === 0) {
|
|
802
|
+
const fallback = skills["general_tool_risk_guard"];
|
|
803
|
+
if (fallback) {
|
|
804
|
+
results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
|
|
880
805
|
}
|
|
881
806
|
}
|
|
882
|
-
return
|
|
883
|
-
}
|
|
884
|
-
function isInlineScriptCommand(actionDetail) {
|
|
885
|
-
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;
|
|
886
|
-
return re.test(actionDetail);
|
|
807
|
+
return results;
|
|
887
808
|
}
|
|
888
|
-
function
|
|
889
|
-
|
|
890
|
-
return
|
|
809
|
+
function formatSkillsForPrompt(skills) {
|
|
810
|
+
if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
|
|
811
|
+
return skills.map(
|
|
812
|
+
(s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
|
|
813
|
+
|
|
814
|
+
${s.skillContent}`
|
|
815
|
+
).join("\n\n---\n\n");
|
|
891
816
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
920
|
-
riskLevel: sp.riskLevel,
|
|
921
|
-
riskType: "file_access",
|
|
922
|
-
confidence: 90,
|
|
923
|
-
source: "heuristic",
|
|
924
|
-
violatedLayers: ["1.1"],
|
|
925
|
-
isCredential: !!sp.credential
|
|
926
|
-
};
|
|
927
|
-
}
|
|
817
|
+
var __filename2, __dirname2, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
818
|
+
var init_skill_loader = __esm({
|
|
819
|
+
"src/engine/skill-loader.ts"() {
|
|
820
|
+
"use strict";
|
|
821
|
+
init_esm_shims();
|
|
822
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
823
|
+
__dirname2 = dirname(__filename2);
|
|
824
|
+
SKILLS_DIR = resolve(__dirname2, "../skills");
|
|
825
|
+
SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
|
|
826
|
+
SKILL_KEYWORD_MAP = {
|
|
827
|
+
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
828
|
+
"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"],
|
|
829
|
+
"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"],
|
|
830
|
+
"browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
|
|
831
|
+
"email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
|
|
832
|
+
"general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
|
|
833
|
+
"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"],
|
|
834
|
+
"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"],
|
|
835
|
+
// 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
|
|
836
|
+
// 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
|
|
837
|
+
"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"],
|
|
838
|
+
"skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
|
|
839
|
+
"supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
|
|
840
|
+
"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"],
|
|
841
|
+
"indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
|
|
842
|
+
};
|
|
843
|
+
cachedSkills = null;
|
|
928
844
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
// src/infra/runtime-config.ts
|
|
848
|
+
var runtime_config_exports = {};
|
|
849
|
+
__export(runtime_config_exports, {
|
|
850
|
+
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
851
|
+
addAgentRule: () => addAgentRule,
|
|
852
|
+
addCustomRule: () => addCustomRule,
|
|
853
|
+
addCustomSkill: () => addCustomSkill,
|
|
854
|
+
deleteAgentRule: () => deleteAgentRule,
|
|
855
|
+
deleteCustomRule: () => deleteCustomRule,
|
|
856
|
+
deleteCustomSkill: () => deleteCustomSkill,
|
|
857
|
+
filterEnabledRules: () => filterEnabledRules,
|
|
858
|
+
generateSourceDefaults: () => generateSourceDefaults,
|
|
859
|
+
getAgentRules: () => getAgentRules,
|
|
860
|
+
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
861
|
+
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
862
|
+
getEnabledAgentExtensions: () => getEnabledAgentExtensions,
|
|
863
|
+
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
864
|
+
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
865
|
+
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
866
|
+
getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
|
|
867
|
+
getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
|
|
868
|
+
getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
|
|
869
|
+
getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
|
|
870
|
+
getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
|
|
871
|
+
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
872
|
+
getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
|
|
873
|
+
getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
|
|
874
|
+
getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
|
|
875
|
+
getEnvOverrides: () => getEnvOverrides,
|
|
876
|
+
getProfileOrDetect: () => getProfileOrDetect,
|
|
877
|
+
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
878
|
+
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
879
|
+
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
880
|
+
isLayerEnabled: () => isLayerEnabled,
|
|
881
|
+
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
882
|
+
mergeConfig: () => mergeConfig,
|
|
883
|
+
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
884
|
+
updateAgentCategoryEnabled: () => updateAgentCategoryEnabled,
|
|
885
|
+
updateAgentRuleEnabled: () => updateAgentRuleEnabled,
|
|
886
|
+
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
887
|
+
updateLayerEnabled: () => updateLayerEnabled,
|
|
888
|
+
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
889
|
+
updateRuleEnabled: () => updateRuleEnabled,
|
|
890
|
+
updateRuleSkipL3: () => updateRuleSkipL3,
|
|
891
|
+
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
892
|
+
updateSkillEnabled: () => updateSkillEnabled
|
|
893
|
+
});
|
|
894
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
895
|
+
import { join } from "path";
|
|
896
|
+
import { homedir } from "os";
|
|
897
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
898
|
+
import { dirname as dirname2 } from "path";
|
|
899
|
+
function getRuntimeConfigPath() {
|
|
900
|
+
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
901
|
+
if (envPath) return envPath;
|
|
902
|
+
return RUNTIME_CONFIG_PATH;
|
|
903
|
+
}
|
|
904
|
+
function saveRuntimeConfig(config) {
|
|
905
|
+
const p = getRuntimeConfigPath();
|
|
906
|
+
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
907
|
+
if (dir && !existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
908
|
+
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
909
|
+
}
|
|
910
|
+
function emptyRuntimeConfig() {
|
|
911
|
+
return {
|
|
912
|
+
version: 1,
|
|
913
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
914
|
+
L1_rules: {},
|
|
915
|
+
L2_rules: {},
|
|
916
|
+
L3_skills: {},
|
|
917
|
+
agent_rules: {}
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function loadRuntimeConfig() {
|
|
921
|
+
const p = getRuntimeConfigPath();
|
|
922
|
+
if (!existsSync3(p)) {
|
|
923
|
+
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
924
|
+
try {
|
|
925
|
+
saveRuntimeConfig(seeded);
|
|
926
|
+
} catch {
|
|
947
927
|
}
|
|
928
|
+
return seeded;
|
|
948
929
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
source: "heuristic",
|
|
967
|
-
violatedLayers: ["1.1"],
|
|
968
|
-
isCredential: !!sp.credential
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
}
|
|
930
|
+
try {
|
|
931
|
+
const data = JSON.parse(readFileSync2(p, "utf-8"));
|
|
932
|
+
const merged = mergeConfig(data, generateSourceDefaults());
|
|
933
|
+
return {
|
|
934
|
+
version: data.version || 1,
|
|
935
|
+
layers: {
|
|
936
|
+
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
937
|
+
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
938
|
+
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
939
|
+
},
|
|
940
|
+
L1_rules: merged.L1_rules || {},
|
|
941
|
+
L2_rules: merged.L2_rules || {},
|
|
942
|
+
L3_skills: merged.L3_skills || {},
|
|
943
|
+
agent_rules: merged.agent_rules || {}
|
|
944
|
+
};
|
|
945
|
+
} catch {
|
|
946
|
+
return emptyRuntimeConfig();
|
|
972
947
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
948
|
+
}
|
|
949
|
+
function isLayerEnabled(layerKey) {
|
|
950
|
+
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
951
|
+
if (disableKey && process.env[disableKey] === "1") return false;
|
|
952
|
+
const runtime = loadRuntimeConfig();
|
|
953
|
+
const layers = runtime.layers || {};
|
|
954
|
+
if (layerKey in layers) return !!layers[layerKey];
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
function getEnvOverrides() {
|
|
958
|
+
const overrides = {};
|
|
959
|
+
for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
|
|
960
|
+
const val = process.env[envKey];
|
|
961
|
+
if (val !== void 0) {
|
|
962
|
+
overrides[layerKey] = {
|
|
963
|
+
env_var: envKey,
|
|
964
|
+
env_value: val,
|
|
965
|
+
overridden: val === "1",
|
|
966
|
+
effect: val === "1" ? "disabled" : "no_override"
|
|
986
967
|
};
|
|
987
968
|
}
|
|
988
969
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
970
|
+
return overrides;
|
|
971
|
+
}
|
|
972
|
+
function filterEnabledRules(rules, categoryEnabled = true) {
|
|
973
|
+
if (!categoryEnabled) return [];
|
|
974
|
+
return rules.filter((r) => r.enabled !== false);
|
|
975
|
+
}
|
|
976
|
+
function getEnabledL1CommandPatterns(runtime) {
|
|
977
|
+
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
978
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
979
|
+
pattern: r.pattern || "",
|
|
980
|
+
risk_level: r.risk_level || "",
|
|
981
|
+
risk_type: r.risk_type || "",
|
|
982
|
+
reason: r.reason || ""
|
|
983
|
+
}));
|
|
984
|
+
}
|
|
985
|
+
function getEnabledL2SensitivePaths(runtime) {
|
|
986
|
+
let cat = runtime.L1_rules?.sensitive_path_access;
|
|
987
|
+
if (!cat || !cat.rules || cat.rules.length === 0) {
|
|
988
|
+
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
999
989
|
}
|
|
1000
|
-
return
|
|
990
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
991
|
+
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
992
|
+
if (r.credential) out.credential = true;
|
|
993
|
+
if (r.read_only) out.read_only = true;
|
|
994
|
+
return out;
|
|
995
|
+
});
|
|
1001
996
|
}
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
return
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
riskLevel: "high",
|
|
1011
|
-
riskType: "script_execution",
|
|
1012
|
-
confidence: 90,
|
|
1013
|
-
source: "heuristic",
|
|
1014
|
-
violatedLayers: ["1.1"]
|
|
1015
|
-
};
|
|
997
|
+
function hasRuntimeConfig() {
|
|
998
|
+
const p = getRuntimeConfigPath();
|
|
999
|
+
if (!existsSync3(p)) return false;
|
|
1000
|
+
try {
|
|
1001
|
+
JSON.parse(readFileSync2(p, "utf-8"));
|
|
1002
|
+
return true;
|
|
1003
|
+
} catch {
|
|
1004
|
+
return false;
|
|
1016
1005
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
source: "heuristic",
|
|
1025
|
-
violatedLayers: ["1.1"]
|
|
1026
|
-
};
|
|
1006
|
+
}
|
|
1007
|
+
function getDisabledL2RulesWithSkipL3() {
|
|
1008
|
+
let runtime;
|
|
1009
|
+
try {
|
|
1010
|
+
runtime = loadRuntimeConfig();
|
|
1011
|
+
} catch {
|
|
1012
|
+
return {};
|
|
1027
1013
|
}
|
|
1028
|
-
|
|
1014
|
+
const result = {};
|
|
1015
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
1016
|
+
const layer = runtime[layerKey] || {};
|
|
1017
|
+
for (const [catName, cat] of Object.entries(layer)) {
|
|
1018
|
+
if (!cat.category_enabled) continue;
|
|
1019
|
+
const items = [];
|
|
1020
|
+
for (const r of cat.rules || []) {
|
|
1021
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
1022
|
+
if (catName === "sensitive_path_access") {
|
|
1023
|
+
if (r.path) items.push(r.path);
|
|
1024
|
+
} else if (catName === "intent_consistency") {
|
|
1025
|
+
items.push(...r.intent_keywords || []);
|
|
1026
|
+
} else if (r.pattern) {
|
|
1027
|
+
items.push(r.pattern);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (items.length > 0) result[catName] = items;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1029
1035
|
}
|
|
1030
|
-
function
|
|
1031
|
-
|
|
1032
|
-
return m ? m[1] : null;
|
|
1036
|
+
function _slugify(text) {
|
|
1037
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
|
|
1033
1038
|
}
|
|
1034
|
-
function
|
|
1035
|
-
|
|
1036
|
-
return localNames.includes(host.replace(/.*@/, ""));
|
|
1039
|
+
function _generatePatternId(pattern) {
|
|
1040
|
+
return `pat_${_slugify(pattern)}`;
|
|
1037
1041
|
}
|
|
1038
|
-
function
|
|
1039
|
-
if (
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
allowed: false,
|
|
1044
|
-
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
1045
|
-
riskLevel: "medium",
|
|
1046
|
-
riskType: "lateral_movement",
|
|
1047
|
-
confidence: 80,
|
|
1048
|
-
source: "heuristic",
|
|
1049
|
-
violatedLayers: ["1.1"]
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
return null;
|
|
1042
|
+
function _deriveDenyMode(rule) {
|
|
1043
|
+
if (rule.deny_mode) return rule.deny_mode;
|
|
1044
|
+
if (rule.credential) return "deny_both";
|
|
1045
|
+
if (rule.read_only) return "deny_read";
|
|
1046
|
+
return "deny_write";
|
|
1054
1047
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
"
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
1067
|
-
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
1068
|
-
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
1069
|
-
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
1070
|
-
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
1071
|
-
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
1072
|
-
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
1073
|
-
// ---- Role Hijacking ----
|
|
1074
|
-
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
1075
|
-
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
1076
|
-
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
1077
|
-
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
1078
|
-
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
1079
|
-
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
1080
|
-
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
1081
|
-
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
1082
|
-
// ---- Command Injection ----
|
|
1083
|
-
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
1084
|
-
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
1085
|
-
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
1086
|
-
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
1087
|
-
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
1088
|
-
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
1089
|
-
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
1090
|
-
// ---- Social Engineering ----
|
|
1091
|
-
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
1092
|
-
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
1093
|
-
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
1094
|
-
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
1095
|
-
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
1096
|
-
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
1097
|
-
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
1098
|
-
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
1099
|
-
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
1100
|
-
// ---- XSS ----
|
|
1101
|
-
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
1102
|
-
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
1103
|
-
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
1104
|
-
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
1105
|
-
// ---- Fake Boundaries ----
|
|
1106
|
-
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
1107
|
-
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
1108
|
-
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
1109
|
-
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
1110
|
-
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
1111
|
-
// ---- Developer Mode ----
|
|
1112
|
-
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
1113
|
-
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
1114
|
-
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
1115
|
-
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
1116
|
-
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
1117
|
-
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
1118
|
-
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
1119
|
-
// 由 agent_ext 机制注入,不再污染通用层。
|
|
1120
|
-
];
|
|
1121
|
-
DANGEROUS_PATTERNS = [
|
|
1122
|
-
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
1123
|
-
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
1124
|
-
{ 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" },
|
|
1125
|
-
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
1126
|
-
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
1127
|
-
{ 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" },
|
|
1128
|
-
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
1129
|
-
{ 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" },
|
|
1130
|
-
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
1131
|
-
{ 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" },
|
|
1132
|
-
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
1133
|
-
{ 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" },
|
|
1134
|
-
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
1135
|
-
{ 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" },
|
|
1136
|
-
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
1137
|
-
// --- Critical: remote code execution ---
|
|
1138
|
-
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
1139
|
-
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
1140
|
-
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
1141
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
1142
|
-
// --- Critical: reverse shell ---
|
|
1143
|
-
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
1144
|
-
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
1145
|
-
// --- Critical: sensitive file access ---
|
|
1146
|
-
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
1147
|
-
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
1148
|
-
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
1149
|
-
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
1150
|
-
// --- Critical: privilege escalation / container escape ---
|
|
1151
|
-
{ 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" },
|
|
1152
|
-
// --- High: dangerous permissions / ownership ---
|
|
1153
|
-
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
1154
|
-
{ 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" },
|
|
1155
|
-
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
1156
|
-
{ 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" },
|
|
1157
|
-
// --- High: system config overwrite ---
|
|
1158
|
-
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
1159
|
-
// --- High: download script + execute risk ---
|
|
1160
|
-
{ 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" },
|
|
1161
|
-
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
1162
|
-
// --- High: package manager pipe-to-shell ---
|
|
1163
|
-
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
1164
|
-
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
1165
|
-
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
1166
|
-
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
1167
|
-
{ 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" },
|
|
1168
|
-
// --- High: network port scanning (横向移动侦察) ---
|
|
1169
|
-
{ 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" },
|
|
1170
|
-
{ 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" },
|
|
1171
|
-
// --- Medium: credential export ---
|
|
1172
|
-
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
1173
|
-
// --- High: shell rc file append (persistence) ---
|
|
1174
|
-
{ 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" },
|
|
1175
|
-
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
1176
|
-
{ 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" },
|
|
1177
|
-
// --- High: non-interactive password modification ---
|
|
1178
|
-
{ 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" },
|
|
1179
|
-
{ 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" },
|
|
1180
|
-
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
1181
|
-
{ 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" },
|
|
1182
|
-
{ 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" },
|
|
1183
|
-
{ 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" },
|
|
1184
|
-
// --- High: non-interactive password modification (extended) ---
|
|
1185
|
-
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
1186
|
-
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
1187
|
-
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
1188
|
-
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
1189
|
-
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
1190
|
-
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
1191
|
-
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
1192
|
-
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
1193
|
-
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
1194
|
-
// --- High: generic command -p parameter password setting ---
|
|
1195
|
-
{ 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" }
|
|
1196
|
-
];
|
|
1197
|
-
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
// src/engine/skill-loader.ts
|
|
1202
|
-
import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
1203
|
-
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
1204
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1205
|
-
function loadAllSkills() {
|
|
1206
|
-
if (cachedSkills) return cachedSkills;
|
|
1207
|
-
cachedSkills = {};
|
|
1208
|
-
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1209
|
-
if (!existsSync3(dir)) {
|
|
1210
|
-
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1211
|
-
return cachedSkills;
|
|
1212
|
-
}
|
|
1213
|
-
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1214
|
-
for (const f of files) {
|
|
1215
|
-
const name = f.replace(".md", "");
|
|
1216
|
-
cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
|
|
1048
|
+
function _syncDenyModeFields(rule) {
|
|
1049
|
+
const dm = rule.deny_mode || "deny_write";
|
|
1050
|
+
if (dm === "deny_both") {
|
|
1051
|
+
rule.credential = true;
|
|
1052
|
+
delete rule.read_only;
|
|
1053
|
+
} else if (dm === "deny_read") {
|
|
1054
|
+
delete rule.credential;
|
|
1055
|
+
rule.read_only = true;
|
|
1056
|
+
} else {
|
|
1057
|
+
delete rule.credential;
|
|
1058
|
+
delete rule.read_only;
|
|
1217
1059
|
}
|
|
1218
|
-
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1219
|
-
return cachedSkills;
|
|
1220
1060
|
}
|
|
1221
|
-
function
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
if (combined.includes(kw)) {
|
|
1232
|
-
if (kw === actionTypeLower) score += 2;
|
|
1233
|
-
else if (actionDetailLower.includes(kw)) score += 1.5;
|
|
1234
|
-
else if (reasonLower.includes(kw)) score += 1;
|
|
1235
|
-
else score += 0.5;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
if (score > 0) scores[skillName] = score;
|
|
1239
|
-
}
|
|
1240
|
-
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
1241
|
-
const top = sorted.slice(0, maxSkills);
|
|
1242
|
-
const maxScore = top[0]?.[1] || 1;
|
|
1243
|
-
const results = [];
|
|
1244
|
-
for (const [name, score] of top) {
|
|
1245
|
-
const content = skills[name];
|
|
1246
|
-
if (content) {
|
|
1247
|
-
results.push({
|
|
1248
|
-
skillName: name,
|
|
1249
|
-
relevance: Math.min(score / maxScore, 1),
|
|
1250
|
-
skillContent: content
|
|
1251
|
-
});
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
if (results.length === 0) {
|
|
1255
|
-
const fallback = skills["general_tool_risk_guard"];
|
|
1256
|
-
if (fallback) {
|
|
1257
|
-
results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
return results;
|
|
1061
|
+
function _l1CommandPatternsToRules() {
|
|
1062
|
+
return DANGEROUS_PATTERNS.map((p) => ({
|
|
1063
|
+
id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
|
|
1064
|
+
pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
|
|
1065
|
+
risk_level: p.severity,
|
|
1066
|
+
risk_type: p.riskType,
|
|
1067
|
+
reason: p.description,
|
|
1068
|
+
enabled: true,
|
|
1069
|
+
builtin: true
|
|
1070
|
+
}));
|
|
1261
1071
|
}
|
|
1262
|
-
function
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1072
|
+
function _l1InjectionKeywordsToRules() {
|
|
1073
|
+
return INJECTION_KEYWORDS.map((kw) => ({
|
|
1074
|
+
id: `inj_${_slugify(kw.keyword)}`,
|
|
1075
|
+
keyword: kw.keyword,
|
|
1076
|
+
risk_level: kw.riskLevel,
|
|
1077
|
+
lang: kw.lang || "en",
|
|
1078
|
+
enabled: true,
|
|
1079
|
+
builtin: true
|
|
1080
|
+
}));
|
|
1269
1081
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1082
|
+
function _l2SensitivePathsToRules() {
|
|
1083
|
+
const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
|
|
1084
|
+
return all.map((sp) => {
|
|
1085
|
+
const denyMode = _deriveDenyMode({
|
|
1086
|
+
deny_mode: sp.deny_mode,
|
|
1087
|
+
credential: sp.credential,
|
|
1088
|
+
read_only: sp.read_only
|
|
1089
|
+
});
|
|
1090
|
+
const rule = {
|
|
1091
|
+
id: `path_${_slugify(sp.path)}`,
|
|
1092
|
+
path: sp.path,
|
|
1093
|
+
risk_level: sp.riskLevel,
|
|
1094
|
+
desc: sp.desc,
|
|
1095
|
+
deny_mode: denyMode,
|
|
1096
|
+
source_deny_mode: denyMode,
|
|
1097
|
+
skip_l3_on_disabled: true,
|
|
1098
|
+
enabled: true,
|
|
1099
|
+
builtin: true
|
|
1100
|
+
};
|
|
1101
|
+
_syncDenyModeFields(rule);
|
|
1102
|
+
return rule;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
function _l2IntentPatternsToRules() {
|
|
1106
|
+
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
1107
|
+
id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
|
|
1108
|
+
intent_keywords: pat.intentKeywords,
|
|
1109
|
+
dangerous_actions: pat.dangerousActions,
|
|
1110
|
+
reason: pat.reason,
|
|
1111
|
+
skip_l3_on_disabled: true,
|
|
1112
|
+
enabled: true,
|
|
1113
|
+
builtin: true
|
|
1114
|
+
}));
|
|
1115
|
+
}
|
|
1116
|
+
function _l2PasswordPatternsToRules() {
|
|
1117
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => ({
|
|
1118
|
+
id: `pw_${_slugify(p.source)}`,
|
|
1119
|
+
pattern: p.source,
|
|
1120
|
+
skip_l3_on_disabled: true,
|
|
1121
|
+
enabled: true,
|
|
1122
|
+
builtin: true
|
|
1123
|
+
}));
|
|
1124
|
+
}
|
|
1125
|
+
function _l2UserDeletionPatternsToRules() {
|
|
1126
|
+
return USER_DELETION_PATTERNS.map((p) => ({
|
|
1127
|
+
id: `ud_${_slugify(p.source)}`,
|
|
1128
|
+
pattern: p.source,
|
|
1129
|
+
skip_l3_on_disabled: true,
|
|
1130
|
+
enabled: true,
|
|
1131
|
+
builtin: true
|
|
1132
|
+
}));
|
|
1133
|
+
}
|
|
1134
|
+
function _l3SkillsToRules() {
|
|
1135
|
+
const skillCategories = {
|
|
1136
|
+
file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
|
|
1137
|
+
network_security: [
|
|
1138
|
+
"data_exfiltration_guard",
|
|
1139
|
+
"browser_web_access_guard",
|
|
1140
|
+
"lateral_movement_guard",
|
|
1141
|
+
"email_operation_guard"
|
|
1142
|
+
],
|
|
1143
|
+
persistence_supply_chain: [
|
|
1144
|
+
"persistence_backdoor_guard",
|
|
1145
|
+
"supply_chain_guard",
|
|
1146
|
+
"skill_installation_guard"
|
|
1147
|
+
],
|
|
1148
|
+
intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
|
|
1149
|
+
resource: ["resource_exhaustion_guard"]
|
|
1150
|
+
};
|
|
1151
|
+
const out = {};
|
|
1152
|
+
for (const [cat, ids] of Object.entries(skillCategories)) {
|
|
1153
|
+
out[cat] = {
|
|
1154
|
+
category_enabled: true,
|
|
1155
|
+
category_desc: "",
|
|
1156
|
+
skills: ids.map((s) => ({
|
|
1157
|
+
id: s,
|
|
1158
|
+
keywords: SKILL_KEYWORD_MAP[s] || [],
|
|
1159
|
+
enabled: true,
|
|
1160
|
+
builtin: true
|
|
1161
|
+
}))
|
|
1295
1162
|
};
|
|
1296
|
-
cachedSkills = null;
|
|
1297
1163
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
});
|
|
1309
|
-
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
|
|
1310
|
-
import { join as join3 } from "path";
|
|
1311
|
-
import { homedir as homedir2 } from "os";
|
|
1312
|
-
function getStatsPath() {
|
|
1313
|
-
const envPath = process.env[ENV_STATS_PATH];
|
|
1314
|
-
return envPath || STATS_FILE;
|
|
1315
|
-
}
|
|
1316
|
-
function loadAllRecords(statsPath) {
|
|
1317
|
-
if (!existsSync6(statsPath)) return [];
|
|
1318
|
-
try {
|
|
1319
|
-
const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
|
|
1320
|
-
if (Array.isArray(data)) return data;
|
|
1321
|
-
if (data && Array.isArray(data.records)) return data.records;
|
|
1322
|
-
return [];
|
|
1323
|
-
} catch (e) {
|
|
1324
|
-
return [];
|
|
1164
|
+
const categorized = new Set(Object.values(skillCategories).flat());
|
|
1165
|
+
for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
|
|
1166
|
+
if (!categorized.has(s)) {
|
|
1167
|
+
(out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
|
|
1168
|
+
id: s,
|
|
1169
|
+
keywords: SKILL_KEYWORD_MAP[s],
|
|
1170
|
+
enabled: true,
|
|
1171
|
+
builtin: true
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1325
1174
|
}
|
|
1175
|
+
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
1176
|
+
return out;
|
|
1326
1177
|
}
|
|
1327
|
-
function
|
|
1178
|
+
function _agentRulesToDefaults() {
|
|
1179
|
+
const out = {};
|
|
1180
|
+
const agentsDir = existsSync3(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync3(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
1181
|
+
if (!existsSync3(agentsDir)) return out;
|
|
1182
|
+
let entries;
|
|
1328
1183
|
try {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
} catch (e) {
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
function recordTokenUsage(record) {
|
|
1336
|
-
const statsPath = getStatsPath();
|
|
1337
|
-
const records = loadAllRecords(statsPath);
|
|
1338
|
-
records.push({
|
|
1339
|
-
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1340
|
-
session_id: record.sessionId,
|
|
1341
|
-
step: record.step,
|
|
1342
|
-
model: record.model,
|
|
1343
|
-
prompt_tokens: record.promptTokens,
|
|
1344
|
-
completion_tokens: record.completionTokens,
|
|
1345
|
-
total_tokens: record.totalTokens
|
|
1346
|
-
});
|
|
1347
|
-
if (records.length > MAX_RECORDS) {
|
|
1348
|
-
records.splice(0, records.length - MAX_RECORDS);
|
|
1349
|
-
}
|
|
1350
|
-
saveRecords(statsPath, records);
|
|
1351
|
-
}
|
|
1352
|
-
function filterByDate(records, days = 0, startDate, endDate) {
|
|
1353
|
-
if (startDate || endDate) {
|
|
1354
|
-
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
1355
|
-
let endDt = endDate ? new Date(endDate) : void 0;
|
|
1356
|
-
if (endDate) {
|
|
1357
|
-
const e = new Date(endDate);
|
|
1358
|
-
e.setHours(23, 59, 59, 999);
|
|
1359
|
-
endDt = e;
|
|
1360
|
-
}
|
|
1361
|
-
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
1362
|
-
return records.filter((r) => {
|
|
1363
|
-
try {
|
|
1364
|
-
const ts = new Date(r.timestamp).getTime();
|
|
1365
|
-
return ts >= startDt.getTime() && ts <= endMs;
|
|
1366
|
-
} catch {
|
|
1367
|
-
return true;
|
|
1368
|
-
}
|
|
1369
|
-
});
|
|
1370
|
-
} else if (days > 0) {
|
|
1371
|
-
const cutoff = Date.now() - days * 864e5;
|
|
1372
|
-
return records.filter((r) => {
|
|
1373
|
-
try {
|
|
1374
|
-
return new Date(r.timestamp).getTime() >= cutoff;
|
|
1375
|
-
} catch {
|
|
1376
|
-
return true;
|
|
1377
|
-
}
|
|
1378
|
-
});
|
|
1184
|
+
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
1185
|
+
} catch {
|
|
1186
|
+
return out;
|
|
1379
1187
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const byStep = {};
|
|
1385
|
-
const byModel = {};
|
|
1386
|
-
const byDate = {};
|
|
1387
|
-
const bucket = (b, key, r) => {
|
|
1388
|
-
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
1389
|
-
e.prompt_tokens += r.prompt_tokens || 0;
|
|
1390
|
-
e.completion_tokens += r.completion_tokens || 0;
|
|
1391
|
-
e.total_tokens += r.total_tokens || 0;
|
|
1392
|
-
e.calls += 1;
|
|
1393
|
-
};
|
|
1394
|
-
for (const r of records) {
|
|
1395
|
-
totalPrompt += r.prompt_tokens || 0;
|
|
1396
|
-
totalCompletion += r.completion_tokens || 0;
|
|
1397
|
-
totalTotal += r.total_tokens || 0;
|
|
1398
|
-
bucket(byStep, r.step || "unknown", r);
|
|
1399
|
-
bucket(byModel, r.model || "unknown", r);
|
|
1400
|
-
let dateKey = "unknown";
|
|
1188
|
+
for (const agentId of entries) {
|
|
1189
|
+
const rulesFile = join(agentsDir, agentId, "rules.json");
|
|
1190
|
+
if (!existsSync3(rulesFile)) continue;
|
|
1191
|
+
let data;
|
|
1401
1192
|
try {
|
|
1402
|
-
|
|
1193
|
+
data = JSON.parse(readFileSync2(rulesFile, "utf-8"));
|
|
1403
1194
|
} catch {
|
|
1195
|
+
continue;
|
|
1404
1196
|
}
|
|
1405
|
-
|
|
1197
|
+
out[agentId] = {
|
|
1198
|
+
builtin_safe_action_types: {
|
|
1199
|
+
category_enabled: true,
|
|
1200
|
+
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",
|
|
1201
|
+
rules: (data.builtin_safe_action_types || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1202
|
+
},
|
|
1203
|
+
builtin_safe_skills: {
|
|
1204
|
+
category_enabled: true,
|
|
1205
|
+
category_desc: "agent \u4FE1\u4EFB\u7684\u5185\u7F6E Skill\uFF08\u547D\u4E2D\u8DF3 L2+L3\uFF09",
|
|
1206
|
+
rules: (data.builtin_safe_skills || []).map((v) => ({ value: v, enabled: true, builtin: true }))
|
|
1207
|
+
},
|
|
1208
|
+
extra_command_patterns: {
|
|
1209
|
+
category_enabled: true,
|
|
1210
|
+
category_desc: "agent \u4E13\u5C5E\u5371\u9669\u547D\u4EE4\u6B63\u5219\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1211
|
+
rules: (data.extra_command_patterns || []).map((p) => ({ ...p, enabled: true, builtin: true }))
|
|
1212
|
+
},
|
|
1213
|
+
extra_injection_keywords: {
|
|
1214
|
+
category_enabled: true,
|
|
1215
|
+
category_desc: "agent \u4E13\u5C5E\u6CE8\u5165\u5173\u952E\u8BCD\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1216
|
+
rules: (data.extra_injection_keywords || []).map((k) => ({ ...k, enabled: true, builtin: true }))
|
|
1217
|
+
},
|
|
1218
|
+
extra_sensitive_paths: {
|
|
1219
|
+
category_enabled: true,
|
|
1220
|
+
category_desc: "agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84\uFF08L1 \u672B\u5C3E\u8FFD\u52A0\uFF09",
|
|
1221
|
+
rules: (data.extra_sensitive_paths || []).map((sp) => ({ ...sp, enabled: true, builtin: true }))
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1406
1224
|
}
|
|
1407
|
-
|
|
1408
|
-
const sortedByDate = {};
|
|
1409
|
-
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
1410
|
-
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
1225
|
+
return out;
|
|
1411
1226
|
}
|
|
1412
|
-
function
|
|
1413
|
-
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1414
|
-
const a = aggregate(records);
|
|
1227
|
+
function generateSourceDefaults() {
|
|
1415
1228
|
return {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1229
|
+
version: 1,
|
|
1230
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
1231
|
+
L1_rules: {
|
|
1232
|
+
dangerous_commands: {
|
|
1233
|
+
category_enabled: true,
|
|
1234
|
+
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",
|
|
1235
|
+
rules: _l1CommandPatternsToRules()
|
|
1236
|
+
},
|
|
1237
|
+
injection_detection: {
|
|
1238
|
+
category_enabled: true,
|
|
1239
|
+
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
1240
|
+
rules: _l1InjectionKeywordsToRules()
|
|
1241
|
+
},
|
|
1242
|
+
sensitive_path_access: {
|
|
1243
|
+
category_enabled: true,
|
|
1244
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
|
|
1245
|
+
rules: _l2SensitivePathsToRules()
|
|
1246
|
+
},
|
|
1247
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1248
|
+
},
|
|
1249
|
+
L2_rules: {
|
|
1250
|
+
intent_consistency: {
|
|
1251
|
+
category_enabled: true,
|
|
1252
|
+
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
1253
|
+
rules: _l2IntentPatternsToRules()
|
|
1254
|
+
},
|
|
1255
|
+
password_modify_consent: {
|
|
1256
|
+
category_enabled: true,
|
|
1257
|
+
category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
|
|
1258
|
+
rules: _l2PasswordPatternsToRules()
|
|
1259
|
+
},
|
|
1260
|
+
user_deletion_consent: {
|
|
1261
|
+
category_enabled: true,
|
|
1262
|
+
category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
|
|
1263
|
+
rules: _l2UserDeletionPatternsToRules()
|
|
1264
|
+
},
|
|
1265
|
+
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
1266
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1267
|
+
},
|
|
1268
|
+
L3_skills: _l3SkillsToRules(),
|
|
1269
|
+
agent_rules: _agentRulesToDefaults()
|
|
1423
1270
|
};
|
|
1424
1271
|
}
|
|
1425
|
-
function
|
|
1426
|
-
|
|
1427
|
-
|
|
1272
|
+
function _ruleFingerprint(rule) {
|
|
1273
|
+
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1274
|
+
return JSON.stringify([
|
|
1275
|
+
[...rule.intent_keywords || []].sort(),
|
|
1276
|
+
[...rule.dangerous_actions || []].sort()
|
|
1277
|
+
]);
|
|
1278
|
+
}
|
|
1279
|
+
if (rule.path) return JSON.stringify([rule.path]);
|
|
1280
|
+
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1281
|
+
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1282
|
+
if (rule.value) return JSON.stringify([rule.value]);
|
|
1283
|
+
return JSON.stringify([rule.id || ""]);
|
|
1428
1284
|
}
|
|
1429
|
-
function
|
|
1430
|
-
const
|
|
1431
|
-
const
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
try {
|
|
1436
|
-
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1437
|
-
} catch {
|
|
1285
|
+
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
1286
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
1287
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
1288
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
1289
|
+
if (!(catName in userLayer)) {
|
|
1290
|
+
userLayer[catName] = srcCat;
|
|
1438
1291
|
continue;
|
|
1439
1292
|
}
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1293
|
+
const userCat = userLayer[catName];
|
|
1294
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1295
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1296
|
+
const srcRules = srcCat.rules || [];
|
|
1297
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
1298
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
1299
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
1300
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
1301
|
+
for (const srcR of srcRules) {
|
|
1302
|
+
const fp = _ruleFingerprint(srcR);
|
|
1303
|
+
srcFps.add(fp);
|
|
1304
|
+
if (userByFp.has(fp)) {
|
|
1305
|
+
const existing = userByFp.get(fp);
|
|
1306
|
+
const userEnabled = existing.enabled;
|
|
1307
|
+
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
1308
|
+
const userDenyMode = existing.deny_mode;
|
|
1309
|
+
const userSourceDeny = existing.source_deny_mode;
|
|
1310
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1311
|
+
Object.assign(existing, srcR);
|
|
1312
|
+
existing.enabled = userEnabled;
|
|
1313
|
+
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
1314
|
+
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
1315
|
+
existing.deny_mode = userDenyMode;
|
|
1316
|
+
}
|
|
1317
|
+
if (existing.deny_mode) _syncDenyModeFields(existing);
|
|
1318
|
+
} else {
|
|
1319
|
+
const newR = { ...srcR, enabled: true };
|
|
1320
|
+
userRules.push(newR);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
for (const r of userRules) {
|
|
1324
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
1325
|
+
r.source_removed = true;
|
|
1326
|
+
r.enabled = false;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (catName === "sensitive_path_access") {
|
|
1330
|
+
for (const r of userRules) {
|
|
1331
|
+
if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
|
|
1332
|
+
_syncDenyModeFields(r);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1445
1335
|
}
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
function resetTokenStats() {
|
|
1451
|
-
const statsPath = getStatsPath();
|
|
1452
|
-
if (existsSync6(statsPath)) {
|
|
1453
|
-
try {
|
|
1454
|
-
unlinkSync(statsPath);
|
|
1455
|
-
} catch {
|
|
1456
|
-
}
|
|
1336
|
+
for (const catName of Object.keys(userLayer)) {
|
|
1337
|
+
if (catName === "user_custom") continue;
|
|
1338
|
+
if (!(catName in srcLayer)) delete userLayer[catName];
|
|
1457
1339
|
}
|
|
1458
1340
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1341
|
+
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
1342
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
1343
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
1344
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
1345
|
+
if (!(catName in userLayer)) {
|
|
1346
|
+
userLayer[catName] = srcCat;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
const userCat = userLayer[catName];
|
|
1350
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1351
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1352
|
+
const userSkills = userCat.skills || (userCat.skills = []);
|
|
1353
|
+
const existingIds = new Set(userSkills.map((s) => s.id));
|
|
1354
|
+
for (const srcS of srcCat.skills || []) {
|
|
1355
|
+
if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
|
|
1356
|
+
}
|
|
1468
1357
|
}
|
|
1469
|
-
});
|
|
1470
|
-
|
|
1471
|
-
// src/infra/runtime-config.ts
|
|
1472
|
-
var runtime_config_exports = {};
|
|
1473
|
-
__export(runtime_config_exports, {
|
|
1474
|
-
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
1475
|
-
addCustomRule: () => addCustomRule,
|
|
1476
|
-
addCustomSkill: () => addCustomSkill,
|
|
1477
|
-
deleteCustomRule: () => deleteCustomRule,
|
|
1478
|
-
deleteCustomSkill: () => deleteCustomSkill,
|
|
1479
|
-
filterEnabledRules: () => filterEnabledRules,
|
|
1480
|
-
generateSourceDefaults: () => generateSourceDefaults,
|
|
1481
|
-
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
1482
|
-
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
1483
|
-
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
1484
|
-
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
1485
|
-
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
1486
|
-
getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
|
|
1487
|
-
getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
|
|
1488
|
-
getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
|
|
1489
|
-
getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
|
|
1490
|
-
getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
|
|
1491
|
-
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
1492
|
-
getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
|
|
1493
|
-
getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
|
|
1494
|
-
getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
|
|
1495
|
-
getEnvOverrides: () => getEnvOverrides,
|
|
1496
|
-
getProfileOrDetect: () => getProfileOrDetect,
|
|
1497
|
-
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
1498
|
-
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
1499
|
-
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
1500
|
-
isLayerEnabled: () => isLayerEnabled,
|
|
1501
|
-
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
1502
|
-
mergeConfig: () => mergeConfig,
|
|
1503
|
-
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
1504
|
-
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
1505
|
-
updateLayerEnabled: () => updateLayerEnabled,
|
|
1506
|
-
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
1507
|
-
updateRuleEnabled: () => updateRuleEnabled,
|
|
1508
|
-
updateRuleSkipL3: () => updateRuleSkipL3,
|
|
1509
|
-
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1510
|
-
updateSkillEnabled: () => updateSkillEnabled
|
|
1511
|
-
});
|
|
1512
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1513
|
-
import { join as join4 } from "path";
|
|
1514
|
-
import { homedir as homedir3 } from "os";
|
|
1515
|
-
function getRuntimeConfigPath() {
|
|
1516
|
-
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
1517
|
-
if (envPath) return envPath;
|
|
1518
|
-
return RUNTIME_CONFIG_PATH;
|
|
1519
|
-
}
|
|
1520
|
-
function saveRuntimeConfig(config) {
|
|
1521
|
-
const p = getRuntimeConfigPath();
|
|
1522
|
-
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1523
|
-
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
1524
|
-
writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1525
1358
|
}
|
|
1526
|
-
function
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1359
|
+
function _mergeAgentRules(userCfg, srcDefaults) {
|
|
1360
|
+
const userAgent = userCfg.agent_rules || (userCfg.agent_rules = {});
|
|
1361
|
+
const srcAgent = srcDefaults.agent_rules || {};
|
|
1362
|
+
for (const [agentId, srcCats] of Object.entries(srcAgent)) {
|
|
1363
|
+
if (!(agentId in userAgent)) {
|
|
1364
|
+
userAgent[agentId] = JSON.parse(JSON.stringify(srcCats));
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
const userCats = userAgent[agentId];
|
|
1368
|
+
for (const [catName, srcCat] of Object.entries(srcCats)) {
|
|
1369
|
+
if (!(catName in userCats)) {
|
|
1370
|
+
userCats[catName] = JSON.parse(JSON.stringify(srcCat));
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
const userCat = userCats[catName];
|
|
1374
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1375
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1376
|
+
const srcRules = srcCat.rules || [];
|
|
1377
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
1378
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
1379
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
1380
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
1381
|
+
for (const srcR of srcRules) {
|
|
1382
|
+
const fp = _ruleFingerprint(srcR);
|
|
1383
|
+
srcFps.add(fp);
|
|
1384
|
+
if (userByFp.has(fp)) {
|
|
1385
|
+
const existing = userByFp.get(fp);
|
|
1386
|
+
const userEnabled = existing.enabled;
|
|
1387
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1388
|
+
Object.assign(existing, srcR);
|
|
1389
|
+
existing.enabled = userEnabled;
|
|
1390
|
+
} else {
|
|
1391
|
+
userRules.push({ ...srcR, enabled: true });
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
for (const r of userRules) {
|
|
1395
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
1396
|
+
r.source_removed = true;
|
|
1397
|
+
r.enabled = false;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1542
1400
|
}
|
|
1543
|
-
return seeded;
|
|
1544
|
-
}
|
|
1545
|
-
try {
|
|
1546
|
-
const data = JSON.parse(readFileSync7(p, "utf-8"));
|
|
1547
|
-
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1548
|
-
return {
|
|
1549
|
-
version: data.version || 1,
|
|
1550
|
-
layers: {
|
|
1551
|
-
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
1552
|
-
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
1553
|
-
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
1554
|
-
},
|
|
1555
|
-
L1_rules: merged.L1_rules || {},
|
|
1556
|
-
L2_rules: merged.L2_rules || {},
|
|
1557
|
-
L3_skills: merged.L3_skills || {}
|
|
1558
|
-
};
|
|
1559
|
-
} catch {
|
|
1560
|
-
return emptyRuntimeConfig();
|
|
1561
1401
|
}
|
|
1562
1402
|
}
|
|
1563
|
-
function
|
|
1564
|
-
const
|
|
1565
|
-
if (
|
|
1566
|
-
|
|
1567
|
-
const
|
|
1568
|
-
|
|
1569
|
-
|
|
1403
|
+
function mergeConfig(userCfg, srcDefaults) {
|
|
1404
|
+
const cfg = userCfg;
|
|
1405
|
+
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
1406
|
+
if (!cfg.layers) cfg.layers = {};
|
|
1407
|
+
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
1408
|
+
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
1409
|
+
}
|
|
1410
|
+
_migrateCategoriesAcrossLayers(
|
|
1411
|
+
cfg,
|
|
1412
|
+
"L2_rules",
|
|
1413
|
+
"L1_rules",
|
|
1414
|
+
["sensitive_path_access"]
|
|
1415
|
+
);
|
|
1416
|
+
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1417
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
1418
|
+
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1419
|
+
_mergeAgentRules(cfg, srcDefaults);
|
|
1420
|
+
return cfg;
|
|
1570
1421
|
}
|
|
1571
|
-
function
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
if (
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1422
|
+
function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
1423
|
+
const src = cfg[srcLayer] || {};
|
|
1424
|
+
const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
|
|
1425
|
+
for (const catName of catNames) {
|
|
1426
|
+
if (!(catName in src)) continue;
|
|
1427
|
+
const srcCat = src[catName];
|
|
1428
|
+
if (!dst[catName]) {
|
|
1429
|
+
dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1430
|
+
}
|
|
1431
|
+
const dstCat = dst[catName];
|
|
1432
|
+
if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
|
|
1433
|
+
if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
|
|
1434
|
+
if (srcCat.rules && srcCat.rules.length > 0) {
|
|
1435
|
+
const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
|
|
1436
|
+
for (const r of srcCat.rules) {
|
|
1437
|
+
const fp = _ruleFingerprint(r);
|
|
1438
|
+
if (!dstFps.has(fp)) {
|
|
1439
|
+
(dstCat.rules = dstCat.rules || []).push(r);
|
|
1440
|
+
dstFps.add(fp);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1582
1443
|
}
|
|
1444
|
+
delete src[catName];
|
|
1583
1445
|
}
|
|
1584
|
-
return overrides;
|
|
1585
|
-
}
|
|
1586
|
-
function filterEnabledRules(rules, categoryEnabled = true) {
|
|
1587
|
-
if (!categoryEnabled) return [];
|
|
1588
|
-
return rules.filter((r) => r.enabled !== false);
|
|
1589
1446
|
}
|
|
1590
|
-
function
|
|
1591
|
-
const cat = runtime.L1_rules?.
|
|
1447
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
1448
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
1592
1449
|
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1593
|
-
|
|
1450
|
+
keyword: r.keyword || "",
|
|
1594
1451
|
risk_level: r.risk_level || "",
|
|
1595
|
-
|
|
1596
|
-
reason: r.reason || ""
|
|
1452
|
+
lang: r.lang || "en"
|
|
1597
1453
|
}));
|
|
1598
1454
|
}
|
|
1599
|
-
function
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
if (r.credential) out.credential = true;
|
|
1607
|
-
if (r.read_only) out.read_only = true;
|
|
1608
|
-
return out;
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
function hasRuntimeConfig() {
|
|
1612
|
-
const p = getRuntimeConfigPath();
|
|
1613
|
-
if (!existsSync7(p)) return false;
|
|
1614
|
-
try {
|
|
1615
|
-
JSON.parse(readFileSync7(p, "utf-8"));
|
|
1616
|
-
return true;
|
|
1617
|
-
} catch {
|
|
1618
|
-
return false;
|
|
1619
|
-
}
|
|
1455
|
+
function getEnabledL2IntentPatterns(runtime) {
|
|
1456
|
+
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
1457
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1458
|
+
intent_keywords: r.intent_keywords || [],
|
|
1459
|
+
dangerous_actions: r.dangerous_actions || [],
|
|
1460
|
+
reason: r.reason || ""
|
|
1461
|
+
}));
|
|
1620
1462
|
}
|
|
1621
|
-
function
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
items.push(r.pattern);
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1463
|
+
function getEnabledL2PasswordPatterns(runtime) {
|
|
1464
|
+
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
1465
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
1466
|
+
}
|
|
1467
|
+
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
1468
|
+
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
1469
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
1470
|
+
}
|
|
1471
|
+
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
1472
|
+
const cat = runtime.L2_rules?.[categoryKey];
|
|
1473
|
+
return cat?.category_enabled ?? true;
|
|
1474
|
+
}
|
|
1475
|
+
function getDisabledSkillIds(runtime) {
|
|
1476
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
1477
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
1478
|
+
if (!catData.category_enabled) {
|
|
1479
|
+
for (const s of catData.skills || []) disabled.add(s.id);
|
|
1480
|
+
} else {
|
|
1481
|
+
for (const s of catData.skills || []) {
|
|
1482
|
+
if (s.enabled === false) disabled.add(s.id);
|
|
1644
1483
|
}
|
|
1645
|
-
if (items.length > 0) result[catName] = items;
|
|
1646
1484
|
}
|
|
1647
1485
|
}
|
|
1648
|
-
return
|
|
1486
|
+
return disabled;
|
|
1649
1487
|
}
|
|
1650
|
-
function
|
|
1651
|
-
|
|
1488
|
+
function getEnabledL1CommandPatternsOrDefault(runtime) {
|
|
1489
|
+
const enabled = getEnabledL1CommandPatterns(runtime);
|
|
1490
|
+
if (enabled.length) return enabled;
|
|
1491
|
+
if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
|
|
1492
|
+
return [];
|
|
1652
1493
|
}
|
|
1653
|
-
function
|
|
1654
|
-
|
|
1494
|
+
function getEnabledL1InjectionKeywordsOrDefault(runtime) {
|
|
1495
|
+
const enabled = getEnabledL1InjectionKeywords(runtime);
|
|
1496
|
+
if (enabled.length) return enabled;
|
|
1497
|
+
if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
|
|
1498
|
+
return [];
|
|
1655
1499
|
}
|
|
1656
|
-
function
|
|
1657
|
-
|
|
1658
|
-
if (
|
|
1659
|
-
if (
|
|
1660
|
-
return
|
|
1500
|
+
function getEnabledL2SensitivePathsOrDefault(runtime) {
|
|
1501
|
+
const enabled = getEnabledL2SensitivePaths(runtime);
|
|
1502
|
+
if (enabled.length) return enabled;
|
|
1503
|
+
if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
|
|
1504
|
+
return [];
|
|
1661
1505
|
}
|
|
1662
|
-
function
|
|
1663
|
-
const
|
|
1664
|
-
if (
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1506
|
+
function getEnabledL2IntentPatternsOrDefault(runtime) {
|
|
1507
|
+
const enabled = getEnabledL2IntentPatterns(runtime);
|
|
1508
|
+
if (enabled.length) return enabled;
|
|
1509
|
+
if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
|
|
1510
|
+
return [];
|
|
1511
|
+
}
|
|
1512
|
+
function getEnabledL2PasswordPatternsOrDefault(runtime) {
|
|
1513
|
+
const enabled = getEnabledL2PasswordPatterns(runtime);
|
|
1514
|
+
if (enabled.length) return enabled;
|
|
1515
|
+
if (!hasRuntimeConfig()) {
|
|
1516
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
|
|
1673
1517
|
}
|
|
1518
|
+
return [];
|
|
1674
1519
|
}
|
|
1675
|
-
function
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
enabled: true,
|
|
1683
|
-
builtin: true
|
|
1684
|
-
}));
|
|
1520
|
+
function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
|
|
1521
|
+
const enabled = getEnabledL2UserDeletionPatterns(runtime);
|
|
1522
|
+
if (enabled.length) return enabled;
|
|
1523
|
+
if (!hasRuntimeConfig()) {
|
|
1524
|
+
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
1525
|
+
}
|
|
1526
|
+
return [];
|
|
1685
1527
|
}
|
|
1686
|
-
function
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
risk_level: kw.riskLevel,
|
|
1691
|
-
lang: kw.lang || "en",
|
|
1692
|
-
enabled: true,
|
|
1693
|
-
builtin: true
|
|
1694
|
-
}));
|
|
1528
|
+
function _validateSkillId(skillId) {
|
|
1529
|
+
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
1530
|
+
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`);
|
|
1531
|
+
}
|
|
1695
1532
|
}
|
|
1696
|
-
function
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
credential: sp.credential,
|
|
1702
|
-
read_only: sp.read_only
|
|
1703
|
-
});
|
|
1704
|
-
const rule = {
|
|
1705
|
-
id: `path_${_slugify(sp.path)}`,
|
|
1706
|
-
path: sp.path,
|
|
1707
|
-
risk_level: sp.riskLevel,
|
|
1708
|
-
desc: sp.desc,
|
|
1709
|
-
deny_mode: denyMode,
|
|
1710
|
-
source_deny_mode: denyMode,
|
|
1711
|
-
skip_l3_on_disabled: true,
|
|
1712
|
-
enabled: true,
|
|
1713
|
-
builtin: true
|
|
1714
|
-
};
|
|
1715
|
-
_syncDenyModeFields(rule);
|
|
1716
|
-
return rule;
|
|
1717
|
-
});
|
|
1533
|
+
function updateLayerEnabled(layerKey, enabled) {
|
|
1534
|
+
const runtime = loadRuntimeConfig();
|
|
1535
|
+
runtime.layers[layerKey] = enabled;
|
|
1536
|
+
saveRuntimeConfig(runtime);
|
|
1537
|
+
return runtime;
|
|
1718
1538
|
}
|
|
1719
|
-
function
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1539
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
1540
|
+
const runtime = loadRuntimeConfig();
|
|
1541
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1542
|
+
if (r.id === ruleId) {
|
|
1543
|
+
r.enabled = enabled;
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
saveRuntimeConfig(runtime);
|
|
1548
|
+
return runtime;
|
|
1729
1549
|
}
|
|
1730
|
-
function
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1550
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
1551
|
+
const runtime = loadRuntimeConfig();
|
|
1552
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1553
|
+
if (r.id === ruleId) {
|
|
1554
|
+
r.deny_mode = denyMode;
|
|
1555
|
+
_syncDenyModeFields(r);
|
|
1556
|
+
break;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
saveRuntimeConfig(runtime);
|
|
1560
|
+
return runtime;
|
|
1738
1561
|
}
|
|
1739
|
-
function
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
}
|
|
1562
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
1563
|
+
const runtime = loadRuntimeConfig();
|
|
1564
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
1565
|
+
if (r.id === ruleId) {
|
|
1566
|
+
r.skip_l3_on_disabled = skipL3;
|
|
1567
|
+
break;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
saveRuntimeConfig(runtime);
|
|
1571
|
+
return runtime;
|
|
1747
1572
|
}
|
|
1748
|
-
function
|
|
1749
|
-
const
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1573
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
1574
|
+
const runtime = loadRuntimeConfig();
|
|
1575
|
+
const cat = runtime[layer]?.[category];
|
|
1576
|
+
if (cat) cat.category_enabled = enabled;
|
|
1577
|
+
saveRuntimeConfig(runtime);
|
|
1578
|
+
return runtime;
|
|
1579
|
+
}
|
|
1580
|
+
function addCustomRule(layer, category, rule) {
|
|
1581
|
+
const runtime = loadRuntimeConfig();
|
|
1582
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
1583
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
1584
|
+
const rules = cat.rules || (cat.rules = []);
|
|
1585
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
1586
|
+
if (!newRule.id) {
|
|
1587
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
1588
|
+
}
|
|
1589
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
1590
|
+
rules.push(newRule);
|
|
1591
|
+
saveRuntimeConfig(runtime);
|
|
1592
|
+
return runtime;
|
|
1593
|
+
}
|
|
1594
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
1595
|
+
const runtime = loadRuntimeConfig();
|
|
1596
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
1597
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
1598
|
+
if (idx >= 0) {
|
|
1599
|
+
const r = rules[idx];
|
|
1600
|
+
if (r.builtin && !r.source_removed) return null;
|
|
1601
|
+
rules.splice(idx, 1);
|
|
1602
|
+
saveRuntimeConfig(runtime);
|
|
1603
|
+
}
|
|
1604
|
+
return runtime;
|
|
1605
|
+
}
|
|
1606
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
1607
|
+
_validateSkillId(skillId);
|
|
1608
|
+
const runtime = loadRuntimeConfig();
|
|
1609
|
+
const skillsDir = join(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
1610
|
+
try {
|
|
1611
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
writeFileSync(join(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
1615
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
1616
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
1617
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
1618
|
+
saveRuntimeConfig(runtime);
|
|
1619
|
+
return runtime;
|
|
1620
|
+
}
|
|
1621
|
+
function deleteCustomSkill(skillId, category) {
|
|
1622
|
+
_validateSkillId(skillId);
|
|
1623
|
+
const runtime = loadRuntimeConfig();
|
|
1624
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
1625
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
1626
|
+
if (idx >= 0) {
|
|
1627
|
+
if (skills[idx].builtin) return null;
|
|
1628
|
+
skills.splice(idx, 1);
|
|
1629
|
+
const skillPath = join(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
1630
|
+
try {
|
|
1631
|
+
if (existsSync3(skillPath)) unlinkSync(skillPath);
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
saveRuntimeConfig(runtime);
|
|
1777
1635
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1636
|
+
return runtime;
|
|
1637
|
+
}
|
|
1638
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
1639
|
+
const runtime = loadRuntimeConfig();
|
|
1640
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
1641
|
+
for (const s of catData.skills || []) {
|
|
1642
|
+
if (s.id === skillId) {
|
|
1643
|
+
s.enabled = enabled;
|
|
1644
|
+
break;
|
|
1645
|
+
}
|
|
1787
1646
|
}
|
|
1788
1647
|
}
|
|
1789
|
-
|
|
1790
|
-
return
|
|
1648
|
+
saveRuntimeConfig(runtime);
|
|
1649
|
+
return runtime;
|
|
1791
1650
|
}
|
|
1792
|
-
function
|
|
1651
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
1652
|
+
const runtime = loadRuntimeConfig();
|
|
1653
|
+
const cat = runtime.L3_skills?.[category];
|
|
1654
|
+
if (cat) cat.category_enabled = enabled;
|
|
1655
|
+
saveRuntimeConfig(runtime);
|
|
1656
|
+
return runtime;
|
|
1657
|
+
}
|
|
1658
|
+
function getProfileOrDetect() {
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
function getAgentRules(runtime) {
|
|
1662
|
+
return runtime.agent_rules || {};
|
|
1663
|
+
}
|
|
1664
|
+
function _agentCategoryEnabledRules(cat) {
|
|
1665
|
+
if (!cat || cat.category_enabled === false) return [];
|
|
1666
|
+
return (cat.rules || []).filter((r) => r.enabled !== false);
|
|
1667
|
+
}
|
|
1668
|
+
function getEnabledAgentExtensions(runtime, agentId) {
|
|
1669
|
+
const agentData = runtime.agent_rules?.[agentId];
|
|
1670
|
+
if (!agentData) return null;
|
|
1671
|
+
const safeAction = _agentCategoryEnabledRules(agentData.builtin_safe_action_types);
|
|
1672
|
+
const safeSkills = _agentCategoryEnabledRules(agentData.builtin_safe_skills);
|
|
1673
|
+
const cmdPatterns = _agentCategoryEnabledRules(agentData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
1674
|
+
pattern: r.pattern,
|
|
1675
|
+
risk_level: r.risk_level || "",
|
|
1676
|
+
risk_type: r.risk_type || "",
|
|
1677
|
+
reason: r.reason || ""
|
|
1678
|
+
}));
|
|
1679
|
+
const injKeywords = _agentCategoryEnabledRules(agentData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
1680
|
+
keyword: r.keyword,
|
|
1681
|
+
risk_level: r.risk_level || "",
|
|
1682
|
+
lang: r.lang || "en"
|
|
1683
|
+
}));
|
|
1684
|
+
const sensitivePaths = _agentCategoryEnabledRules(agentData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
1685
|
+
path: r.path,
|
|
1686
|
+
risk_level: r.risk_level || "",
|
|
1687
|
+
desc: r.desc || "",
|
|
1688
|
+
credential: r.credential,
|
|
1689
|
+
read_only: r.read_only,
|
|
1690
|
+
deny_mode: r.deny_mode
|
|
1691
|
+
}));
|
|
1793
1692
|
return {
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
injection_detection: {
|
|
1803
|
-
category_enabled: true,
|
|
1804
|
-
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
1805
|
-
rules: _l1InjectionKeywordsToRules()
|
|
1806
|
-
},
|
|
1807
|
-
sensitive_path_access: {
|
|
1808
|
-
category_enabled: true,
|
|
1809
|
-
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
|
|
1810
|
-
rules: _l2SensitivePathsToRules()
|
|
1811
|
-
},
|
|
1812
|
-
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1813
|
-
},
|
|
1814
|
-
L2_rules: {
|
|
1815
|
-
intent_consistency: {
|
|
1816
|
-
category_enabled: true,
|
|
1817
|
-
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
1818
|
-
rules: _l2IntentPatternsToRules()
|
|
1819
|
-
},
|
|
1820
|
-
password_modify_consent: {
|
|
1821
|
-
category_enabled: true,
|
|
1822
|
-
category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
|
|
1823
|
-
rules: _l2PasswordPatternsToRules()
|
|
1824
|
-
},
|
|
1825
|
-
user_deletion_consent: {
|
|
1826
|
-
category_enabled: true,
|
|
1827
|
-
category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
|
|
1828
|
-
rules: _l2UserDeletionPatternsToRules()
|
|
1829
|
-
},
|
|
1830
|
-
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
1831
|
-
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1832
|
-
},
|
|
1833
|
-
L3_skills: _l3SkillsToRules()
|
|
1693
|
+
agent_id: agentId,
|
|
1694
|
+
description: "",
|
|
1695
|
+
extra_command_patterns: cmdPatterns,
|
|
1696
|
+
extra_injection_keywords: injKeywords,
|
|
1697
|
+
extra_sensitive_paths: sensitivePaths,
|
|
1698
|
+
builtin_safe_skills: safeSkills.filter((r) => r.value).map((r) => r.value),
|
|
1699
|
+
builtin_safe_action_types: safeAction.filter((r) => r.value).map((r) => r.value),
|
|
1700
|
+
extra_skills: []
|
|
1834
1701
|
};
|
|
1835
1702
|
}
|
|
1836
|
-
function
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
])
|
|
1703
|
+
function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
|
|
1704
|
+
const runtime = loadRuntimeConfig();
|
|
1705
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1706
|
+
if (cat) {
|
|
1707
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1708
|
+
for (const r of cat.rules || []) {
|
|
1709
|
+
if (_ruleFingerprint(r) === targetFp) {
|
|
1710
|
+
r.enabled = enabled;
|
|
1711
|
+
break;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1842
1714
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1846
|
-
return JSON.stringify([rule.id || ""]);
|
|
1715
|
+
saveRuntimeConfig(runtime);
|
|
1716
|
+
return runtime;
|
|
1847
1717
|
}
|
|
1848
|
-
function
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1718
|
+
function addAgentRule(agentId, category, rule) {
|
|
1719
|
+
const runtime = loadRuntimeConfig();
|
|
1720
|
+
if (!runtime.agent_rules) runtime.agent_rules = {};
|
|
1721
|
+
if (!runtime.agent_rules[agentId]) runtime.agent_rules[agentId] = {};
|
|
1722
|
+
const cats = runtime.agent_rules[agentId];
|
|
1723
|
+
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
1724
|
+
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
1725
|
+
saveRuntimeConfig(runtime);
|
|
1726
|
+
return runtime;
|
|
1727
|
+
}
|
|
1728
|
+
function deleteAgentRule(agentId, category, ruleFp) {
|
|
1729
|
+
const runtime = loadRuntimeConfig();
|
|
1730
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1731
|
+
if (cat) {
|
|
1732
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
1733
|
+
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
1734
|
+
}
|
|
1735
|
+
saveRuntimeConfig(runtime);
|
|
1736
|
+
return runtime;
|
|
1737
|
+
}
|
|
1738
|
+
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
1739
|
+
const runtime = loadRuntimeConfig();
|
|
1740
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
1741
|
+
if (cat) cat.category_enabled = enabled;
|
|
1742
|
+
saveRuntimeConfig(runtime);
|
|
1743
|
+
return runtime;
|
|
1744
|
+
}
|
|
1745
|
+
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;
|
|
1746
|
+
var init_runtime_config = __esm({
|
|
1747
|
+
"src/infra/runtime-config.ts"() {
|
|
1748
|
+
"use strict";
|
|
1749
|
+
init_esm_shims();
|
|
1750
|
+
init_heuristic();
|
|
1751
|
+
init_logic_rules();
|
|
1752
|
+
init_skill_loader();
|
|
1753
|
+
__dirname3 = dirname2(fileURLToPath3(import.meta.url));
|
|
1754
|
+
RUNTIME_CONFIG_DIR = join(homedir(), ".config", "agentmoss");
|
|
1755
|
+
RUNTIME_CONFIG_PATH = join(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
1756
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
1757
|
+
LAYER_DISABLE_ENV = {
|
|
1758
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
1759
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
1760
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
1761
|
+
};
|
|
1762
|
+
DENY_MODE_LABELS = {
|
|
1763
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
1764
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
1765
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
1766
|
+
};
|
|
1767
|
+
_AGENTS_DIR_PROD = join(__dirname3, "agents");
|
|
1768
|
+
_AGENTS_DIR_DEV = join(__dirname3, "..", "..", "..", "agent_moss", "agents");
|
|
1769
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
// src/agents/index.ts
|
|
1774
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1775
|
+
import { resolve as resolve2, dirname as dirname3 } from "path";
|
|
1776
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1777
|
+
function resolveAgentsDir() {
|
|
1778
|
+
if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
1779
|
+
if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
1780
|
+
return AGENTS_DIR_PROD;
|
|
1781
|
+
}
|
|
1782
|
+
function getAgentExtensions(agentId) {
|
|
1783
|
+
if (!agentId) return null;
|
|
1784
|
+
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
1785
|
+
let ext = null;
|
|
1786
|
+
try {
|
|
1787
|
+
const { hasRuntimeConfig: hasRuntimeConfig2, loadRuntimeConfig: loadRuntimeConfig2, getEnabledAgentExtensions: getEnabledAgentExtensions2 } = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
1788
|
+
if (hasRuntimeConfig2()) {
|
|
1789
|
+
ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
|
|
1855
1790
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
const
|
|
1860
|
-
const
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1791
|
+
} catch {
|
|
1792
|
+
}
|
|
1793
|
+
if (ext === null) {
|
|
1794
|
+
const agentsDir = resolveAgentsDir();
|
|
1795
|
+
const rulesFile = resolve2(agentsDir, agentId, "rules.json");
|
|
1796
|
+
if (!existsSync4(rulesFile)) {
|
|
1797
|
+
cache.set(agentId, null);
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
try {
|
|
1801
|
+
const raw = readFileSync3(rulesFile, "utf-8");
|
|
1802
|
+
ext = JSON.parse(raw);
|
|
1803
|
+
if (!ext.agent_id) {
|
|
1804
|
+
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
1805
|
+
cache.set(agentId, null);
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
} catch (e) {
|
|
1809
|
+
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
1810
|
+
cache.set(agentId, null);
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
cache.set(agentId, ext);
|
|
1815
|
+
console.log(
|
|
1816
|
+
`[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`
|
|
1817
|
+
);
|
|
1818
|
+
return ext;
|
|
1819
|
+
}
|
|
1820
|
+
var __filename3, __dirname4, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
1821
|
+
var init_agents = __esm({
|
|
1822
|
+
"src/agents/index.ts"() {
|
|
1823
|
+
"use strict";
|
|
1824
|
+
init_esm_shims();
|
|
1825
|
+
__filename3 = fileURLToPath4(import.meta.url);
|
|
1826
|
+
__dirname4 = dirname3(__filename3);
|
|
1827
|
+
AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
|
|
1828
|
+
AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
|
|
1829
|
+
cache = /* @__PURE__ */ new Map();
|
|
1830
|
+
}
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
// src/engine/heuristic.ts
|
|
1834
|
+
function runHeuristicCheck(input) {
|
|
1835
|
+
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
1836
|
+
const matched = [];
|
|
1837
|
+
let highestSeverity = "low";
|
|
1838
|
+
let highestRiskType = "";
|
|
1839
|
+
const userRules = getCustomRules();
|
|
1840
|
+
for (const rule of userRules) {
|
|
1841
|
+
try {
|
|
1842
|
+
const re = new RegExp(rule.pattern);
|
|
1843
|
+
if (re.test(command)) {
|
|
1844
|
+
if (rule.action === "Deny") {
|
|
1845
|
+
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
1846
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1847
|
+
highestSeverity = rule.severity;
|
|
1848
|
+
highestRiskType = "custom_rule";
|
|
1849
|
+
}
|
|
1879
1850
|
}
|
|
1880
|
-
if (existing.deny_mode) _syncDenyModeFields(existing);
|
|
1881
|
-
} else {
|
|
1882
|
-
const newR = { ...srcR, enabled: true };
|
|
1883
|
-
userRules.push(newR);
|
|
1884
1851
|
}
|
|
1852
|
+
} catch {
|
|
1885
1853
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1854
|
+
}
|
|
1855
|
+
let topRiskType = "";
|
|
1856
|
+
for (const rule of DANGEROUS_PATTERNS) {
|
|
1857
|
+
if (rule.pattern.test(command)) {
|
|
1858
|
+
matched.push(rule.description);
|
|
1859
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1860
|
+
highestSeverity = rule.severity;
|
|
1861
|
+
topRiskType = rule.riskType;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
highestRiskType = topRiskType || highestRiskType;
|
|
1866
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
1867
|
+
if (agentExt?.extra_command_patterns) {
|
|
1868
|
+
for (const rule of agentExt.extra_command_patterns) {
|
|
1869
|
+
try {
|
|
1870
|
+
const re = new RegExp(rule.pattern, "i");
|
|
1871
|
+
if (re.test(command)) {
|
|
1872
|
+
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
1873
|
+
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
1874
|
+
highestSeverity = rule.risk_level;
|
|
1875
|
+
highestRiskType = rule.risk_type;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
} catch {
|
|
1890
1879
|
}
|
|
1891
1880
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1881
|
+
}
|
|
1882
|
+
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
1883
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
1884
|
+
if (injectionHits.length > 0) {
|
|
1885
|
+
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
1886
|
+
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
1887
|
+
if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
|
|
1888
|
+
highestSeverity = topHit.riskLevel;
|
|
1889
|
+
highestRiskType = "prompt_injection";
|
|
1897
1890
|
}
|
|
1898
1891
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1892
|
+
const actionTypeL = input.aNext.actionType.toLowerCase();
|
|
1893
|
+
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
1894
|
+
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
1895
|
+
if (!isExcludedType) {
|
|
1896
|
+
const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
1897
|
+
if (pathResult) return pathResult;
|
|
1898
|
+
}
|
|
1899
|
+
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
1900
|
+
if (dangerousResult) return dangerousResult;
|
|
1901
|
+
const lateralResult = checkLateralMovement(detailL);
|
|
1902
|
+
if (lateralResult) return lateralResult;
|
|
1903
|
+
if (matched.length > 0) {
|
|
1904
|
+
return {
|
|
1905
|
+
allowed: false,
|
|
1906
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
1907
|
+
riskLevel: highestSeverity,
|
|
1908
|
+
riskType: highestRiskType,
|
|
1909
|
+
confidence: 95,
|
|
1910
|
+
source: "heuristic",
|
|
1911
|
+
violatedLayers: ["1.1"]
|
|
1912
|
+
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
1913
|
+
};
|
|
1902
1914
|
}
|
|
1915
|
+
return null;
|
|
1903
1916
|
}
|
|
1904
|
-
function
|
|
1905
|
-
const
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
userLayer[catName] = srcCat;
|
|
1910
|
-
continue;
|
|
1917
|
+
function checkInjectionKeywords(text, extraKeywords) {
|
|
1918
|
+
const hits = [];
|
|
1919
|
+
for (const entry of INJECTION_KEYWORDS) {
|
|
1920
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1921
|
+
hits.push(entry);
|
|
1911
1922
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
|
|
1923
|
+
}
|
|
1924
|
+
if (extraKeywords) {
|
|
1925
|
+
for (const entry of extraKeywords) {
|
|
1926
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
1927
|
+
hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
|
|
1928
|
+
}
|
|
1919
1929
|
}
|
|
1920
1930
|
}
|
|
1931
|
+
return hits;
|
|
1921
1932
|
}
|
|
1922
|
-
function
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
if (!cfg.layers) cfg.layers = {};
|
|
1926
|
-
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
1927
|
-
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
1928
|
-
}
|
|
1929
|
-
_migrateCategoriesAcrossLayers(
|
|
1930
|
-
cfg,
|
|
1931
|
-
"L2_rules",
|
|
1932
|
-
"L1_rules",
|
|
1933
|
-
["sensitive_path_access"]
|
|
1934
|
-
);
|
|
1935
|
-
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1936
|
-
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
1937
|
-
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1938
|
-
return cfg;
|
|
1933
|
+
function isInlineScriptCommand(actionDetail) {
|
|
1934
|
+
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;
|
|
1935
|
+
return re.test(actionDetail);
|
|
1939
1936
|
}
|
|
1940
|
-
function
|
|
1941
|
-
const
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
if (
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
const fp = _ruleFingerprint(r);
|
|
1956
|
-
if (!dstFps.has(fp)) {
|
|
1957
|
-
(dstCat.rules = dstCat.rules || []).push(r);
|
|
1958
|
-
dstFps.add(fp);
|
|
1937
|
+
function severityRank(s) {
|
|
1938
|
+
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
1939
|
+
return ranks[s] || 0;
|
|
1940
|
+
}
|
|
1941
|
+
function checkSensitivePath(command, cwd, agentExt) {
|
|
1942
|
+
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
1943
|
+
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/];
|
|
1944
|
+
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
1945
|
+
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"]);
|
|
1946
|
+
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));
|
|
1947
|
+
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
1948
|
+
if (command.includes(sp.path.toLowerCase())) {
|
|
1949
|
+
if (sp.path === "/dev/random") {
|
|
1950
|
+
if (isWriteOpFlag) {
|
|
1951
|
+
continue;
|
|
1959
1952
|
}
|
|
1953
|
+
return {
|
|
1954
|
+
allowed: false,
|
|
1955
|
+
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1956
|
+
riskLevel: sp.riskLevel,
|
|
1957
|
+
riskType: "file_access",
|
|
1958
|
+
confidence: 90,
|
|
1959
|
+
source: "heuristic",
|
|
1960
|
+
violatedLayers: ["1.1"]
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1964
|
+
continue;
|
|
1960
1965
|
}
|
|
1966
|
+
return {
|
|
1967
|
+
allowed: false,
|
|
1968
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1969
|
+
riskLevel: sp.riskLevel,
|
|
1970
|
+
riskType: "file_access",
|
|
1971
|
+
confidence: 90,
|
|
1972
|
+
source: "heuristic",
|
|
1973
|
+
violatedLayers: ["1.1"],
|
|
1974
|
+
isCredential: !!sp.credential
|
|
1975
|
+
};
|
|
1961
1976
|
}
|
|
1962
|
-
delete src[catName];
|
|
1963
1977
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
}));
|
|
1972
|
-
}
|
|
1973
|
-
function getEnabledL2IntentPatterns(runtime) {
|
|
1974
|
-
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
1975
|
-
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1976
|
-
intent_keywords: r.intent_keywords || [],
|
|
1977
|
-
dangerous_actions: r.dangerous_actions || [],
|
|
1978
|
-
reason: r.reason || ""
|
|
1979
|
-
}));
|
|
1980
|
-
}
|
|
1981
|
-
function getEnabledL2PasswordPatterns(runtime) {
|
|
1982
|
-
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
1983
|
-
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
1984
|
-
}
|
|
1985
|
-
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
1986
|
-
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
1987
|
-
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
1988
|
-
}
|
|
1989
|
-
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
1990
|
-
const cat = runtime.L2_rules?.[categoryKey];
|
|
1991
|
-
return cat?.category_enabled ?? true;
|
|
1992
|
-
}
|
|
1993
|
-
function getDisabledSkillIds(runtime) {
|
|
1994
|
-
const disabled = /* @__PURE__ */ new Set();
|
|
1995
|
-
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
1996
|
-
if (!catData.category_enabled) {
|
|
1997
|
-
for (const s of catData.skills || []) disabled.add(s.id);
|
|
1998
|
-
} else {
|
|
1999
|
-
for (const s of catData.skills || []) {
|
|
2000
|
-
if (s.enabled === false) disabled.add(s.id);
|
|
1978
|
+
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
1979
|
+
const expanded = `${expandedCwd}/${sp.path}`;
|
|
1980
|
+
const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1981
|
+
const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
1982
|
+
if (pathRe.test(command) || expandedRe.test(command)) {
|
|
1983
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
1984
|
+
continue;
|
|
2001
1985
|
}
|
|
1986
|
+
return {
|
|
1987
|
+
allowed: false,
|
|
1988
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1989
|
+
riskLevel: sp.riskLevel,
|
|
1990
|
+
riskType: "file_access",
|
|
1991
|
+
confidence: 90,
|
|
1992
|
+
source: "heuristic",
|
|
1993
|
+
violatedLayers: ["1.1"],
|
|
1994
|
+
isCredential: !!sp.credential
|
|
1995
|
+
};
|
|
2002
1996
|
}
|
|
2003
1997
|
}
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
1998
|
+
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1999
|
+
for (const sp of agentExt.extra_sensitive_paths) {
|
|
2000
|
+
const spPath = sp.path.toLowerCase();
|
|
2001
|
+
const expanded = `${expandedCwd}/${spPath}`;
|
|
2002
|
+
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
2003
|
+
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
2004
|
+
if (denyMode === "deny_read") {
|
|
2005
|
+
if (isWriteOpFlag) continue;
|
|
2006
|
+
} else if (denyMode === "deny_write") {
|
|
2007
|
+
if (!isWriteOpFlag) continue;
|
|
2008
|
+
}
|
|
2009
|
+
return {
|
|
2010
|
+
allowed: false,
|
|
2011
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
2012
|
+
riskLevel: sp.risk_level,
|
|
2013
|
+
riskType: "file_access",
|
|
2014
|
+
confidence: 90,
|
|
2015
|
+
source: "heuristic",
|
|
2016
|
+
violatedLayers: ["1.1"],
|
|
2017
|
+
isCredential: !!sp.credential
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
for (const cp of CREDENTIAL_PATHS) {
|
|
2023
|
+
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2024
|
+
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
2025
|
+
if (re.test(command)) {
|
|
2026
|
+
return {
|
|
2027
|
+
allowed: false,
|
|
2028
|
+
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
2029
|
+
riskLevel: cp.riskLevel,
|
|
2030
|
+
riskType: "file_access",
|
|
2031
|
+
confidence: 90,
|
|
2032
|
+
source: "heuristic",
|
|
2033
|
+
violatedLayers: ["1.1"],
|
|
2034
|
+
isCredential: true
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
2039
|
+
return {
|
|
2040
|
+
allowed: false,
|
|
2041
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
2042
|
+
riskLevel: "critical",
|
|
2043
|
+
riskType: "file_access",
|
|
2044
|
+
confidence: 90,
|
|
2045
|
+
source: "heuristic",
|
|
2046
|
+
violatedLayers: ["1.1"]
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
return null;
|
|
2011
2050
|
}
|
|
2012
|
-
function
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2051
|
+
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2052
|
+
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2053
|
+
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
2054
|
+
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
2055
|
+
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
2056
|
+
return {
|
|
2057
|
+
allowed: false,
|
|
2058
|
+
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
2059
|
+
riskLevel: "high",
|
|
2060
|
+
riskType: "script_execution",
|
|
2061
|
+
confidence: 90,
|
|
2062
|
+
source: "heuristic",
|
|
2063
|
+
violatedLayers: ["1.1"]
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
2067
|
+
return {
|
|
2068
|
+
allowed: false,
|
|
2069
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
2070
|
+
riskLevel: "critical",
|
|
2071
|
+
riskType: "file_access",
|
|
2072
|
+
confidence: 95,
|
|
2073
|
+
source: "heuristic",
|
|
2074
|
+
violatedLayers: ["1.1"]
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
return null;
|
|
2017
2078
|
}
|
|
2018
|
-
function
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2021
|
-
if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
|
|
2022
|
-
return [];
|
|
2079
|
+
function extractRemoteHost(command) {
|
|
2080
|
+
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
2081
|
+
return m ? m[1] : null;
|
|
2023
2082
|
}
|
|
2024
|
-
function
|
|
2025
|
-
const
|
|
2026
|
-
|
|
2027
|
-
if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
|
|
2028
|
-
return [];
|
|
2083
|
+
function isLocalHost(host) {
|
|
2084
|
+
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
2085
|
+
return localNames.includes(host.replace(/.*@/, ""));
|
|
2029
2086
|
}
|
|
2030
|
-
function
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2087
|
+
function checkLateralMovement(actionDetail) {
|
|
2088
|
+
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
2089
|
+
const targetHost = extractRemoteHost(actionDetail);
|
|
2090
|
+
if (targetHost && !isLocalHost(targetHost)) {
|
|
2091
|
+
return {
|
|
2092
|
+
allowed: false,
|
|
2093
|
+
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
2094
|
+
riskLevel: "medium",
|
|
2095
|
+
riskType: "lateral_movement",
|
|
2096
|
+
confidence: 80,
|
|
2097
|
+
source: "heuristic",
|
|
2098
|
+
violatedLayers: ["1.1"]
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2035
2101
|
}
|
|
2036
|
-
return
|
|
2102
|
+
return null;
|
|
2037
2103
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2104
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
|
|
2105
|
+
var init_heuristic = __esm({
|
|
2106
|
+
"src/engine/heuristic.ts"() {
|
|
2107
|
+
"use strict";
|
|
2108
|
+
init_esm_shims();
|
|
2109
|
+
init_config();
|
|
2110
|
+
init_agents();
|
|
2111
|
+
init_logic_rules();
|
|
2112
|
+
INJECTION_KEYWORDS = [
|
|
2113
|
+
// ---- Instruction Override ----
|
|
2114
|
+
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
2115
|
+
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
2116
|
+
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
2117
|
+
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
2118
|
+
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
2119
|
+
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
2120
|
+
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
2121
|
+
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
2122
|
+
// ---- Role Hijacking ----
|
|
2123
|
+
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
2124
|
+
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
2125
|
+
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
2126
|
+
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
2127
|
+
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2128
|
+
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
2129
|
+
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
2130
|
+
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
2131
|
+
// ---- Command Injection ----
|
|
2132
|
+
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
2133
|
+
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
2134
|
+
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
2135
|
+
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
2136
|
+
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
2137
|
+
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
2138
|
+
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
2139
|
+
// ---- Social Engineering ----
|
|
2140
|
+
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
2141
|
+
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
2142
|
+
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
2143
|
+
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
2144
|
+
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
2145
|
+
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
2146
|
+
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
2147
|
+
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
2148
|
+
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
2149
|
+
// ---- XSS ----
|
|
2150
|
+
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
2151
|
+
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
2152
|
+
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
2153
|
+
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
2154
|
+
// ---- Fake Boundaries ----
|
|
2155
|
+
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
2156
|
+
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
2157
|
+
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
2158
|
+
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
2159
|
+
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
2160
|
+
// ---- Developer Mode ----
|
|
2161
|
+
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
2162
|
+
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
2163
|
+
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
2164
|
+
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
2165
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
2166
|
+
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
2167
|
+
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
2168
|
+
// 由 agent_ext 机制注入,不再污染通用层。
|
|
2169
|
+
];
|
|
2170
|
+
DANGEROUS_PATTERNS = [
|
|
2171
|
+
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
2172
|
+
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
2173
|
+
{ 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" },
|
|
2174
|
+
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
2175
|
+
// 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
|
|
2176
|
+
{ 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" },
|
|
2177
|
+
// 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
|
|
2178
|
+
{ 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" },
|
|
2179
|
+
// 4. rm -f absolute path (no -r) — 同样排除临时目录
|
|
2180
|
+
{ 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" },
|
|
2181
|
+
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
2182
|
+
{ 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" },
|
|
2183
|
+
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
2184
|
+
{ 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" },
|
|
2185
|
+
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2186
|
+
// --- Critical: remote code execution ---
|
|
2187
|
+
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2188
|
+
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2189
|
+
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2190
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2191
|
+
// --- Critical: reverse shell ---
|
|
2192
|
+
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2193
|
+
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
2194
|
+
// --- Critical: sensitive file access ---
|
|
2195
|
+
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
2196
|
+
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
2197
|
+
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
2198
|
+
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2199
|
+
// --- Critical: privilege escalation / container escape ---
|
|
2200
|
+
{ 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" },
|
|
2201
|
+
// --- High: dangerous permissions / ownership ---
|
|
2202
|
+
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
2203
|
+
{ 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" },
|
|
2204
|
+
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2205
|
+
{ 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" },
|
|
2206
|
+
// --- High: system config overwrite ---
|
|
2207
|
+
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2208
|
+
// --- High: download script + execute risk ---
|
|
2209
|
+
{ 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" },
|
|
2210
|
+
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
2211
|
+
// --- High: package manager pipe-to-shell ---
|
|
2212
|
+
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
2213
|
+
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
2214
|
+
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
2215
|
+
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
2216
|
+
{ 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" },
|
|
2217
|
+
// --- High: network port scanning (横向移动侦察) ---
|
|
2218
|
+
{ 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" },
|
|
2219
|
+
{ 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" },
|
|
2220
|
+
// --- Medium: credential export ---
|
|
2221
|
+
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2222
|
+
// --- High: shell rc file append (persistence) ---
|
|
2223
|
+
{ 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" },
|
|
2224
|
+
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2225
|
+
{ 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" },
|
|
2226
|
+
// --- High: non-interactive password modification ---
|
|
2227
|
+
{ 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" },
|
|
2228
|
+
{ 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" },
|
|
2229
|
+
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
2230
|
+
{ 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" },
|
|
2231
|
+
{ 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" },
|
|
2232
|
+
{ 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" },
|
|
2233
|
+
// --- High: non-interactive password modification (extended) ---
|
|
2234
|
+
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
2235
|
+
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
2236
|
+
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
2237
|
+
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
2238
|
+
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
2239
|
+
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
2240
|
+
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
2241
|
+
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
2242
|
+
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
2243
|
+
// --- High: generic command -p parameter password setting ---
|
|
2244
|
+
{ 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" }
|
|
2245
|
+
];
|
|
2246
|
+
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
2043
2247
|
}
|
|
2044
|
-
|
|
2248
|
+
});
|
|
2249
|
+
|
|
2250
|
+
// src/engine/token-stats.ts
|
|
2251
|
+
var token_stats_exports = {};
|
|
2252
|
+
__export(token_stats_exports, {
|
|
2253
|
+
getRecentRecords: () => getRecentRecords,
|
|
2254
|
+
getTokenStats: () => getTokenStats,
|
|
2255
|
+
getTokenTrend: () => getTokenTrend,
|
|
2256
|
+
recordTokenUsage: () => recordTokenUsage,
|
|
2257
|
+
resetTokenStats: () => resetTokenStats
|
|
2258
|
+
});
|
|
2259
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2260
|
+
import { join as join4 } from "path";
|
|
2261
|
+
import { homedir as homedir3 } from "os";
|
|
2262
|
+
function getStatsPath() {
|
|
2263
|
+
const envPath = process.env[ENV_STATS_PATH];
|
|
2264
|
+
return envPath || STATS_FILE;
|
|
2045
2265
|
}
|
|
2046
|
-
function
|
|
2047
|
-
if (!
|
|
2048
|
-
|
|
2266
|
+
function loadAllRecords(statsPath) {
|
|
2267
|
+
if (!existsSync7(statsPath)) return [];
|
|
2268
|
+
try {
|
|
2269
|
+
const data = JSON.parse(readFileSync7(statsPath, "utf-8"));
|
|
2270
|
+
if (Array.isArray(data)) return data;
|
|
2271
|
+
if (data && Array.isArray(data.records)) return data.records;
|
|
2272
|
+
return [];
|
|
2273
|
+
} catch (e) {
|
|
2274
|
+
return [];
|
|
2049
2275
|
}
|
|
2050
2276
|
}
|
|
2051
|
-
function
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
}
|
|
2057
|
-
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
2058
|
-
const runtime = loadRuntimeConfig();
|
|
2059
|
-
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2060
|
-
if (r.id === ruleId) {
|
|
2061
|
-
r.enabled = enabled;
|
|
2062
|
-
break;
|
|
2063
|
-
}
|
|
2277
|
+
function saveRecords(statsPath, records) {
|
|
2278
|
+
try {
|
|
2279
|
+
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
2280
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
2281
|
+
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
2282
|
+
} catch (e) {
|
|
2064
2283
|
}
|
|
2065
|
-
saveRuntimeConfig(runtime);
|
|
2066
|
-
return runtime;
|
|
2067
2284
|
}
|
|
2068
|
-
function
|
|
2069
|
-
const
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2285
|
+
function recordTokenUsage(record) {
|
|
2286
|
+
const statsPath = getStatsPath();
|
|
2287
|
+
const records = loadAllRecords(statsPath);
|
|
2288
|
+
records.push({
|
|
2289
|
+
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2290
|
+
session_id: record.sessionId,
|
|
2291
|
+
step: record.step,
|
|
2292
|
+
model: record.model,
|
|
2293
|
+
prompt_tokens: record.promptTokens,
|
|
2294
|
+
completion_tokens: record.completionTokens,
|
|
2295
|
+
total_tokens: record.totalTokens
|
|
2296
|
+
});
|
|
2297
|
+
if (records.length > MAX_RECORDS) {
|
|
2298
|
+
records.splice(0, records.length - MAX_RECORDS);
|
|
2076
2299
|
}
|
|
2077
|
-
|
|
2078
|
-
return runtime;
|
|
2300
|
+
saveRecords(statsPath, records);
|
|
2079
2301
|
}
|
|
2080
|
-
function
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2302
|
+
function filterByDate(records, days = 0, startDate, endDate) {
|
|
2303
|
+
if (startDate || endDate) {
|
|
2304
|
+
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
2305
|
+
let endDt = endDate ? new Date(endDate) : void 0;
|
|
2306
|
+
if (endDate) {
|
|
2307
|
+
const e = new Date(endDate);
|
|
2308
|
+
e.setHours(23, 59, 59, 999);
|
|
2309
|
+
endDt = e;
|
|
2086
2310
|
}
|
|
2311
|
+
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
2312
|
+
return records.filter((r) => {
|
|
2313
|
+
try {
|
|
2314
|
+
const ts = new Date(r.timestamp).getTime();
|
|
2315
|
+
return ts >= startDt.getTime() && ts <= endMs;
|
|
2316
|
+
} catch {
|
|
2317
|
+
return true;
|
|
2318
|
+
}
|
|
2319
|
+
});
|
|
2320
|
+
} else if (days > 0) {
|
|
2321
|
+
const cutoff = Date.now() - days * 864e5;
|
|
2322
|
+
return records.filter((r) => {
|
|
2323
|
+
try {
|
|
2324
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
2325
|
+
} catch {
|
|
2326
|
+
return true;
|
|
2327
|
+
}
|
|
2328
|
+
});
|
|
2087
2329
|
}
|
|
2088
|
-
|
|
2089
|
-
return runtime;
|
|
2090
|
-
}
|
|
2091
|
-
function updateCategoryEnabled(layer, category, enabled) {
|
|
2092
|
-
const runtime = loadRuntimeConfig();
|
|
2093
|
-
const cat = runtime[layer]?.[category];
|
|
2094
|
-
if (cat) cat.category_enabled = enabled;
|
|
2095
|
-
saveRuntimeConfig(runtime);
|
|
2096
|
-
return runtime;
|
|
2330
|
+
return records;
|
|
2097
2331
|
}
|
|
2098
|
-
function
|
|
2099
|
-
|
|
2100
|
-
const
|
|
2101
|
-
const
|
|
2102
|
-
const
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2332
|
+
function aggregate(records) {
|
|
2333
|
+
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
2334
|
+
const byStep = {};
|
|
2335
|
+
const byModel = {};
|
|
2336
|
+
const byDate = {};
|
|
2337
|
+
const bucket = (b, key, r) => {
|
|
2338
|
+
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
2339
|
+
e.prompt_tokens += r.prompt_tokens || 0;
|
|
2340
|
+
e.completion_tokens += r.completion_tokens || 0;
|
|
2341
|
+
e.total_tokens += r.total_tokens || 0;
|
|
2342
|
+
e.calls += 1;
|
|
2343
|
+
};
|
|
2344
|
+
for (const r of records) {
|
|
2345
|
+
totalPrompt += r.prompt_tokens || 0;
|
|
2346
|
+
totalCompletion += r.completion_tokens || 0;
|
|
2347
|
+
totalTotal += r.total_tokens || 0;
|
|
2348
|
+
bucket(byStep, r.step || "unknown", r);
|
|
2349
|
+
bucket(byModel, r.model || "unknown", r);
|
|
2350
|
+
let dateKey = "unknown";
|
|
2351
|
+
try {
|
|
2352
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2353
|
+
} catch {
|
|
2354
|
+
}
|
|
2355
|
+
bucket(byDate, dateKey, r);
|
|
2106
2356
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
return
|
|
2357
|
+
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
2358
|
+
const sortedByDate = {};
|
|
2359
|
+
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
2360
|
+
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
2111
2361
|
}
|
|
2112
|
-
function
|
|
2113
|
-
const
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2362
|
+
function getTokenStats(days = 0, startDate, endDate) {
|
|
2363
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2364
|
+
const a = aggregate(records);
|
|
2365
|
+
return {
|
|
2366
|
+
total_prompt_tokens: a.totalPrompt,
|
|
2367
|
+
total_completion_tokens: a.totalCompletion,
|
|
2368
|
+
total_tokens: a.totalTotal,
|
|
2369
|
+
total_calls: records.length,
|
|
2370
|
+
by_step: a.byStep,
|
|
2371
|
+
by_model: a.byModel,
|
|
2372
|
+
by_date: a.byDate
|
|
2373
|
+
};
|
|
2123
2374
|
}
|
|
2124
|
-
function
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2128
|
-
try {
|
|
2129
|
-
mkdirSync2(skillsDir, { recursive: true });
|
|
2130
|
-
} catch {
|
|
2131
|
-
}
|
|
2132
|
-
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2133
|
-
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2134
|
-
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2135
|
-
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
2136
|
-
saveRuntimeConfig(runtime);
|
|
2137
|
-
return runtime;
|
|
2375
|
+
function getRecentRecords(limit = 20) {
|
|
2376
|
+
const records = loadAllRecords(getStatsPath());
|
|
2377
|
+
return records.slice(-limit);
|
|
2138
2378
|
}
|
|
2139
|
-
function
|
|
2140
|
-
|
|
2141
|
-
const
|
|
2142
|
-
const
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
if (skills[idx].builtin) return null;
|
|
2146
|
-
skills.splice(idx, 1);
|
|
2147
|
-
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2379
|
+
function getTokenTrend(days = 0, startDate, endDate) {
|
|
2380
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
2381
|
+
const series = {};
|
|
2382
|
+
const datesSet = /* @__PURE__ */ new Set();
|
|
2383
|
+
for (const r of records) {
|
|
2384
|
+
let dateKey;
|
|
2148
2385
|
try {
|
|
2149
|
-
|
|
2386
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
2150
2387
|
} catch {
|
|
2388
|
+
continue;
|
|
2151
2389
|
}
|
|
2152
|
-
|
|
2390
|
+
const model = r.model || "unknown";
|
|
2391
|
+
const tokens = r.total_tokens || 0;
|
|
2392
|
+
datesSet.add(dateKey);
|
|
2393
|
+
series[model] = series[model] || {};
|
|
2394
|
+
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
2153
2395
|
}
|
|
2154
|
-
|
|
2396
|
+
const dates = Array.from(datesSet).sort();
|
|
2397
|
+
const models = Object.keys(series).sort();
|
|
2398
|
+
return { dates, models, series };
|
|
2155
2399
|
}
|
|
2156
|
-
function
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
break;
|
|
2163
|
-
}
|
|
2400
|
+
function resetTokenStats() {
|
|
2401
|
+
const statsPath = getStatsPath();
|
|
2402
|
+
if (existsSync7(statsPath)) {
|
|
2403
|
+
try {
|
|
2404
|
+
unlinkSync2(statsPath);
|
|
2405
|
+
} catch {
|
|
2164
2406
|
}
|
|
2165
2407
|
}
|
|
2166
|
-
saveRuntimeConfig(runtime);
|
|
2167
|
-
return runtime;
|
|
2168
|
-
}
|
|
2169
|
-
function updateSkillCategoryEnabled(category, enabled) {
|
|
2170
|
-
const runtime = loadRuntimeConfig();
|
|
2171
|
-
const cat = runtime.L3_skills?.[category];
|
|
2172
|
-
if (cat) cat.category_enabled = enabled;
|
|
2173
|
-
saveRuntimeConfig(runtime);
|
|
2174
|
-
return runtime;
|
|
2175
2408
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
|
|
2180
|
-
var init_runtime_config = __esm({
|
|
2181
|
-
"src/infra/runtime-config.ts"() {
|
|
2409
|
+
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
2410
|
+
var init_token_stats = __esm({
|
|
2411
|
+
"src/engine/token-stats.ts"() {
|
|
2182
2412
|
"use strict";
|
|
2183
2413
|
init_esm_shims();
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2189
|
-
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2190
|
-
LAYER_DISABLE_ENV = {
|
|
2191
|
-
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2192
|
-
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2193
|
-
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2194
|
-
};
|
|
2195
|
-
DENY_MODE_LABELS = {
|
|
2196
|
-
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2197
|
-
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2198
|
-
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2199
|
-
};
|
|
2200
|
-
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2414
|
+
STATS_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2415
|
+
STATS_FILE = join4(STATS_DIR, "agent_moss_token_stats.json");
|
|
2416
|
+
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
2417
|
+
MAX_RECORDS = 1e4;
|
|
2201
2418
|
}
|
|
2202
2419
|
});
|
|
2203
2420
|
|
|
@@ -2283,9 +2500,9 @@ var require_bindings = __commonJS({
|
|
|
2283
2500
|
init_esm_shims();
|
|
2284
2501
|
var fs4 = __require("fs");
|
|
2285
2502
|
var path3 = __require("path");
|
|
2286
|
-
var
|
|
2503
|
+
var fileURLToPath7 = require_file_uri_to_path();
|
|
2287
2504
|
var join6 = path3.join;
|
|
2288
|
-
var
|
|
2505
|
+
var dirname7 = path3.dirname;
|
|
2289
2506
|
var exists = fs4.accessSync && function(path4) {
|
|
2290
2507
|
try {
|
|
2291
2508
|
fs4.accessSync(path4);
|
|
@@ -2395,12 +2612,12 @@ var require_bindings = __commonJS({
|
|
|
2395
2612
|
Error.stackTraceLimit = origSTL;
|
|
2396
2613
|
var fileSchema = "file://";
|
|
2397
2614
|
if (fileName.indexOf(fileSchema) === 0) {
|
|
2398
|
-
fileName =
|
|
2615
|
+
fileName = fileURLToPath7(fileName);
|
|
2399
2616
|
}
|
|
2400
2617
|
return fileName;
|
|
2401
2618
|
};
|
|
2402
2619
|
exports.getRoot = function getRoot(file) {
|
|
2403
|
-
var dir =
|
|
2620
|
+
var dir = dirname7(file), prev;
|
|
2404
2621
|
while (true) {
|
|
2405
2622
|
if (dir === ".") {
|
|
2406
2623
|
dir = process.cwd();
|
|
@@ -3056,28 +3273,28 @@ init_skill_loader();
|
|
|
3056
3273
|
|
|
3057
3274
|
// src/engine/template-loader.ts
|
|
3058
3275
|
init_esm_shims();
|
|
3059
|
-
import { readFileSync as
|
|
3060
|
-
import { resolve as resolve3, dirname as
|
|
3061
|
-
import { fileURLToPath as
|
|
3062
|
-
var __filename4 =
|
|
3063
|
-
var
|
|
3064
|
-
var TEMPLATES_DIR = resolve3(
|
|
3065
|
-
var TEMPLATES_DIR_FALLBACK = resolve3(
|
|
3276
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
3277
|
+
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
3278
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3279
|
+
var __filename4 = fileURLToPath5(import.meta.url);
|
|
3280
|
+
var __dirname5 = dirname4(__filename4);
|
|
3281
|
+
var TEMPLATES_DIR = resolve3(__dirname5, "../templates");
|
|
3282
|
+
var TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
|
|
3066
3283
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3067
3284
|
var cachedTemplates = null;
|
|
3068
3285
|
function loadTemplates() {
|
|
3069
3286
|
if (cachedTemplates) return cachedTemplates;
|
|
3070
3287
|
cachedTemplates = {};
|
|
3071
|
-
const dir =
|
|
3072
|
-
if (!
|
|
3288
|
+
const dir = existsSync5(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
3289
|
+
if (!existsSync5(dir)) {
|
|
3073
3290
|
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
3074
3291
|
return cachedTemplates;
|
|
3075
3292
|
}
|
|
3076
3293
|
for (const file of TEMPLATE_FILES) {
|
|
3077
3294
|
const filePath = resolve3(dir, file);
|
|
3078
|
-
if (
|
|
3295
|
+
if (existsSync5(filePath)) {
|
|
3079
3296
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3080
|
-
cachedTemplates[key] =
|
|
3297
|
+
cachedTemplates[key] = readFileSync4(filePath, "utf-8");
|
|
3081
3298
|
}
|
|
3082
3299
|
}
|
|
3083
3300
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -3086,8 +3303,8 @@ function loadTemplates() {
|
|
|
3086
3303
|
|
|
3087
3304
|
// src/engine/script-analyzer.ts
|
|
3088
3305
|
init_esm_shims();
|
|
3089
|
-
import { readFileSync as
|
|
3090
|
-
import { resolve as resolve4, join } from "path";
|
|
3306
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3307
|
+
import { resolve as resolve4, join as join2 } from "path";
|
|
3091
3308
|
var SUSPICIOUS_PATTERNS = [
|
|
3092
3309
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
3093
3310
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -3220,8 +3437,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3220
3437
|
}
|
|
3221
3438
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3222
3439
|
try {
|
|
3223
|
-
const expandedPath = scriptPath.startsWith("~") ?
|
|
3224
|
-
const raw =
|
|
3440
|
+
const expandedPath = scriptPath.startsWith("~") ? join2(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
|
|
3441
|
+
const raw = readFileSync5(expandedPath, "utf-8");
|
|
3225
3442
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3226
3443
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
3227
3444
|
} catch (e) {
|
|
@@ -3275,9 +3492,9 @@ ${result.scriptContent}
|
|
|
3275
3492
|
init_esm_shims();
|
|
3276
3493
|
init_heuristic();
|
|
3277
3494
|
init_logic_rules();
|
|
3278
|
-
import { readFileSync as
|
|
3279
|
-
import { resolve as resolve5, join as
|
|
3280
|
-
import { homedir } from "os";
|
|
3495
|
+
import { readFileSync as readFileSync6, statSync, existsSync as existsSync6 } from "fs";
|
|
3496
|
+
import { resolve as resolve5, join as join3, isAbsolute, dirname as dirname5 } from "path";
|
|
3497
|
+
import { homedir as homedir2 } from "os";
|
|
3281
3498
|
function emptyChain() {
|
|
3282
3499
|
return {
|
|
3283
3500
|
nodes: [],
|
|
@@ -3505,7 +3722,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
3505
3722
|
let resolved;
|
|
3506
3723
|
try {
|
|
3507
3724
|
resolved = resolvePath(p);
|
|
3508
|
-
if (
|
|
3725
|
+
if (existsSync6(resolved)) {
|
|
3509
3726
|
const stat = statSync(resolved);
|
|
3510
3727
|
if (stat.isFile()) return resolved;
|
|
3511
3728
|
}
|
|
@@ -3517,20 +3734,20 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
3517
3734
|
function resolvePath(p) {
|
|
3518
3735
|
let expanded = p;
|
|
3519
3736
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
3520
|
-
expanded =
|
|
3737
|
+
expanded = join3(homedir2(), expanded.slice(1));
|
|
3521
3738
|
}
|
|
3522
3739
|
return resolve5(expanded);
|
|
3523
3740
|
}
|
|
3524
3741
|
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
3525
3742
|
try {
|
|
3526
3743
|
const path3 = resolvePath(scriptPath);
|
|
3527
|
-
if (!
|
|
3744
|
+
if (!existsSync6(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
3528
3745
|
const stat = statSync(path3);
|
|
3529
3746
|
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
3530
3747
|
if (stat.size > maxFileSize) {
|
|
3531
3748
|
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
3532
3749
|
}
|
|
3533
|
-
const content =
|
|
3750
|
+
const content = readFileSync6(path3, "utf-8");
|
|
3534
3751
|
const lineCount = content ? content.split("\n").length : 0;
|
|
3535
3752
|
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
3536
3753
|
} catch (e) {
|
|
@@ -3547,9 +3764,9 @@ function extractChildScriptPaths(content, baseDir) {
|
|
|
3547
3764
|
const pathStr = m[1];
|
|
3548
3765
|
if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
|
|
3549
3766
|
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
3550
|
-
const full = isAbsolute(pathStr) ? pathStr :
|
|
3767
|
+
const full = isAbsolute(pathStr) ? pathStr : join3(baseDir, pathStr);
|
|
3551
3768
|
const resolved = resolvePath(full);
|
|
3552
|
-
if (
|
|
3769
|
+
if (existsSync6(resolved)) {
|
|
3553
3770
|
try {
|
|
3554
3771
|
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
3555
3772
|
} catch {
|
|
@@ -3564,7 +3781,7 @@ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 10
|
|
|
3564
3781
|
const result = emptyChain();
|
|
3565
3782
|
result.entryPath = entryPath;
|
|
3566
3783
|
const entry = resolvePath(entryPath);
|
|
3567
|
-
const baseDir =
|
|
3784
|
+
const baseDir = existsSync6(entry) ? dirname5(entry) : input.cwd || process.cwd();
|
|
3568
3785
|
resolveRecursive({
|
|
3569
3786
|
path: entry,
|
|
3570
3787
|
depth: 0,
|
|
@@ -3639,7 +3856,7 @@ function resolveRecursive(ctx) {
|
|
|
3639
3856
|
maxTotalSize,
|
|
3640
3857
|
visited,
|
|
3641
3858
|
result,
|
|
3642
|
-
baseDir:
|
|
3859
|
+
baseDir: dirname5(resolvePath(childPath)),
|
|
3643
3860
|
input
|
|
3644
3861
|
});
|
|
3645
3862
|
if (result.hasCritical) return;
|
|
@@ -3901,7 +4118,7 @@ function formatHints(previous) {
|
|
|
3901
4118
|
const hints = [];
|
|
3902
4119
|
if (previous.heuristic) {
|
|
3903
4120
|
hints.push(
|
|
3904
|
-
`\
|
|
4121
|
+
`\u7279\u5F81\u5339\u914D\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
3905
4122
|
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
3906
4123
|
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
3907
4124
|
);
|
|
@@ -3913,7 +4130,7 @@ function formatHints(previous) {
|
|
|
3913
4130
|
\u539F\u56E0: ${previous.logic.reason}`
|
|
3914
4131
|
);
|
|
3915
4132
|
}
|
|
3916
|
-
return hints.length > 0 ? hints.join("\n\n") : "\
|
|
4133
|
+
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";
|
|
3917
4134
|
}
|
|
3918
4135
|
async function analyzeWithLLM(prompt, config) {
|
|
3919
4136
|
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
@@ -4121,6 +4338,7 @@ async function runAnalysis(input) {
|
|
|
4121
4338
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
4122
4339
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
4123
4340
|
const lowerDetail = rawDetail.toLowerCase();
|
|
4341
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
4124
4342
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
4125
4343
|
return {
|
|
4126
4344
|
allowed: true,
|
|
@@ -4132,7 +4350,23 @@ async function runAnalysis(input) {
|
|
|
4132
4350
|
violatedLayers: []
|
|
4133
4351
|
};
|
|
4134
4352
|
}
|
|
4135
|
-
|
|
4353
|
+
if (agentExt?.builtin_safe_action_types && agentExt.builtin_safe_action_types.length > 0) {
|
|
4354
|
+
const hit = agentExt.builtin_safe_action_types.some((t) => {
|
|
4355
|
+
const tl = t.toLowerCase();
|
|
4356
|
+
return tl.endsWith("-") ? actionType.startsWith(tl) : actionType === tl;
|
|
4357
|
+
});
|
|
4358
|
+
if (hit) {
|
|
4359
|
+
return {
|
|
4360
|
+
allowed: true,
|
|
4361
|
+
reason: `agent \u5185\u90E8\u5B89\u5168\u64CD\u4F5C\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
4362
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
4363
|
+
riskType: "",
|
|
4364
|
+
confidence: 95,
|
|
4365
|
+
source: "whitelist_bypass",
|
|
4366
|
+
violatedLayers: []
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4136
4370
|
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
4137
4371
|
if (agentExt?.builtin_safe_skills) {
|
|
4138
4372
|
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
@@ -4422,7 +4656,10 @@ async function analyzeRoute(c) {
|
|
|
4422
4656
|
reason: req.reason,
|
|
4423
4657
|
osType,
|
|
4424
4658
|
cwd: req.cwd,
|
|
4425
|
-
|
|
4659
|
+
// agent_id 透传:优先顶层;为空兜底 metadata.agent_type(AgentGate V1
|
|
4660
|
+
// normalizer 历史上只把 agent_type 塞进 metadata,顶层 agent_id 为空 →
|
|
4661
|
+
// 定制规则不生效。AgentGate 已修,此处兜底防御其他只传 metadata 的调用方)。
|
|
4662
|
+
agentId: req.agent_id || req.metadata?.agent_type || "",
|
|
4426
4663
|
llmConfig: req.metadata?.llm_config,
|
|
4427
4664
|
llmLogPath: req.metadata?.llm_log_path
|
|
4428
4665
|
});
|
|
@@ -4478,10 +4715,10 @@ init_runtime_config();
|
|
|
4478
4715
|
init_token_stats();
|
|
4479
4716
|
import { Hono } from "hono";
|
|
4480
4717
|
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4481
|
-
import { join as join5, dirname as
|
|
4482
|
-
import { fileURLToPath as
|
|
4483
|
-
var
|
|
4484
|
-
var STATIC_DIR = join5(
|
|
4718
|
+
import { join as join5, dirname as dirname6 } from "path";
|
|
4719
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
4720
|
+
var __dirname6 = dirname6(fileURLToPath6(import.meta.url));
|
|
4721
|
+
var STATIC_DIR = join5(__dirname6, "static");
|
|
4485
4722
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
4486
4723
|
function getConsoleToken() {
|
|
4487
4724
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -4598,14 +4835,52 @@ function createConsoleRouter() {
|
|
|
4598
4835
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
4599
4836
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
4600
4837
|
}
|
|
4601
|
-
const skillPath = join5(
|
|
4838
|
+
const skillPath = join5(dirname6(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
4602
4839
|
if (!existsSync9(skillPath)) {
|
|
4603
|
-
const builtinPath = join5(
|
|
4840
|
+
const builtinPath = join5(__dirname6, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
4604
4841
|
if (existsSync9(builtinPath)) return c.text(readFileSync8(builtinPath, "utf-8"));
|
|
4605
4842
|
return c.json({ error: "skill not found" }, 404);
|
|
4606
4843
|
}
|
|
4607
4844
|
return c.text(readFileSync8(skillPath, "utf-8"));
|
|
4608
4845
|
});
|
|
4846
|
+
console2.get("/api/agents", (c) => {
|
|
4847
|
+
const rc = loadRuntimeConfig();
|
|
4848
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4849
|
+
});
|
|
4850
|
+
console2.put("/api/agents/rules/enabled", async (c) => {
|
|
4851
|
+
const b = await c.req.json();
|
|
4852
|
+
const fp = {};
|
|
4853
|
+
if (b.value) fp.value = b.value;
|
|
4854
|
+
if (b.pattern) fp.pattern = b.pattern;
|
|
4855
|
+
if (b.keyword) fp.keyword = b.keyword;
|
|
4856
|
+
if (b.path) fp.path = b.path;
|
|
4857
|
+
const rc = updateAgentRuleEnabled(b.agent_id, b.category, fp, b.enabled);
|
|
4858
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4859
|
+
});
|
|
4860
|
+
console2.post("/api/agents/rules", async (c) => {
|
|
4861
|
+
const b = await c.req.json();
|
|
4862
|
+
const rule = {};
|
|
4863
|
+
for (const [k, v] of Object.entries(b)) {
|
|
4864
|
+
if (!["agent_id", "category"].includes(k) && v !== void 0 && v !== null) rule[k] = v;
|
|
4865
|
+
}
|
|
4866
|
+
const rc = addAgentRule(b.agent_id, b.category, rule);
|
|
4867
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4868
|
+
});
|
|
4869
|
+
console2.delete("/api/agents/rules", async (c) => {
|
|
4870
|
+
const b = await c.req.json();
|
|
4871
|
+
const fp = {};
|
|
4872
|
+
if (b.value) fp.value = b.value;
|
|
4873
|
+
if (b.pattern) fp.pattern = b.pattern;
|
|
4874
|
+
if (b.keyword) fp.keyword = b.keyword;
|
|
4875
|
+
if (b.path) fp.path = b.path;
|
|
4876
|
+
const rc = deleteAgentRule(b.agent_id, b.category, fp);
|
|
4877
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4878
|
+
});
|
|
4879
|
+
console2.put("/api/agents/categories/enabled", async (c) => {
|
|
4880
|
+
const b = await c.req.json();
|
|
4881
|
+
const rc = updateAgentCategoryEnabled(b.agent_id, b.category, b.enabled);
|
|
4882
|
+
return c.json({ agents: getAgentRules(rc) });
|
|
4883
|
+
});
|
|
4609
4884
|
console2.get("/api/config", (c) => {
|
|
4610
4885
|
const rc = loadRuntimeConfig();
|
|
4611
4886
|
return c.json({ config: rc, config_path: getRuntimeConfigPath() });
|