@kenhkl/agent-moss 0.16.12 → 0.16.14
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 -0
- package/dist/cli.js +1065 -1216
- package/dist/data/agent_moss.service +4 -0
- package/dist/engine/coordinator.js +514 -872
- package/dist/rules/formal_rules.json +61 -0
- package/dist/rules/heuristic_rules.json +40 -0
- package/dist/rules/inline_rules.json +151 -0
- package/dist/rules/l1_rules.json +115 -49
- package/dist/rules/logic_rules.json +73 -0
- package/dist/rules/manifest.json +6 -0
- package/dist/rules/platform_traits.json +389 -0
- package/dist/rules/script_rules.json +311 -0
- package/dist/server.js +877 -1050
- package/dist/static/index.html +26 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -22,103 +22,14 @@ var init_esm_shims = __esm({
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
// src/package-json.ts
|
|
26
|
-
import { createRequire } from "module";
|
|
27
|
-
var require2, pkg, version;
|
|
28
|
-
var init_package_json = __esm({
|
|
29
|
-
"src/package-json.ts"() {
|
|
30
|
-
"use strict";
|
|
31
|
-
init_esm_shims();
|
|
32
|
-
require2 = createRequire(import.meta.url);
|
|
33
|
-
pkg = require2("../package.json");
|
|
34
|
-
version = pkg.version;
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// src/models.ts
|
|
39
|
-
import { z } from "zod";
|
|
40
|
-
var ActionItemSchema, PromptTurnSchema, AnalyzeRequestSchema, AnalyzeResponseSchema, HealthResponseSchema, ReviewRequestSchema, ReviewResponseSchema;
|
|
41
|
-
var init_models = __esm({
|
|
42
|
-
"src/models.ts"() {
|
|
43
|
-
"use strict";
|
|
44
|
-
init_esm_shims();
|
|
45
|
-
init_package_json();
|
|
46
|
-
ActionItemSchema = z.object({
|
|
47
|
-
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
48
|
-
action_detail: z.string().describe("Action detail / command content"),
|
|
49
|
-
// 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
|
|
50
|
-
// 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
|
|
51
|
-
// 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
|
|
52
|
-
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
|
|
53
|
-
});
|
|
54
|
-
PromptTurnSchema = z.object({
|
|
55
|
-
text: z.string().default("").describe("User prompt text at this turn"),
|
|
56
|
-
actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
|
|
57
|
-
});
|
|
58
|
-
AnalyzeRequestSchema = z.object({
|
|
59
|
-
session_id: z.string().describe("Session / agent unique identifier"),
|
|
60
|
-
prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
|
|
61
|
-
prompt_history: z.array(PromptTurnSchema).default([]).describe("Full interleaved prompt history (ordered turns, each with its triggered actions). Optional; empty = fall back to prompt_session only."),
|
|
62
|
-
action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
|
|
63
|
-
session_history: z.array(z.object({
|
|
64
|
-
session_id: z.string(),
|
|
65
|
-
action_history: z.array(ActionItemSchema).default([])
|
|
66
|
-
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
67
|
-
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
68
|
-
reason: z.string().default("").describe("Reason for executing this action"),
|
|
69
|
-
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
70
|
-
cwd: z.string().default("").describe("Current working directory"),
|
|
71
|
-
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
72
|
-
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
73
|
-
});
|
|
74
|
-
AnalyzeResponseSchema = z.object({
|
|
75
|
-
decision: z.enum(["Allow", "Deny"]).describe("Allow or Deny"),
|
|
76
|
-
reason: z.string().describe("Decision reason"),
|
|
77
|
-
risk_level: z.enum(["low", "medium", "high", "critical"]).default("low"),
|
|
78
|
-
risk_type: z.string().default(""),
|
|
79
|
-
violated_layers: z.array(z.string()).default([]),
|
|
80
|
-
policy: z.string().default(""),
|
|
81
|
-
violated_policy: z.string().default(""),
|
|
82
|
-
confidence: z.number().int().min(0).max(100).default(100),
|
|
83
|
-
analysis_duration_ms: z.number().default(0),
|
|
84
|
-
// 组合攻击链反例路径:每步 [session_id, seq, stage, timestamp],含真实发生时间
|
|
85
|
-
violation_chain: z.array(z.array(z.union([z.string(), z.number()]).nullable())).default([])
|
|
86
|
-
});
|
|
87
|
-
HealthResponseSchema = z.object({
|
|
88
|
-
status: z.string().default("healthy"),
|
|
89
|
-
version: z.string().default(version),
|
|
90
|
-
// 归属实例标识:值来自 env AGENT_MOSS_INSTANCE(spawn/systemd 注入),默认空串。
|
|
91
|
-
// 多个 agentmoss 同机并存时,调用方按 instance 过滤避免连到别人的进程。
|
|
92
|
-
// 空=未配置(独立部署或旧版),调用方可选择兼容或严格过滤。
|
|
93
|
-
instance: z.string().default("")
|
|
94
|
-
});
|
|
95
|
-
ReviewRequestSchema = z.object({
|
|
96
|
-
agent_id: z.string().default("").describe("\u76EE\u6807 agent \u6807\u8BC6\uFF08\u51B3\u5B9A\u72B6\u6001\u5E93\u5206\u6876\uFF09"),
|
|
97
|
-
db_path: z.string().default("").describe("AgentGate \u672C\u5730\u5B58\u50A8\u8DEF\u5F84\uFF08SQLite \u6587\u4EF6 \u6216 tool_calls JSON \u76EE\u5F55\uFF09\uFF1B\u4E3A\u7A7A\u5219\u7528 sessions"),
|
|
98
|
-
sessions: z.array(z.object({
|
|
99
|
-
session_id: z.string(),
|
|
100
|
-
action_history: z.array(ActionItemSchema).default([])
|
|
101
|
-
})).default([]).describe("\u591A session \u5386\u53F2\u52A8\u4F5C\uFF08\u66FF\u4EE3/\u515C\u5E95 db_path\uFF09"),
|
|
102
|
-
a_next: ActionItemSchema.optional().describe("\u53EF\u9009\uFF1Aseed \u540E\u8FDE\u540C\u5F53\u524D\u52A8\u4F5C\u4E00\u8D77\u5224\u5B9A"),
|
|
103
|
-
is_error: z.boolean().default(false).describe("\u5F53\u524D\u52A8\u4F5C\u6267\u884C\u662F\u5426\u5931\u8D25"),
|
|
104
|
-
output_pending: z.boolean().default(false).describe("\u8FD4\u56DE\u524D\u5E8F\u94FE\u6E05\u5355 pending_prefixes")
|
|
105
|
-
});
|
|
106
|
-
ReviewResponseSchema = z.object({
|
|
107
|
-
agent_id: z.string().default(""),
|
|
108
|
-
seeded_nodes: z.number().default(0),
|
|
109
|
-
verified: z.boolean().default(true),
|
|
110
|
-
violations: z.array(z.record(z.unknown())).default([]),
|
|
111
|
-
pending_prefixes: z.array(z.record(z.unknown())).default([]),
|
|
112
|
-
state_nodes: z.number().default(0),
|
|
113
|
-
discovered_patterns: z.array(z.record(z.unknown())).default([]).describe("\u6570\u636E\u9A71\u52A8\u53D1\u73B0\uFF1A\u8DE8 session \u9891\u7E41\u5371\u9669\u7EC4\u5408\uFF08\u9884\u7F6E\u94FE\u76F2\u533A\uFF0CPatternMiner\uFF09")
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
25
|
// src/infra/paths.ts
|
|
119
26
|
import { join } from "path";
|
|
120
27
|
import { homedir } from "os";
|
|
121
28
|
import { readFileSync } from "fs";
|
|
29
|
+
function getRulesRoot() {
|
|
30
|
+
const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
|
|
31
|
+
return raw ? raw : null;
|
|
32
|
+
}
|
|
122
33
|
function isRoot() {
|
|
123
34
|
const getuid = process.getuid;
|
|
124
35
|
return typeof getuid === "function" && getuid() === 0;
|
|
@@ -188,7 +99,7 @@ function getTokenStatsPath() {
|
|
|
188
99
|
}
|
|
189
100
|
return join(getDataRoot(), "agent_moss_token_stats.json");
|
|
190
101
|
}
|
|
191
|
-
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT;
|
|
102
|
+
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT;
|
|
192
103
|
var init_paths = __esm({
|
|
193
104
|
"src/infra/paths.ts"() {
|
|
194
105
|
"use strict";
|
|
@@ -196,282 +107,53 @@ var init_paths = __esm({
|
|
|
196
107
|
ENV_DATA_ROOT = "AGENT_MOSS_DATA_DIR";
|
|
197
108
|
ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
|
|
198
109
|
DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
|
|
110
|
+
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
199
111
|
}
|
|
200
112
|
});
|
|
201
113
|
|
|
202
|
-
// src/
|
|
203
|
-
function
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
function splitCommandSegments(command) {
|
|
207
|
-
const segments = [];
|
|
208
|
-
let cur = "";
|
|
209
|
-
let quote = null;
|
|
210
|
-
for (let i = 0; i < command.length; i++) {
|
|
211
|
-
const ch = command[i];
|
|
212
|
-
if (quote) {
|
|
213
|
-
cur += ch;
|
|
214
|
-
if (ch === quote) quote = null;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (ch === "'" || ch === '"') {
|
|
218
|
-
quote = ch;
|
|
219
|
-
cur += ch;
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (/\||&|;|\n/.test(ch)) {
|
|
223
|
-
segments.push(cur);
|
|
224
|
-
segments.push(ch);
|
|
225
|
-
cur = "";
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
cur += ch;
|
|
229
|
-
}
|
|
230
|
-
if (cur) segments.push(cur);
|
|
231
|
-
return segments.map((s) => s.trim()).filter((s) => s.length > 0 && !/^(?:\|\||&&|[|;&])$/.test(s));
|
|
232
|
-
}
|
|
233
|
-
function extractFirstCommand(command) {
|
|
234
|
-
const segments = splitCommandSegments(command);
|
|
235
|
-
return segments.length ? segments[0] : "";
|
|
114
|
+
// src/infra/rules-mode.ts
|
|
115
|
+
function truthy(name) {
|
|
116
|
+
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
236
117
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const segments = splitCommandSegments(command);
|
|
249
|
-
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
250
|
-
if (command.includes(">")) {
|
|
251
|
-
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
252
|
-
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
253
|
-
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
254
|
-
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
255
|
-
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
256
|
-
return false;
|
|
118
|
+
function getRulesMode() {
|
|
119
|
+
if (cached !== null) return cached;
|
|
120
|
+
const raw = (process.env[ENV_RULES_MODE] || "").trim().toLowerCase();
|
|
121
|
+
if (raw) {
|
|
122
|
+
if (VALID_MODES.has(raw)) {
|
|
123
|
+
cached = raw;
|
|
124
|
+
} else {
|
|
125
|
+
console.error(
|
|
126
|
+
`[AgentMoss] ${ENV_RULES_MODE}=${JSON.stringify(raw)} \u975E\u6CD5\uFF08\u53EA\u652F\u6301 hot / cold / builtin\uFF09\uFF1B\u4E3A\u5B89\u5168\u8D77\u89C1\u6309 builtin\uFF08\u4EC5\u5185\u7F6E\u89C4\u5219\uFF09\u5904\u7406`
|
|
127
|
+
);
|
|
128
|
+
cached = "builtin";
|
|
257
129
|
}
|
|
130
|
+
return cached;
|
|
258
131
|
}
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
function extractInlineCode(command) {
|
|
265
|
-
const parts = [];
|
|
266
|
-
let m;
|
|
267
|
-
INLINE_CODE_RE.lastIndex = 0;
|
|
268
|
-
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
269
|
-
const code = m[1] ?? m[2];
|
|
270
|
-
if (code) parts.push(code);
|
|
271
|
-
}
|
|
272
|
-
ECHO_STRING_RE.lastIndex = 0;
|
|
273
|
-
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
274
|
-
const code = m[1] ?? m[2];
|
|
275
|
-
if (code) parts.push(code);
|
|
276
|
-
}
|
|
277
|
-
return parts.join("\n");
|
|
132
|
+
if (truthy(ENV_RULES_BUILTIN_ONLY)) cached = "builtin";
|
|
133
|
+
else if (truthy(ENV_DISABLE_RULE_HOT_RELOAD)) cached = "cold";
|
|
134
|
+
else cached = "hot";
|
|
135
|
+
return cached;
|
|
278
136
|
}
|
|
279
|
-
function
|
|
280
|
-
return
|
|
281
|
-
p.lastIndex = 0;
|
|
282
|
-
return p.test(command);
|
|
283
|
-
});
|
|
137
|
+
function isHotReloadEnabled() {
|
|
138
|
+
return getRulesMode() === "hot";
|
|
284
139
|
}
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
140
|
+
function isRuntimeConfigDisabled() {
|
|
141
|
+
return getRulesMode() !== "hot";
|
|
288
142
|
}
|
|
289
|
-
function
|
|
290
|
-
|
|
291
|
-
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
143
|
+
function isBuiltinOnly() {
|
|
144
|
+
return getRulesMode() === "builtin";
|
|
292
145
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
297
|
-
}
|
|
298
|
-
if (hasWriteExecChain(command)) {
|
|
299
|
-
return {
|
|
300
|
-
actionable: true,
|
|
301
|
-
allowed: false,
|
|
302
|
-
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
303
|
-
riskLevel: "critical",
|
|
304
|
-
riskType: "script_execution"
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
308
|
-
if (redirMatch) {
|
|
309
|
-
return {
|
|
310
|
-
actionable: true,
|
|
311
|
-
allowed: false,
|
|
312
|
-
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
313
|
-
riskLevel: "critical",
|
|
314
|
-
riskType: "file_access"
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
318
|
-
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
319
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
320
|
-
}
|
|
321
|
-
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
322
|
-
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
323
|
-
return {
|
|
324
|
-
actionable: true,
|
|
325
|
-
allowed: false,
|
|
326
|
-
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
327
|
-
riskLevel: "critical",
|
|
328
|
-
riskType: "file_access"
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
actionable: true,
|
|
333
|
-
allowed: true,
|
|
334
|
-
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
335
|
-
riskLevel: "low",
|
|
336
|
-
riskType: "file_access"
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_SEG_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SHELL_RC_FRAGMENTS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
340
|
-
var init_inline_analyzer = __esm({
|
|
341
|
-
"src/engine/inline-analyzer.ts"() {
|
|
146
|
+
var ENV_RULES_MODE, ENV_RULES_BUILTIN_ONLY, ENV_DISABLE_RULE_HOT_RELOAD, TRUTHY, VALID_MODES, cached;
|
|
147
|
+
var init_rules_mode = __esm({
|
|
148
|
+
"src/infra/rules-mode.ts"() {
|
|
342
149
|
"use strict";
|
|
343
150
|
init_esm_shims();
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"open(",
|
|
351
|
-
"read(",
|
|
352
|
-
"write(",
|
|
353
|
-
"fopen(",
|
|
354
|
-
"system(",
|
|
355
|
-
"exec(",
|
|
356
|
-
"eval(",
|
|
357
|
-
"popen(",
|
|
358
|
-
"load(",
|
|
359
|
-
"dump(",
|
|
360
|
-
// 文件读写(组合形态)
|
|
361
|
-
"readline",
|
|
362
|
-
"readlines",
|
|
363
|
-
"writelines",
|
|
364
|
-
"getpass",
|
|
365
|
-
"pickle.load",
|
|
366
|
-
"yaml.load",
|
|
367
|
-
"json.load",
|
|
368
|
-
"json.read",
|
|
369
|
-
"configparser",
|
|
370
|
-
"fread",
|
|
371
|
-
"fwrite",
|
|
372
|
-
"fgets",
|
|
373
|
-
// 子进程执行(组合形态)
|
|
374
|
-
"os.system",
|
|
375
|
-
"os.popen",
|
|
376
|
-
"subprocess",
|
|
377
|
-
"os.exec",
|
|
378
|
-
"os.spawn",
|
|
379
|
-
"import os",
|
|
380
|
-
"import subprocess",
|
|
381
|
-
"shell=true",
|
|
382
|
-
"check_output",
|
|
383
|
-
"check_call",
|
|
384
|
-
"run(",
|
|
385
|
-
// 绕过变体检测
|
|
386
|
-
"getattr(",
|
|
387
|
-
"__builtins__",
|
|
388
|
-
"globals(",
|
|
389
|
-
"locals(",
|
|
390
|
-
// PHP
|
|
391
|
-
"file_get_contents",
|
|
392
|
-
"file_put_contents",
|
|
393
|
-
"passthru",
|
|
394
|
-
"shell_exec",
|
|
395
|
-
"proc_open",
|
|
396
|
-
// Perl/Ruby
|
|
397
|
-
"<stdin>",
|
|
398
|
-
"open my",
|
|
399
|
-
// Node/JS(2026-09 补,与 Python _READ_WRITE_KEYWORDS 同步):历史缺口 ——
|
|
400
|
-
// 识别表里一条 Node API 都没有,于是 node -e "require('fs').readFileSync('/etc/shadow')"
|
|
401
|
-
// 判为纯文本字面量 → 放行(真漏报)。
|
|
402
|
-
"readfilesync(",
|
|
403
|
-
"writefilesync(",
|
|
404
|
-
"appendfilesync(",
|
|
405
|
-
"unlinksync(",
|
|
406
|
-
"rmsync(",
|
|
407
|
-
"mkdirsync(",
|
|
408
|
-
"execsync(",
|
|
409
|
-
"spawnsync(",
|
|
410
|
-
"execfile",
|
|
411
|
-
"createreadstream",
|
|
412
|
-
"createwritestream",
|
|
413
|
-
"fs.read",
|
|
414
|
-
"fs.write",
|
|
415
|
-
"fs.promises",
|
|
416
|
-
"require('fs')",
|
|
417
|
-
'require("fs")',
|
|
418
|
-
"require('node:fs')",
|
|
419
|
-
'require("node:fs")',
|
|
420
|
-
"require('child_process')",
|
|
421
|
-
'require("child_process")',
|
|
422
|
-
"require('https')",
|
|
423
|
-
'require("https")'
|
|
424
|
-
];
|
|
425
|
-
WRITE_EXEC_CHAIN_PATTERNS = [
|
|
426
|
-
// echo/cat/printf "..." > file && bash/sh/python file
|
|
427
|
-
/(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
|
|
428
|
-
// ... | bash / | sh / | python(管道喂解释器执行)
|
|
429
|
-
/\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
|
|
430
|
-
// eval "$(cat ...)" / eval "$(echo ...)"
|
|
431
|
-
/eval\s+"\$\(/gi,
|
|
432
|
-
// xargs bash / xargs sh
|
|
433
|
-
/\|\s*xargs\s+(?:bash|sh|zsh)/gi
|
|
434
|
-
];
|
|
435
|
-
SHELL_RC_FRAGMENTS = [
|
|
436
|
-
".bashrc",
|
|
437
|
-
".zshrc",
|
|
438
|
-
".profile",
|
|
439
|
-
".bash_profile",
|
|
440
|
-
".zprofile"
|
|
441
|
-
];
|
|
442
|
-
SENSITIVE_PATH_FRAGMENTS = [
|
|
443
|
-
"/etc/shadow",
|
|
444
|
-
"/etc/gshadow",
|
|
445
|
-
"/etc/passwd",
|
|
446
|
-
"/etc/sudoers",
|
|
447
|
-
"/etc/hosts",
|
|
448
|
-
"/etc/crontab",
|
|
449
|
-
"/etc/systemd/",
|
|
450
|
-
"/etc/ssh/sshd_config",
|
|
451
|
-
"/boot/",
|
|
452
|
-
"/proc/sys/",
|
|
453
|
-
"/sys/",
|
|
454
|
-
"/dev/mem",
|
|
455
|
-
"/dev/kmsg",
|
|
456
|
-
".ssh/id_rsa",
|
|
457
|
-
".ssh/id_ed25519",
|
|
458
|
-
".ssh/authorized_keys",
|
|
459
|
-
...SHELL_RC_FRAGMENTS
|
|
460
|
-
];
|
|
461
|
-
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
462
|
-
DIR_TRAVERSAL_KEYWORDS = [
|
|
463
|
-
// Python 目录遍历
|
|
464
|
-
"os.listdir",
|
|
465
|
-
"os.walk",
|
|
466
|
-
"os.scandir",
|
|
467
|
-
"glob.glob",
|
|
468
|
-
"glob.iglob",
|
|
469
|
-
"pathlib.",
|
|
470
|
-
"iterdir",
|
|
471
|
-
"rglob",
|
|
472
|
-
"scandir(",
|
|
473
|
-
"listdir("
|
|
474
|
-
];
|
|
151
|
+
ENV_RULES_MODE = "AGENT_MOSS_RULES_MODE";
|
|
152
|
+
ENV_RULES_BUILTIN_ONLY = "AGENT_MOSS_RULES_BUILTIN_ONLY";
|
|
153
|
+
ENV_DISABLE_RULE_HOT_RELOAD = "AGENT_MOSS_DISABLE_RULE_HOT_RELOAD";
|
|
154
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
155
|
+
VALID_MODES = /* @__PURE__ */ new Set(["hot", "cold", "builtin"]);
|
|
156
|
+
cached = null;
|
|
475
157
|
}
|
|
476
158
|
});
|
|
477
159
|
|
|
@@ -501,13 +183,32 @@ function parseDirEnv(envName) {
|
|
|
501
183
|
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
502
184
|
}
|
|
503
185
|
function rulesDirs() {
|
|
186
|
+
if (isBuiltinOnly()) return [];
|
|
504
187
|
const dirs = [];
|
|
505
188
|
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
506
189
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
507
190
|
return [...new Set(dirs)];
|
|
508
191
|
}
|
|
192
|
+
function rulesDirCands() {
|
|
193
|
+
const root = getRulesRoot();
|
|
194
|
+
if (root) return [join2(root, "rules")];
|
|
195
|
+
const cands = [
|
|
196
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
197
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
198
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
199
|
+
resolve(__dirname2, "../rules"),
|
|
200
|
+
resolve(__dirname2, "rules"),
|
|
201
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
202
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
203
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
204
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
205
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
206
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
207
|
+
];
|
|
208
|
+
return cands.filter((c) => existsSync(c));
|
|
209
|
+
}
|
|
509
210
|
function findRulesFile(name) {
|
|
510
|
-
for (const dir of
|
|
211
|
+
for (const dir of rulesDirCands()) {
|
|
511
212
|
const p = resolve(dir, name);
|
|
512
213
|
if (existsSync(p)) return p;
|
|
513
214
|
}
|
|
@@ -517,10 +218,16 @@ function resolveRulesFile(name) {
|
|
|
517
218
|
return findRulesFile(name);
|
|
518
219
|
}
|
|
519
220
|
function loadJson(name) {
|
|
221
|
+
if (!isHotReloadEnabled()) {
|
|
222
|
+
const cached2 = frozenJsonCache.get(name);
|
|
223
|
+
if (cached2 !== void 0) return cached2;
|
|
224
|
+
}
|
|
520
225
|
const p = findRulesFile(name);
|
|
521
226
|
if (!p) return {};
|
|
522
227
|
try {
|
|
523
|
-
|
|
228
|
+
const parsed = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
229
|
+
if (!isHotReloadEnabled()) frozenJsonCache.set(name, parsed);
|
|
230
|
+
return parsed;
|
|
524
231
|
} catch {
|
|
525
232
|
return {};
|
|
526
233
|
}
|
|
@@ -557,8 +264,76 @@ function invalidScenarioRulesReason(data) {
|
|
|
557
264
|
function loadL1() {
|
|
558
265
|
return loadJson("l1_rules.json");
|
|
559
266
|
}
|
|
560
|
-
function loadL2() {
|
|
561
|
-
return loadJson("l2_rules.json");
|
|
267
|
+
function loadL2() {
|
|
268
|
+
return loadJson("l2_rules.json");
|
|
269
|
+
}
|
|
270
|
+
function regexFlags(flags) {
|
|
271
|
+
let out = "";
|
|
272
|
+
for (const ch of String(flags ?? "")) if (RE_FLAG_MAP[ch]) out += RE_FLAG_MAP[ch];
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
function compileRuleRegex(rule, extraFlags = "") {
|
|
276
|
+
return new RegExp(String(rule.pattern), regexFlags(rule.flags) + extraFlags);
|
|
277
|
+
}
|
|
278
|
+
function resolveKeywordSet(group, keywordSets) {
|
|
279
|
+
if (typeof group === "string") return new Set(keywordSets[group.slice(1)] ?? []);
|
|
280
|
+
return new Set(Array.isArray(group) ? group : []);
|
|
281
|
+
}
|
|
282
|
+
function loadFormalRules() {
|
|
283
|
+
return loadJson("formal_rules.json");
|
|
284
|
+
}
|
|
285
|
+
function loadLogicRules() {
|
|
286
|
+
return loadJson("logic_rules.json");
|
|
287
|
+
}
|
|
288
|
+
function loadHeuristicRules() {
|
|
289
|
+
return loadJson("heuristic_rules.json");
|
|
290
|
+
}
|
|
291
|
+
function loadScriptRules() {
|
|
292
|
+
return loadJson("script_rules.json");
|
|
293
|
+
}
|
|
294
|
+
function loadInline() {
|
|
295
|
+
return loadJson("inline_rules.json");
|
|
296
|
+
}
|
|
297
|
+
function loadPlatformTraits(osName) {
|
|
298
|
+
const data = loadJson("platform_traits.json");
|
|
299
|
+
if (!data || Object.keys(data).length === 0) {
|
|
300
|
+
console.error(
|
|
301
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 platform_traits.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u56DE\u843D\u4E3A\u7A7A\uFF08\u767D\u540D\u5355\u4E3A\u7A7A\uFF0C\u5224\u5B9A\u4F1A\u53D8\u4E25\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
302
|
+
);
|
|
303
|
+
return {};
|
|
304
|
+
}
|
|
305
|
+
const name = resolveTraitsOs(osName);
|
|
306
|
+
const entry = data[name];
|
|
307
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
308
|
+
console.error(`[AgentMoss] platform_traits.json \u91CC\u6CA1\u6709\u53EF\u7528\u7684 ${name} \u6761\u76EE \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u4E3A\u7A7A\u3002`);
|
|
309
|
+
return {};
|
|
310
|
+
}
|
|
311
|
+
const out = {};
|
|
312
|
+
for (const f of TRAITS_FIELDS) if (f in entry) out[f] = entry[f];
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
315
|
+
function resolveTraitsOs(osName) {
|
|
316
|
+
const data = loadJson("platform_traits.json");
|
|
317
|
+
let name = String(osName || "").trim().toLowerCase() || TRAITS_OS_FALLBACK;
|
|
318
|
+
const seen = /* @__PURE__ */ new Set();
|
|
319
|
+
for (; ; ) {
|
|
320
|
+
if (seen.has(name)) {
|
|
321
|
+
console.error(`[AgentMoss] platform_traits.json \u7684 inherits \u6210\u73AF\uFF08${name}\uFF09\u2014\u2014 \u56DE\u843D linux\u3002`);
|
|
322
|
+
return TRAITS_OS_FALLBACK;
|
|
323
|
+
}
|
|
324
|
+
seen.add(name);
|
|
325
|
+
const entry = data[name];
|
|
326
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
327
|
+
const nxt = entry.inherits;
|
|
328
|
+
if (typeof nxt === "string" && nxt.trim().toLowerCase() !== name) {
|
|
329
|
+
name = nxt.trim().toLowerCase();
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
return name;
|
|
333
|
+
}
|
|
334
|
+
if (name === TRAITS_OS_FALLBACK) return name;
|
|
335
|
+
name = TRAITS_OS_FALLBACK;
|
|
336
|
+
}
|
|
562
337
|
}
|
|
563
338
|
function loadL3() {
|
|
564
339
|
const data = { ...loadJson("l3_rules.json") };
|
|
@@ -845,6 +620,7 @@ function scanRuleDirs() {
|
|
|
845
620
|
return { override, extra, loaded, ignored };
|
|
846
621
|
}
|
|
847
622
|
function rulesCached() {
|
|
623
|
+
if (!isHotReloadEnabled() && _RULES_CACHE) return _RULES_CACHE;
|
|
848
624
|
const dirs = rulesDirs();
|
|
849
625
|
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
850
626
|
const files = [];
|
|
@@ -869,79 +645,517 @@ function rulesCached() {
|
|
|
869
645
|
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
870
646
|
return _RULES_CACHE;
|
|
871
647
|
}
|
|
872
|
-
function identicalToBuiltin(override) {
|
|
873
|
-
const l1 = loadL1();
|
|
874
|
-
const l2 = loadL2();
|
|
875
|
-
for (const [cat, items] of Object.entries(override)) {
|
|
876
|
-
const builtin = l1[cat] ?? l2[cat];
|
|
877
|
-
if (!Array.isArray(builtin) || builtin.length !== items.length) return false;
|
|
878
|
-
if (JSON.stringify(builtin) !== JSON.stringify(items)) return false;
|
|
648
|
+
function identicalToBuiltin(override) {
|
|
649
|
+
const l1 = loadL1();
|
|
650
|
+
const l2 = loadL2();
|
|
651
|
+
for (const [cat, items] of Object.entries(override)) {
|
|
652
|
+
const builtin = l1[cat] ?? l2[cat];
|
|
653
|
+
if (!Array.isArray(builtin) || builtin.length !== items.length) return false;
|
|
654
|
+
if (JSON.stringify(builtin) !== JSON.stringify(items)) return false;
|
|
655
|
+
}
|
|
656
|
+
return Object.keys(override).length > 0;
|
|
657
|
+
}
|
|
658
|
+
function loadOverrideRules() {
|
|
659
|
+
if (isBuiltinOnly()) return {};
|
|
660
|
+
return rulesCached().override;
|
|
661
|
+
}
|
|
662
|
+
function loadExtraRules() {
|
|
663
|
+
if (isBuiltinOnly()) return {};
|
|
664
|
+
return rulesCached().extra;
|
|
665
|
+
}
|
|
666
|
+
function ruleFilesLoaded() {
|
|
667
|
+
return rulesCached().loaded;
|
|
668
|
+
}
|
|
669
|
+
function ignoredRuleFiles() {
|
|
670
|
+
return rulesCached().ignored;
|
|
671
|
+
}
|
|
672
|
+
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS, frozenJsonCache, SCENARIO_LIST_KEYS, RE_FLAG_MAP, TRAITS_FIELDS, TRAITS_OS_FALLBACK, EXTRA_CATEGORIES, STRUCT_KEYS, EXTRA_FLAT_FIELD, _RULES_CACHE, warnedIgnored;
|
|
673
|
+
var init_rules_loader = __esm({
|
|
674
|
+
"src/infra/rules-loader.ts"() {
|
|
675
|
+
"use strict";
|
|
676
|
+
init_esm_shims();
|
|
677
|
+
init_paths();
|
|
678
|
+
init_rules_mode();
|
|
679
|
+
init_runtime_config_bridge();
|
|
680
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
681
|
+
__dirname2 = dirname(__filename2);
|
|
682
|
+
OVERRIDE_FILENAME = "override.json";
|
|
683
|
+
EXTRA_FILENAME = "extra.json";
|
|
684
|
+
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
685
|
+
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
686
|
+
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
687
|
+
RE_FLAG_MAP = { i: "i", s: "s", m: "m" };
|
|
688
|
+
TRAITS_FIELDS = [
|
|
689
|
+
"write_keywords",
|
|
690
|
+
"read_keywords",
|
|
691
|
+
"write_command_res",
|
|
692
|
+
"read_only_commands",
|
|
693
|
+
"fully_safe_commands",
|
|
694
|
+
"readonly_sensitive_commands",
|
|
695
|
+
"safe_action_types",
|
|
696
|
+
"readonly_sensitive_action_types"
|
|
697
|
+
];
|
|
698
|
+
TRAITS_OS_FALLBACK = "linux";
|
|
699
|
+
EXTRA_CATEGORIES = {
|
|
700
|
+
dangerous_commands: ["pattern"],
|
|
701
|
+
injection_detection: ["keyword", "lang"],
|
|
702
|
+
sensitive_path_access: ["path"],
|
|
703
|
+
intent_consistency: ["intent_keywords"],
|
|
704
|
+
password_modify_consent: ["pattern"],
|
|
705
|
+
user_deletion_consent: ["pattern"]
|
|
706
|
+
};
|
|
707
|
+
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
708
|
+
EXTRA_FLAT_FIELD = [
|
|
709
|
+
[["path"], "sensitive_path_access"],
|
|
710
|
+
[["keyword", "lang"], "injection_detection"],
|
|
711
|
+
[["intent_keywords"], "intent_consistency"],
|
|
712
|
+
[["pattern", "risk_level"], "dangerous_commands"],
|
|
713
|
+
[["pattern", "risk_type"], "dangerous_commands"]
|
|
714
|
+
];
|
|
715
|
+
_RULES_CACHE = null;
|
|
716
|
+
warnedIgnored = /* @__PURE__ */ new Set();
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
// src/infra/rules-contract.ts
|
|
721
|
+
var rules_contract_exports = {};
|
|
722
|
+
__export(rules_contract_exports, {
|
|
723
|
+
MANIFEST_FILE: () => MANIFEST_FILE,
|
|
724
|
+
REQUIRED_RULE_FILES: () => REQUIRED_RULE_FILES,
|
|
725
|
+
RULES_SCHEMA_VERSION: () => RULES_SCHEMA_VERSION,
|
|
726
|
+
checkRulesAssets: () => checkRulesAssets,
|
|
727
|
+
enforceRulesContract: () => enforceRulesContract,
|
|
728
|
+
formatRulesIntegrity: () => formatRulesIntegrity
|
|
729
|
+
});
|
|
730
|
+
import { createHash } from "crypto";
|
|
731
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
732
|
+
import { join as join3 } from "path";
|
|
733
|
+
function sha256Short(data, n = 12) {
|
|
734
|
+
return createHash("sha256").update(data).digest("hex").slice(0, n);
|
|
735
|
+
}
|
|
736
|
+
function rulesDir() {
|
|
737
|
+
if (getRulesRoot()) return join3(getRulesRoot(), "rules");
|
|
738
|
+
const probe = resolveRulesFile("l1_rules.json");
|
|
739
|
+
return probe ? probe.slice(0, probe.length - "l1_rules.json".length - 1) : null;
|
|
740
|
+
}
|
|
741
|
+
function checkRulesAssets() {
|
|
742
|
+
const rep = {
|
|
743
|
+
ok: true,
|
|
744
|
+
schemaVersion: 0,
|
|
745
|
+
fingerprint: "",
|
|
746
|
+
fileHashes: {},
|
|
747
|
+
errors: [],
|
|
748
|
+
external: getRulesRoot() !== null
|
|
749
|
+
};
|
|
750
|
+
const dir = rulesDir();
|
|
751
|
+
if (!dir) {
|
|
752
|
+
rep.errors.push("\u627E\u4E0D\u5230\u89C4\u5219\u76EE\u5F55\uFF08\u5305\u5185\u89C4\u5219\u6587\u4EF6\u7F3A\u5931\uFF0C\u4E14\u672A\u8BBE AGENT_MOSS_RULES_ROOT\uFF09");
|
|
753
|
+
rep.ok = false;
|
|
754
|
+
return rep;
|
|
755
|
+
}
|
|
756
|
+
const manifestPath = join3(dir, MANIFEST_FILE);
|
|
757
|
+
if (!existsSync2(manifestPath)) {
|
|
758
|
+
rep.errors.push(`\u7F3A\u5C11 ${MANIFEST_FILE}\uFF08\u89C4\u5219\u5305\u5FC5\u987B\u58F0\u660E schema \u7248\u672C\uFF09`);
|
|
759
|
+
} else {
|
|
760
|
+
try {
|
|
761
|
+
const m = JSON.parse(readFileSync3(manifestPath, "utf-8")) || {};
|
|
762
|
+
const ver = m.schema_version;
|
|
763
|
+
if (typeof ver !== "number" || !Number.isInteger(ver)) {
|
|
764
|
+
rep.errors.push(`${MANIFEST_FILE} \u7F3A\u5C11\u6574\u6570 schema_version`);
|
|
765
|
+
} else {
|
|
766
|
+
rep.schemaVersion = ver;
|
|
767
|
+
if (ver !== RULES_SCHEMA_VERSION) {
|
|
768
|
+
rep.errors.push(
|
|
769
|
+
`schema \u7248\u672C\u4E0D\u5339\u914D\uFF1A\u89C4\u5219\u5305\u662F ${ver}\uFF0C\u672C\u5F15\u64CE\u8BA4\u8BC6 ${RULES_SCHEMA_VERSION}\u3002\u8BF7\u5347\u7EA7\u5F15\u64CE\u6216\u6539\u7528\u5BF9\u5E94\u7248\u672C\u7684\u89C4\u5219\u5305\u3002`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
} catch (e) {
|
|
774
|
+
rep.errors.push(`${MANIFEST_FILE} \u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${String(e)}`);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const fpParts = [];
|
|
778
|
+
for (const [name, requiredKeys] of Object.entries(REQUIRED_RULE_FILES)) {
|
|
779
|
+
const p = join3(dir, name);
|
|
780
|
+
if (!existsSync2(p)) {
|
|
781
|
+
rep.errors.push(`\u7F3A\u5C11\u89C4\u5219\u6587\u4EF6 ${name}`);
|
|
782
|
+
rep.fileHashes[name] = "MISSING";
|
|
783
|
+
fpParts.push(`${name}:MISSING`);
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
let raw = null;
|
|
787
|
+
try {
|
|
788
|
+
raw = readFileSync3(p);
|
|
789
|
+
} catch (e) {
|
|
790
|
+
rep.errors.push(`${name} \u8BFB\u53D6\u5931\u8D25: ${String(e)}`);
|
|
791
|
+
rep.fileHashes[name] = "UNREADABLE";
|
|
792
|
+
fpParts.push(`${name}:UNREADABLE`);
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
rep.fileHashes[name] = sha256Short(raw);
|
|
796
|
+
fpParts.push(`${name}:${rep.fileHashes[name]}`);
|
|
797
|
+
let data = null;
|
|
798
|
+
try {
|
|
799
|
+
data = JSON.parse(raw.toString("utf-8")) || {};
|
|
800
|
+
} catch (e) {
|
|
801
|
+
rep.errors.push(`${name} \u89E3\u6790\u5931\u8D25: ${String(e)}`);
|
|
802
|
+
rep.fileHashes[name] = "BADJSON";
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (typeof data !== "object" || Array.isArray(data)) {
|
|
806
|
+
rep.errors.push(`${name} \u9876\u5C42\u5FC5\u987B\u662F JSON \u5BF9\u8C61`);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const missing = requiredKeys.filter((k) => !(k in data));
|
|
810
|
+
if (missing.length) rep.errors.push(`${name} \u7F3A\u5C11\u9876\u5C42\u952E: ${missing.join(", ")}`);
|
|
811
|
+
}
|
|
812
|
+
rep.fingerprint = sha256Short([...fpParts].sort().join("\n"));
|
|
813
|
+
rep.ok = rep.errors.length === 0;
|
|
814
|
+
return rep;
|
|
815
|
+
}
|
|
816
|
+
function formatRulesIntegrity(rep) {
|
|
817
|
+
const src = rep.external ? `\u5916\u90E8\u89C4\u5219\u5305 ${getRulesRoot()}` : "\u5305\u5185\u89C4\u5219";
|
|
818
|
+
const head = `\u89C4\u5219\u5305: ${src} | schema v${rep.schemaVersion} | \u6307\u7EB9 ${rep.fingerprint || "-"}`;
|
|
819
|
+
if (rep.ok) return head;
|
|
820
|
+
return `${head} | \u274C ${rep.errors.length} \u4E2A\u95EE\u9898: ${rep.errors.slice(0, 5).join("\uFF1B")}`;
|
|
821
|
+
}
|
|
822
|
+
function enforceRulesContract() {
|
|
823
|
+
const rep = checkRulesAssets();
|
|
824
|
+
if (rep.ok) {
|
|
825
|
+
console.log(`[AgentMoss] ${formatRulesIntegrity(rep)}`);
|
|
826
|
+
return rep;
|
|
827
|
+
}
|
|
828
|
+
const msg = formatRulesIntegrity(rep);
|
|
829
|
+
if (rep.external) {
|
|
830
|
+
console.error(`[AgentMoss] \u274C ${msg}`);
|
|
831
|
+
throw new Error(
|
|
832
|
+
`\u89C4\u5219\u5305\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u542F\u52A8\uFF08AGENT_MOSS_RULES_ROOT \u5DF2\u663E\u5F0F\u6307\u5B9A\uFF0C\u4E0D\u5141\u8BB8\u5E26\u7740\u6B8B\u7F3A\u89C4\u5219\u8FD0\u884C\uFF09\uFF1A${rep.errors.join("\uFF1B")}`
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
console.error(`[AgentMoss] \u26A0\uFE0F ${msg}\uFF08\u4F7F\u7528\u5305\u5185\u89C4\u5219\uFF0C\u7167\u5E38\u542F\u52A8\uFF1B\u82E5\u662F npm \u5B89\u88C5\u8BF7\u91CD\u88C5\uFF09`);
|
|
836
|
+
return rep;
|
|
837
|
+
}
|
|
838
|
+
var RULES_SCHEMA_VERSION, REQUIRED_RULE_FILES, MANIFEST_FILE;
|
|
839
|
+
var init_rules_contract = __esm({
|
|
840
|
+
"src/infra/rules-contract.ts"() {
|
|
841
|
+
"use strict";
|
|
842
|
+
init_esm_shims();
|
|
843
|
+
init_paths();
|
|
844
|
+
init_rules_loader();
|
|
845
|
+
RULES_SCHEMA_VERSION = 1;
|
|
846
|
+
REQUIRED_RULE_FILES = {
|
|
847
|
+
"l1_rules.json": ["dangerous_commands", "injection_detection", "sensitive_path_access"],
|
|
848
|
+
"l2_rules.json": ["intent_consistency", "password_modify_consent", "user_deletion_consent"],
|
|
849
|
+
"l3_rules.json": [],
|
|
850
|
+
"mc_rules.json": ["rules", "ap_patterns"],
|
|
851
|
+
"tool_registry.json": ["tools"],
|
|
852
|
+
"platform_traits.json": ["linux", "windows"],
|
|
853
|
+
"inline_rules.json": ["read_write_keywords", "dir_traversal_keywords", "write_exec_chain_patterns"],
|
|
854
|
+
"script_rules.json": [
|
|
855
|
+
"suspicious_patterns",
|
|
856
|
+
"keyword_combination_risks",
|
|
857
|
+
"single_keyword_risks",
|
|
858
|
+
"script_exec_patterns"
|
|
859
|
+
],
|
|
860
|
+
"heuristic_rules.json": [
|
|
861
|
+
"sensitive_excluded_prefixes",
|
|
862
|
+
"search_action_prefixes",
|
|
863
|
+
"loopback_hosts",
|
|
864
|
+
"temp_dirs"
|
|
865
|
+
],
|
|
866
|
+
"logic_rules.json": ["indirect_file_access", "write_commands", "source_arg_commands"],
|
|
867
|
+
"formal_rules.json": ["privesc", "compute_action_types", "pattern_miner", "output_commands"]
|
|
868
|
+
};
|
|
869
|
+
MANIFEST_FILE = "manifest.json";
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// src/package-json.ts
|
|
874
|
+
import { createRequire } from "module";
|
|
875
|
+
var require2, pkg, version;
|
|
876
|
+
var init_package_json = __esm({
|
|
877
|
+
"src/package-json.ts"() {
|
|
878
|
+
"use strict";
|
|
879
|
+
init_esm_shims();
|
|
880
|
+
require2 = createRequire(import.meta.url);
|
|
881
|
+
pkg = require2("../package.json");
|
|
882
|
+
version = pkg.version;
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// src/models.ts
|
|
887
|
+
import { z } from "zod";
|
|
888
|
+
var ActionItemSchema, PromptTurnSchema, AnalyzeRequestSchema, AnalyzeResponseSchema, HealthResponseSchema, ReviewRequestSchema, ReviewResponseSchema;
|
|
889
|
+
var init_models = __esm({
|
|
890
|
+
"src/models.ts"() {
|
|
891
|
+
"use strict";
|
|
892
|
+
init_esm_shims();
|
|
893
|
+
init_package_json();
|
|
894
|
+
ActionItemSchema = z.object({
|
|
895
|
+
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
896
|
+
action_detail: z.string().describe("Action detail / command content"),
|
|
897
|
+
// 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
|
|
898
|
+
// 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
|
|
899
|
+
// 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
|
|
900
|
+
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
|
|
901
|
+
});
|
|
902
|
+
PromptTurnSchema = z.object({
|
|
903
|
+
text: z.string().default("").describe("User prompt text at this turn"),
|
|
904
|
+
actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
|
|
905
|
+
});
|
|
906
|
+
AnalyzeRequestSchema = z.object({
|
|
907
|
+
session_id: z.string().describe("Session / agent unique identifier"),
|
|
908
|
+
prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
|
|
909
|
+
prompt_history: z.array(PromptTurnSchema).default([]).describe("Full interleaved prompt history (ordered turns, each with its triggered actions). Optional; empty = fall back to prompt_session only."),
|
|
910
|
+
action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
|
|
911
|
+
session_history: z.array(z.object({
|
|
912
|
+
session_id: z.string(),
|
|
913
|
+
action_history: z.array(ActionItemSchema).default([])
|
|
914
|
+
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
915
|
+
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
916
|
+
reason: z.string().default("").describe("Reason for executing this action"),
|
|
917
|
+
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
918
|
+
cwd: z.string().default("").describe("Current working directory"),
|
|
919
|
+
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
920
|
+
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
921
|
+
});
|
|
922
|
+
AnalyzeResponseSchema = z.object({
|
|
923
|
+
decision: z.enum(["Allow", "Deny"]).describe("Allow or Deny"),
|
|
924
|
+
reason: z.string().describe("Decision reason"),
|
|
925
|
+
risk_level: z.enum(["low", "medium", "high", "critical"]).default("low"),
|
|
926
|
+
risk_type: z.string().default(""),
|
|
927
|
+
violated_layers: z.array(z.string()).default([]),
|
|
928
|
+
policy: z.string().default(""),
|
|
929
|
+
violated_policy: z.string().default(""),
|
|
930
|
+
confidence: z.number().int().min(0).max(100).default(100),
|
|
931
|
+
analysis_duration_ms: z.number().default(0),
|
|
932
|
+
// 组合攻击链反例路径:每步 [session_id, seq, stage, timestamp],含真实发生时间
|
|
933
|
+
violation_chain: z.array(z.array(z.union([z.string(), z.number()]).nullable())).default([])
|
|
934
|
+
});
|
|
935
|
+
HealthResponseSchema = z.object({
|
|
936
|
+
status: z.string().default("healthy"),
|
|
937
|
+
version: z.string().default(version),
|
|
938
|
+
// 归属实例标识:值来自 env AGENT_MOSS_INSTANCE(spawn/systemd 注入),默认空串。
|
|
939
|
+
// 多个 agentmoss 同机并存时,调用方按 instance 过滤避免连到别人的进程。
|
|
940
|
+
// 空=未配置(独立部署或旧版),调用方可选择兼容或严格过滤。
|
|
941
|
+
instance: z.string().default("")
|
|
942
|
+
});
|
|
943
|
+
ReviewRequestSchema = z.object({
|
|
944
|
+
agent_id: z.string().default("").describe("\u76EE\u6807 agent \u6807\u8BC6\uFF08\u51B3\u5B9A\u72B6\u6001\u5E93\u5206\u6876\uFF09"),
|
|
945
|
+
db_path: z.string().default("").describe("AgentGate \u672C\u5730\u5B58\u50A8\u8DEF\u5F84\uFF08SQLite \u6587\u4EF6 \u6216 tool_calls JSON \u76EE\u5F55\uFF09\uFF1B\u4E3A\u7A7A\u5219\u7528 sessions"),
|
|
946
|
+
sessions: z.array(z.object({
|
|
947
|
+
session_id: z.string(),
|
|
948
|
+
action_history: z.array(ActionItemSchema).default([])
|
|
949
|
+
})).default([]).describe("\u591A session \u5386\u53F2\u52A8\u4F5C\uFF08\u66FF\u4EE3/\u515C\u5E95 db_path\uFF09"),
|
|
950
|
+
a_next: ActionItemSchema.optional().describe("\u53EF\u9009\uFF1Aseed \u540E\u8FDE\u540C\u5F53\u524D\u52A8\u4F5C\u4E00\u8D77\u5224\u5B9A"),
|
|
951
|
+
is_error: z.boolean().default(false).describe("\u5F53\u524D\u52A8\u4F5C\u6267\u884C\u662F\u5426\u5931\u8D25"),
|
|
952
|
+
output_pending: z.boolean().default(false).describe("\u8FD4\u56DE\u524D\u5E8F\u94FE\u6E05\u5355 pending_prefixes")
|
|
953
|
+
});
|
|
954
|
+
ReviewResponseSchema = z.object({
|
|
955
|
+
agent_id: z.string().default(""),
|
|
956
|
+
seeded_nodes: z.number().default(0),
|
|
957
|
+
verified: z.boolean().default(true),
|
|
958
|
+
violations: z.array(z.record(z.unknown())).default([]),
|
|
959
|
+
pending_prefixes: z.array(z.record(z.unknown())).default([]),
|
|
960
|
+
state_nodes: z.number().default(0),
|
|
961
|
+
discovered_patterns: z.array(z.record(z.unknown())).default([]).describe("\u6570\u636E\u9A71\u52A8\u53D1\u73B0\uFF1A\u8DE8 session \u9891\u7E41\u5371\u9669\u7EC4\u5408\uFF08\u9884\u7F6E\u94FE\u76F2\u533A\uFF0CPatternMiner\uFF09")
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// src/engine/shell-split.ts
|
|
967
|
+
function hasCommandSubstitution(text) {
|
|
968
|
+
return CMD_SUBSTITUTION.test(text);
|
|
969
|
+
}
|
|
970
|
+
function splitCommandSegments(command) {
|
|
971
|
+
const segments = [];
|
|
972
|
+
let cur = "";
|
|
973
|
+
let quote = null;
|
|
974
|
+
for (let i = 0; i < command.length; i++) {
|
|
975
|
+
const ch = command[i];
|
|
976
|
+
if (quote) {
|
|
977
|
+
cur += ch;
|
|
978
|
+
if (ch === quote) quote = null;
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (ch === "'" || ch === '"') {
|
|
982
|
+
quote = ch;
|
|
983
|
+
cur += ch;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (/\||&|;|\n/.test(ch)) {
|
|
987
|
+
segments.push(cur);
|
|
988
|
+
segments.push(ch);
|
|
989
|
+
cur = "";
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
cur += ch;
|
|
993
|
+
}
|
|
994
|
+
if (cur) segments.push(cur);
|
|
995
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0 && !/^(?:\|\||&&|[|;&])$/.test(s));
|
|
996
|
+
}
|
|
997
|
+
function extractFirstCommand(command) {
|
|
998
|
+
const segments = splitCommandSegments(command);
|
|
999
|
+
return segments.length ? segments[0] : "";
|
|
1000
|
+
}
|
|
1001
|
+
var CMD_SUBSTITUTION;
|
|
1002
|
+
var init_shell_split = __esm({
|
|
1003
|
+
"src/engine/shell-split.ts"() {
|
|
1004
|
+
"use strict";
|
|
1005
|
+
init_esm_shims();
|
|
1006
|
+
CMD_SUBSTITUTION = /\$\(|`|</;
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
// src/engine/inline-analyzer.ts
|
|
1011
|
+
function buildChainRegex(pattern, flags) {
|
|
1012
|
+
return new RegExp(pattern, (flags || "") + "g");
|
|
1013
|
+
}
|
|
1014
|
+
function isLiteralTextCommand(command) {
|
|
1015
|
+
const segments = splitCommandSegments(command);
|
|
1016
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
1017
|
+
if (command.includes(">")) {
|
|
1018
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1019
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
1020
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
1021
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
1022
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
function extractInlineCode(command) {
|
|
1032
|
+
const parts = [];
|
|
1033
|
+
let m;
|
|
1034
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
1035
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
1036
|
+
const code = m[1] ?? m[2];
|
|
1037
|
+
if (code) parts.push(code);
|
|
879
1038
|
}
|
|
880
|
-
|
|
1039
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
1040
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
1041
|
+
const code = m[1] ?? m[2];
|
|
1042
|
+
if (code) parts.push(code);
|
|
1043
|
+
}
|
|
1044
|
+
return parts.join("\n");
|
|
881
1045
|
}
|
|
882
|
-
function
|
|
883
|
-
return
|
|
1046
|
+
function hasWriteExecChain(command) {
|
|
1047
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
1048
|
+
p.lastIndex = 0;
|
|
1049
|
+
return p.test(command);
|
|
1050
|
+
});
|
|
884
1051
|
}
|
|
885
|
-
function
|
|
886
|
-
|
|
1052
|
+
function hasDirTraversal(code) {
|
|
1053
|
+
const lower = code.toLowerCase();
|
|
1054
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
887
1055
|
}
|
|
888
|
-
function
|
|
889
|
-
|
|
1056
|
+
function hasReadWriteKeyword(code) {
|
|
1057
|
+
const lower = code.toLowerCase();
|
|
1058
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
890
1059
|
}
|
|
891
|
-
function
|
|
892
|
-
|
|
1060
|
+
function analyzeInlineScript(command) {
|
|
1061
|
+
const code = extractInlineCode(command);
|
|
1062
|
+
if (!code) {
|
|
1063
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
1064
|
+
}
|
|
1065
|
+
if (hasWriteExecChain(command)) {
|
|
1066
|
+
return {
|
|
1067
|
+
actionable: true,
|
|
1068
|
+
allowed: false,
|
|
1069
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
1070
|
+
riskLevel: "critical",
|
|
1071
|
+
riskType: "script_execution"
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
1075
|
+
if (redirMatch) {
|
|
1076
|
+
return {
|
|
1077
|
+
actionable: true,
|
|
1078
|
+
allowed: false,
|
|
1079
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
1080
|
+
riskLevel: "critical",
|
|
1081
|
+
riskType: "file_access"
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
1085
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
1086
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
1087
|
+
}
|
|
1088
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
1089
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
1090
|
+
return {
|
|
1091
|
+
actionable: true,
|
|
1092
|
+
allowed: false,
|
|
1093
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
1094
|
+
riskLevel: "critical",
|
|
1095
|
+
riskType: "file_access"
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
return {
|
|
1099
|
+
actionable: true,
|
|
1100
|
+
allowed: true,
|
|
1101
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
1102
|
+
riskLevel: "low",
|
|
1103
|
+
riskType: "file_access"
|
|
1104
|
+
};
|
|
893
1105
|
}
|
|
894
|
-
var
|
|
895
|
-
var
|
|
896
|
-
"src/
|
|
1106
|
+
var INLINE_RULES, INLINE_CODE_RE, ECHO_STRING_RE, ECHO_SEG_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, _REDIRECT_PATHS, SHELL_RC_FRAGMENTS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
1107
|
+
var init_inline_analyzer = __esm({
|
|
1108
|
+
"src/engine/inline-analyzer.ts"() {
|
|
897
1109
|
"use strict";
|
|
898
1110
|
init_esm_shims();
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
918
|
-
resolve(process.cwd(), "agent_moss/rules")
|
|
919
|
-
];
|
|
920
|
-
return cands.filter((c) => existsSync(c));
|
|
921
|
-
})();
|
|
922
|
-
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
923
|
-
EXTRA_CATEGORIES = {
|
|
924
|
-
dangerous_commands: ["pattern"],
|
|
925
|
-
injection_detection: ["keyword", "lang"],
|
|
926
|
-
sensitive_path_access: ["path"],
|
|
927
|
-
intent_consistency: ["intent_keywords"],
|
|
928
|
-
password_modify_consent: ["pattern"],
|
|
929
|
-
user_deletion_consent: ["pattern"]
|
|
930
|
-
};
|
|
931
|
-
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
932
|
-
EXTRA_FLAT_FIELD = [
|
|
933
|
-
[["path"], "sensitive_path_access"],
|
|
934
|
-
[["keyword", "lang"], "injection_detection"],
|
|
935
|
-
[["intent_keywords"], "intent_consistency"],
|
|
936
|
-
[["pattern", "risk_level"], "dangerous_commands"],
|
|
937
|
-
[["pattern", "risk_type"], "dangerous_commands"]
|
|
1111
|
+
init_shell_split();
|
|
1112
|
+
init_rules_loader();
|
|
1113
|
+
INLINE_RULES = loadInline();
|
|
1114
|
+
if (!INLINE_RULES || Object.keys(INLINE_RULES).length === 0) {
|
|
1115
|
+
console.error(
|
|
1116
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 inline_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u5185\u8054\u811A\u672C\u5224\u5B9A\u8BCD\u8868\u4E3A\u7A7A\uFF0C\u5185\u8054\u811A\u672C\u4E2D\u7684\u654F\u611F\u8DEF\u5F84\u8BFB\u5199\u5C06\u6309\u300C\u6587\u672C\u5B57\u9762\u91CF\u300D\u653E\u884C\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
1120
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
1121
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
1122
|
+
READ_WRITE_KEYWORDS = INLINE_RULES.read_write_keywords ?? [];
|
|
1123
|
+
WRITE_EXEC_CHAIN_PATTERNS = (INLINE_RULES.write_exec_chain_patterns ?? []).filter((p) => p && typeof p.pattern === "string").map((p) => buildChainRegex(p.pattern, p.flags ?? ""));
|
|
1124
|
+
_REDIRECT_PATHS = INLINE_RULES.redirect_sensitive_paths ?? {};
|
|
1125
|
+
SHELL_RC_FRAGMENTS = _REDIRECT_PATHS.shell_rc_fragments ?? [];
|
|
1126
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
1127
|
+
..._REDIRECT_PATHS.ts_static ?? [],
|
|
1128
|
+
...SHELL_RC_FRAGMENTS
|
|
938
1129
|
];
|
|
939
|
-
|
|
940
|
-
|
|
1130
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
1131
|
+
DIR_TRAVERSAL_KEYWORDS = INLINE_RULES.dir_traversal_keywords ?? [];
|
|
941
1132
|
}
|
|
942
1133
|
});
|
|
943
1134
|
|
|
944
1135
|
// src/engine/platform-traits.ts
|
|
1136
|
+
function buildTraits(osName) {
|
|
1137
|
+
const t = loadPlatformTraits(osName);
|
|
1138
|
+
const strs = (k) => Array.isArray(t[k]) ? t[k] : [];
|
|
1139
|
+
return {
|
|
1140
|
+
writeKeywords: strs("write_keywords"),
|
|
1141
|
+
readKeywords: strs("read_keywords"),
|
|
1142
|
+
writeCommandRes: strs("write_command_res").map((p) => new RegExp(p)),
|
|
1143
|
+
readOnlyCommands: new Set(strs("read_only_commands")),
|
|
1144
|
+
fullySafeCommands: strs("fully_safe_commands").map((p) => new RegExp(p, "i")),
|
|
1145
|
+
readonlySensitiveCommands: strs("readonly_sensitive_commands").map((p) => new RegExp(p, "i")),
|
|
1146
|
+
safeActionTypes: new Set(strs("safe_action_types")),
|
|
1147
|
+
readonlySensitiveActionTypes: new Set(strs("readonly_sensitive_action_types"))
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function traitsFor(key) {
|
|
1151
|
+
const resolved = resolveTraitsOs(key);
|
|
1152
|
+
let t = traitsCache.get(resolved);
|
|
1153
|
+
if (!t) {
|
|
1154
|
+
t = buildTraits(resolved);
|
|
1155
|
+
traitsCache.set(resolved, t);
|
|
1156
|
+
}
|
|
1157
|
+
return t;
|
|
1158
|
+
}
|
|
945
1159
|
function getPlatformTraits(osType) {
|
|
946
1160
|
const accepted = acceptedOsValues(osType);
|
|
947
1161
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -949,368 +1163,18 @@ function getPlatformTraits(osType) {
|
|
|
949
1163
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
950
1164
|
return LINUX_TRAITS;
|
|
951
1165
|
}
|
|
952
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
1166
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
953
1167
|
var init_platform_traits = __esm({
|
|
954
1168
|
"src/engine/platform-traits.ts"() {
|
|
955
1169
|
"use strict";
|
|
956
1170
|
init_esm_shims();
|
|
957
1171
|
init_logic_rules();
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
"modify",
|
|
965
|
-
"\u4FEE\u6539",
|
|
966
|
-
"update",
|
|
967
|
-
"\u66F4\u65B0",
|
|
968
|
-
"delete",
|
|
969
|
-
"\u5220\u9664",
|
|
970
|
-
"remove",
|
|
971
|
-
"\u79FB\u9664",
|
|
972
|
-
"create",
|
|
973
|
-
"\u521B\u5EFA",
|
|
974
|
-
"append",
|
|
975
|
-
"\u8FFD\u52A0",
|
|
976
|
-
"truncate",
|
|
977
|
-
"\u622A\u65AD",
|
|
978
|
-
"overwrite",
|
|
979
|
-
"\u8986\u76D6",
|
|
980
|
-
"replace",
|
|
981
|
-
"\u66FF\u6362",
|
|
982
|
-
"edit"
|
|
983
|
-
],
|
|
984
|
-
readKeywords: [
|
|
985
|
-
"read",
|
|
986
|
-
"\u8BFB\u53D6",
|
|
987
|
-
"cat",
|
|
988
|
-
"head",
|
|
989
|
-
"tail",
|
|
990
|
-
"less",
|
|
991
|
-
"more",
|
|
992
|
-
"view",
|
|
993
|
-
"\u67E5\u770B",
|
|
994
|
-
"open",
|
|
995
|
-
"\u6253\u5F00",
|
|
996
|
-
"load",
|
|
997
|
-
"\u52A0\u8F7D",
|
|
998
|
-
"grep",
|
|
999
|
-
"search",
|
|
1000
|
-
"\u641C\u7D22",
|
|
1001
|
-
"find",
|
|
1002
|
-
"\u67E5\u627E"
|
|
1003
|
-
],
|
|
1004
|
-
writeCommandRes: [
|
|
1005
|
-
/\brm\b/,
|
|
1006
|
-
/\bunlink\b/,
|
|
1007
|
-
/\brmdir\b/,
|
|
1008
|
-
/\bshred\b/,
|
|
1009
|
-
/\bcp\b/,
|
|
1010
|
-
/\bmv\b/,
|
|
1011
|
-
/\binstall\b/,
|
|
1012
|
-
/\btee\b/,
|
|
1013
|
-
/\bdd\b/,
|
|
1014
|
-
/\bmkfs\b/,
|
|
1015
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
1016
|
-
/\bchmod\b/,
|
|
1017
|
-
/\bchown\b/,
|
|
1018
|
-
/\btruncate\b/
|
|
1019
|
-
],
|
|
1020
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1021
|
-
"cat",
|
|
1022
|
-
"head",
|
|
1023
|
-
"tail",
|
|
1024
|
-
"less",
|
|
1025
|
-
"more",
|
|
1026
|
-
"grep",
|
|
1027
|
-
"find",
|
|
1028
|
-
"awk",
|
|
1029
|
-
"sed",
|
|
1030
|
-
"sort",
|
|
1031
|
-
"uniq",
|
|
1032
|
-
"wc",
|
|
1033
|
-
"cut",
|
|
1034
|
-
"strings",
|
|
1035
|
-
"od",
|
|
1036
|
-
"xxd",
|
|
1037
|
-
"hexdump",
|
|
1038
|
-
"tr",
|
|
1039
|
-
"file",
|
|
1040
|
-
"stat",
|
|
1041
|
-
"du",
|
|
1042
|
-
"df",
|
|
1043
|
-
"ls",
|
|
1044
|
-
"dir",
|
|
1045
|
-
"tree",
|
|
1046
|
-
"nl",
|
|
1047
|
-
"tac",
|
|
1048
|
-
"rev",
|
|
1049
|
-
"echo",
|
|
1050
|
-
"printf",
|
|
1051
|
-
"lsblk",
|
|
1052
|
-
"blockdev",
|
|
1053
|
-
"smartctl",
|
|
1054
|
-
"udevadm",
|
|
1055
|
-
"dmidecode",
|
|
1056
|
-
"lscpu",
|
|
1057
|
-
"lspci",
|
|
1058
|
-
"lsusb",
|
|
1059
|
-
"lsmem",
|
|
1060
|
-
"lsns",
|
|
1061
|
-
"lsof",
|
|
1062
|
-
"hwinfo",
|
|
1063
|
-
"inxi",
|
|
1064
|
-
"fdisk",
|
|
1065
|
-
"parted",
|
|
1066
|
-
"dmesg",
|
|
1067
|
-
"journalctl",
|
|
1068
|
-
"systemctl",
|
|
1069
|
-
"hostnamectl",
|
|
1070
|
-
"localectl",
|
|
1071
|
-
"timedatectl",
|
|
1072
|
-
"ps",
|
|
1073
|
-
"top",
|
|
1074
|
-
"free",
|
|
1075
|
-
"vmstat",
|
|
1076
|
-
"iostat",
|
|
1077
|
-
"mpstat",
|
|
1078
|
-
"pidof",
|
|
1079
|
-
"pgrep",
|
|
1080
|
-
"ip",
|
|
1081
|
-
"ifconfig",
|
|
1082
|
-
"route",
|
|
1083
|
-
"ss",
|
|
1084
|
-
"netstat",
|
|
1085
|
-
"arp",
|
|
1086
|
-
"ethtool",
|
|
1087
|
-
"nmcli",
|
|
1088
|
-
"uname",
|
|
1089
|
-
"arch",
|
|
1090
|
-
"nproc",
|
|
1091
|
-
"getconf",
|
|
1092
|
-
"getent",
|
|
1093
|
-
"id",
|
|
1094
|
-
"whoami",
|
|
1095
|
-
"who",
|
|
1096
|
-
"w",
|
|
1097
|
-
"last",
|
|
1098
|
-
"uptime",
|
|
1099
|
-
"lsmod",
|
|
1100
|
-
"modinfo",
|
|
1101
|
-
"sysctl"
|
|
1102
|
-
]),
|
|
1103
|
-
fullySafeCommands: [
|
|
1104
|
-
/^ls(?:\s|$)/i,
|
|
1105
|
-
/^dir(?:\s|$)/i,
|
|
1106
|
-
/^pwd(?:\s|$)/i,
|
|
1107
|
-
/^which\s+/i,
|
|
1108
|
-
/^whereis\s+/i,
|
|
1109
|
-
/^realpath\s+/i,
|
|
1110
|
-
/^basename\s+/i,
|
|
1111
|
-
/^dirname\s+/i,
|
|
1112
|
-
/^file\s+/i,
|
|
1113
|
-
/^stat\s+/i,
|
|
1114
|
-
/^du(?:\s|$)/i,
|
|
1115
|
-
/^echo\s+/i,
|
|
1116
|
-
/^printf\s+/i,
|
|
1117
|
-
/^type\s+/i,
|
|
1118
|
-
/^command\s+/i,
|
|
1119
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
1120
|
-
/^whoami(?:\s|$)/i,
|
|
1121
|
-
/^id(?:\s|$)/i,
|
|
1122
|
-
/^hostname(?:\s|$)/i,
|
|
1123
|
-
/^uname(?:\s|$)/i,
|
|
1124
|
-
/^date(?:\s|$)/i,
|
|
1125
|
-
/^env(?:\s|$)/i,
|
|
1126
|
-
/^printenv(?:\s|$)/i,
|
|
1127
|
-
/^tty(?:\s|$)/i,
|
|
1128
|
-
/^arch(?:\s|$)/i,
|
|
1129
|
-
/^uptime(?:\s|$)/i,
|
|
1130
|
-
/^groups(?:\s|$)/i,
|
|
1131
|
-
/^logname(?:\s|$)/i,
|
|
1132
|
-
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
1133
|
-
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
1134
|
-
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
1135
|
-
/^df(?:\s|$)/i,
|
|
1136
|
-
/^free(?:\s|$)/i,
|
|
1137
|
-
/^vmstat(?:\s|$)/i,
|
|
1138
|
-
/^nproc(?:\s|$)/i,
|
|
1139
|
-
/^lscpu(?:\s|$)/i,
|
|
1140
|
-
/^lsblk(?:\s|$)/i,
|
|
1141
|
-
/^lspci(?:\s|$)/i,
|
|
1142
|
-
/^lsusb(?:\s|$)/i,
|
|
1143
|
-
/^lsmod(?:\s|$)/i
|
|
1144
|
-
],
|
|
1145
|
-
readonlySensitiveCommands: [
|
|
1146
|
-
/^cat\s+/i,
|
|
1147
|
-
/^head(?:\s|$)/i,
|
|
1148
|
-
/^tail(?:\s|$)/i,
|
|
1149
|
-
/^less(?:\s|$)/i,
|
|
1150
|
-
/^more(?:\s|$)/i,
|
|
1151
|
-
/^wc(?:\s|$)/i,
|
|
1152
|
-
/^grep(?:\s|$)/i,
|
|
1153
|
-
/^find\s+/i,
|
|
1154
|
-
/^ag(?:\s|$)/i,
|
|
1155
|
-
/^rg(?:\s|$)/i,
|
|
1156
|
-
/^awk\s+/i,
|
|
1157
|
-
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
1158
|
-
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
1159
|
-
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
1160
|
-
/^cut\s+/i,
|
|
1161
|
-
/^sort(?:\s|$)/i,
|
|
1162
|
-
/^uniq(?:\s|$)/i,
|
|
1163
|
-
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
1164
|
-
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
1165
|
-
/^ps(?:\s|$)/i,
|
|
1166
|
-
/^ss(?:\s|$)/i,
|
|
1167
|
-
/^netstat(?:\s|$)/i,
|
|
1168
|
-
/^lsof(?:\s|$)/i,
|
|
1169
|
-
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
1170
|
-
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
1171
|
-
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
1172
|
-
/^service\s+\S+\s+status\b/i,
|
|
1173
|
-
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
1174
|
-
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
1175
|
-
// 的话只看紧跟 journalctl 的第一个参数,
|
|
1176
|
-
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
1177
|
-
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
1178
|
-
]
|
|
1179
|
-
};
|
|
1180
|
-
WINDOWS_TRAITS = {
|
|
1181
|
-
writeKeywords: [
|
|
1182
|
-
"write",
|
|
1183
|
-
"\u5199\u5165",
|
|
1184
|
-
"save",
|
|
1185
|
-
"\u4FDD\u5B58",
|
|
1186
|
-
"modify",
|
|
1187
|
-
"\u4FEE\u6539",
|
|
1188
|
-
"update",
|
|
1189
|
-
"\u66F4\u65B0",
|
|
1190
|
-
"delete",
|
|
1191
|
-
"\u5220\u9664",
|
|
1192
|
-
"remove",
|
|
1193
|
-
"\u79FB\u9664",
|
|
1194
|
-
"create",
|
|
1195
|
-
"\u521B\u5EFA",
|
|
1196
|
-
"append",
|
|
1197
|
-
"\u8FFD\u52A0",
|
|
1198
|
-
"overwrite",
|
|
1199
|
-
"\u8986\u76D6",
|
|
1200
|
-
"replace",
|
|
1201
|
-
"\u66FF\u6362",
|
|
1202
|
-
"set",
|
|
1203
|
-
"\u8BBE\u7F6E"
|
|
1204
|
-
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
1205
|
-
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
1206
|
-
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
1207
|
-
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
1208
|
-
],
|
|
1209
|
-
readKeywords: [
|
|
1210
|
-
"read",
|
|
1211
|
-
"\u8BFB\u53D6",
|
|
1212
|
-
"type",
|
|
1213
|
-
"findstr",
|
|
1214
|
-
"dir",
|
|
1215
|
-
"view",
|
|
1216
|
-
"\u67E5\u770B",
|
|
1217
|
-
"open",
|
|
1218
|
-
"\u6253\u5F00",
|
|
1219
|
-
"load",
|
|
1220
|
-
"\u52A0\u8F7D",
|
|
1221
|
-
"search",
|
|
1222
|
-
"\u641C\u7D22",
|
|
1223
|
-
"find",
|
|
1224
|
-
"\u67E5\u627E",
|
|
1225
|
-
"get",
|
|
1226
|
-
"\u83B7\u53D6"
|
|
1227
|
-
],
|
|
1228
|
-
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
1229
|
-
writeCommandRes: [
|
|
1230
|
-
/\b(del|erase)\b/,
|
|
1231
|
-
/\brmdir\b/,
|
|
1232
|
-
/\b(rd)\b/,
|
|
1233
|
-
/\bcopy\b/,
|
|
1234
|
-
/\b(xcopy|robocopy)\b/,
|
|
1235
|
-
/\b(move|movefile)\b/,
|
|
1236
|
-
/\breplace\b/,
|
|
1237
|
-
/\bicacls\b/,
|
|
1238
|
-
/\battrib\b/,
|
|
1239
|
-
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
1240
|
-
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
1241
|
-
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
1242
|
-
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
1243
|
-
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
1244
|
-
/\bformat\b/,
|
|
1245
|
-
/\bdiskpart\b/,
|
|
1246
|
-
/\bmkdir\b/,
|
|
1247
|
-
/\bmd\b/,
|
|
1248
|
-
/\bsetx\b/,
|
|
1249
|
-
/\bwmic\b/
|
|
1250
|
-
],
|
|
1251
|
-
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
1252
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1253
|
-
"type",
|
|
1254
|
-
"findstr",
|
|
1255
|
-
"more",
|
|
1256
|
-
"tree",
|
|
1257
|
-
"dir",
|
|
1258
|
-
"where",
|
|
1259
|
-
"ver",
|
|
1260
|
-
"cls",
|
|
1261
|
-
"echo",
|
|
1262
|
-
"help",
|
|
1263
|
-
"fc",
|
|
1264
|
-
"comp",
|
|
1265
|
-
"find",
|
|
1266
|
-
"sort",
|
|
1267
|
-
"powershell",
|
|
1268
|
-
"cmd",
|
|
1269
|
-
"netstat",
|
|
1270
|
-
"ipconfig",
|
|
1271
|
-
"tasklist",
|
|
1272
|
-
"systeminfo",
|
|
1273
|
-
"driverquery",
|
|
1274
|
-
"reg",
|
|
1275
|
-
"wmic",
|
|
1276
|
-
"whoami",
|
|
1277
|
-
"hostname",
|
|
1278
|
-
"getmac",
|
|
1279
|
-
"query",
|
|
1280
|
-
"w32tm",
|
|
1281
|
-
"diskpart"
|
|
1282
|
-
]),
|
|
1283
|
-
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
1284
|
-
fullySafeCommands: [
|
|
1285
|
-
/^dir(?:\s|$)/,
|
|
1286
|
-
/^where\s+/,
|
|
1287
|
-
/^echo\s+/,
|
|
1288
|
-
/^cd(?:\s|$)/,
|
|
1289
|
-
/^tree(?:\s|$)/,
|
|
1290
|
-
/^help(?:\s|$)/,
|
|
1291
|
-
/^cls(?:\s|$)/,
|
|
1292
|
-
/^ver(?:\s|$)/,
|
|
1293
|
-
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
1294
|
-
/^whoami(?:\s|$)/,
|
|
1295
|
-
/^hostname(?:\s|$)/,
|
|
1296
|
-
/^systeminfo(?:\s|$)/,
|
|
1297
|
-
/^tasklist(?:\s|$)/,
|
|
1298
|
-
/^getmac(?:\s|$)/,
|
|
1299
|
-
/^driverquery(?:\s|$)/,
|
|
1300
|
-
/^netstat(?:\s|$)/
|
|
1301
|
-
],
|
|
1302
|
-
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
1303
|
-
readonlySensitiveCommands: [
|
|
1304
|
-
/^type\s+/,
|
|
1305
|
-
/^findstr\s+/,
|
|
1306
|
-
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
1307
|
-
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
1308
|
-
/^sc\s+query\b/,
|
|
1309
|
-
/^reg\s+query\b/
|
|
1310
|
-
]
|
|
1311
|
-
};
|
|
1312
|
-
MACOS_TRAITS = LINUX_TRAITS;
|
|
1313
|
-
HARMONY_TRAITS = LINUX_TRAITS;
|
|
1172
|
+
init_rules_loader();
|
|
1173
|
+
traitsCache = /* @__PURE__ */ new Map();
|
|
1174
|
+
LINUX_TRAITS = traitsFor("linux");
|
|
1175
|
+
WINDOWS_TRAITS = traitsFor("windows");
|
|
1176
|
+
MACOS_TRAITS = traitsFor("macos");
|
|
1177
|
+
HARMONY_TRAITS = traitsFor("harmony");
|
|
1314
1178
|
}
|
|
1315
1179
|
});
|
|
1316
1180
|
|
|
@@ -1469,7 +1333,6 @@ function checkIntentConsistency(input) {
|
|
|
1469
1333
|
const { aNext, promptSession, promptHistory } = input;
|
|
1470
1334
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1471
1335
|
const actionType = aNext.actionType.toLowerCase();
|
|
1472
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1473
1336
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1474
1337
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1475
1338
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1497,7 +1360,6 @@ function runLogicCheck(input, opts) {
|
|
|
1497
1360
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1498
1361
|
const actionType = aNext.actionType.toLowerCase();
|
|
1499
1362
|
const resolvedCwd = cwd || "/";
|
|
1500
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1501
1363
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1502
1364
|
if (!opts?.skipReadBeforeWrite) {
|
|
1503
1365
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1609,7 +1471,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1609
1471
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1610
1472
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1611
1473
|
if (!isWrite) return null;
|
|
1612
|
-
const WRITE_CMDS =
|
|
1474
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1613
1475
|
if (isBashLike) {
|
|
1614
1476
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1615
1477
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1641,7 +1503,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1641
1503
|
}
|
|
1642
1504
|
if (!hasWriteSeg) return null;
|
|
1643
1505
|
}
|
|
1644
|
-
const SOURCE_ARG_CMDS =
|
|
1506
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1645
1507
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1646
1508
|
const targetPaths = [];
|
|
1647
1509
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1689,7 +1551,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1689
1551
|
}
|
|
1690
1552
|
}
|
|
1691
1553
|
}
|
|
1692
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1693
1554
|
let filteredPaths = uniqueTargetPaths;
|
|
1694
1555
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1695
1556
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1790,10 +1651,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1790
1651
|
if (!hasTraversal) {
|
|
1791
1652
|
return null;
|
|
1792
1653
|
}
|
|
1793
|
-
const READ_KEYWORDS =
|
|
1794
|
-
const WRITE_KEYWORDS2 =
|
|
1795
|
-
const EXEC_KEYWORDS =
|
|
1796
|
-
const OPEN_KEYWORDS =
|
|
1654
|
+
const READ_KEYWORDS = IFA_READ_KEYWORDS;
|
|
1655
|
+
const WRITE_KEYWORDS2 = IFA_WRITE_KEYWORDS;
|
|
1656
|
+
const EXEC_KEYWORDS = IFA_EXEC_KEYWORDS;
|
|
1657
|
+
const OPEN_KEYWORDS = IFA_OPEN_KEYWORDS;
|
|
1797
1658
|
const code = extractInlineCode(actionDetail);
|
|
1798
1659
|
if (!code) return null;
|
|
1799
1660
|
const codeLower = code.toLowerCase();
|
|
@@ -1856,7 +1717,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1856
1717
|
violatedLayers: ["L2.1"]
|
|
1857
1718
|
};
|
|
1858
1719
|
}
|
|
1859
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1720
|
+
var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
1860
1721
|
var init_logic_rules = __esm({
|
|
1861
1722
|
"src/engine/logic-rules.ts"() {
|
|
1862
1723
|
"use strict";
|
|
@@ -1866,6 +1727,10 @@ var init_logic_rules = __esm({
|
|
|
1866
1727
|
init_rules_loader();
|
|
1867
1728
|
init_platform_traits();
|
|
1868
1729
|
init_shell_split();
|
|
1730
|
+
_LOGIC = loadLogicRules();
|
|
1731
|
+
_HEURISTIC = loadHeuristicRules();
|
|
1732
|
+
EXCLUDED_PREFIXES = _HEURISTIC.sensitive_excluded_prefixes ?? [];
|
|
1733
|
+
TEMP_DIRS = (_HEURISTIC.temp_dirs ?? []).map((d) => d + "/");
|
|
1869
1734
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1870
1735
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1871
1736
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1886,7 +1751,7 @@ var init_logic_rules = __esm({
|
|
|
1886
1751
|
"list_dir",
|
|
1887
1752
|
"count_text_length",
|
|
1888
1753
|
"search",
|
|
1889
|
-
// filemgr 只读三件套(globfiles 在
|
|
1754
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1890
1755
|
"filemgr-globfiles",
|
|
1891
1756
|
"filemgr-readfile",
|
|
1892
1757
|
"filemgr-grepfiles",
|
|
@@ -1898,7 +1763,7 @@ var init_logic_rules = __esm({
|
|
|
1898
1763
|
"tail",
|
|
1899
1764
|
"cat"
|
|
1900
1765
|
]);
|
|
1901
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1766
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1902
1767
|
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362", "edit"];
|
|
1903
1768
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1904
1769
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1939,25 +1804,24 @@ var init_logic_rules = __esm({
|
|
|
1939
1804
|
}));
|
|
1940
1805
|
})();
|
|
1941
1806
|
INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
1942
|
-
DIR_TRAVERSAL_KW = [
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
"pathlib.",
|
|
1949
|
-
"iterdir",
|
|
1950
|
-
"rglob",
|
|
1951
|
-
"scandir(",
|
|
1952
|
-
"listdir("
|
|
1953
|
-
];
|
|
1807
|
+
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1808
|
+
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
1809
|
+
IFA_READ_KEYWORDS = _IFA.read_keywords ?? [];
|
|
1810
|
+
IFA_WRITE_KEYWORDS = _IFA.write_keywords ?? [];
|
|
1811
|
+
IFA_EXEC_KEYWORDS = _IFA.exec_keywords ?? [];
|
|
1812
|
+
IFA_OPEN_KEYWORDS = _IFA.open_keywords ?? [];
|
|
1954
1813
|
}
|
|
1955
1814
|
});
|
|
1956
1815
|
|
|
1957
1816
|
// src/engine/asset-dir.ts
|
|
1958
|
-
import { existsSync as
|
|
1959
|
-
import { resolve as resolve2 } from "path";
|
|
1817
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1818
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
1960
1819
|
function assetDirCandidates(moduleDir, name) {
|
|
1820
|
+
const root = getRulesRoot();
|
|
1821
|
+
if (root) {
|
|
1822
|
+
const p = join4(root, name);
|
|
1823
|
+
return [p, p];
|
|
1824
|
+
}
|
|
1961
1825
|
return [
|
|
1962
1826
|
resolve2(moduleDir, name),
|
|
1963
1827
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -1965,7 +1829,7 @@ function assetDirCandidates(moduleDir, name) {
|
|
|
1965
1829
|
}
|
|
1966
1830
|
function resolveAssetDir(moduleDir, name) {
|
|
1967
1831
|
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
1968
|
-
if (
|
|
1832
|
+
if (existsSync4(dir)) return dir;
|
|
1969
1833
|
}
|
|
1970
1834
|
return null;
|
|
1971
1835
|
}
|
|
@@ -1973,11 +1837,12 @@ var init_asset_dir = __esm({
|
|
|
1973
1837
|
"src/engine/asset-dir.ts"() {
|
|
1974
1838
|
"use strict";
|
|
1975
1839
|
init_esm_shims();
|
|
1840
|
+
init_paths();
|
|
1976
1841
|
}
|
|
1977
1842
|
});
|
|
1978
1843
|
|
|
1979
1844
|
// src/engine/skill-loader.ts
|
|
1980
|
-
import { readFileSync as
|
|
1845
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3, existsSync as existsSync5 } from "fs";
|
|
1981
1846
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1982
1847
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1983
1848
|
function loadAllSkills() {
|
|
@@ -1992,7 +1857,7 @@ function loadAllSkills() {
|
|
|
1992
1857
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
|
|
1993
1858
|
for (const f of files) {
|
|
1994
1859
|
const name = f.replace(".md", "");
|
|
1995
|
-
cachedSkills[name] =
|
|
1860
|
+
cachedSkills[name] = readFileSync4(resolve3(dir, f), "utf-8");
|
|
1996
1861
|
}
|
|
1997
1862
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1998
1863
|
return cachedSkills;
|
|
@@ -2001,6 +1866,7 @@ function userSkillsDir() {
|
|
|
2001
1866
|
return resolve3(getDataRoot(), "agent_moss_skills");
|
|
2002
1867
|
}
|
|
2003
1868
|
function runtimeCustomSkillKeywords() {
|
|
1869
|
+
if (isRuntimeConfigDisabled()) return {};
|
|
2004
1870
|
try {
|
|
2005
1871
|
const rt = runtimeConfigBridge.current?.loadRuntimeConfig?.();
|
|
2006
1872
|
if (!rt) return {};
|
|
@@ -2018,10 +1884,11 @@ function runtimeCustomSkillKeywords() {
|
|
|
2018
1884
|
}
|
|
2019
1885
|
}
|
|
2020
1886
|
function loadUserSkill(name) {
|
|
1887
|
+
if (isRuntimeConfigDisabled()) return "";
|
|
2021
1888
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
2022
1889
|
try {
|
|
2023
1890
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
2024
|
-
userSkillCache[name] =
|
|
1891
|
+
userSkillCache[name] = existsSync5(p) ? readFileSync4(p, "utf-8") : "";
|
|
2025
1892
|
} catch {
|
|
2026
1893
|
userSkillCache[name] = "";
|
|
2027
1894
|
}
|
|
@@ -2086,6 +1953,7 @@ var init_skill_loader = __esm({
|
|
|
2086
1953
|
init_esm_shims();
|
|
2087
1954
|
init_rules_loader();
|
|
2088
1955
|
init_paths();
|
|
1956
|
+
init_rules_mode();
|
|
2089
1957
|
init_asset_dir();
|
|
2090
1958
|
init_runtime_config_bridge();
|
|
2091
1959
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -2146,6 +2014,7 @@ __export(runtime_config_exports, {
|
|
|
2146
2014
|
getEnabledScenarioExtensions: () => getEnabledScenarioExtensions,
|
|
2147
2015
|
getEnvOverrides: () => getEnvOverrides,
|
|
2148
2016
|
getProfileOrDetect: () => getProfileOrDetect,
|
|
2017
|
+
getRuleSourceFingerprint: () => getRuleSourceFingerprint,
|
|
2149
2018
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
2150
2019
|
getScenarioRules: () => getScenarioRules,
|
|
2151
2020
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
@@ -2168,8 +2037,8 @@ __export(runtime_config_exports, {
|
|
|
2168
2037
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
2169
2038
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2170
2039
|
});
|
|
2171
|
-
import { readFileSync as
|
|
2172
|
-
import { join as
|
|
2040
|
+
import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2041
|
+
import { join as join5 } from "path";
|
|
2173
2042
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2174
2043
|
import { dirname as dirname3 } from "path";
|
|
2175
2044
|
function runtimeDir() {
|
|
@@ -2180,10 +2049,25 @@ function getRuntimeConfigPath() {
|
|
|
2180
2049
|
if (envPath) return envPath;
|
|
2181
2050
|
return RUNTIME_CONFIG_PATH;
|
|
2182
2051
|
}
|
|
2052
|
+
function getRuleSourceFingerprint() {
|
|
2053
|
+
if (isRuntimeConfigDisabled()) return `mode:${getRulesMode()}`;
|
|
2054
|
+
const p = getRuntimeConfigPath();
|
|
2055
|
+
try {
|
|
2056
|
+
const st = statSync2(p);
|
|
2057
|
+
return `${p}|${st.mtimeMs}|${st.size}`;
|
|
2058
|
+
} catch {
|
|
2059
|
+
return `${p}|absent`;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2183
2062
|
function saveRuntimeConfig(config) {
|
|
2063
|
+
if (isRuntimeConfigDisabled()) {
|
|
2064
|
+
throw new Error(
|
|
2065
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1A\u4FEE\u6539\u89C4\u5219\u4E0D\u4F1A\u751F\u6548\uFF0C\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u3002`
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2184
2068
|
const p = getRuntimeConfigPath();
|
|
2185
2069
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2186
|
-
if (dir && !
|
|
2070
|
+
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
2187
2071
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
2188
2072
|
invalidateRuntimeConfigCache();
|
|
2189
2073
|
}
|
|
@@ -2204,8 +2088,20 @@ function invalidateRuntimeConfigCache() {
|
|
|
2204
2088
|
hasRuntimeConfigCache.clear();
|
|
2205
2089
|
}
|
|
2206
2090
|
function loadRuntimeConfig() {
|
|
2091
|
+
if (isRuntimeConfigDisabled()) {
|
|
2092
|
+
return {
|
|
2093
|
+
version: 1,
|
|
2094
|
+
layers: { ...DEFAULT_LAYERS },
|
|
2095
|
+
L1_rules: {},
|
|
2096
|
+
L2_rules: {},
|
|
2097
|
+
L3_skills: {},
|
|
2098
|
+
L2_2: {},
|
|
2099
|
+
agent_rules: {},
|
|
2100
|
+
scenario_rules: {}
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2207
2103
|
const p = getRuntimeConfigPath();
|
|
2208
|
-
if (!
|
|
2104
|
+
if (!existsSync6(p)) {
|
|
2209
2105
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
2210
2106
|
try {
|
|
2211
2107
|
saveRuntimeConfig(seeded);
|
|
@@ -2219,7 +2115,7 @@ function loadRuntimeConfig() {
|
|
|
2219
2115
|
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
2220
2116
|
return runtimeConfigCache.value;
|
|
2221
2117
|
}
|
|
2222
|
-
const data = JSON.parse(
|
|
2118
|
+
const data = JSON.parse(readFileSync5(p, "utf-8"));
|
|
2223
2119
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
2224
2120
|
const result = {
|
|
2225
2121
|
version: data.version || 1,
|
|
@@ -2250,6 +2146,7 @@ function loadRuntimeConfig() {
|
|
|
2250
2146
|
function isLayerEnabled(layerKey) {
|
|
2251
2147
|
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2252
2148
|
if (disableKey && process.env[disableKey] === "1") return false;
|
|
2149
|
+
if (isRuntimeConfigDisabled()) return true;
|
|
2253
2150
|
const runtime = loadRuntimeConfig();
|
|
2254
2151
|
const layers = runtime.layers || {};
|
|
2255
2152
|
if (layerKey in layers) return !!layers[layerKey];
|
|
@@ -2276,7 +2173,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2276
2173
|
}
|
|
2277
2174
|
function _filterRulesByOs(rules, osType) {
|
|
2278
2175
|
const accepted = acceptedOsValues(osType);
|
|
2279
|
-
return rules.filter((r) =>
|
|
2176
|
+
return rules.filter((r) => {
|
|
2177
|
+
const os4 = String(r.os || "linux").toLowerCase();
|
|
2178
|
+
return os4 === "all" || accepted.includes(os4);
|
|
2179
|
+
});
|
|
2280
2180
|
}
|
|
2281
2181
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2282
2182
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2350,6 +2250,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2350
2250
|
});
|
|
2351
2251
|
}
|
|
2352
2252
|
function hasRuntimeConfig() {
|
|
2253
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
2353
2254
|
const p = getRuntimeConfigPath();
|
|
2354
2255
|
let key;
|
|
2355
2256
|
try {
|
|
@@ -2358,11 +2259,11 @@ function hasRuntimeConfig() {
|
|
|
2358
2259
|
} catch {
|
|
2359
2260
|
return false;
|
|
2360
2261
|
}
|
|
2361
|
-
const
|
|
2362
|
-
if (
|
|
2262
|
+
const cached2 = hasRuntimeConfigCache.get(key);
|
|
2263
|
+
if (cached2 !== void 0) return cached2;
|
|
2363
2264
|
let ok = false;
|
|
2364
2265
|
try {
|
|
2365
|
-
JSON.parse(
|
|
2266
|
+
JSON.parse(readFileSync5(p, "utf-8"));
|
|
2366
2267
|
ok = true;
|
|
2367
2268
|
} catch {
|
|
2368
2269
|
ok = false;
|
|
@@ -2462,6 +2363,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2462
2363
|
keyword: kw.keyword,
|
|
2463
2364
|
risk_level: kw.riskLevel,
|
|
2464
2365
|
lang: kw.lang || "en",
|
|
2366
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2367
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2368
|
+
os: kw.os,
|
|
2465
2369
|
enabled: true,
|
|
2466
2370
|
builtin: true
|
|
2467
2371
|
}));
|
|
@@ -2579,8 +2483,9 @@ function _l3SkillsToRules() {
|
|
|
2579
2483
|
}
|
|
2580
2484
|
function _scenarioRulesToDefaults() {
|
|
2581
2485
|
const out = {};
|
|
2582
|
-
const
|
|
2583
|
-
|
|
2486
|
+
const _root = getRulesRoot();
|
|
2487
|
+
const scenDir = _root ? join5(_root, "scenarios") : existsSync6(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync6(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2488
|
+
if (!existsSync6(scenDir)) return out;
|
|
2584
2489
|
let entries;
|
|
2585
2490
|
try {
|
|
2586
2491
|
entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2588,11 +2493,11 @@ function _scenarioRulesToDefaults() {
|
|
|
2588
2493
|
return out;
|
|
2589
2494
|
}
|
|
2590
2495
|
for (const scenarioId of entries) {
|
|
2591
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2592
|
-
if (!
|
|
2496
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join5(scenDir, scenarioId, "rules.json");
|
|
2497
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2593
2498
|
let data;
|
|
2594
2499
|
try {
|
|
2595
|
-
data = JSON.parse(
|
|
2500
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2596
2501
|
} catch {
|
|
2597
2502
|
continue;
|
|
2598
2503
|
}
|
|
@@ -2628,8 +2533,9 @@ function _scenarioRulesToDefaults() {
|
|
|
2628
2533
|
}
|
|
2629
2534
|
function _agentRulesToDefaults() {
|
|
2630
2535
|
const out = {};
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2536
|
+
const _root = getRulesRoot();
|
|
2537
|
+
const agentsDir = _root ? join5(_root, "agents") : existsSync6(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync6(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2538
|
+
if (!existsSync6(agentsDir)) return out;
|
|
2633
2539
|
let entries;
|
|
2634
2540
|
try {
|
|
2635
2541
|
entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2637,11 +2543,11 @@ function _agentRulesToDefaults() {
|
|
|
2637
2543
|
return out;
|
|
2638
2544
|
}
|
|
2639
2545
|
for (const agentId of entries) {
|
|
2640
|
-
const rulesFile =
|
|
2641
|
-
if (!
|
|
2546
|
+
const rulesFile = join5(agentsDir, agentId, "rules.json");
|
|
2547
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2642
2548
|
let data;
|
|
2643
2549
|
try {
|
|
2644
|
-
data = JSON.parse(
|
|
2550
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2645
2551
|
} catch {
|
|
2646
2552
|
continue;
|
|
2647
2553
|
}
|
|
@@ -3016,7 +2922,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
3016
2922
|
}
|
|
3017
2923
|
function _filterDefaultsByOs(rules, osType) {
|
|
3018
2924
|
const accepted = acceptedOsValues(osType);
|
|
3019
|
-
return rules.filter((r) =>
|
|
2925
|
+
return rules.filter((r) => {
|
|
2926
|
+
if (!r.os) return true;
|
|
2927
|
+
const os4 = String(r.os).toLowerCase();
|
|
2928
|
+
return os4 === "all" || accepted.includes(os4);
|
|
2929
|
+
});
|
|
3020
2930
|
}
|
|
3021
2931
|
function _hasRulesForOs(rules, osType) {
|
|
3022
2932
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3239,13 +3149,18 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
3239
3149
|
}
|
|
3240
3150
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
3241
3151
|
_validateSkillId(skillId);
|
|
3152
|
+
if (isRuntimeConfigDisabled()) {
|
|
3153
|
+
throw new Error(
|
|
3154
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF0C\u4E0D\u5141\u8BB8\u65B0\u589E\u81EA\u5B9A\u4E49 skill\u3002`
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3242
3157
|
const runtime = loadRuntimeConfig();
|
|
3243
|
-
const skillsDir =
|
|
3158
|
+
const skillsDir = join5(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3244
3159
|
try {
|
|
3245
3160
|
mkdirSync(skillsDir, { recursive: true });
|
|
3246
3161
|
} catch {
|
|
3247
3162
|
}
|
|
3248
|
-
writeFileSync(
|
|
3163
|
+
writeFileSync(join5(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3249
3164
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3250
3165
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3251
3166
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3260,9 +3175,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3260
3175
|
if (idx >= 0) {
|
|
3261
3176
|
if (skills[idx].builtin) return null;
|
|
3262
3177
|
skills.splice(idx, 1);
|
|
3263
|
-
const skillPath =
|
|
3178
|
+
const skillPath = join5(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3264
3179
|
try {
|
|
3265
|
-
if (
|
|
3180
|
+
if (existsSync6(skillPath)) unlinkSync(skillPath);
|
|
3266
3181
|
} catch {
|
|
3267
3182
|
}
|
|
3268
3183
|
saveRuntimeConfig(runtime);
|
|
@@ -3465,10 +3380,11 @@ var init_runtime_config = __esm({
|
|
|
3465
3380
|
init_skill_loader();
|
|
3466
3381
|
init_rules_loader();
|
|
3467
3382
|
init_runtime_config_bridge();
|
|
3383
|
+
init_rules_mode();
|
|
3468
3384
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3469
3385
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3470
3386
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3471
|
-
RUNTIME_CONFIG_PATH =
|
|
3387
|
+
RUNTIME_CONFIG_PATH = join5(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3472
3388
|
LAYER_DISABLE_ENV = {
|
|
3473
3389
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3474
3390
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3496,10 +3412,10 @@ var init_runtime_config = __esm({
|
|
|
3496
3412
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3497
3413
|
};
|
|
3498
3414
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3499
|
-
_AGENTS_DIR_PROD =
|
|
3500
|
-
_AGENTS_DIR_DEV =
|
|
3501
|
-
_SCENARIOS_DIR_PROD =
|
|
3502
|
-
_SCENARIOS_DIR_DEV =
|
|
3415
|
+
_AGENTS_DIR_PROD = join5(__dirname4, "agents");
|
|
3416
|
+
_AGENTS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3417
|
+
_SCENARIOS_DIR_PROD = join5(__dirname4, "scenarios");
|
|
3418
|
+
_SCENARIOS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3503
3419
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3504
3420
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3505
3421
|
registerRuntimeConfigBridge({
|
|
@@ -3536,6 +3452,7 @@ __export(config_exports, {
|
|
|
3536
3452
|
isPolicyGenEnabled: () => isPolicyGenEnabled,
|
|
3537
3453
|
isPolicySaveEnabled: () => isPolicySaveEnabled,
|
|
3538
3454
|
probeLLMConnection: () => probeLLMConnection,
|
|
3455
|
+
reloadCustomRules: () => reloadCustomRules,
|
|
3539
3456
|
updateLLMConfig: () => updateLLMConfig,
|
|
3540
3457
|
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
3541
3458
|
wildcardToRegExp: () => wildcardToRegExp
|
|
@@ -3616,17 +3533,19 @@ function wildcardToRegExp(pattern) {
|
|
|
3616
3533
|
}
|
|
3617
3534
|
function loadCustomRules() {
|
|
3618
3535
|
const rules = [];
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3536
|
+
if (!isBuiltinOnly()) {
|
|
3537
|
+
try {
|
|
3538
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
3539
|
+
if (raw) {
|
|
3540
|
+
const parsed = JSON.parse(raw);
|
|
3541
|
+
if (Array.isArray(parsed)) {
|
|
3542
|
+
for (const r of parsed) {
|
|
3543
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
3544
|
+
}
|
|
3626
3545
|
}
|
|
3627
3546
|
}
|
|
3547
|
+
} catch {
|
|
3628
3548
|
}
|
|
3629
|
-
} catch {
|
|
3630
3549
|
}
|
|
3631
3550
|
for (const rulesPath of USER_RULES_PATHS) {
|
|
3632
3551
|
try {
|
|
@@ -3655,6 +3574,9 @@ function userRuleVariantPattern(pattern) {
|
|
|
3655
3574
|
}
|
|
3656
3575
|
return null;
|
|
3657
3576
|
}
|
|
3577
|
+
function reloadCustomRules() {
|
|
3578
|
+
customRules = loadCustomRules();
|
|
3579
|
+
}
|
|
3658
3580
|
function getLLMConfig() {
|
|
3659
3581
|
return llmConfig;
|
|
3660
3582
|
}
|
|
@@ -3742,6 +3664,7 @@ function layerEnabledOrDefault(key) {
|
|
|
3742
3664
|
}
|
|
3743
3665
|
}
|
|
3744
3666
|
function isBrainEnabled() {
|
|
3667
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
3745
3668
|
return getAgentMossSetting("AGENT_MOSS_BRAIN_ENABLED", "") === "1";
|
|
3746
3669
|
}
|
|
3747
3670
|
function getAgentMossSetting(key, def = "") {
|
|
@@ -3783,6 +3706,7 @@ var init_config = __esm({
|
|
|
3783
3706
|
init_esm_shims();
|
|
3784
3707
|
init_paths();
|
|
3785
3708
|
init_runtime_config();
|
|
3709
|
+
init_rules_mode();
|
|
3786
3710
|
__configModuleDir = (() => {
|
|
3787
3711
|
try {
|
|
3788
3712
|
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
@@ -3829,17 +3753,21 @@ var init_config = __esm({
|
|
|
3829
3753
|
});
|
|
3830
3754
|
|
|
3831
3755
|
// src/agents/index.ts
|
|
3832
|
-
import { readFileSync as
|
|
3833
|
-
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3756
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3757
|
+
import { resolve as resolve4, dirname as dirname4, join as join6 } from "path";
|
|
3834
3758
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3835
3759
|
function resolveAgentsDir() {
|
|
3836
|
-
|
|
3837
|
-
if (
|
|
3760
|
+
const root = getRulesRoot();
|
|
3761
|
+
if (root) return join6(root, "agents");
|
|
3762
|
+
if (existsSync7(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3763
|
+
if (existsSync7(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3838
3764
|
return AGENTS_DIR_PROD;
|
|
3839
3765
|
}
|
|
3840
3766
|
function getAgentExtensions(agentId) {
|
|
3841
3767
|
if (!agentId) return null;
|
|
3842
|
-
|
|
3768
|
+
const fp = getRuleSourceFingerprint();
|
|
3769
|
+
const cached2 = cache.get(agentId);
|
|
3770
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3843
3771
|
let ext = null;
|
|
3844
3772
|
try {
|
|
3845
3773
|
if (hasRuntimeConfig()) {
|
|
@@ -3850,25 +3778,25 @@ function getAgentExtensions(agentId) {
|
|
|
3850
3778
|
if (ext === null) {
|
|
3851
3779
|
const agentsDir = resolveAgentsDir();
|
|
3852
3780
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3853
|
-
if (!
|
|
3854
|
-
cache.set(agentId, null);
|
|
3781
|
+
if (!existsSync7(rulesFile)) {
|
|
3782
|
+
cache.set(agentId, { fp, ext: null });
|
|
3855
3783
|
return null;
|
|
3856
3784
|
}
|
|
3857
3785
|
try {
|
|
3858
|
-
const raw =
|
|
3786
|
+
const raw = readFileSync6(rulesFile, "utf-8");
|
|
3859
3787
|
ext = JSON.parse(raw);
|
|
3860
3788
|
if (!ext.agent_id) {
|
|
3861
3789
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3862
|
-
cache.set(agentId, null);
|
|
3790
|
+
cache.set(agentId, { fp, ext: null });
|
|
3863
3791
|
return null;
|
|
3864
3792
|
}
|
|
3865
3793
|
} catch (e) {
|
|
3866
3794
|
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
3867
|
-
cache.set(agentId, null);
|
|
3795
|
+
cache.set(agentId, { fp, ext: null });
|
|
3868
3796
|
return null;
|
|
3869
3797
|
}
|
|
3870
3798
|
}
|
|
3871
|
-
cache.set(agentId, ext);
|
|
3799
|
+
cache.set(agentId, { fp, ext });
|
|
3872
3800
|
console.error(
|
|
3873
3801
|
`[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`
|
|
3874
3802
|
);
|
|
@@ -3894,6 +3822,7 @@ var init_agents = __esm({
|
|
|
3894
3822
|
"src/agents/index.ts"() {
|
|
3895
3823
|
"use strict";
|
|
3896
3824
|
init_esm_shims();
|
|
3825
|
+
init_paths();
|
|
3897
3826
|
init_runtime_config();
|
|
3898
3827
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3899
3828
|
__dirname5 = dirname4(__filename4);
|
|
@@ -3904,17 +3833,21 @@ var init_agents = __esm({
|
|
|
3904
3833
|
});
|
|
3905
3834
|
|
|
3906
3835
|
// src/scenarios/index.ts
|
|
3907
|
-
import { readFileSync as
|
|
3908
|
-
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
3836
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
3837
|
+
import { resolve as resolve5, dirname as dirname5, join as join7 } from "path";
|
|
3909
3838
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3910
3839
|
function resolveScenariosDir() {
|
|
3911
|
-
|
|
3912
|
-
if (
|
|
3840
|
+
const root = getRulesRoot();
|
|
3841
|
+
if (root) return join7(root, "scenarios");
|
|
3842
|
+
if (existsSync8(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3843
|
+
if (existsSync8(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3913
3844
|
return SCENARIOS_DIR_PROD;
|
|
3914
3845
|
}
|
|
3915
3846
|
function getScenarioExtensions(scenarioId) {
|
|
3916
3847
|
if (!scenarioId) return null;
|
|
3917
|
-
|
|
3848
|
+
const fp = getRuleSourceFingerprint();
|
|
3849
|
+
const cached2 = cache2.get(scenarioId);
|
|
3850
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3918
3851
|
let ext = null;
|
|
3919
3852
|
let overrideHit = null;
|
|
3920
3853
|
try {
|
|
@@ -3937,17 +3870,17 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3937
3870
|
`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${overrideHit}\uFF08\u5185\u7F6E\u9ED8\u8BA4 ${builtinFile} \u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C\u5185\u7F6E\u573A\u666F\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\u3002`
|
|
3938
3871
|
);
|
|
3939
3872
|
}
|
|
3940
|
-
if (!
|
|
3941
|
-
cache2.set(scenarioId, null);
|
|
3873
|
+
if (!existsSync8(rulesFile)) {
|
|
3874
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3942
3875
|
return null;
|
|
3943
3876
|
}
|
|
3944
3877
|
try {
|
|
3945
|
-
const raw =
|
|
3878
|
+
const raw = readFileSync7(rulesFile, "utf-8");
|
|
3946
3879
|
const data = JSON.parse(raw);
|
|
3947
3880
|
const id = data.scenario_id || data.agent_id || "";
|
|
3948
3881
|
if (!id) {
|
|
3949
3882
|
console.warn(`[AgentMoss scenarios] rules.json \u7F3A\u5C11 scenario_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3950
|
-
cache2.set(scenarioId, null);
|
|
3883
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3951
3884
|
return null;
|
|
3952
3885
|
}
|
|
3953
3886
|
ext = {
|
|
@@ -3962,11 +3895,11 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3962
3895
|
};
|
|
3963
3896
|
} catch (e) {
|
|
3964
3897
|
console.error(`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} rules.json \u5931\u8D25:`, e.message);
|
|
3965
|
-
cache2.set(scenarioId, null);
|
|
3898
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3966
3899
|
return null;
|
|
3967
3900
|
}
|
|
3968
3901
|
}
|
|
3969
|
-
cache2.set(scenarioId, ext);
|
|
3902
|
+
cache2.set(scenarioId, { fp, ext });
|
|
3970
3903
|
console.error(
|
|
3971
3904
|
`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} \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`
|
|
3972
3905
|
);
|
|
@@ -3986,6 +3919,7 @@ var init_scenarios = __esm({
|
|
|
3986
3919
|
"src/scenarios/index.ts"() {
|
|
3987
3920
|
"use strict";
|
|
3988
3921
|
init_esm_shims();
|
|
3922
|
+
init_paths();
|
|
3989
3923
|
init_agents();
|
|
3990
3924
|
init_runtime_config();
|
|
3991
3925
|
init_rules_loader();
|
|
@@ -4392,7 +4326,7 @@ function severityRank(s) {
|
|
|
4392
4326
|
}
|
|
4393
4327
|
function isSearchActionType(actionType) {
|
|
4394
4328
|
const at = actionType.toLowerCase();
|
|
4395
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4329
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4396
4330
|
}
|
|
4397
4331
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4398
4332
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4682,8 +4616,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4682
4616
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4683
4617
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4684
4618
|
const scan = stripHeredocContent(actionDetail);
|
|
4685
|
-
const
|
|
4686
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4619
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4687
4620
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4688
4621
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4689
4622
|
const wildcardDeleteRe = /(?:\brm\b|\b(r?mdir|del|delete|remove|rmdir|unlink)\b|\b删除\b|\b移除\b)[\s\S]{0,40}?[*?]|[*?][\s\S]{0,40}?\b(?:rm|del|delete|remove|rmdir|unlink)\b|rm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\S*[*?]/i;
|
|
@@ -4829,7 +4762,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4829
4762
|
}
|
|
4830
4763
|
return null;
|
|
4831
4764
|
}
|
|
4832
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, LOOPBACK_URL_RE, LOOPBACK_HOSTS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, PY_EXEC_IO_RE, PY_PRINT_RE, PY_BIGNUM_RE, PY_INLINE_C_RE, PY_CMD_SUBST_C_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4765
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, LOOPBACK_URL_RE, HEURISTIC_RULES, LOOPBACK_HOSTS, TEMP_DIRS2, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, PY_EXEC_IO_RE, PY_PRINT_RE, PY_BIGNUM_RE, PY_INLINE_C_RE, PY_CMD_SUBST_C_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, SEARCH_ACTION_EXACT, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4833
4766
|
var init_heuristic = __esm({
|
|
4834
4767
|
"src/engine/heuristic.ts"() {
|
|
4835
4768
|
"use strict";
|
|
@@ -4846,7 +4779,8 @@ var init_heuristic = __esm({
|
|
|
4846
4779
|
return kws.map((k) => ({
|
|
4847
4780
|
keyword: k.keyword,
|
|
4848
4781
|
riskLevel: k.risk_level,
|
|
4849
|
-
lang: k.lang || "en"
|
|
4782
|
+
lang: k.lang || "en",
|
|
4783
|
+
os: String(k.os || "all").toLowerCase()
|
|
4850
4784
|
}));
|
|
4851
4785
|
})();
|
|
4852
4786
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4854,7 +4788,9 @@ var init_heuristic = __esm({
|
|
|
4854
4788
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4855
4789
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4856
4790
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4857
|
-
|
|
4791
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4792
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4793
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4858
4794
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4859
4795
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4860
4796
|
PY_EXEC_IO_RE = /os\.system|os\.popen|subprocess|eval\s*\(|exec\s*\(|__import__|open\s*\(|socket|urllib|requests\.|shutil|os\.(?:remove|unlink|rename|makedirs?|listdir|scandir|walk|chmod|chown|kill|environ)|iterdir|glob\s*\(|path\(/i;
|
|
@@ -4862,15 +4798,16 @@ var init_heuristic = __esm({
|
|
|
4862
4798
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4863
4799
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4864
4800
|
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4865
|
-
SENSITIVE_EXCLUDED_PREFIXES =
|
|
4866
|
-
SEARCH_ACTION_PREFIXES =
|
|
4801
|
+
SENSITIVE_EXCLUDED_PREFIXES = HEURISTIC_RULES.sensitive_excluded_prefixes ?? [];
|
|
4802
|
+
SEARCH_ACTION_PREFIXES = HEURISTIC_RULES.search_action_prefixes ?? [];
|
|
4803
|
+
SEARCH_ACTION_EXACT = HEURISTIC_RULES.search_action_exact ?? [];
|
|
4867
4804
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4868
4805
|
HOME_ASSIGN_RE = /([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(~[A-Za-z0-9_./-]*|\$HOME[A-Za-z0-9_./-]*|\$\{HOME\}[A-Za-z0-9_./-]*|\/home\/[^\s;|&]+|\$\((?:[^()]|\([^()]*\))*\))(?=\s|;|$|\||&)/gi;
|
|
4869
4806
|
}
|
|
4870
4807
|
});
|
|
4871
4808
|
|
|
4872
4809
|
// src/engine/template-loader.ts
|
|
4873
|
-
import { readFileSync as
|
|
4810
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4874
4811
|
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
4875
4812
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
4876
4813
|
function loadTemplates() {
|
|
@@ -4884,9 +4821,9 @@ function loadTemplates() {
|
|
|
4884
4821
|
}
|
|
4885
4822
|
for (const file of TEMPLATE_FILES) {
|
|
4886
4823
|
const filePath = resolve6(dir, file);
|
|
4887
|
-
if (
|
|
4824
|
+
if (existsSync9(filePath)) {
|
|
4888
4825
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
4889
|
-
cachedTemplates[key] =
|
|
4826
|
+
cachedTemplates[key] = readFileSync8(filePath, "utf-8");
|
|
4890
4827
|
}
|
|
4891
4828
|
}
|
|
4892
4829
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -4906,8 +4843,8 @@ var init_template_loader = __esm({
|
|
|
4906
4843
|
});
|
|
4907
4844
|
|
|
4908
4845
|
// src/engine/script-analyzer.ts
|
|
4909
|
-
import { readFileSync as
|
|
4910
|
-
import { resolve as resolve7, join as
|
|
4846
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4847
|
+
import { resolve as resolve7, join as join8 } from "path";
|
|
4911
4848
|
function emptyResult() {
|
|
4912
4849
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
4913
4850
|
}
|
|
@@ -4933,10 +4870,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4933
4870
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
4934
4871
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
4935
4872
|
}
|
|
4936
|
-
const networkPostKeywords =
|
|
4873
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
4937
4874
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
4938
4875
|
if (hasNetworkPost) {
|
|
4939
|
-
const envPatterns =
|
|
4876
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
4940
4877
|
const contentLower = scriptContent.toLowerCase();
|
|
4941
4878
|
for (const p of envPatterns) {
|
|
4942
4879
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -4960,8 +4897,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4960
4897
|
}
|
|
4961
4898
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4962
4899
|
try {
|
|
4963
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
4964
|
-
const raw =
|
|
4900
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join8(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
4901
|
+
const raw = readFileSync9(expandedPath, "utf-8");
|
|
4965
4902
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4966
4903
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
4967
4904
|
} catch (e) {
|
|
@@ -5010,90 +4947,28 @@ ${result.scriptContent}
|
|
|
5010
4947
|
\`\`\`
|
|
5011
4948
|
`;
|
|
5012
4949
|
}
|
|
5013
|
-
var SUSPICIOUS_PATTERNS, SCRIPT_EXEC_PATTERNS, KEYWORD_COMBINATION_RISKS, SINGLE_KEYWORD_RISKS, LEVEL_ORDER;
|
|
4950
|
+
var SCRIPT_RULES, SUSPICIOUS_PATTERNS, SCRIPT_EXEC_PATTERNS, KEYWORD_SETS, KEYWORD_COMBINATION_RISKS, SINGLE_KEYWORD_RISKS, LEVEL_ORDER;
|
|
5014
4951
|
var init_script_analyzer = __esm({
|
|
5015
4952
|
"src/engine/script-analyzer.ts"() {
|
|
5016
4953
|
"use strict";
|
|
5017
4954
|
init_esm_shims();
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
5036
|
-
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
5037
|
-
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
5038
|
-
// Persistence backdoor
|
|
5039
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
5040
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
5041
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
5042
|
-
];
|
|
5043
|
-
SCRIPT_EXEC_PATTERNS = [
|
|
5044
|
-
/bash\s+(\S+\.sh)/,
|
|
5045
|
-
/sh\s+(\S+\.sh)/,
|
|
5046
|
-
/\.\/(\S+\.sh)/,
|
|
5047
|
-
/source\s+(\S+\.sh)/,
|
|
5048
|
-
/\.\s+(\S+\.sh)/,
|
|
5049
|
-
/zsh\s+(\S+\.sh)/
|
|
5050
|
-
];
|
|
5051
|
-
KEYWORD_COMBINATION_RISKS = [
|
|
5052
|
-
{
|
|
5053
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5054
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
5055
|
-
level: "critical",
|
|
5056
|
-
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
|
|
5057
|
-
},
|
|
5058
|
-
{
|
|
5059
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5060
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
5061
|
-
level: "critical",
|
|
5062
|
-
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
|
|
5063
|
-
},
|
|
5064
|
-
{
|
|
5065
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
5066
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5067
|
-
level: "high",
|
|
5068
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
5069
|
-
},
|
|
5070
|
-
{
|
|
5071
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
5072
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5073
|
-
level: "high",
|
|
5074
|
-
reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
|
|
5075
|
-
}
|
|
5076
|
-
];
|
|
5077
|
-
SINGLE_KEYWORD_RISKS = {
|
|
5078
|
-
"curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
5079
|
-
"curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
5080
|
-
"curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
5081
|
-
"wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
5082
|
-
"wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
|
|
5083
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5084
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5085
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5086
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5087
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5088
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5089
|
-
"base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5090
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
5091
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5092
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5093
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5094
|
-
"authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5095
|
-
"ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
|
|
5096
|
-
};
|
|
4955
|
+
init_rules_loader();
|
|
4956
|
+
SCRIPT_RULES = loadScriptRules();
|
|
4957
|
+
if (!SCRIPT_RULES || Object.keys(SCRIPT_RULES).length === 0) {
|
|
4958
|
+
console.error(
|
|
4959
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 script_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u811A\u672C\u5173\u952E\u8BCD\u626B\u63CF\u5C06\u4E0D\u547D\u4E2D\u4EFB\u4F55\u6A21\u5F0F\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
4960
|
+
);
|
|
4961
|
+
}
|
|
4962
|
+
SUSPICIOUS_PATTERNS = (SCRIPT_RULES.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
4963
|
+
SCRIPT_EXEC_PATTERNS = ((SCRIPT_RULES.script_exec_patterns ?? {}).legacy ?? []).map((r) => compileRuleRegex(r));
|
|
4964
|
+
KEYWORD_SETS = SCRIPT_RULES.keyword_sets ?? {};
|
|
4965
|
+
KEYWORD_COMBINATION_RISKS = (SCRIPT_RULES.keyword_combination_risks ?? []).map((r) => ({
|
|
4966
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS),
|
|
4967
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS),
|
|
4968
|
+
level: String(r.level),
|
|
4969
|
+
reason: String(r.reason)
|
|
4970
|
+
}));
|
|
4971
|
+
SINGLE_KEYWORD_RISKS = SCRIPT_RULES.single_keyword_risks ?? {};
|
|
5097
4972
|
LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5098
4973
|
}
|
|
5099
4974
|
});
|
|
@@ -6637,8 +6512,8 @@ var init_tracker = __esm({
|
|
|
6637
6512
|
});
|
|
6638
6513
|
|
|
6639
6514
|
// src/engine/script-content-analyzer.ts
|
|
6640
|
-
import { statSync as statSync4, existsSync as
|
|
6641
|
-
import { resolve as resolve9, join as
|
|
6515
|
+
import { statSync as statSync4, existsSync as existsSync11 } from "fs";
|
|
6516
|
+
import { resolve as resolve9, join as join10, isAbsolute as isAbsolute2 } from "path";
|
|
6642
6517
|
import { homedir as homedir3 } from "os";
|
|
6643
6518
|
function emptyChain() {
|
|
6644
6519
|
return {
|
|
@@ -6671,10 +6546,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6671
6546
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6672
6547
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6673
6548
|
}
|
|
6674
|
-
const networkPostKeywords =
|
|
6549
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6675
6550
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6676
6551
|
if (hasNetworkPost) {
|
|
6677
|
-
const envPatterns =
|
|
6552
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6678
6553
|
const contentLower = scriptContent.toLowerCase();
|
|
6679
6554
|
for (const p of envPatterns) {
|
|
6680
6555
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -6750,7 +6625,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6750
6625
|
if (looksLikeScriptPath(tok)) {
|
|
6751
6626
|
try {
|
|
6752
6627
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6753
|
-
if (
|
|
6628
|
+
if (existsSync11(resolved)) {
|
|
6754
6629
|
const stat = statSync4(resolved);
|
|
6755
6630
|
if (stat.isFile()) return resolved;
|
|
6756
6631
|
}
|
|
@@ -6764,7 +6639,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6764
6639
|
const p = m[1];
|
|
6765
6640
|
try {
|
|
6766
6641
|
const resolved = resolvePath2(p);
|
|
6767
|
-
if (
|
|
6642
|
+
if (existsSync11(resolved)) {
|
|
6768
6643
|
const stat = statSync4(resolved);
|
|
6769
6644
|
if (stat.isFile()) return resolved;
|
|
6770
6645
|
}
|
|
@@ -6780,7 +6655,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6780
6655
|
let resolved;
|
|
6781
6656
|
try {
|
|
6782
6657
|
resolved = resolvePath2(p);
|
|
6783
|
-
if (
|
|
6658
|
+
if (existsSync11(resolved)) {
|
|
6784
6659
|
const stat = statSync4(resolved);
|
|
6785
6660
|
if (stat.isFile()) return resolved;
|
|
6786
6661
|
}
|
|
@@ -6821,7 +6696,7 @@ function shlexSplit(s) {
|
|
|
6821
6696
|
function resolvePath2(p) {
|
|
6822
6697
|
let expanded = p;
|
|
6823
6698
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6824
|
-
expanded =
|
|
6699
|
+
expanded = join10(homedir3(), expanded.slice(1));
|
|
6825
6700
|
}
|
|
6826
6701
|
return resolve9(expanded);
|
|
6827
6702
|
}
|
|
@@ -6945,7 +6820,7 @@ function analyzeScriptContent2(input, ext) {
|
|
|
6945
6820
|
isInline = true;
|
|
6946
6821
|
}
|
|
6947
6822
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
6948
|
-
resolvedPath =
|
|
6823
|
+
resolvedPath = join10(input.cwd || process.cwd(), resolvedPath);
|
|
6949
6824
|
}
|
|
6950
6825
|
const l1Adapter = (content) => {
|
|
6951
6826
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7030,7 +6905,7 @@ ${contentPreview}
|
|
|
7030
6905
|
if (chain.maxSizeReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u7D2F\u8BA1\u6587\u4EF6\u5927\u5C0F\u89E6\u8FBE\u4E0A\u9650\uFF0C\u90E8\u5206\u811A\u672C\u53EF\u80FD\u672A\u88AB\u5B8C\u6574\u8BFB\u53D6\u3002\n";
|
|
7031
6906
|
return header + parts.join("\n---\n\n");
|
|
7032
6907
|
}
|
|
7033
|
-
var SUSPICIOUS_PATTERNS2, SCRIPT_EXEC_PATTERNS2, KEYWORD_COMBINATION_RISKS2, SINGLE_KEYWORD_RISKS2, LEVEL_ORDER2;
|
|
6908
|
+
var SCRIPT_RULES2, SUSPICIOUS_PATTERNS2, SCRIPT_EXEC_PATTERNS2, KEYWORD_SETS2, KEYWORD_COMBINATION_RISKS2, SINGLE_KEYWORD_RISKS2, LEVEL_ORDER2;
|
|
7034
6909
|
var init_script_content_analyzer = __esm({
|
|
7035
6910
|
"src/engine/script-content-analyzer.ts"() {
|
|
7036
6911
|
"use strict";
|
|
@@ -7040,87 +6915,23 @@ var init_script_content_analyzer = __esm({
|
|
|
7040
6915
|
init_logic_rules();
|
|
7041
6916
|
init_inline_analyzer();
|
|
7042
6917
|
init_tracker();
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
7061
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
7062
|
-
];
|
|
7063
|
-
SCRIPT_EXEC_PATTERNS2 = [
|
|
7064
|
-
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
7065
|
-
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
7066
|
-
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
7067
|
-
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
7068
|
-
/\.\/(\S+)/,
|
|
7069
|
-
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
7070
|
-
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
7071
|
-
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
7072
|
-
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
7073
|
-
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
7074
|
-
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
7075
|
-
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
7076
|
-
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
7077
|
-
];
|
|
7078
|
-
KEYWORD_COMBINATION_RISKS2 = [
|
|
7079
|
-
{
|
|
7080
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7081
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
7082
|
-
level: "critical",
|
|
7083
|
-
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
|
|
7084
|
-
},
|
|
7085
|
-
{
|
|
7086
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7087
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
7088
|
-
level: "critical",
|
|
7089
|
-
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
|
|
7090
|
-
},
|
|
7091
|
-
{
|
|
7092
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
7093
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7094
|
-
level: "high",
|
|
7095
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
7096
|
-
},
|
|
7097
|
-
{
|
|
7098
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
7099
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7100
|
-
level: "high",
|
|
7101
|
-
reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
|
|
7102
|
-
}
|
|
7103
|
-
];
|
|
7104
|
-
SINGLE_KEYWORD_RISKS2 = {
|
|
7105
|
-
"curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
7106
|
-
"curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
7107
|
-
"curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
7108
|
-
"wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
7109
|
-
"wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
|
|
7110
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7111
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7112
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7113
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7114
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7115
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7116
|
-
"base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7117
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
7118
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7119
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7120
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
7121
|
-
"authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
7122
|
-
"ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
|
|
7123
|
-
};
|
|
6918
|
+
init_rules_loader();
|
|
6919
|
+
SCRIPT_RULES2 = loadScriptRules();
|
|
6920
|
+
if (!SCRIPT_RULES2 || Object.keys(SCRIPT_RULES2).length === 0) {
|
|
6921
|
+
console.error(
|
|
6922
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 script_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u811A\u672C\u5185\u5BB9\u5173\u952E\u8BCD\u626B\u63CF\u5C06\u4E0D\u547D\u4E2D\u4EFB\u4F55\u6A21\u5F0F\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
6923
|
+
);
|
|
6924
|
+
}
|
|
6925
|
+
SUSPICIOUS_PATTERNS2 = (SCRIPT_RULES2.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
6926
|
+
SCRIPT_EXEC_PATTERNS2 = ((SCRIPT_RULES2.script_exec_patterns ?? {}).chain ?? []).map((r) => compileRuleRegex(r));
|
|
6927
|
+
KEYWORD_SETS2 = SCRIPT_RULES2.keyword_sets ?? {};
|
|
6928
|
+
KEYWORD_COMBINATION_RISKS2 = (SCRIPT_RULES2.keyword_combination_risks ?? []).map((r) => ({
|
|
6929
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS2),
|
|
6930
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS2),
|
|
6931
|
+
level: String(r.level),
|
|
6932
|
+
reason: String(r.reason)
|
|
6933
|
+
}));
|
|
6934
|
+
SINGLE_KEYWORD_RISKS2 = SCRIPT_RULES2.single_keyword_risks ?? {};
|
|
7124
6935
|
LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
7125
6936
|
}
|
|
7126
6937
|
});
|
|
@@ -7135,16 +6946,16 @@ __export(token_stats_exports, {
|
|
|
7135
6946
|
recordTokenUsage: () => recordTokenUsage,
|
|
7136
6947
|
resetTokenStats: () => resetTokenStats
|
|
7137
6948
|
});
|
|
7138
|
-
import { readFileSync as
|
|
6949
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
7139
6950
|
function getStatsPath() {
|
|
7140
6951
|
const envPath = process.env[ENV_STATS_PATH];
|
|
7141
6952
|
if (envPath) return envPath;
|
|
7142
6953
|
return getTokenStatsPath();
|
|
7143
6954
|
}
|
|
7144
6955
|
function loadAllRecords(statsPath) {
|
|
7145
|
-
if (!
|
|
6956
|
+
if (!existsSync12(statsPath)) return [];
|
|
7146
6957
|
try {
|
|
7147
|
-
const data = JSON.parse(
|
|
6958
|
+
const data = JSON.parse(readFileSync12(statsPath, "utf-8"));
|
|
7148
6959
|
if (Array.isArray(data)) return data;
|
|
7149
6960
|
if (data && Array.isArray(data.records)) return data.records;
|
|
7150
6961
|
return [];
|
|
@@ -7155,7 +6966,7 @@ function loadAllRecords(statsPath) {
|
|
|
7155
6966
|
function saveRecords(statsPath, records) {
|
|
7156
6967
|
try {
|
|
7157
6968
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
7158
|
-
if (dir && !
|
|
6969
|
+
if (dir && !existsSync12(dir)) mkdirSync2(dir, { recursive: true });
|
|
7159
6970
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
7160
6971
|
} catch (e) {
|
|
7161
6972
|
}
|
|
@@ -7282,7 +7093,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
7282
7093
|
}
|
|
7283
7094
|
function resetTokenStats() {
|
|
7284
7095
|
const statsPath = getStatsPath();
|
|
7285
|
-
if (
|
|
7096
|
+
if (existsSync12(statsPath)) {
|
|
7286
7097
|
try {
|
|
7287
7098
|
unlinkSync2(statsPath);
|
|
7288
7099
|
} catch {
|
|
@@ -7320,10 +7131,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7320
7131
|
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
7321
7132
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
7322
7133
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
7323
|
-
const
|
|
7324
|
-
if (
|
|
7134
|
+
const cached2 = llmCache.get(cacheKey);
|
|
7135
|
+
if (cached2) {
|
|
7325
7136
|
console.log("[AgentMoss] LLM cache hit");
|
|
7326
|
-
return
|
|
7137
|
+
return cached2;
|
|
7327
7138
|
}
|
|
7328
7139
|
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
7329
7140
|
const templates = loadTemplates();
|
|
@@ -7539,8 +7350,8 @@ var init_llm_analyzer = __esm({
|
|
|
7539
7350
|
});
|
|
7540
7351
|
|
|
7541
7352
|
// src/brain/rule-store.ts
|
|
7542
|
-
import { existsSync as
|
|
7543
|
-
import { join as
|
|
7353
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
7354
|
+
import { join as join11 } from "path";
|
|
7544
7355
|
import { homedir as homedir4 } from "os";
|
|
7545
7356
|
function getRulesPath() {
|
|
7546
7357
|
const envPath = process.env[ENV_RULES_PATH];
|
|
@@ -7559,7 +7370,7 @@ var init_rule_store = __esm({
|
|
|
7559
7370
|
"src/brain/rule-store.ts"() {
|
|
7560
7371
|
"use strict";
|
|
7561
7372
|
init_esm_shims();
|
|
7562
|
-
DEFAULT_RULES_PATH =
|
|
7373
|
+
DEFAULT_RULES_PATH = join11(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
7563
7374
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7564
7375
|
STATUS_DRAFT = "draft";
|
|
7565
7376
|
STATUS_APPROVED = "approved";
|
|
@@ -7576,11 +7387,11 @@ var init_rule_store = __esm({
|
|
|
7576
7387
|
this.data = this.load();
|
|
7577
7388
|
}
|
|
7578
7389
|
load() {
|
|
7579
|
-
if (!
|
|
7390
|
+
if (!existsSync13(this.path)) {
|
|
7580
7391
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
7581
7392
|
}
|
|
7582
7393
|
try {
|
|
7583
|
-
const raw = JSON.parse(
|
|
7394
|
+
const raw = JSON.parse(readFileSync13(this.path, "utf-8"));
|
|
7584
7395
|
return {
|
|
7585
7396
|
version: raw.version || 1,
|
|
7586
7397
|
rules: raw.rules || [],
|
|
@@ -8174,9 +7985,9 @@ var init_reviewer = __esm({
|
|
|
8174
7985
|
});
|
|
8175
7986
|
|
|
8176
7987
|
// src/brain/skill-manager.ts
|
|
8177
|
-
import { existsSync as
|
|
7988
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync14, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8178
7989
|
import { homedir as homedir5 } from "os";
|
|
8179
|
-
import { join as
|
|
7990
|
+
import { join as join12 } from "path";
|
|
8180
7991
|
import { randomUUID } from "crypto";
|
|
8181
7992
|
function getMetaPath(override) {
|
|
8182
7993
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8186,14 +7997,14 @@ var init_skill_manager = __esm({
|
|
|
8186
7997
|
"src/brain/skill-manager.ts"() {
|
|
8187
7998
|
"use strict";
|
|
8188
7999
|
init_esm_shims();
|
|
8189
|
-
DEFAULT_SKILL_META_PATH =
|
|
8000
|
+
DEFAULT_SKILL_META_PATH = join12(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8190
8001
|
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8191
8002
|
BrainSkillManager = class {
|
|
8192
8003
|
constructor(skillsDir, metaPath) {
|
|
8193
8004
|
this.skillsDir = skillsDir;
|
|
8194
8005
|
this.metaPath = getMetaPath(metaPath);
|
|
8195
8006
|
try {
|
|
8196
|
-
mkdirSync4(
|
|
8007
|
+
mkdirSync4(join12(this.metaPath, ".."), { recursive: true });
|
|
8197
8008
|
} catch {
|
|
8198
8009
|
}
|
|
8199
8010
|
this.load();
|
|
@@ -8203,9 +8014,9 @@ var init_skill_manager = __esm({
|
|
|
8203
8014
|
skills = [];
|
|
8204
8015
|
metaPath;
|
|
8205
8016
|
load() {
|
|
8206
|
-
if (!
|
|
8017
|
+
if (!existsSync14(this.metaPath)) return;
|
|
8207
8018
|
try {
|
|
8208
|
-
const data = JSON.parse(
|
|
8019
|
+
const data = JSON.parse(readFileSync14(this.metaPath, "utf-8"));
|
|
8209
8020
|
this.skills = data.skills || [];
|
|
8210
8021
|
} catch {
|
|
8211
8022
|
this.skills = [];
|
|
@@ -8228,7 +8039,7 @@ var init_skill_manager = __esm({
|
|
|
8228
8039
|
let changed = false;
|
|
8229
8040
|
for (const sk of this.skills) {
|
|
8230
8041
|
if (sk.status !== "active") continue;
|
|
8231
|
-
if (!
|
|
8042
|
+
if (!existsSync14(join12(this.skillsDir, `${sk.name}.md`))) {
|
|
8232
8043
|
sk.status = "stale";
|
|
8233
8044
|
changed = true;
|
|
8234
8045
|
}
|
|
@@ -8239,9 +8050,9 @@ var init_skill_manager = __esm({
|
|
|
8239
8050
|
addSkill(skill) {
|
|
8240
8051
|
const id = skill.id || randomUUID();
|
|
8241
8052
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8242
|
-
if (!
|
|
8243
|
-
const mdPath =
|
|
8244
|
-
if (
|
|
8053
|
+
if (!existsSync14(this.skillsDir)) return "";
|
|
8054
|
+
const mdPath = join12(this.skillsDir, `${name}.md`);
|
|
8055
|
+
if (existsSync14(mdPath)) return "";
|
|
8245
8056
|
try {
|
|
8246
8057
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8247
8058
|
} catch {
|
|
@@ -8290,12 +8101,12 @@ var init_skill_manager = __esm({
|
|
|
8290
8101
|
const out = [];
|
|
8291
8102
|
for (const sk of this.skills) {
|
|
8292
8103
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8293
|
-
const mdPath =
|
|
8294
|
-
if (!
|
|
8104
|
+
const mdPath = join12(this.skillsDir, `${sk.name}.md`);
|
|
8105
|
+
if (!existsSync14(mdPath)) continue;
|
|
8295
8106
|
out.push({
|
|
8296
8107
|
name: sk.name,
|
|
8297
8108
|
category: sk.category || "",
|
|
8298
|
-
content:
|
|
8109
|
+
content: readFileSync14(mdPath, "utf-8"),
|
|
8299
8110
|
keywords: sk.keywords || []
|
|
8300
8111
|
});
|
|
8301
8112
|
}
|
|
@@ -8407,7 +8218,7 @@ __export(contributor_exports, {
|
|
|
8407
8218
|
});
|
|
8408
8219
|
import { mkdirSync as mkdirSync5, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8409
8220
|
import { tmpdir } from "os";
|
|
8410
|
-
import { join as
|
|
8221
|
+
import { join as join13 } from "path";
|
|
8411
8222
|
import { spawnSync } from "child_process";
|
|
8412
8223
|
import { dirname as dirname9 } from "path";
|
|
8413
8224
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
@@ -8428,32 +8239,32 @@ function collectSkills(skillMgr) {
|
|
|
8428
8239
|
}
|
|
8429
8240
|
function writeExportFiles(outDir, rules, skills) {
|
|
8430
8241
|
const written = [];
|
|
8431
|
-
const
|
|
8432
|
-
mkdirSync5(
|
|
8242
|
+
const rulesDir2 = join13(outDir, "rules");
|
|
8243
|
+
mkdirSync5(rulesDir2, { recursive: true });
|
|
8433
8244
|
const byLayer = /* @__PURE__ */ new Map();
|
|
8434
8245
|
for (const r of rules) {
|
|
8435
8246
|
if (!byLayer.has(r.layer)) byLayer.set(r.layer, []);
|
|
8436
8247
|
byLayer.get(r.layer).push(r);
|
|
8437
8248
|
}
|
|
8438
8249
|
for (const layer of [...byLayer.keys()].sort((a, b) => a - b)) {
|
|
8439
|
-
const path6 =
|
|
8250
|
+
const path6 = join13(rulesDir2, `l${layer}_brain_rules.json`);
|
|
8440
8251
|
writeFileSync5(path6, JSON.stringify(byLayer.get(layer), null, 2), "utf-8");
|
|
8441
8252
|
written.push(path6);
|
|
8442
8253
|
}
|
|
8443
8254
|
if (rules.length === 0) {
|
|
8444
|
-
const path6 =
|
|
8255
|
+
const path6 = join13(rulesDir2, "empty.txt");
|
|
8445
8256
|
writeFileSync5(path6, "(no brain rules to contribute)", "utf-8");
|
|
8446
8257
|
written.push(path6);
|
|
8447
8258
|
}
|
|
8448
|
-
const skillsDir =
|
|
8259
|
+
const skillsDir = join13(outDir, "skills");
|
|
8449
8260
|
mkdirSync5(skillsDir, { recursive: true });
|
|
8450
8261
|
for (const sk of skills) {
|
|
8451
|
-
const path6 =
|
|
8262
|
+
const path6 = join13(skillsDir, `${sk.name}.md`);
|
|
8452
8263
|
writeFileSync5(path6, sk.content || "", "utf-8");
|
|
8453
8264
|
written.push(path6);
|
|
8454
8265
|
}
|
|
8455
8266
|
if (skills.length === 0) {
|
|
8456
|
-
const path6 =
|
|
8267
|
+
const path6 = join13(skillsDir, "empty.txt");
|
|
8457
8268
|
writeFileSync5(path6, "(no brain skills to contribute)", "utf-8");
|
|
8458
8269
|
written.push(path6);
|
|
8459
8270
|
}
|
|
@@ -8583,7 +8394,7 @@ async function contribute(opts = {}) {
|
|
|
8583
8394
|
console.log(`[Brain] push \u76EE\u6807: ${pushTarget}`);
|
|
8584
8395
|
console.log(`[Brain] MR \u6307\u5411: ${DEFAULT_TARGET_REPO}`);
|
|
8585
8396
|
const store = new BrainRuleStore();
|
|
8586
|
-
const skillsDir = resolveAssetDir(__dirname8, "skills") ??
|
|
8397
|
+
const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join13(__dirname8, "..", "..", "agent_moss", "skills");
|
|
8587
8398
|
const skillMgr = new BrainSkillManager(skillsDir);
|
|
8588
8399
|
let rules;
|
|
8589
8400
|
let skills;
|
|
@@ -8598,9 +8409,9 @@ async function contribute(opts = {}) {
|
|
|
8598
8409
|
return 0;
|
|
8599
8410
|
}
|
|
8600
8411
|
console.log(`[Brain] \u5BFC\u51FA: ${rules.length} \u6761\u89C4\u5219, ${skills.length} \u4E2A Skill`);
|
|
8601
|
-
const workdir = mkdtempSync(
|
|
8412
|
+
const workdir = mkdtempSync(join13(tmpdir(), "agentmoss-contrib-"));
|
|
8602
8413
|
try {
|
|
8603
|
-
writeExportFiles(
|
|
8414
|
+
writeExportFiles(join13(workdir, "agent_moss", "brain_contrib"), rules, skills);
|
|
8604
8415
|
console.log(`[Brain] \u63A2\u6D4B ${pushTarget} \u8BBF\u95EE\u6743\u9650 ...`);
|
|
8605
8416
|
if (!probeAccess(pushTarget)) {
|
|
8606
8417
|
const username = getCurrentUsername();
|
|
@@ -8848,9 +8659,10 @@ var init_decode = __esm({
|
|
|
8848
8659
|
"src/engine/formal-engine/decode.ts"() {
|
|
8849
8660
|
"use strict";
|
|
8850
8661
|
init_esm_shims();
|
|
8662
|
+
init_rules_loader();
|
|
8851
8663
|
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8852
8664
|
QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8853
|
-
OUTPUT_CMDS =
|
|
8665
|
+
OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8854
8666
|
STAGE_PRIORITY = ["EXFIL", "PRIVESC", "PERSIST", "SCAN", "LISTEN"];
|
|
8855
8667
|
PURPOSE_EXEC_RE = /subprocess\.\w+\s*\(|os\.system\s*\(|shell\s*=\s*True|\beval\s*\(|\bexec\s*\(|os\.popen\s*\(|\/exec\b/i;
|
|
8856
8668
|
LISTEN_LEAD_RE = /http\.server|flask\s+run|nohup\b|nc\s+-l|socat\s+TCP-LISTEN/i;
|
|
@@ -9568,7 +9380,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9568
9380
|
}
|
|
9569
9381
|
return violations;
|
|
9570
9382
|
}
|
|
9571
|
-
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
9383
|
+
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
9572
9384
|
var init_properties = __esm({
|
|
9573
9385
|
"src/engine/formal-engine/properties.ts"() {
|
|
9574
9386
|
"use strict";
|
|
@@ -9585,11 +9397,12 @@ var init_properties = __esm({
|
|
|
9585
9397
|
EXFIL_ACTION_RE = /\bcurl\b|\bwget\b|\bscp\b|\brsync\b|\brclone\b|\bftp\b|connect\s+.*\d+\.\d+\.\d+\.\d+|open\s+.*tcp|base64\s+.*\-o/i;
|
|
9586
9398
|
CRED_STRICT_RE = /(known_hosts|\.ssh|id_rsa|\.pem|\b\.key\b|\/etc\/shadow|\/etc\/passwd|\.aws\/credentials|credentials\.json|netrc|password|secrets?\.ya?ml|credentials?\s*(file|path)?\s*(=|\:)|\bconfig.*(password|token))/i;
|
|
9587
9399
|
INLINE_SHELL_RE = /(?:os\.system|os\.popen)\s*\(\s*['"]([^'"]+)['"]|subprocess\.[a-z_]+\(\s*\[([^\]]+)\]|subprocess\.[a-z_]+\(\s*['"]([^'"]+)['"]|(?:bash|sh|zsh)\s+-c\s*['"]([^'"]+)['"]/gi;
|
|
9588
|
-
|
|
9589
|
-
|
|
9400
|
+
FORMAL_RULES = loadFormalRules();
|
|
9401
|
+
PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
9402
|
+
SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
9590
9403
|
TOOL_REGISTRY = null;
|
|
9591
9404
|
COMPUTE_RE = /subprocess|os\.system|shell\s*=\s*True|\beval\(|\bexec\(|bash\s+[^\s]|sh\s+-c|python3?\s+|node\s+|\btimeout\s+|nohup\s+|\b\.\/[\w./-]+/i;
|
|
9592
|
-
STRONG_COMPUTE_TYPES =
|
|
9405
|
+
STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
9593
9406
|
DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
|
|
9594
9407
|
ALL_PROPERTIES = [
|
|
9595
9408
|
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
@@ -10113,10 +9926,10 @@ var init_stateful = __esm({
|
|
|
10113
9926
|
import * as fs4 from "fs";
|
|
10114
9927
|
import * as os2 from "os";
|
|
10115
9928
|
import * as path4 from "path";
|
|
10116
|
-
import { createHash } from "crypto";
|
|
9929
|
+
import { createHash as createHash2 } from "crypto";
|
|
10117
9930
|
function defaultStateDbPath() {
|
|
10118
9931
|
const dir = process.env.AGENT_MOSS_STATE_DIR || path4.join(os2.homedir(), ".agentmoss");
|
|
10119
|
-
const tag =
|
|
9932
|
+
const tag = createHash2("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
|
|
10120
9933
|
return path4.join(dir, `stateful_chains_${tag}.json`);
|
|
10121
9934
|
}
|
|
10122
9935
|
var ChainStateStore;
|
|
@@ -10376,7 +10189,7 @@ async function runAnalysis(input) {
|
|
|
10376
10189
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
10377
10190
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
10378
10191
|
const lowerDetail = rawDetail.toLowerCase();
|
|
10379
|
-
if (
|
|
10192
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
10380
10193
|
return {
|
|
10381
10194
|
allowed: true,
|
|
10382
10195
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -10419,7 +10232,7 @@ async function runAnalysis(input) {
|
|
|
10419
10232
|
violatedLayers: []
|
|
10420
10233
|
};
|
|
10421
10234
|
}
|
|
10422
|
-
let skipLLM =
|
|
10235
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
10423
10236
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
10424
10237
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
10425
10238
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|
|
@@ -10651,7 +10464,7 @@ async function runAnalysis(input) {
|
|
|
10651
10464
|
}
|
|
10652
10465
|
return llmResult;
|
|
10653
10466
|
}
|
|
10654
|
-
var
|
|
10467
|
+
var TRUSTED_SKILLS_BASE, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
10655
10468
|
var init_coordinator = __esm({
|
|
10656
10469
|
"src/engine/coordinator.ts"() {
|
|
10657
10470
|
"use strict";
|
|
@@ -10674,25 +10487,7 @@ var init_coordinator = __esm({
|
|
|
10674
10487
|
init_asset_dir();
|
|
10675
10488
|
init_runtime_config_bridge();
|
|
10676
10489
|
init_formal_engine();
|
|
10677
|
-
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
10678
|
-
"ask_user_question",
|
|
10679
|
-
"glob",
|
|
10680
|
-
"list_dir",
|
|
10681
|
-
"ls",
|
|
10682
|
-
"count_text_length",
|
|
10683
|
-
"filemgr-globfiles"
|
|
10684
|
-
]);
|
|
10685
10490
|
TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
10686
|
-
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
10687
|
-
"read",
|
|
10688
|
-
"file_read",
|
|
10689
|
-
"read_file",
|
|
10690
|
-
"head",
|
|
10691
|
-
"tail",
|
|
10692
|
-
"grep",
|
|
10693
|
-
"filemgr-readfile",
|
|
10694
|
-
"filemgr-grepfiles"
|
|
10695
|
-
]);
|
|
10696
10491
|
_brainStore = null;
|
|
10697
10492
|
_brainExtractor = null;
|
|
10698
10493
|
_brainSkillManager = null;
|
|
@@ -10705,10 +10500,10 @@ var init_coordinator = __esm({
|
|
|
10705
10500
|
});
|
|
10706
10501
|
|
|
10707
10502
|
// src/infra/policy-cache.ts
|
|
10708
|
-
import { createHash as
|
|
10709
|
-
import { existsSync as
|
|
10503
|
+
import { createHash as createHash3 } from "crypto";
|
|
10504
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync16, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
10710
10505
|
import { tmpdir as tmpdir2 } from "os";
|
|
10711
|
-
import { join as
|
|
10506
|
+
import { join as join15 } from "path";
|
|
10712
10507
|
function cacheDir() {
|
|
10713
10508
|
return cacheDirOverride ?? CACHE_DIR;
|
|
10714
10509
|
}
|
|
@@ -10722,7 +10517,7 @@ var init_policy_cache = __esm({
|
|
|
10722
10517
|
"use strict";
|
|
10723
10518
|
init_esm_shims();
|
|
10724
10519
|
init_config();
|
|
10725
|
-
CACHE_DIR =
|
|
10520
|
+
CACHE_DIR = join15(tmpdir2(), "agent_moss_policy_cache");
|
|
10726
10521
|
cacheDirOverride = null;
|
|
10727
10522
|
PolicyCache = class {
|
|
10728
10523
|
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
@@ -10735,9 +10530,9 @@ var init_policy_cache = __esm({
|
|
|
10735
10530
|
return `${sessionId}::${promptSession}`;
|
|
10736
10531
|
}
|
|
10737
10532
|
cacheFilePath(sessionId, promptSession) {
|
|
10738
|
-
const promptHash =
|
|
10533
|
+
const promptHash = createHash3("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10739
10534
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10740
|
-
return
|
|
10535
|
+
return join15(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10741
10536
|
}
|
|
10742
10537
|
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10743
10538
|
get(sessionId, promptSession) {
|
|
@@ -10750,9 +10545,9 @@ var init_policy_cache = __esm({
|
|
|
10750
10545
|
return hit;
|
|
10751
10546
|
}
|
|
10752
10547
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10753
|
-
if (
|
|
10548
|
+
if (existsSync16(file)) {
|
|
10754
10549
|
try {
|
|
10755
|
-
const policy =
|
|
10550
|
+
const policy = readFileSync16(file, "utf-8");
|
|
10756
10551
|
this.remember(key, policy);
|
|
10757
10552
|
return policy;
|
|
10758
10553
|
} catch {
|
|
@@ -10766,7 +10561,7 @@ var init_policy_cache = __esm({
|
|
|
10766
10561
|
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10767
10562
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10768
10563
|
try {
|
|
10769
|
-
mkdirSync7(
|
|
10564
|
+
mkdirSync7(join15(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10770
10565
|
writeFileSync7(file, policy, "utf-8");
|
|
10771
10566
|
} catch {
|
|
10772
10567
|
}
|
|
@@ -10792,12 +10587,12 @@ var init_policy_cache = __esm({
|
|
|
10792
10587
|
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10793
10588
|
}
|
|
10794
10589
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10795
|
-
const dir =
|
|
10590
|
+
const dir = join15(cacheDir(), safeSessionId);
|
|
10796
10591
|
try {
|
|
10797
|
-
if (
|
|
10592
|
+
if (existsSync16(dir)) {
|
|
10798
10593
|
for (const f of readdirSync5(dir)) {
|
|
10799
10594
|
try {
|
|
10800
|
-
unlinkSync3(
|
|
10595
|
+
unlinkSync3(join15(dir, f));
|
|
10801
10596
|
} catch {
|
|
10802
10597
|
}
|
|
10803
10598
|
}
|
|
@@ -10809,11 +10604,11 @@ var init_policy_cache = __esm({
|
|
|
10809
10604
|
clear() {
|
|
10810
10605
|
this.memory.clear();
|
|
10811
10606
|
try {
|
|
10812
|
-
if (
|
|
10607
|
+
if (existsSync16(cacheDir())) {
|
|
10813
10608
|
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10814
|
-
const dir =
|
|
10609
|
+
const dir = join15(cacheDir(), sessionDir);
|
|
10815
10610
|
try {
|
|
10816
|
-
for (const f of readdirSync5(dir)) unlinkSync3(
|
|
10611
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join15(dir, f));
|
|
10817
10612
|
rmdirSync(dir);
|
|
10818
10613
|
} catch {
|
|
10819
10614
|
}
|
|
@@ -10909,9 +10704,9 @@ __export(analyzer_exports, {
|
|
|
10909
10704
|
savePolicyToFile: () => savePolicyToFile,
|
|
10910
10705
|
validateToml: () => validateToml
|
|
10911
10706
|
});
|
|
10912
|
-
import { existsSync as
|
|
10707
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
|
|
10913
10708
|
import { tmpdir as tmpdir3 } from "os";
|
|
10914
|
-
import { join as
|
|
10709
|
+
import { join as join16, resolve as resolve10 } from "path";
|
|
10915
10710
|
import { parse as parseToml } from "smol-toml";
|
|
10916
10711
|
function _setPolicyOutputDirForTest(dir) {
|
|
10917
10712
|
policyOutputDirOverride = dir;
|
|
@@ -10925,7 +10720,7 @@ function savePolicyToFile(sessionId, policyToml) {
|
|
|
10925
10720
|
const outDir = policyOutputDir();
|
|
10926
10721
|
mkdirSync8(outDir, { recursive: true });
|
|
10927
10722
|
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10928
|
-
const filepath =
|
|
10723
|
+
const filepath = join16(outDir, `${safeName}.toml`);
|
|
10929
10724
|
writeFileSync8(filepath, policyToml, "utf-8");
|
|
10930
10725
|
return filepath;
|
|
10931
10726
|
} catch (err) {
|
|
@@ -10964,11 +10759,11 @@ function loadPolicyMappingDoc() {
|
|
|
10964
10759
|
const dir = resolveAssetDir(__dirname, "templates");
|
|
10965
10760
|
if (dir) {
|
|
10966
10761
|
const p = resolve10(dir, "policy_mapping.md");
|
|
10967
|
-
if (
|
|
10762
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
10968
10763
|
}
|
|
10969
10764
|
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
10970
10765
|
const p = resolve10(cand, "policy_mapping.md");
|
|
10971
|
-
if (
|
|
10766
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
10972
10767
|
}
|
|
10973
10768
|
return "";
|
|
10974
10769
|
}
|
|
@@ -11073,12 +10868,12 @@ async function analyzeInner(input, mossLog) {
|
|
|
11073
10868
|
}
|
|
11074
10869
|
const cache3 = getDefaultCache();
|
|
11075
10870
|
mossLog.startStep("step2_cache_lookup");
|
|
11076
|
-
const
|
|
11077
|
-
if (
|
|
10871
|
+
const cached2 = cache3.get(input.sessionId, input.promptSession);
|
|
10872
|
+
if (cached2 !== null) {
|
|
11078
10873
|
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u547D\u4E2D");
|
|
11079
|
-
savePolicyToFile(input.sessionId,
|
|
11080
|
-
const res = allowResult(
|
|
11081
|
-
mossLog.logResult("Allow", res.reason,
|
|
10874
|
+
savePolicyToFile(input.sessionId, cached2);
|
|
10875
|
+
const res = allowResult(cached2, judgment);
|
|
10876
|
+
mossLog.logResult("Allow", res.reason, cached2);
|
|
11082
10877
|
return res;
|
|
11083
10878
|
}
|
|
11084
10879
|
if (!isPolicyGenEnabled()) {
|
|
@@ -11138,7 +10933,7 @@ var init_analyzer = __esm({
|
|
|
11138
10933
|
init_asset_dir();
|
|
11139
10934
|
init_token_stats();
|
|
11140
10935
|
init_logging();
|
|
11141
|
-
POLICY_OUTPUT_DIR =
|
|
10936
|
+
POLICY_OUTPUT_DIR = join16(tmpdir3(), "agent_moss");
|
|
11142
10937
|
policyOutputDirOverride = null;
|
|
11143
10938
|
DEFAULT_READONLY_POLICY = `landlock_optional = false
|
|
11144
10939
|
mount_isolation_fallback = false
|
|
@@ -11361,14 +11156,16 @@ function mineDangerousPatterns(sessions, minSup = 2, maxLen = 4) {
|
|
|
11361
11156
|
out.sort((a, b) => b.support - a.support || b.pattern.length - a.pattern.length);
|
|
11362
11157
|
return out;
|
|
11363
11158
|
}
|
|
11364
|
-
var DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11159
|
+
var _PM, DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11365
11160
|
var init_pattern_miner = __esm({
|
|
11366
11161
|
"src/engine/formal-engine/pattern_miner.ts"() {
|
|
11367
11162
|
"use strict";
|
|
11368
11163
|
init_esm_shims();
|
|
11164
|
+
init_rules_loader();
|
|
11369
11165
|
init_builder();
|
|
11370
|
-
|
|
11371
|
-
|
|
11166
|
+
_PM = loadFormalRules().pattern_miner ?? {};
|
|
11167
|
+
DANGER_STAGES = new Set(_PM.danger_stages ?? []);
|
|
11168
|
+
SENSITIVE_PAIRS = new Set((_PM.sensitive_pairs ?? []).map((p) => p.join("|")));
|
|
11372
11169
|
}
|
|
11373
11170
|
});
|
|
11374
11171
|
|
|
@@ -11548,7 +11345,7 @@ var init_observable = __esm({
|
|
|
11548
11345
|
});
|
|
11549
11346
|
|
|
11550
11347
|
// src/routes.ts
|
|
11551
|
-
import { join as
|
|
11348
|
+
import { join as join18, dirname as dirname11 } from "path";
|
|
11552
11349
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
11553
11350
|
async function healthRoute(c) {
|
|
11554
11351
|
return c.json(HealthResponseSchema.parse({
|
|
@@ -11797,7 +11594,7 @@ function getBrainRuleStore() {
|
|
|
11797
11594
|
return new BrainRuleStore();
|
|
11798
11595
|
}
|
|
11799
11596
|
function getBrainSkillManager() {
|
|
11800
|
-
const dir = resolveAssetDir(__dirname9, "skills") ??
|
|
11597
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join18(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11801
11598
|
return new BrainSkillManager(dir);
|
|
11802
11599
|
}
|
|
11803
11600
|
async function brainRulesRoute(c) {
|
|
@@ -11910,8 +11707,8 @@ var init_routes = __esm({
|
|
|
11910
11707
|
|
|
11911
11708
|
// src/console/router.ts
|
|
11912
11709
|
import { Hono } from "hono";
|
|
11913
|
-
import { readFileSync as
|
|
11914
|
-
import { join as
|
|
11710
|
+
import { readFileSync as readFileSync19, existsSync as existsSync19, unlinkSync as unlinkSync4 } from "fs";
|
|
11711
|
+
import { join as join19, dirname as dirname12 } from "path";
|
|
11915
11712
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
11916
11713
|
function getConsoleToken() {
|
|
11917
11714
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -11931,21 +11728,35 @@ function authMiddleware() {
|
|
|
11931
11728
|
await next();
|
|
11932
11729
|
};
|
|
11933
11730
|
}
|
|
11731
|
+
function displayRuntimeConfig() {
|
|
11732
|
+
return isRuntimeConfigDisabled() ? generateSourceDefaults() : loadRuntimeConfig();
|
|
11733
|
+
}
|
|
11734
|
+
function builtinOnlyReadOnlyMiddleware() {
|
|
11735
|
+
return async (c, next) => {
|
|
11736
|
+
if (isRuntimeConfigDisabled() && !["GET", "HEAD", "OPTIONS"].includes(c.req.method)) {
|
|
11737
|
+
return c.json({
|
|
11738
|
+
detail: `\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1AConsole \u53EA\u8BFB\uFF0C\u4FEE\u6539\u4E0D\u4F1A\u4FDD\u5B58\u3002\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u540E\u518D\u6539\u89C4\u5219\u3002`
|
|
11739
|
+
}, 403);
|
|
11740
|
+
}
|
|
11741
|
+
await next();
|
|
11742
|
+
};
|
|
11743
|
+
}
|
|
11934
11744
|
function createConsoleRouter() {
|
|
11935
11745
|
const console2 = new Hono();
|
|
11936
11746
|
console2.use("/api/*", authMiddleware());
|
|
11747
|
+
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
11937
11748
|
const serveIndex = (c) => {
|
|
11938
|
-
const indexFile =
|
|
11939
|
-
if (
|
|
11940
|
-
return c.html(
|
|
11749
|
+
const indexFile = join19(STATIC_DIR, "index.html");
|
|
11750
|
+
if (existsSync19(indexFile)) {
|
|
11751
|
+
return c.html(readFileSync19(indexFile, "utf-8"));
|
|
11941
11752
|
}
|
|
11942
11753
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
11943
11754
|
};
|
|
11944
11755
|
console2.get("/", serveIndex);
|
|
11945
11756
|
console2.get("", serveIndex);
|
|
11946
11757
|
console2.get("/api/layers", (c) => {
|
|
11947
|
-
const rc =
|
|
11948
|
-
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
11758
|
+
const rc = displayRuntimeConfig();
|
|
11759
|
+
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
11949
11760
|
});
|
|
11950
11761
|
console2.put("/api/layers", async (c) => {
|
|
11951
11762
|
const body = await c.req.json();
|
|
@@ -11980,7 +11791,7 @@ function createConsoleRouter() {
|
|
|
11980
11791
|
const rules = mc.rules || [];
|
|
11981
11792
|
let catEnabled = true;
|
|
11982
11793
|
try {
|
|
11983
|
-
const rt =
|
|
11794
|
+
const rt = displayRuntimeConfig();
|
|
11984
11795
|
catEnabled = rt.L2_2?.ltl_rules?.category_enabled !== false;
|
|
11985
11796
|
} catch {
|
|
11986
11797
|
}
|
|
@@ -12036,7 +11847,7 @@ function createConsoleRouter() {
|
|
|
12036
11847
|
return rc;
|
|
12037
11848
|
}
|
|
12038
11849
|
console2.get("/api/rules", (c) => {
|
|
12039
|
-
const rc =
|
|
11850
|
+
const rc = displayRuntimeConfig();
|
|
12040
11851
|
const layer = c.req.query("layer");
|
|
12041
11852
|
const category = c.req.query("category");
|
|
12042
11853
|
const out = {};
|
|
@@ -12101,8 +11912,8 @@ function createConsoleRouter() {
|
|
|
12101
11912
|
return c.json(withL22View(rc));
|
|
12102
11913
|
});
|
|
12103
11914
|
console2.get("/api/skills", (c) => {
|
|
12104
|
-
const rc =
|
|
12105
|
-
return c.json(rc.L3_skills);
|
|
11915
|
+
const rc = displayRuntimeConfig();
|
|
11916
|
+
return c.json(rc.L3_skills || {});
|
|
12106
11917
|
});
|
|
12107
11918
|
console2.put("/api/skills/enabled", async (c) => {
|
|
12108
11919
|
const b = await c.req.json();
|
|
@@ -12132,18 +11943,21 @@ function createConsoleRouter() {
|
|
|
12132
11943
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
12133
11944
|
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
12134
11945
|
}
|
|
12135
|
-
const userPath =
|
|
11946
|
+
const userPath = join19(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
12136
11947
|
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
12137
|
-
const candidates = [
|
|
11948
|
+
const candidates = [
|
|
11949
|
+
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
11950
|
+
builtinDir ? join19(builtinDir, `${skillId}.md`) : ""
|
|
11951
|
+
].filter(Boolean);
|
|
12138
11952
|
for (const p of candidates) {
|
|
12139
|
-
if (
|
|
12140
|
-
return c.json({ skill_id: skillId, path: p, content:
|
|
11953
|
+
if (existsSync19(p)) {
|
|
11954
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync19(p, "utf-8") });
|
|
12141
11955
|
}
|
|
12142
11956
|
}
|
|
12143
11957
|
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
12144
11958
|
});
|
|
12145
11959
|
console2.get("/api/agents", (c) => {
|
|
12146
|
-
const rc =
|
|
11960
|
+
const rc = displayRuntimeConfig();
|
|
12147
11961
|
return c.json({ agents: getAgentRules(rc) });
|
|
12148
11962
|
});
|
|
12149
11963
|
console2.put("/api/agents/rules/enabled", async (c) => {
|
|
@@ -12182,7 +11996,7 @@ function createConsoleRouter() {
|
|
|
12182
11996
|
});
|
|
12183
11997
|
const enabledScenarioIds = () => (process.env.AGENT_MOSS_SCENARIO || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
12184
11998
|
console2.get("/api/scenarios", (c) => {
|
|
12185
|
-
const all = getScenarioRules(
|
|
11999
|
+
const all = getScenarioRules(displayRuntimeConfig());
|
|
12186
12000
|
const enabled = enabledScenarioIds();
|
|
12187
12001
|
if (!enabled.length) return c.json({ scenarios: {}, enabled: [] });
|
|
12188
12002
|
const shown = {};
|
|
@@ -12224,8 +12038,8 @@ function createConsoleRouter() {
|
|
|
12224
12038
|
return c.json({ scenarios: getScenarioRules(rc), enabled: enabledScenarioIds() });
|
|
12225
12039
|
});
|
|
12226
12040
|
console2.get("/api/config", (c) => {
|
|
12227
|
-
const rc =
|
|
12228
|
-
return c.json({ ...rc, L2_2: getL22View() });
|
|
12041
|
+
const rc = displayRuntimeConfig();
|
|
12042
|
+
return c.json({ ...rc, L2_2: getL22View(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
12229
12043
|
});
|
|
12230
12044
|
console2.get("/api/env-overrides", (c) => {
|
|
12231
12045
|
return c.json({ overrides: getEnvOverrides() });
|
|
@@ -12234,7 +12048,7 @@ function createConsoleRouter() {
|
|
|
12234
12048
|
const defaults = generateSourceDefaults();
|
|
12235
12049
|
try {
|
|
12236
12050
|
const p = getRuntimeConfigPath();
|
|
12237
|
-
if (
|
|
12051
|
+
if (existsSync19(p)) unlinkSync4(p);
|
|
12238
12052
|
} catch {
|
|
12239
12053
|
}
|
|
12240
12054
|
return c.json(withL22View(defaults));
|
|
@@ -12269,8 +12083,9 @@ var init_router = __esm({
|
|
|
12269
12083
|
init_token_stats();
|
|
12270
12084
|
init_rules_loader();
|
|
12271
12085
|
init_asset_dir();
|
|
12086
|
+
init_rules_mode();
|
|
12272
12087
|
__dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
12273
|
-
STATIC_DIR =
|
|
12088
|
+
STATIC_DIR = join19(__dirname10, "static");
|
|
12274
12089
|
CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
12275
12090
|
}
|
|
12276
12091
|
});
|
|
@@ -12297,37 +12112,57 @@ import { Hono as Hono2 } from "hono";
|
|
|
12297
12112
|
import { cors } from "hono/cors";
|
|
12298
12113
|
import { logger } from "hono/logger";
|
|
12299
12114
|
import net from "net";
|
|
12300
|
-
import { existsSync as
|
|
12115
|
+
import { existsSync as existsSync20 } from "fs";
|
|
12301
12116
|
function logDeploymentSelfCheck() {
|
|
12302
12117
|
try {
|
|
12118
|
+
const mode = getRulesMode();
|
|
12119
|
+
const builtinOnly = isBuiltinOnly();
|
|
12120
|
+
const runtimeDisabled = isRuntimeConfigDisabled();
|
|
12303
12121
|
console.log("================ AgentMoss \u90E8\u7F72\u81EA\u68C0 ================");
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
}
|
|
12312
|
-
const loaded = ruleFilesLoaded();
|
|
12313
|
-
if (loaded.length > 0) {
|
|
12314
|
-
const ov = loadOverrideRules();
|
|
12315
|
-
const ex = loadExtraRules();
|
|
12316
|
-
console.error(`\u26A0\uFE0F \u5DF2\u4ECE\u89C4\u5219\u76EE\u5F55\u52A0\u8F7D ${loaded.length} \u4E2A\u6587\u4EF6\uFF08\u6574\u4EFD\u66FF\u6362 / \u8FFD\u52A0\uFF0C\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762\uFF09:`);
|
|
12317
|
-
for (const f of loaded) console.error(` ${f}`);
|
|
12318
|
-
const ovs = Object.entries(ov).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12319
|
-
if (ovs) console.error(` \u8986\u76D6\u7C7B\u522B: ${ovs}\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF1A\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09`);
|
|
12320
|
-
const exs = Object.entries(ex).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12321
|
-
if (exs) console.log(` \u8FFD\u52A0\u7C7B\u522B: ${exs}\uFF08\u5185\u7F6E\u4FDD\u7559\uFF0C\u8FFD\u52A0\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\uFF09`);
|
|
12322
|
-
}
|
|
12323
|
-
const ignored = ignoredRuleFiles();
|
|
12324
|
-
if (ignored.length > 0) {
|
|
12122
|
+
if (runtimeDisabled) {
|
|
12123
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}\uFF08**\u5DF2\u5FFD\u7565**\uFF1A\u89C4\u5219\u6765\u6E90\u6A21\u5F0F=${mode}\uFF09`);
|
|
12124
|
+
if (builtinOnly) {
|
|
12125
|
+
console.error(
|
|
12126
|
+
"\u89C4\u5219\u6765\u6E90: **\u4EC5\u5185\u7F6E** \u2014\u2014 \u4E0D\u8BFB runtime \u914D\u7F6E / Console \u81EA\u5B9A\u4E49 / \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES\uFF1B\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
12127
|
+
);
|
|
12128
|
+
} else {
|
|
12325
12129
|
console.error(
|
|
12326
|
-
|
|
12130
|
+
"\u89C4\u5219\u6765\u6E90: **\u51B7\u542F\u52A8\uFF08cold\uFF09** \u2014\u2014 \u542F\u52A8\u65F6\u8BFB\u53D6\u5185\u7F6E\u89C4\u5219 + \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES / \u573A\u666F\uFF0C\u4E4B\u540E\u51BB\u7ED3\uFF1B\u4E0D\u8BFB runtime \u914D\u7F6E / Console\uFF0C\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
12327
12131
|
);
|
|
12328
|
-
|
|
12132
|
+
console.error("\u89C4\u5219\u76EE\u5F55\u5185\u5BB9\u6539\u52A8\u9700\u91CD\u542F\u670D\u52A1\u624D\u751F\u6548\u3002");
|
|
12133
|
+
}
|
|
12134
|
+
console.error("Console: \u53EA\u8BFB\uFF08\u89C4\u5219\u5199\u63A5\u53E3\u8FD4\u56DE 403\uFF09\uFF1B\u4FEE\u6539\u89C4\u5219\u9700\u91CD\u542F\u670D\u52A1\u5E76\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u3002");
|
|
12135
|
+
} else {
|
|
12136
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}`);
|
|
12137
|
+
}
|
|
12138
|
+
const layerKeys = ["L1_heuristic", "L2_logic_rules", "L3_llm_analysis", "agent_rules_enabled", "scenario_rules_enabled"];
|
|
12139
|
+
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
12140
|
+
if (!builtinOnly) {
|
|
12141
|
+
try {
|
|
12142
|
+
const dirs = rulesDirs().filter((d) => existsSync20(d));
|
|
12143
|
+
if (dirs.length > 0) {
|
|
12144
|
+
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12145
|
+
}
|
|
12146
|
+
const loaded = ruleFilesLoaded();
|
|
12147
|
+
if (loaded.length > 0) {
|
|
12148
|
+
const ov = loadOverrideRules();
|
|
12149
|
+
const ex = loadExtraRules();
|
|
12150
|
+
console.error(`\u26A0\uFE0F \u5DF2\u4ECE\u89C4\u5219\u76EE\u5F55\u52A0\u8F7D ${loaded.length} \u4E2A\u6587\u4EF6\uFF08\u6574\u4EFD\u66FF\u6362 / \u8FFD\u52A0\uFF0C\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762\uFF09:`);
|
|
12151
|
+
for (const f of loaded) console.error(` ${f}`);
|
|
12152
|
+
const ovs = Object.entries(ov).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12153
|
+
if (ovs) console.error(` \u8986\u76D6\u7C7B\u522B: ${ovs}\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF1A\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09`);
|
|
12154
|
+
const exs = Object.entries(ex).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12155
|
+
if (exs) console.log(` \u8FFD\u52A0\u7C7B\u522B: ${exs}\uFF08\u5185\u7F6E\u4FDD\u7559\uFF0C\u8FFD\u52A0\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\uFF09`);
|
|
12156
|
+
}
|
|
12157
|
+
const ignored = ignoredRuleFiles();
|
|
12158
|
+
if (ignored.length > 0) {
|
|
12159
|
+
console.error(
|
|
12160
|
+
`\u26A0\uFE0F \u89C4\u5219\u76EE\u5F55\u91CC\u6709 ${ignored.length} \u4E2A json **\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u53EA\u8BA4 ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09:`
|
|
12161
|
+
);
|
|
12162
|
+
for (const f of ignored) console.error(` ${f}`);
|
|
12163
|
+
}
|
|
12164
|
+
} catch {
|
|
12329
12165
|
}
|
|
12330
|
-
} catch {
|
|
12331
12166
|
}
|
|
12332
12167
|
const scenarioRaw = (process.env.AGENT_MOSS_SCENARIO || "").trim();
|
|
12333
12168
|
if (scenarioRaw) {
|
|
@@ -12343,6 +12178,7 @@ function logDeploymentSelfCheck() {
|
|
|
12343
12178
|
}
|
|
12344
12179
|
}
|
|
12345
12180
|
function createApp() {
|
|
12181
|
+
enforceRulesContract();
|
|
12346
12182
|
const app = new Hono2();
|
|
12347
12183
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
12348
12184
|
app.use("*", logger());
|
|
@@ -12431,7 +12267,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12431
12267
|
server = null;
|
|
12432
12268
|
},
|
|
12433
12269
|
serve: async () => {
|
|
12434
|
-
const { mkdirSync: mkdirSync10, existsSync:
|
|
12270
|
+
const { mkdirSync: mkdirSync10, existsSync: existsSync22, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12435
12271
|
const { dirname: dirname14 } = await import("path");
|
|
12436
12272
|
const dir = dirname14(socketPath);
|
|
12437
12273
|
if (dir) {
|
|
@@ -12440,7 +12276,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12440
12276
|
} catch {
|
|
12441
12277
|
}
|
|
12442
12278
|
}
|
|
12443
|
-
if (
|
|
12279
|
+
if (existsSync22(socketPath)) {
|
|
12444
12280
|
const { connect } = await import("net");
|
|
12445
12281
|
const inUse = await new Promise((resolve11) => {
|
|
12446
12282
|
const probe = connect(socketPath);
|
|
@@ -12482,9 +12318,11 @@ var init_server = __esm({
|
|
|
12482
12318
|
"src/server.ts"() {
|
|
12483
12319
|
"use strict";
|
|
12484
12320
|
init_esm_shims();
|
|
12321
|
+
init_rules_contract();
|
|
12485
12322
|
init_routes();
|
|
12486
12323
|
init_console();
|
|
12487
12324
|
init_runtime_config();
|
|
12325
|
+
init_rules_mode();
|
|
12488
12326
|
init_rules_loader();
|
|
12489
12327
|
WARMUP_COMMANDS = {
|
|
12490
12328
|
linux: "whoami",
|
|
@@ -12504,26 +12342,26 @@ __export(rules_tools_exports, {
|
|
|
12504
12342
|
rulesStatusText: () => rulesStatusText,
|
|
12505
12343
|
templateSourceDir: () => templateSourceDir
|
|
12506
12344
|
});
|
|
12507
|
-
import { existsSync as
|
|
12508
|
-
import { dirname as dirname13, join as
|
|
12345
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
|
|
12346
|
+
import { dirname as dirname13, join as join20 } from "path";
|
|
12509
12347
|
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
12510
12348
|
function initRulesDir(targetDir, opts = {}) {
|
|
12511
12349
|
mkdirSync9(targetDir, { recursive: true });
|
|
12512
12350
|
const written = [];
|
|
12513
|
-
const extraPath =
|
|
12514
|
-
if (opts.force || !
|
|
12351
|
+
const extraPath = join20(targetDir, EXTRA_FILENAME);
|
|
12352
|
+
if (opts.force || !existsSync21(extraPath)) {
|
|
12515
12353
|
writeFileSync9(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
|
|
12516
12354
|
written.push(extraPath);
|
|
12517
12355
|
}
|
|
12518
|
-
const samplePath =
|
|
12519
|
-
if (opts.force || !
|
|
12356
|
+
const samplePath = join20(targetDir, OVERRIDE_FILENAME);
|
|
12357
|
+
if (opts.force || !existsSync21(samplePath)) {
|
|
12520
12358
|
let payload = OVERRIDE_TEMPLATE;
|
|
12521
12359
|
if (opts.withBuiltin) {
|
|
12522
12360
|
let data = {};
|
|
12523
12361
|
const builtin = resolveRulesFile("l1_rules.json");
|
|
12524
|
-
if (builtin &&
|
|
12362
|
+
if (builtin && existsSync21(builtin)) {
|
|
12525
12363
|
try {
|
|
12526
|
-
data = JSON.parse(
|
|
12364
|
+
data = JSON.parse(readFileSync20(builtin, "utf-8")) || {};
|
|
12527
12365
|
} catch {
|
|
12528
12366
|
data = {};
|
|
12529
12367
|
}
|
|
@@ -12534,17 +12372,17 @@ function initRulesDir(targetDir, opts = {}) {
|
|
|
12534
12372
|
writeFileSync9(samplePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
12535
12373
|
written.push(samplePath);
|
|
12536
12374
|
}
|
|
12537
|
-
const readmeSrc =
|
|
12538
|
-
const readmeDst =
|
|
12539
|
-
if (
|
|
12540
|
-
writeFileSync9(readmeDst,
|
|
12375
|
+
const readmeSrc = join20(templateSourceDir(), "README.md");
|
|
12376
|
+
const readmeDst = join20(targetDir, "README.md");
|
|
12377
|
+
if (existsSync21(readmeSrc) && (opts.force || !existsSync21(readmeDst))) {
|
|
12378
|
+
writeFileSync9(readmeDst, readFileSync20(readmeSrc, "utf-8"), "utf-8");
|
|
12541
12379
|
written.push(readmeDst);
|
|
12542
12380
|
}
|
|
12543
12381
|
return written;
|
|
12544
12382
|
}
|
|
12545
12383
|
function templateSourceDir() {
|
|
12546
|
-
const cands = [
|
|
12547
|
-
for (const c of cands) if (
|
|
12384
|
+
const cands = [join20(__dirname11, "data", "rules_template"), join20(__dirname11, "..", "..", "agent_moss", "data", "rules_template")];
|
|
12385
|
+
for (const c of cands) if (existsSync21(c)) return c;
|
|
12548
12386
|
return cands[0];
|
|
12549
12387
|
}
|
|
12550
12388
|
function templateFiles(dirs) {
|
|
@@ -12552,7 +12390,7 @@ function templateFiles(dirs) {
|
|
|
12552
12390
|
for (const d of dirs) {
|
|
12553
12391
|
try {
|
|
12554
12392
|
for (const name of readdirSync7(d)) {
|
|
12555
|
-
if (name.endsWith(".json.\u793A\u4F8B")) out.push(
|
|
12393
|
+
if (name.endsWith(".json.\u793A\u4F8B")) out.push(join20(d, name));
|
|
12556
12394
|
}
|
|
12557
12395
|
} catch {
|
|
12558
12396
|
}
|
|
@@ -12561,7 +12399,7 @@ function templateFiles(dirs) {
|
|
|
12561
12399
|
}
|
|
12562
12400
|
function rulesStatusText() {
|
|
12563
12401
|
const lines = [];
|
|
12564
|
-
const existing = rulesDirs().filter((d) =>
|
|
12402
|
+
const existing = rulesDirs().filter((d) => existsSync21(d));
|
|
12565
12403
|
lines.push("\u89C4\u5219\u76EE\u5F55: " + (existing.length > 0 ? existing.join(", ") : "\uFF08\u672A\u8BBE\u7F6E / \u4E0D\u5B58\u5728\uFF09"));
|
|
12566
12404
|
if (existing.length > 0) {
|
|
12567
12405
|
lines.push(` \uFF08\u53EA\u8BFB\u5176\u4E2D\u7684 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME}\uFF1B\u5176\u4ED6\u6587\u4EF6\u540D\u4E00\u5F8B\u4E0D\u8BFB\uFF09`);
|
|
@@ -12713,6 +12551,17 @@ async function analyzeRawCommand(command, useJson = false) {
|
|
|
12713
12551
|
async function main() {
|
|
12714
12552
|
const args = process.argv.slice(2);
|
|
12715
12553
|
const command = args[0];
|
|
12554
|
+
try {
|
|
12555
|
+
const { checkRulesAssets: checkRulesAssets2, enforceRulesContract: enforceRulesContract2, formatRulesIntegrity: formatRulesIntegrity2 } = await Promise.resolve().then(() => (init_rules_contract(), rules_contract_exports));
|
|
12556
|
+
const rep = checkRulesAssets2();
|
|
12557
|
+
if (!rep.ok) {
|
|
12558
|
+
enforceRulesContract2();
|
|
12559
|
+
}
|
|
12560
|
+
if (rep.external) console.error(formatRulesIntegrity2(rep));
|
|
12561
|
+
} catch (e) {
|
|
12562
|
+
console.error(`\u274C ${e instanceof Error ? e.message : String(e)}`);
|
|
12563
|
+
process.exit(2);
|
|
12564
|
+
}
|
|
12716
12565
|
if (command === "--version" || command === "-V") {
|
|
12717
12566
|
console.log(`agent-moss ${version}`);
|
|
12718
12567
|
process.exit(0);
|