@kenhkl/agent-moss 0.16.13 → 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/dist/cli.js +958 -1257
- package/dist/engine/coordinator.js +405 -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 +692 -1013
- 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,367 +107,110 @@ 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
|
|
114
|
+
// src/infra/rules-mode.ts
|
|
115
|
+
function truthy(name) {
|
|
116
|
+
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
205
117
|
}
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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;
|
|
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";
|
|
227
129
|
}
|
|
228
|
-
|
|
130
|
+
return cached;
|
|
229
131
|
}
|
|
230
|
-
if (
|
|
231
|
-
|
|
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;
|
|
232
136
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
return segments.length ? segments[0] : "";
|
|
137
|
+
function isHotReloadEnabled() {
|
|
138
|
+
return getRulesMode() === "hot";
|
|
236
139
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
140
|
+
function isRuntimeConfigDisabled() {
|
|
141
|
+
return getRulesMode() !== "hot";
|
|
142
|
+
}
|
|
143
|
+
function isBuiltinOnly() {
|
|
144
|
+
return getRulesMode() === "builtin";
|
|
145
|
+
}
|
|
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"() {
|
|
240
149
|
"use strict";
|
|
241
150
|
init_esm_shims();
|
|
242
|
-
|
|
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;
|
|
243
157
|
}
|
|
244
158
|
});
|
|
245
159
|
|
|
246
|
-
// src/
|
|
247
|
-
function
|
|
248
|
-
|
|
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;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
return true;
|
|
160
|
+
// src/infra/runtime-config-bridge.ts
|
|
161
|
+
function registerRuntimeConfigBridge(bridge) {
|
|
162
|
+
runtimeConfigBridge.current = bridge;
|
|
263
163
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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);
|
|
164
|
+
var runtimeConfigBridge;
|
|
165
|
+
var init_runtime_config_bridge = __esm({
|
|
166
|
+
"src/infra/runtime-config-bridge.ts"() {
|
|
167
|
+
"use strict";
|
|
168
|
+
init_esm_shims();
|
|
169
|
+
runtimeConfigBridge = { current: null };
|
|
276
170
|
}
|
|
277
|
-
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// src/infra/rules-loader.ts
|
|
174
|
+
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
175
|
+
import { resolve, dirname, join as join2 } from "path";
|
|
176
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
177
|
+
function dataRootDir() {
|
|
178
|
+
return getDataRoot();
|
|
278
179
|
}
|
|
279
|
-
function
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
});
|
|
180
|
+
function parseDirEnv(envName) {
|
|
181
|
+
const raw = (process.env[envName] || "").trim();
|
|
182
|
+
if (!raw) return [];
|
|
183
|
+
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
284
184
|
}
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
|
|
185
|
+
function rulesDirs() {
|
|
186
|
+
if (isBuiltinOnly()) return [];
|
|
187
|
+
const dirs = [];
|
|
188
|
+
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
189
|
+
dirs.push(resolve(dataRootDir(), "rules"));
|
|
190
|
+
return [...new Set(dirs)];
|
|
288
191
|
}
|
|
289
|
-
function
|
|
290
|
-
const
|
|
291
|
-
|
|
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));
|
|
292
209
|
}
|
|
293
|
-
function
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
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"() {
|
|
342
|
-
"use strict";
|
|
343
|
-
init_esm_shims();
|
|
344
|
-
init_shell_split();
|
|
345
|
-
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
346
|
-
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
347
|
-
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
348
|
-
READ_WRITE_KEYWORDS = [
|
|
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
|
-
];
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
// src/infra/runtime-config-bridge.ts
|
|
479
|
-
function registerRuntimeConfigBridge(bridge) {
|
|
480
|
-
runtimeConfigBridge.current = bridge;
|
|
481
|
-
}
|
|
482
|
-
var runtimeConfigBridge;
|
|
483
|
-
var init_runtime_config_bridge = __esm({
|
|
484
|
-
"src/infra/runtime-config-bridge.ts"() {
|
|
485
|
-
"use strict";
|
|
486
|
-
init_esm_shims();
|
|
487
|
-
runtimeConfigBridge = { current: null };
|
|
488
|
-
}
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
// src/infra/rules-mode.ts
|
|
492
|
-
function truthy(name) {
|
|
493
|
-
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
494
|
-
}
|
|
495
|
-
function getRulesMode() {
|
|
496
|
-
if (cached !== null) return cached;
|
|
497
|
-
const raw = (process.env[ENV_RULES_MODE] || "").trim().toLowerCase();
|
|
498
|
-
if (raw) {
|
|
499
|
-
if (VALID_MODES.has(raw)) {
|
|
500
|
-
cached = raw;
|
|
501
|
-
} else {
|
|
502
|
-
console.error(
|
|
503
|
-
`[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`
|
|
504
|
-
);
|
|
505
|
-
cached = "builtin";
|
|
506
|
-
}
|
|
507
|
-
return cached;
|
|
508
|
-
}
|
|
509
|
-
if (truthy(ENV_RULES_BUILTIN_ONLY)) cached = "builtin";
|
|
510
|
-
else if (truthy(ENV_DISABLE_RULE_HOT_RELOAD)) cached = "cold";
|
|
511
|
-
else cached = "hot";
|
|
512
|
-
return cached;
|
|
513
|
-
}
|
|
514
|
-
function isHotReloadEnabled() {
|
|
515
|
-
return getRulesMode() === "hot";
|
|
516
|
-
}
|
|
517
|
-
function isRuntimeConfigDisabled() {
|
|
518
|
-
return getRulesMode() !== "hot";
|
|
519
|
-
}
|
|
520
|
-
function isBuiltinOnly() {
|
|
521
|
-
return getRulesMode() === "builtin";
|
|
522
|
-
}
|
|
523
|
-
var ENV_RULES_MODE, ENV_RULES_BUILTIN_ONLY, ENV_DISABLE_RULE_HOT_RELOAD, TRUTHY, VALID_MODES, cached;
|
|
524
|
-
var init_rules_mode = __esm({
|
|
525
|
-
"src/infra/rules-mode.ts"() {
|
|
526
|
-
"use strict";
|
|
527
|
-
init_esm_shims();
|
|
528
|
-
ENV_RULES_MODE = "AGENT_MOSS_RULES_MODE";
|
|
529
|
-
ENV_RULES_BUILTIN_ONLY = "AGENT_MOSS_RULES_BUILTIN_ONLY";
|
|
530
|
-
ENV_DISABLE_RULE_HOT_RELOAD = "AGENT_MOSS_DISABLE_RULE_HOT_RELOAD";
|
|
531
|
-
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
532
|
-
VALID_MODES = /* @__PURE__ */ new Set(["hot", "cold", "builtin"]);
|
|
533
|
-
cached = null;
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
// src/infra/rules-loader.ts
|
|
538
|
-
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
539
|
-
import { resolve, dirname, join as join2 } from "path";
|
|
540
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
541
|
-
function dataRootDir() {
|
|
542
|
-
return getDataRoot();
|
|
543
|
-
}
|
|
544
|
-
function parseDirEnv(envName) {
|
|
545
|
-
const raw = (process.env[envName] || "").trim();
|
|
546
|
-
if (!raw) return [];
|
|
547
|
-
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
548
|
-
}
|
|
549
|
-
function rulesDirs() {
|
|
550
|
-
if (isBuiltinOnly()) return [];
|
|
551
|
-
const dirs = [];
|
|
552
|
-
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
553
|
-
dirs.push(resolve(dataRootDir(), "rules"));
|
|
554
|
-
return [...new Set(dirs)];
|
|
555
|
-
}
|
|
556
|
-
function findRulesFile(name) {
|
|
557
|
-
for (const dir of RULES_DIR_CANDS) {
|
|
558
|
-
const p = resolve(dir, name);
|
|
559
|
-
if (existsSync(p)) return p;
|
|
210
|
+
function findRulesFile(name) {
|
|
211
|
+
for (const dir of rulesDirCands()) {
|
|
212
|
+
const p = resolve(dir, name);
|
|
213
|
+
if (existsSync(p)) return p;
|
|
560
214
|
}
|
|
561
215
|
return null;
|
|
562
216
|
}
|
|
@@ -613,6 +267,74 @@ function loadL1() {
|
|
|
613
267
|
function loadL2() {
|
|
614
268
|
return loadJson("l2_rules.json");
|
|
615
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
|
+
}
|
|
337
|
+
}
|
|
616
338
|
function loadL3() {
|
|
617
339
|
const data = { ...loadJson("l3_rules.json") };
|
|
618
340
|
try {
|
|
@@ -895,111 +617,545 @@ function scanRuleDirs() {
|
|
|
895
617
|
);
|
|
896
618
|
}
|
|
897
619
|
}
|
|
898
|
-
return { override, extra, loaded, ignored };
|
|
620
|
+
return { override, extra, loaded, ignored };
|
|
621
|
+
}
|
|
622
|
+
function rulesCached() {
|
|
623
|
+
if (!isHotReloadEnabled() && _RULES_CACHE) return _RULES_CACHE;
|
|
624
|
+
const dirs = rulesDirs();
|
|
625
|
+
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
626
|
+
const files = [];
|
|
627
|
+
for (const d of dirs) {
|
|
628
|
+
if (!existsSync(d)) continue;
|
|
629
|
+
try {
|
|
630
|
+
for (const name of readdirSync(d).sort()) {
|
|
631
|
+
if (name.toLowerCase().endsWith(".json")) files.push(resolve(d, name));
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const fp = JSON.stringify(files.map((p) => {
|
|
637
|
+
try {
|
|
638
|
+
const st = statSync(p);
|
|
639
|
+
return [p, st.mtimeMs, st.size];
|
|
640
|
+
} catch {
|
|
641
|
+
return [p, -1, -1];
|
|
642
|
+
}
|
|
643
|
+
}));
|
|
644
|
+
if (_RULES_CACHE && _RULES_CACHE.sig === sig && _RULES_CACHE.fp === fp) return _RULES_CACHE;
|
|
645
|
+
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
646
|
+
return _RULES_CACHE;
|
|
647
|
+
}
|
|
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");
|
|
899
1013
|
}
|
|
900
|
-
function
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}
|
|
911
|
-
} catch {
|
|
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;
|
|
912
1024
|
}
|
|
913
1025
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
} catch {
|
|
919
|
-
return [p, -1, -1];
|
|
920
|
-
}
|
|
921
|
-
}));
|
|
922
|
-
if (_RULES_CACHE && _RULES_CACHE.sig === sig && _RULES_CACHE.fp === fp) return _RULES_CACHE;
|
|
923
|
-
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
924
|
-
return _RULES_CACHE;
|
|
1026
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
return true;
|
|
925
1030
|
}
|
|
926
|
-
function
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (
|
|
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);
|
|
933
1038
|
}
|
|
934
|
-
|
|
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");
|
|
935
1045
|
}
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
|
|
1046
|
+
function hasWriteExecChain(command) {
|
|
1047
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
1048
|
+
p.lastIndex = 0;
|
|
1049
|
+
return p.test(command);
|
|
1050
|
+
});
|
|
939
1051
|
}
|
|
940
|
-
function
|
|
941
|
-
|
|
942
|
-
return
|
|
1052
|
+
function hasDirTraversal(code) {
|
|
1053
|
+
const lower = code.toLowerCase();
|
|
1054
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
943
1055
|
}
|
|
944
|
-
function
|
|
945
|
-
|
|
1056
|
+
function hasReadWriteKeyword(code) {
|
|
1057
|
+
const lower = code.toLowerCase();
|
|
1058
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
946
1059
|
}
|
|
947
|
-
function
|
|
948
|
-
|
|
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
|
+
};
|
|
949
1105
|
}
|
|
950
|
-
var
|
|
951
|
-
var
|
|
952
|
-
"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"() {
|
|
953
1109
|
"use strict";
|
|
954
1110
|
init_esm_shims();
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
974
|
-
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
975
|
-
resolve(process.cwd(), "agent_moss/rules")
|
|
976
|
-
];
|
|
977
|
-
return cands.filter((c) => existsSync(c));
|
|
978
|
-
})();
|
|
979
|
-
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
980
|
-
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
981
|
-
EXTRA_CATEGORIES = {
|
|
982
|
-
dangerous_commands: ["pattern"],
|
|
983
|
-
injection_detection: ["keyword", "lang"],
|
|
984
|
-
sensitive_path_access: ["path"],
|
|
985
|
-
intent_consistency: ["intent_keywords"],
|
|
986
|
-
password_modify_consent: ["pattern"],
|
|
987
|
-
user_deletion_consent: ["pattern"]
|
|
988
|
-
};
|
|
989
|
-
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
990
|
-
EXTRA_FLAT_FIELD = [
|
|
991
|
-
[["path"], "sensitive_path_access"],
|
|
992
|
-
[["keyword", "lang"], "injection_detection"],
|
|
993
|
-
[["intent_keywords"], "intent_consistency"],
|
|
994
|
-
[["pattern", "risk_level"], "dangerous_commands"],
|
|
995
|
-
[["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
|
|
996
1129
|
];
|
|
997
|
-
|
|
998
|
-
|
|
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 ?? [];
|
|
999
1132
|
}
|
|
1000
1133
|
});
|
|
1001
1134
|
|
|
1002
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
|
+
}
|
|
1003
1159
|
function getPlatformTraits(osType) {
|
|
1004
1160
|
const accepted = acceptedOsValues(osType);
|
|
1005
1161
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -1007,368 +1163,18 @@ function getPlatformTraits(osType) {
|
|
|
1007
1163
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
1008
1164
|
return LINUX_TRAITS;
|
|
1009
1165
|
}
|
|
1010
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
1166
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
1011
1167
|
var init_platform_traits = __esm({
|
|
1012
1168
|
"src/engine/platform-traits.ts"() {
|
|
1013
1169
|
"use strict";
|
|
1014
1170
|
init_esm_shims();
|
|
1015
1171
|
init_logic_rules();
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
"modify",
|
|
1023
|
-
"\u4FEE\u6539",
|
|
1024
|
-
"update",
|
|
1025
|
-
"\u66F4\u65B0",
|
|
1026
|
-
"delete",
|
|
1027
|
-
"\u5220\u9664",
|
|
1028
|
-
"remove",
|
|
1029
|
-
"\u79FB\u9664",
|
|
1030
|
-
"create",
|
|
1031
|
-
"\u521B\u5EFA",
|
|
1032
|
-
"append",
|
|
1033
|
-
"\u8FFD\u52A0",
|
|
1034
|
-
"truncate",
|
|
1035
|
-
"\u622A\u65AD",
|
|
1036
|
-
"overwrite",
|
|
1037
|
-
"\u8986\u76D6",
|
|
1038
|
-
"replace",
|
|
1039
|
-
"\u66FF\u6362",
|
|
1040
|
-
"edit"
|
|
1041
|
-
],
|
|
1042
|
-
readKeywords: [
|
|
1043
|
-
"read",
|
|
1044
|
-
"\u8BFB\u53D6",
|
|
1045
|
-
"cat",
|
|
1046
|
-
"head",
|
|
1047
|
-
"tail",
|
|
1048
|
-
"less",
|
|
1049
|
-
"more",
|
|
1050
|
-
"view",
|
|
1051
|
-
"\u67E5\u770B",
|
|
1052
|
-
"open",
|
|
1053
|
-
"\u6253\u5F00",
|
|
1054
|
-
"load",
|
|
1055
|
-
"\u52A0\u8F7D",
|
|
1056
|
-
"grep",
|
|
1057
|
-
"search",
|
|
1058
|
-
"\u641C\u7D22",
|
|
1059
|
-
"find",
|
|
1060
|
-
"\u67E5\u627E"
|
|
1061
|
-
],
|
|
1062
|
-
writeCommandRes: [
|
|
1063
|
-
/\brm\b/,
|
|
1064
|
-
/\bunlink\b/,
|
|
1065
|
-
/\brmdir\b/,
|
|
1066
|
-
/\bshred\b/,
|
|
1067
|
-
/\bcp\b/,
|
|
1068
|
-
/\bmv\b/,
|
|
1069
|
-
/\binstall\b/,
|
|
1070
|
-
/\btee\b/,
|
|
1071
|
-
/\bdd\b/,
|
|
1072
|
-
/\bmkfs\b/,
|
|
1073
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
1074
|
-
/\bchmod\b/,
|
|
1075
|
-
/\bchown\b/,
|
|
1076
|
-
/\btruncate\b/
|
|
1077
|
-
],
|
|
1078
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1079
|
-
"cat",
|
|
1080
|
-
"head",
|
|
1081
|
-
"tail",
|
|
1082
|
-
"less",
|
|
1083
|
-
"more",
|
|
1084
|
-
"grep",
|
|
1085
|
-
"find",
|
|
1086
|
-
"awk",
|
|
1087
|
-
"sed",
|
|
1088
|
-
"sort",
|
|
1089
|
-
"uniq",
|
|
1090
|
-
"wc",
|
|
1091
|
-
"cut",
|
|
1092
|
-
"strings",
|
|
1093
|
-
"od",
|
|
1094
|
-
"xxd",
|
|
1095
|
-
"hexdump",
|
|
1096
|
-
"tr",
|
|
1097
|
-
"file",
|
|
1098
|
-
"stat",
|
|
1099
|
-
"du",
|
|
1100
|
-
"df",
|
|
1101
|
-
"ls",
|
|
1102
|
-
"dir",
|
|
1103
|
-
"tree",
|
|
1104
|
-
"nl",
|
|
1105
|
-
"tac",
|
|
1106
|
-
"rev",
|
|
1107
|
-
"echo",
|
|
1108
|
-
"printf",
|
|
1109
|
-
"lsblk",
|
|
1110
|
-
"blockdev",
|
|
1111
|
-
"smartctl",
|
|
1112
|
-
"udevadm",
|
|
1113
|
-
"dmidecode",
|
|
1114
|
-
"lscpu",
|
|
1115
|
-
"lspci",
|
|
1116
|
-
"lsusb",
|
|
1117
|
-
"lsmem",
|
|
1118
|
-
"lsns",
|
|
1119
|
-
"lsof",
|
|
1120
|
-
"hwinfo",
|
|
1121
|
-
"inxi",
|
|
1122
|
-
"fdisk",
|
|
1123
|
-
"parted",
|
|
1124
|
-
"dmesg",
|
|
1125
|
-
"journalctl",
|
|
1126
|
-
"systemctl",
|
|
1127
|
-
"hostnamectl",
|
|
1128
|
-
"localectl",
|
|
1129
|
-
"timedatectl",
|
|
1130
|
-
"ps",
|
|
1131
|
-
"top",
|
|
1132
|
-
"free",
|
|
1133
|
-
"vmstat",
|
|
1134
|
-
"iostat",
|
|
1135
|
-
"mpstat",
|
|
1136
|
-
"pidof",
|
|
1137
|
-
"pgrep",
|
|
1138
|
-
"ip",
|
|
1139
|
-
"ifconfig",
|
|
1140
|
-
"route",
|
|
1141
|
-
"ss",
|
|
1142
|
-
"netstat",
|
|
1143
|
-
"arp",
|
|
1144
|
-
"ethtool",
|
|
1145
|
-
"nmcli",
|
|
1146
|
-
"uname",
|
|
1147
|
-
"arch",
|
|
1148
|
-
"nproc",
|
|
1149
|
-
"getconf",
|
|
1150
|
-
"getent",
|
|
1151
|
-
"id",
|
|
1152
|
-
"whoami",
|
|
1153
|
-
"who",
|
|
1154
|
-
"w",
|
|
1155
|
-
"last",
|
|
1156
|
-
"uptime",
|
|
1157
|
-
"lsmod",
|
|
1158
|
-
"modinfo",
|
|
1159
|
-
"sysctl"
|
|
1160
|
-
]),
|
|
1161
|
-
fullySafeCommands: [
|
|
1162
|
-
/^ls(?:\s|$)/i,
|
|
1163
|
-
/^dir(?:\s|$)/i,
|
|
1164
|
-
/^pwd(?:\s|$)/i,
|
|
1165
|
-
/^which\s+/i,
|
|
1166
|
-
/^whereis\s+/i,
|
|
1167
|
-
/^realpath\s+/i,
|
|
1168
|
-
/^basename\s+/i,
|
|
1169
|
-
/^dirname\s+/i,
|
|
1170
|
-
/^file\s+/i,
|
|
1171
|
-
/^stat\s+/i,
|
|
1172
|
-
/^du(?:\s|$)/i,
|
|
1173
|
-
/^echo\s+/i,
|
|
1174
|
-
/^printf\s+/i,
|
|
1175
|
-
/^type\s+/i,
|
|
1176
|
-
/^command\s+/i,
|
|
1177
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
1178
|
-
/^whoami(?:\s|$)/i,
|
|
1179
|
-
/^id(?:\s|$)/i,
|
|
1180
|
-
/^hostname(?:\s|$)/i,
|
|
1181
|
-
/^uname(?:\s|$)/i,
|
|
1182
|
-
/^date(?:\s|$)/i,
|
|
1183
|
-
/^env(?:\s|$)/i,
|
|
1184
|
-
/^printenv(?:\s|$)/i,
|
|
1185
|
-
/^tty(?:\s|$)/i,
|
|
1186
|
-
/^arch(?:\s|$)/i,
|
|
1187
|
-
/^uptime(?:\s|$)/i,
|
|
1188
|
-
/^groups(?:\s|$)/i,
|
|
1189
|
-
/^logname(?:\s|$)/i,
|
|
1190
|
-
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
1191
|
-
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
1192
|
-
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
1193
|
-
/^df(?:\s|$)/i,
|
|
1194
|
-
/^free(?:\s|$)/i,
|
|
1195
|
-
/^vmstat(?:\s|$)/i,
|
|
1196
|
-
/^nproc(?:\s|$)/i,
|
|
1197
|
-
/^lscpu(?:\s|$)/i,
|
|
1198
|
-
/^lsblk(?:\s|$)/i,
|
|
1199
|
-
/^lspci(?:\s|$)/i,
|
|
1200
|
-
/^lsusb(?:\s|$)/i,
|
|
1201
|
-
/^lsmod(?:\s|$)/i
|
|
1202
|
-
],
|
|
1203
|
-
readonlySensitiveCommands: [
|
|
1204
|
-
/^cat\s+/i,
|
|
1205
|
-
/^head(?:\s|$)/i,
|
|
1206
|
-
/^tail(?:\s|$)/i,
|
|
1207
|
-
/^less(?:\s|$)/i,
|
|
1208
|
-
/^more(?:\s|$)/i,
|
|
1209
|
-
/^wc(?:\s|$)/i,
|
|
1210
|
-
/^grep(?:\s|$)/i,
|
|
1211
|
-
/^find\s+/i,
|
|
1212
|
-
/^ag(?:\s|$)/i,
|
|
1213
|
-
/^rg(?:\s|$)/i,
|
|
1214
|
-
/^awk\s+/i,
|
|
1215
|
-
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
1216
|
-
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
1217
|
-
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
1218
|
-
/^cut\s+/i,
|
|
1219
|
-
/^sort(?:\s|$)/i,
|
|
1220
|
-
/^uniq(?:\s|$)/i,
|
|
1221
|
-
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
1222
|
-
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
1223
|
-
/^ps(?:\s|$)/i,
|
|
1224
|
-
/^ss(?:\s|$)/i,
|
|
1225
|
-
/^netstat(?:\s|$)/i,
|
|
1226
|
-
/^lsof(?:\s|$)/i,
|
|
1227
|
-
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
1228
|
-
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
1229
|
-
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
1230
|
-
/^service\s+\S+\s+status\b/i,
|
|
1231
|
-
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
1232
|
-
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
1233
|
-
// 的话只看紧跟 journalctl 的第一个参数,
|
|
1234
|
-
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
1235
|
-
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
1236
|
-
]
|
|
1237
|
-
};
|
|
1238
|
-
WINDOWS_TRAITS = {
|
|
1239
|
-
writeKeywords: [
|
|
1240
|
-
"write",
|
|
1241
|
-
"\u5199\u5165",
|
|
1242
|
-
"save",
|
|
1243
|
-
"\u4FDD\u5B58",
|
|
1244
|
-
"modify",
|
|
1245
|
-
"\u4FEE\u6539",
|
|
1246
|
-
"update",
|
|
1247
|
-
"\u66F4\u65B0",
|
|
1248
|
-
"delete",
|
|
1249
|
-
"\u5220\u9664",
|
|
1250
|
-
"remove",
|
|
1251
|
-
"\u79FB\u9664",
|
|
1252
|
-
"create",
|
|
1253
|
-
"\u521B\u5EFA",
|
|
1254
|
-
"append",
|
|
1255
|
-
"\u8FFD\u52A0",
|
|
1256
|
-
"overwrite",
|
|
1257
|
-
"\u8986\u76D6",
|
|
1258
|
-
"replace",
|
|
1259
|
-
"\u66FF\u6362",
|
|
1260
|
-
"set",
|
|
1261
|
-
"\u8BBE\u7F6E"
|
|
1262
|
-
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
1263
|
-
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
1264
|
-
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
1265
|
-
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
1266
|
-
],
|
|
1267
|
-
readKeywords: [
|
|
1268
|
-
"read",
|
|
1269
|
-
"\u8BFB\u53D6",
|
|
1270
|
-
"type",
|
|
1271
|
-
"findstr",
|
|
1272
|
-
"dir",
|
|
1273
|
-
"view",
|
|
1274
|
-
"\u67E5\u770B",
|
|
1275
|
-
"open",
|
|
1276
|
-
"\u6253\u5F00",
|
|
1277
|
-
"load",
|
|
1278
|
-
"\u52A0\u8F7D",
|
|
1279
|
-
"search",
|
|
1280
|
-
"\u641C\u7D22",
|
|
1281
|
-
"find",
|
|
1282
|
-
"\u67E5\u627E",
|
|
1283
|
-
"get",
|
|
1284
|
-
"\u83B7\u53D6"
|
|
1285
|
-
],
|
|
1286
|
-
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
1287
|
-
writeCommandRes: [
|
|
1288
|
-
/\b(del|erase)\b/,
|
|
1289
|
-
/\brmdir\b/,
|
|
1290
|
-
/\b(rd)\b/,
|
|
1291
|
-
/\bcopy\b/,
|
|
1292
|
-
/\b(xcopy|robocopy)\b/,
|
|
1293
|
-
/\b(move|movefile)\b/,
|
|
1294
|
-
/\breplace\b/,
|
|
1295
|
-
/\bicacls\b/,
|
|
1296
|
-
/\battrib\b/,
|
|
1297
|
-
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
1298
|
-
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
1299
|
-
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
1300
|
-
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
1301
|
-
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
1302
|
-
/\bformat\b/,
|
|
1303
|
-
/\bdiskpart\b/,
|
|
1304
|
-
/\bmkdir\b/,
|
|
1305
|
-
/\bmd\b/,
|
|
1306
|
-
/\bsetx\b/,
|
|
1307
|
-
/\bwmic\b/
|
|
1308
|
-
],
|
|
1309
|
-
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
1310
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1311
|
-
"type",
|
|
1312
|
-
"findstr",
|
|
1313
|
-
"more",
|
|
1314
|
-
"tree",
|
|
1315
|
-
"dir",
|
|
1316
|
-
"where",
|
|
1317
|
-
"ver",
|
|
1318
|
-
"cls",
|
|
1319
|
-
"echo",
|
|
1320
|
-
"help",
|
|
1321
|
-
"fc",
|
|
1322
|
-
"comp",
|
|
1323
|
-
"find",
|
|
1324
|
-
"sort",
|
|
1325
|
-
"powershell",
|
|
1326
|
-
"cmd",
|
|
1327
|
-
"netstat",
|
|
1328
|
-
"ipconfig",
|
|
1329
|
-
"tasklist",
|
|
1330
|
-
"systeminfo",
|
|
1331
|
-
"driverquery",
|
|
1332
|
-
"reg",
|
|
1333
|
-
"wmic",
|
|
1334
|
-
"whoami",
|
|
1335
|
-
"hostname",
|
|
1336
|
-
"getmac",
|
|
1337
|
-
"query",
|
|
1338
|
-
"w32tm",
|
|
1339
|
-
"diskpart"
|
|
1340
|
-
]),
|
|
1341
|
-
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
1342
|
-
fullySafeCommands: [
|
|
1343
|
-
/^dir(?:\s|$)/,
|
|
1344
|
-
/^where\s+/,
|
|
1345
|
-
/^echo\s+/,
|
|
1346
|
-
/^cd(?:\s|$)/,
|
|
1347
|
-
/^tree(?:\s|$)/,
|
|
1348
|
-
/^help(?:\s|$)/,
|
|
1349
|
-
/^cls(?:\s|$)/,
|
|
1350
|
-
/^ver(?:\s|$)/,
|
|
1351
|
-
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
1352
|
-
/^whoami(?:\s|$)/,
|
|
1353
|
-
/^hostname(?:\s|$)/,
|
|
1354
|
-
/^systeminfo(?:\s|$)/,
|
|
1355
|
-
/^tasklist(?:\s|$)/,
|
|
1356
|
-
/^getmac(?:\s|$)/,
|
|
1357
|
-
/^driverquery(?:\s|$)/,
|
|
1358
|
-
/^netstat(?:\s|$)/
|
|
1359
|
-
],
|
|
1360
|
-
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
1361
|
-
readonlySensitiveCommands: [
|
|
1362
|
-
/^type\s+/,
|
|
1363
|
-
/^findstr\s+/,
|
|
1364
|
-
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
1365
|
-
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
1366
|
-
/^sc\s+query\b/,
|
|
1367
|
-
/^reg\s+query\b/
|
|
1368
|
-
]
|
|
1369
|
-
};
|
|
1370
|
-
MACOS_TRAITS = LINUX_TRAITS;
|
|
1371
|
-
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");
|
|
1372
1178
|
}
|
|
1373
1179
|
});
|
|
1374
1180
|
|
|
@@ -1527,7 +1333,6 @@ function checkIntentConsistency(input) {
|
|
|
1527
1333
|
const { aNext, promptSession, promptHistory } = input;
|
|
1528
1334
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1529
1335
|
const actionType = aNext.actionType.toLowerCase();
|
|
1530
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1531
1336
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1532
1337
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1533
1338
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1555,7 +1360,6 @@ function runLogicCheck(input, opts) {
|
|
|
1555
1360
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1556
1361
|
const actionType = aNext.actionType.toLowerCase();
|
|
1557
1362
|
const resolvedCwd = cwd || "/";
|
|
1558
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1559
1363
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1560
1364
|
if (!opts?.skipReadBeforeWrite) {
|
|
1561
1365
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1667,7 +1471,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1667
1471
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1668
1472
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1669
1473
|
if (!isWrite) return null;
|
|
1670
|
-
const WRITE_CMDS =
|
|
1474
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1671
1475
|
if (isBashLike) {
|
|
1672
1476
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1673
1477
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1699,7 +1503,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1699
1503
|
}
|
|
1700
1504
|
if (!hasWriteSeg) return null;
|
|
1701
1505
|
}
|
|
1702
|
-
const SOURCE_ARG_CMDS =
|
|
1506
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1703
1507
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1704
1508
|
const targetPaths = [];
|
|
1705
1509
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1747,7 +1551,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1747
1551
|
}
|
|
1748
1552
|
}
|
|
1749
1553
|
}
|
|
1750
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1751
1554
|
let filteredPaths = uniqueTargetPaths;
|
|
1752
1555
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1753
1556
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1848,10 +1651,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1848
1651
|
if (!hasTraversal) {
|
|
1849
1652
|
return null;
|
|
1850
1653
|
}
|
|
1851
|
-
const READ_KEYWORDS =
|
|
1852
|
-
const WRITE_KEYWORDS2 =
|
|
1853
|
-
const EXEC_KEYWORDS =
|
|
1854
|
-
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;
|
|
1855
1658
|
const code = extractInlineCode(actionDetail);
|
|
1856
1659
|
if (!code) return null;
|
|
1857
1660
|
const codeLower = code.toLowerCase();
|
|
@@ -1914,7 +1717,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1914
1717
|
violatedLayers: ["L2.1"]
|
|
1915
1718
|
};
|
|
1916
1719
|
}
|
|
1917
|
-
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;
|
|
1918
1721
|
var init_logic_rules = __esm({
|
|
1919
1722
|
"src/engine/logic-rules.ts"() {
|
|
1920
1723
|
"use strict";
|
|
@@ -1924,6 +1727,10 @@ var init_logic_rules = __esm({
|
|
|
1924
1727
|
init_rules_loader();
|
|
1925
1728
|
init_platform_traits();
|
|
1926
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 + "/");
|
|
1927
1734
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1928
1735
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1929
1736
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1944,7 +1751,7 @@ var init_logic_rules = __esm({
|
|
|
1944
1751
|
"list_dir",
|
|
1945
1752
|
"count_text_length",
|
|
1946
1753
|
"search",
|
|
1947
|
-
// filemgr 只读三件套(globfiles 在
|
|
1754
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1948
1755
|
"filemgr-globfiles",
|
|
1949
1756
|
"filemgr-readfile",
|
|
1950
1757
|
"filemgr-grepfiles",
|
|
@@ -1956,7 +1763,7 @@ var init_logic_rules = __esm({
|
|
|
1956
1763
|
"tail",
|
|
1957
1764
|
"cat"
|
|
1958
1765
|
]);
|
|
1959
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1766
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1960
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"];
|
|
1961
1768
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1962
1769
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1997,25 +1804,24 @@ var init_logic_rules = __esm({
|
|
|
1997
1804
|
}));
|
|
1998
1805
|
})();
|
|
1999
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;
|
|
2000
|
-
DIR_TRAVERSAL_KW = [
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
"pathlib.",
|
|
2007
|
-
"iterdir",
|
|
2008
|
-
"rglob",
|
|
2009
|
-
"scandir(",
|
|
2010
|
-
"listdir("
|
|
2011
|
-
];
|
|
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 ?? [];
|
|
2012
1813
|
}
|
|
2013
1814
|
});
|
|
2014
1815
|
|
|
2015
1816
|
// src/engine/asset-dir.ts
|
|
2016
|
-
import { existsSync as
|
|
2017
|
-
import { resolve as resolve2 } from "path";
|
|
1817
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1818
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
2018
1819
|
function assetDirCandidates(moduleDir, name) {
|
|
1820
|
+
const root = getRulesRoot();
|
|
1821
|
+
if (root) {
|
|
1822
|
+
const p = join4(root, name);
|
|
1823
|
+
return [p, p];
|
|
1824
|
+
}
|
|
2019
1825
|
return [
|
|
2020
1826
|
resolve2(moduleDir, name),
|
|
2021
1827
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -2023,7 +1829,7 @@ function assetDirCandidates(moduleDir, name) {
|
|
|
2023
1829
|
}
|
|
2024
1830
|
function resolveAssetDir(moduleDir, name) {
|
|
2025
1831
|
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
2026
|
-
if (
|
|
1832
|
+
if (existsSync4(dir)) return dir;
|
|
2027
1833
|
}
|
|
2028
1834
|
return null;
|
|
2029
1835
|
}
|
|
@@ -2031,11 +1837,12 @@ var init_asset_dir = __esm({
|
|
|
2031
1837
|
"src/engine/asset-dir.ts"() {
|
|
2032
1838
|
"use strict";
|
|
2033
1839
|
init_esm_shims();
|
|
1840
|
+
init_paths();
|
|
2034
1841
|
}
|
|
2035
1842
|
});
|
|
2036
1843
|
|
|
2037
1844
|
// src/engine/skill-loader.ts
|
|
2038
|
-
import { readFileSync as
|
|
1845
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3, existsSync as existsSync5 } from "fs";
|
|
2039
1846
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
2040
1847
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2041
1848
|
function loadAllSkills() {
|
|
@@ -2050,7 +1857,7 @@ function loadAllSkills() {
|
|
|
2050
1857
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
|
|
2051
1858
|
for (const f of files) {
|
|
2052
1859
|
const name = f.replace(".md", "");
|
|
2053
|
-
cachedSkills[name] =
|
|
1860
|
+
cachedSkills[name] = readFileSync4(resolve3(dir, f), "utf-8");
|
|
2054
1861
|
}
|
|
2055
1862
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
2056
1863
|
return cachedSkills;
|
|
@@ -2081,7 +1888,7 @@ function loadUserSkill(name) {
|
|
|
2081
1888
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
2082
1889
|
try {
|
|
2083
1890
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
2084
|
-
userSkillCache[name] =
|
|
1891
|
+
userSkillCache[name] = existsSync5(p) ? readFileSync4(p, "utf-8") : "";
|
|
2085
1892
|
} catch {
|
|
2086
1893
|
userSkillCache[name] = "";
|
|
2087
1894
|
}
|
|
@@ -2230,8 +2037,8 @@ __export(runtime_config_exports, {
|
|
|
2230
2037
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
2231
2038
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2232
2039
|
});
|
|
2233
|
-
import { readFileSync as
|
|
2234
|
-
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";
|
|
2235
2042
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2236
2043
|
import { dirname as dirname3 } from "path";
|
|
2237
2044
|
function runtimeDir() {
|
|
@@ -2260,7 +2067,7 @@ function saveRuntimeConfig(config) {
|
|
|
2260
2067
|
}
|
|
2261
2068
|
const p = getRuntimeConfigPath();
|
|
2262
2069
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2263
|
-
if (dir && !
|
|
2070
|
+
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
2264
2071
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
2265
2072
|
invalidateRuntimeConfigCache();
|
|
2266
2073
|
}
|
|
@@ -2294,7 +2101,7 @@ function loadRuntimeConfig() {
|
|
|
2294
2101
|
};
|
|
2295
2102
|
}
|
|
2296
2103
|
const p = getRuntimeConfigPath();
|
|
2297
|
-
if (!
|
|
2104
|
+
if (!existsSync6(p)) {
|
|
2298
2105
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
2299
2106
|
try {
|
|
2300
2107
|
saveRuntimeConfig(seeded);
|
|
@@ -2308,7 +2115,7 @@ function loadRuntimeConfig() {
|
|
|
2308
2115
|
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
2309
2116
|
return runtimeConfigCache.value;
|
|
2310
2117
|
}
|
|
2311
|
-
const data = JSON.parse(
|
|
2118
|
+
const data = JSON.parse(readFileSync5(p, "utf-8"));
|
|
2312
2119
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
2313
2120
|
const result = {
|
|
2314
2121
|
version: data.version || 1,
|
|
@@ -2366,7 +2173,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2366
2173
|
}
|
|
2367
2174
|
function _filterRulesByOs(rules, osType) {
|
|
2368
2175
|
const accepted = acceptedOsValues(osType);
|
|
2369
|
-
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
|
+
});
|
|
2370
2180
|
}
|
|
2371
2181
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2372
2182
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2453,7 +2263,7 @@ function hasRuntimeConfig() {
|
|
|
2453
2263
|
if (cached2 !== void 0) return cached2;
|
|
2454
2264
|
let ok = false;
|
|
2455
2265
|
try {
|
|
2456
|
-
JSON.parse(
|
|
2266
|
+
JSON.parse(readFileSync5(p, "utf-8"));
|
|
2457
2267
|
ok = true;
|
|
2458
2268
|
} catch {
|
|
2459
2269
|
ok = false;
|
|
@@ -2553,6 +2363,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2553
2363
|
keyword: kw.keyword,
|
|
2554
2364
|
risk_level: kw.riskLevel,
|
|
2555
2365
|
lang: kw.lang || "en",
|
|
2366
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2367
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2368
|
+
os: kw.os,
|
|
2556
2369
|
enabled: true,
|
|
2557
2370
|
builtin: true
|
|
2558
2371
|
}));
|
|
@@ -2670,8 +2483,9 @@ function _l3SkillsToRules() {
|
|
|
2670
2483
|
}
|
|
2671
2484
|
function _scenarioRulesToDefaults() {
|
|
2672
2485
|
const out = {};
|
|
2673
|
-
const
|
|
2674
|
-
|
|
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;
|
|
2675
2489
|
let entries;
|
|
2676
2490
|
try {
|
|
2677
2491
|
entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2679,11 +2493,11 @@ function _scenarioRulesToDefaults() {
|
|
|
2679
2493
|
return out;
|
|
2680
2494
|
}
|
|
2681
2495
|
for (const scenarioId of entries) {
|
|
2682
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2683
|
-
if (!
|
|
2496
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join5(scenDir, scenarioId, "rules.json");
|
|
2497
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2684
2498
|
let data;
|
|
2685
2499
|
try {
|
|
2686
|
-
data = JSON.parse(
|
|
2500
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2687
2501
|
} catch {
|
|
2688
2502
|
continue;
|
|
2689
2503
|
}
|
|
@@ -2719,8 +2533,9 @@ function _scenarioRulesToDefaults() {
|
|
|
2719
2533
|
}
|
|
2720
2534
|
function _agentRulesToDefaults() {
|
|
2721
2535
|
const out = {};
|
|
2722
|
-
const
|
|
2723
|
-
|
|
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;
|
|
2724
2539
|
let entries;
|
|
2725
2540
|
try {
|
|
2726
2541
|
entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2728,11 +2543,11 @@ function _agentRulesToDefaults() {
|
|
|
2728
2543
|
return out;
|
|
2729
2544
|
}
|
|
2730
2545
|
for (const agentId of entries) {
|
|
2731
|
-
const rulesFile =
|
|
2732
|
-
if (!
|
|
2546
|
+
const rulesFile = join5(agentsDir, agentId, "rules.json");
|
|
2547
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2733
2548
|
let data;
|
|
2734
2549
|
try {
|
|
2735
|
-
data = JSON.parse(
|
|
2550
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2736
2551
|
} catch {
|
|
2737
2552
|
continue;
|
|
2738
2553
|
}
|
|
@@ -3107,7 +2922,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
3107
2922
|
}
|
|
3108
2923
|
function _filterDefaultsByOs(rules, osType) {
|
|
3109
2924
|
const accepted = acceptedOsValues(osType);
|
|
3110
|
-
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
|
+
});
|
|
3111
2930
|
}
|
|
3112
2931
|
function _hasRulesForOs(rules, osType) {
|
|
3113
2932
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3336,12 +3155,12 @@ function addCustomSkill(skillId, category, keywords, content) {
|
|
|
3336
3155
|
);
|
|
3337
3156
|
}
|
|
3338
3157
|
const runtime = loadRuntimeConfig();
|
|
3339
|
-
const skillsDir =
|
|
3158
|
+
const skillsDir = join5(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3340
3159
|
try {
|
|
3341
3160
|
mkdirSync(skillsDir, { recursive: true });
|
|
3342
3161
|
} catch {
|
|
3343
3162
|
}
|
|
3344
|
-
writeFileSync(
|
|
3163
|
+
writeFileSync(join5(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3345
3164
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3346
3165
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3347
3166
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3356,9 +3175,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3356
3175
|
if (idx >= 0) {
|
|
3357
3176
|
if (skills[idx].builtin) return null;
|
|
3358
3177
|
skills.splice(idx, 1);
|
|
3359
|
-
const skillPath =
|
|
3178
|
+
const skillPath = join5(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3360
3179
|
try {
|
|
3361
|
-
if (
|
|
3180
|
+
if (existsSync6(skillPath)) unlinkSync(skillPath);
|
|
3362
3181
|
} catch {
|
|
3363
3182
|
}
|
|
3364
3183
|
saveRuntimeConfig(runtime);
|
|
@@ -3565,7 +3384,7 @@ var init_runtime_config = __esm({
|
|
|
3565
3384
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3566
3385
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3567
3386
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3568
|
-
RUNTIME_CONFIG_PATH =
|
|
3387
|
+
RUNTIME_CONFIG_PATH = join5(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3569
3388
|
LAYER_DISABLE_ENV = {
|
|
3570
3389
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3571
3390
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3593,10 +3412,10 @@ var init_runtime_config = __esm({
|
|
|
3593
3412
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3594
3413
|
};
|
|
3595
3414
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3596
|
-
_AGENTS_DIR_PROD =
|
|
3597
|
-
_AGENTS_DIR_DEV =
|
|
3598
|
-
_SCENARIOS_DIR_PROD =
|
|
3599
|
-
_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");
|
|
3600
3419
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3601
3420
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3602
3421
|
registerRuntimeConfigBridge({
|
|
@@ -3934,12 +3753,14 @@ var init_config = __esm({
|
|
|
3934
3753
|
});
|
|
3935
3754
|
|
|
3936
3755
|
// src/agents/index.ts
|
|
3937
|
-
import { readFileSync as
|
|
3938
|
-
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";
|
|
3939
3758
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3940
3759
|
function resolveAgentsDir() {
|
|
3941
|
-
|
|
3942
|
-
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;
|
|
3943
3764
|
return AGENTS_DIR_PROD;
|
|
3944
3765
|
}
|
|
3945
3766
|
function getAgentExtensions(agentId) {
|
|
@@ -3957,12 +3778,12 @@ function getAgentExtensions(agentId) {
|
|
|
3957
3778
|
if (ext === null) {
|
|
3958
3779
|
const agentsDir = resolveAgentsDir();
|
|
3959
3780
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3960
|
-
if (!
|
|
3781
|
+
if (!existsSync7(rulesFile)) {
|
|
3961
3782
|
cache.set(agentId, { fp, ext: null });
|
|
3962
3783
|
return null;
|
|
3963
3784
|
}
|
|
3964
3785
|
try {
|
|
3965
|
-
const raw =
|
|
3786
|
+
const raw = readFileSync6(rulesFile, "utf-8");
|
|
3966
3787
|
ext = JSON.parse(raw);
|
|
3967
3788
|
if (!ext.agent_id) {
|
|
3968
3789
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
@@ -4001,6 +3822,7 @@ var init_agents = __esm({
|
|
|
4001
3822
|
"src/agents/index.ts"() {
|
|
4002
3823
|
"use strict";
|
|
4003
3824
|
init_esm_shims();
|
|
3825
|
+
init_paths();
|
|
4004
3826
|
init_runtime_config();
|
|
4005
3827
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
4006
3828
|
__dirname5 = dirname4(__filename4);
|
|
@@ -4011,12 +3833,14 @@ var init_agents = __esm({
|
|
|
4011
3833
|
});
|
|
4012
3834
|
|
|
4013
3835
|
// src/scenarios/index.ts
|
|
4014
|
-
import { readFileSync as
|
|
4015
|
-
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";
|
|
4016
3838
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
4017
3839
|
function resolveScenariosDir() {
|
|
4018
|
-
|
|
4019
|
-
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;
|
|
4020
3844
|
return SCENARIOS_DIR_PROD;
|
|
4021
3845
|
}
|
|
4022
3846
|
function getScenarioExtensions(scenarioId) {
|
|
@@ -4046,12 +3870,12 @@ function getScenarioExtensions(scenarioId) {
|
|
|
4046
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`
|
|
4047
3871
|
);
|
|
4048
3872
|
}
|
|
4049
|
-
if (!
|
|
3873
|
+
if (!existsSync8(rulesFile)) {
|
|
4050
3874
|
cache2.set(scenarioId, { fp, ext: null });
|
|
4051
3875
|
return null;
|
|
4052
3876
|
}
|
|
4053
3877
|
try {
|
|
4054
|
-
const raw =
|
|
3878
|
+
const raw = readFileSync7(rulesFile, "utf-8");
|
|
4055
3879
|
const data = JSON.parse(raw);
|
|
4056
3880
|
const id = data.scenario_id || data.agent_id || "";
|
|
4057
3881
|
if (!id) {
|
|
@@ -4095,6 +3919,7 @@ var init_scenarios = __esm({
|
|
|
4095
3919
|
"src/scenarios/index.ts"() {
|
|
4096
3920
|
"use strict";
|
|
4097
3921
|
init_esm_shims();
|
|
3922
|
+
init_paths();
|
|
4098
3923
|
init_agents();
|
|
4099
3924
|
init_runtime_config();
|
|
4100
3925
|
init_rules_loader();
|
|
@@ -4501,7 +4326,7 @@ function severityRank(s) {
|
|
|
4501
4326
|
}
|
|
4502
4327
|
function isSearchActionType(actionType) {
|
|
4503
4328
|
const at = actionType.toLowerCase();
|
|
4504
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4329
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4505
4330
|
}
|
|
4506
4331
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4507
4332
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4791,8 +4616,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4791
4616
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4792
4617
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4793
4618
|
const scan = stripHeredocContent(actionDetail);
|
|
4794
|
-
const
|
|
4795
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4619
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4796
4620
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4797
4621
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4798
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;
|
|
@@ -4938,7 +4762,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4938
4762
|
}
|
|
4939
4763
|
return null;
|
|
4940
4764
|
}
|
|
4941
|
-
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;
|
|
4942
4766
|
var init_heuristic = __esm({
|
|
4943
4767
|
"src/engine/heuristic.ts"() {
|
|
4944
4768
|
"use strict";
|
|
@@ -4955,7 +4779,8 @@ var init_heuristic = __esm({
|
|
|
4955
4779
|
return kws.map((k) => ({
|
|
4956
4780
|
keyword: k.keyword,
|
|
4957
4781
|
riskLevel: k.risk_level,
|
|
4958
|
-
lang: k.lang || "en"
|
|
4782
|
+
lang: k.lang || "en",
|
|
4783
|
+
os: String(k.os || "all").toLowerCase()
|
|
4959
4784
|
}));
|
|
4960
4785
|
})();
|
|
4961
4786
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4963,7 +4788,9 @@ var init_heuristic = __esm({
|
|
|
4963
4788
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4964
4789
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4965
4790
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4966
|
-
|
|
4791
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4792
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4793
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4967
4794
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4968
4795
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4969
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;
|
|
@@ -4971,15 +4798,16 @@ var init_heuristic = __esm({
|
|
|
4971
4798
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4972
4799
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4973
4800
|
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4974
|
-
SENSITIVE_EXCLUDED_PREFIXES =
|
|
4975
|
-
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 ?? [];
|
|
4976
4804
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4977
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;
|
|
4978
4806
|
}
|
|
4979
4807
|
});
|
|
4980
4808
|
|
|
4981
4809
|
// src/engine/template-loader.ts
|
|
4982
|
-
import { readFileSync as
|
|
4810
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4983
4811
|
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
4984
4812
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
4985
4813
|
function loadTemplates() {
|
|
@@ -4993,9 +4821,9 @@ function loadTemplates() {
|
|
|
4993
4821
|
}
|
|
4994
4822
|
for (const file of TEMPLATE_FILES) {
|
|
4995
4823
|
const filePath = resolve6(dir, file);
|
|
4996
|
-
if (
|
|
4824
|
+
if (existsSync9(filePath)) {
|
|
4997
4825
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
4998
|
-
cachedTemplates[key] =
|
|
4826
|
+
cachedTemplates[key] = readFileSync8(filePath, "utf-8");
|
|
4999
4827
|
}
|
|
5000
4828
|
}
|
|
5001
4829
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -5015,8 +4843,8 @@ var init_template_loader = __esm({
|
|
|
5015
4843
|
});
|
|
5016
4844
|
|
|
5017
4845
|
// src/engine/script-analyzer.ts
|
|
5018
|
-
import { readFileSync as
|
|
5019
|
-
import { resolve as resolve7, join as
|
|
4846
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4847
|
+
import { resolve as resolve7, join as join8 } from "path";
|
|
5020
4848
|
function emptyResult() {
|
|
5021
4849
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
5022
4850
|
}
|
|
@@ -5042,10 +4870,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5042
4870
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
5043
4871
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
5044
4872
|
}
|
|
5045
|
-
const networkPostKeywords =
|
|
4873
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
5046
4874
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
5047
4875
|
if (hasNetworkPost) {
|
|
5048
|
-
const envPatterns =
|
|
4876
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
5049
4877
|
const contentLower = scriptContent.toLowerCase();
|
|
5050
4878
|
for (const p of envPatterns) {
|
|
5051
4879
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -5069,8 +4897,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5069
4897
|
}
|
|
5070
4898
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
5071
4899
|
try {
|
|
5072
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
5073
|
-
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");
|
|
5074
4902
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
5075
4903
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
5076
4904
|
} catch (e) {
|
|
@@ -5119,90 +4947,28 @@ ${result.scriptContent}
|
|
|
5119
4947
|
\`\`\`
|
|
5120
4948
|
`;
|
|
5121
4949
|
}
|
|
5122
|
-
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;
|
|
5123
4951
|
var init_script_analyzer = __esm({
|
|
5124
4952
|
"src/engine/script-analyzer.ts"() {
|
|
5125
4953
|
"use strict";
|
|
5126
4954
|
init_esm_shims();
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
5145
|
-
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
5146
|
-
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
5147
|
-
// Persistence backdoor
|
|
5148
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
5149
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
5150
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
5151
|
-
];
|
|
5152
|
-
SCRIPT_EXEC_PATTERNS = [
|
|
5153
|
-
/bash\s+(\S+\.sh)/,
|
|
5154
|
-
/sh\s+(\S+\.sh)/,
|
|
5155
|
-
/\.\/(\S+\.sh)/,
|
|
5156
|
-
/source\s+(\S+\.sh)/,
|
|
5157
|
-
/\.\s+(\S+\.sh)/,
|
|
5158
|
-
/zsh\s+(\S+\.sh)/
|
|
5159
|
-
];
|
|
5160
|
-
KEYWORD_COMBINATION_RISKS = [
|
|
5161
|
-
{
|
|
5162
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5163
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
5164
|
-
level: "critical",
|
|
5165
|
-
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"
|
|
5166
|
-
},
|
|
5167
|
-
{
|
|
5168
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5169
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
5170
|
-
level: "critical",
|
|
5171
|
-
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"
|
|
5172
|
-
},
|
|
5173
|
-
{
|
|
5174
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
5175
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5176
|
-
level: "high",
|
|
5177
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
5178
|
-
},
|
|
5179
|
-
{
|
|
5180
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
5181
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5182
|
-
level: "high",
|
|
5183
|
-
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"
|
|
5184
|
-
}
|
|
5185
|
-
];
|
|
5186
|
-
SINGLE_KEYWORD_RISKS = {
|
|
5187
|
-
"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" },
|
|
5188
|
-
"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" },
|
|
5189
|
-
"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" },
|
|
5190
|
-
"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" },
|
|
5191
|
-
"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" },
|
|
5192
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5193
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5194
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5195
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5196
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5197
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5198
|
-
"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" },
|
|
5199
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
5200
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5201
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5202
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5203
|
-
"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" },
|
|
5204
|
-
"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" }
|
|
5205
|
-
};
|
|
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 ?? {};
|
|
5206
4972
|
LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5207
4973
|
}
|
|
5208
4974
|
});
|
|
@@ -6746,8 +6512,8 @@ var init_tracker = __esm({
|
|
|
6746
6512
|
});
|
|
6747
6513
|
|
|
6748
6514
|
// src/engine/script-content-analyzer.ts
|
|
6749
|
-
import { statSync as statSync4, existsSync as
|
|
6750
|
-
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";
|
|
6751
6517
|
import { homedir as homedir3 } from "os";
|
|
6752
6518
|
function emptyChain() {
|
|
6753
6519
|
return {
|
|
@@ -6780,10 +6546,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6780
6546
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6781
6547
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6782
6548
|
}
|
|
6783
|
-
const networkPostKeywords =
|
|
6549
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6784
6550
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6785
6551
|
if (hasNetworkPost) {
|
|
6786
|
-
const envPatterns =
|
|
6552
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6787
6553
|
const contentLower = scriptContent.toLowerCase();
|
|
6788
6554
|
for (const p of envPatterns) {
|
|
6789
6555
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -6859,7 +6625,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6859
6625
|
if (looksLikeScriptPath(tok)) {
|
|
6860
6626
|
try {
|
|
6861
6627
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6862
|
-
if (
|
|
6628
|
+
if (existsSync11(resolved)) {
|
|
6863
6629
|
const stat = statSync4(resolved);
|
|
6864
6630
|
if (stat.isFile()) return resolved;
|
|
6865
6631
|
}
|
|
@@ -6873,7 +6639,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6873
6639
|
const p = m[1];
|
|
6874
6640
|
try {
|
|
6875
6641
|
const resolved = resolvePath2(p);
|
|
6876
|
-
if (
|
|
6642
|
+
if (existsSync11(resolved)) {
|
|
6877
6643
|
const stat = statSync4(resolved);
|
|
6878
6644
|
if (stat.isFile()) return resolved;
|
|
6879
6645
|
}
|
|
@@ -6889,7 +6655,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6889
6655
|
let resolved;
|
|
6890
6656
|
try {
|
|
6891
6657
|
resolved = resolvePath2(p);
|
|
6892
|
-
if (
|
|
6658
|
+
if (existsSync11(resolved)) {
|
|
6893
6659
|
const stat = statSync4(resolved);
|
|
6894
6660
|
if (stat.isFile()) return resolved;
|
|
6895
6661
|
}
|
|
@@ -6930,7 +6696,7 @@ function shlexSplit(s) {
|
|
|
6930
6696
|
function resolvePath2(p) {
|
|
6931
6697
|
let expanded = p;
|
|
6932
6698
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6933
|
-
expanded =
|
|
6699
|
+
expanded = join10(homedir3(), expanded.slice(1));
|
|
6934
6700
|
}
|
|
6935
6701
|
return resolve9(expanded);
|
|
6936
6702
|
}
|
|
@@ -7054,7 +6820,7 @@ function analyzeScriptContent2(input, ext) {
|
|
|
7054
6820
|
isInline = true;
|
|
7055
6821
|
}
|
|
7056
6822
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
7057
|
-
resolvedPath =
|
|
6823
|
+
resolvedPath = join10(input.cwd || process.cwd(), resolvedPath);
|
|
7058
6824
|
}
|
|
7059
6825
|
const l1Adapter = (content) => {
|
|
7060
6826
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7139,7 +6905,7 @@ ${contentPreview}
|
|
|
7139
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";
|
|
7140
6906
|
return header + parts.join("\n---\n\n");
|
|
7141
6907
|
}
|
|
7142
|
-
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;
|
|
7143
6909
|
var init_script_content_analyzer = __esm({
|
|
7144
6910
|
"src/engine/script-content-analyzer.ts"() {
|
|
7145
6911
|
"use strict";
|
|
@@ -7149,87 +6915,23 @@ var init_script_content_analyzer = __esm({
|
|
|
7149
6915
|
init_logic_rules();
|
|
7150
6916
|
init_inline_analyzer();
|
|
7151
6917
|
init_tracker();
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
7170
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
7171
|
-
];
|
|
7172
|
-
SCRIPT_EXEC_PATTERNS2 = [
|
|
7173
|
-
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
7174
|
-
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
7175
|
-
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
7176
|
-
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
7177
|
-
/\.\/(\S+)/,
|
|
7178
|
-
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
7179
|
-
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
7180
|
-
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
7181
|
-
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
7182
|
-
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
7183
|
-
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
7184
|
-
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
7185
|
-
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
7186
|
-
];
|
|
7187
|
-
KEYWORD_COMBINATION_RISKS2 = [
|
|
7188
|
-
{
|
|
7189
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7190
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
7191
|
-
level: "critical",
|
|
7192
|
-
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"
|
|
7193
|
-
},
|
|
7194
|
-
{
|
|
7195
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7196
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
7197
|
-
level: "critical",
|
|
7198
|
-
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"
|
|
7199
|
-
},
|
|
7200
|
-
{
|
|
7201
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
7202
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7203
|
-
level: "high",
|
|
7204
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
7205
|
-
},
|
|
7206
|
-
{
|
|
7207
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
7208
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7209
|
-
level: "high",
|
|
7210
|
-
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"
|
|
7211
|
-
}
|
|
7212
|
-
];
|
|
7213
|
-
SINGLE_KEYWORD_RISKS2 = {
|
|
7214
|
-
"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" },
|
|
7215
|
-
"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" },
|
|
7216
|
-
"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" },
|
|
7217
|
-
"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" },
|
|
7218
|
-
"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" },
|
|
7219
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7220
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7221
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7222
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7223
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7224
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7225
|
-
"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" },
|
|
7226
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
7227
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7228
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7229
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
7230
|
-
"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" },
|
|
7231
|
-
"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" }
|
|
7232
|
-
};
|
|
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 ?? {};
|
|
7233
6935
|
LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
7234
6936
|
}
|
|
7235
6937
|
});
|
|
@@ -7244,16 +6946,16 @@ __export(token_stats_exports, {
|
|
|
7244
6946
|
recordTokenUsage: () => recordTokenUsage,
|
|
7245
6947
|
resetTokenStats: () => resetTokenStats
|
|
7246
6948
|
});
|
|
7247
|
-
import { readFileSync as
|
|
6949
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
7248
6950
|
function getStatsPath() {
|
|
7249
6951
|
const envPath = process.env[ENV_STATS_PATH];
|
|
7250
6952
|
if (envPath) return envPath;
|
|
7251
6953
|
return getTokenStatsPath();
|
|
7252
6954
|
}
|
|
7253
6955
|
function loadAllRecords(statsPath) {
|
|
7254
|
-
if (!
|
|
6956
|
+
if (!existsSync12(statsPath)) return [];
|
|
7255
6957
|
try {
|
|
7256
|
-
const data = JSON.parse(
|
|
6958
|
+
const data = JSON.parse(readFileSync12(statsPath, "utf-8"));
|
|
7257
6959
|
if (Array.isArray(data)) return data;
|
|
7258
6960
|
if (data && Array.isArray(data.records)) return data.records;
|
|
7259
6961
|
return [];
|
|
@@ -7264,7 +6966,7 @@ function loadAllRecords(statsPath) {
|
|
|
7264
6966
|
function saveRecords(statsPath, records) {
|
|
7265
6967
|
try {
|
|
7266
6968
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
7267
|
-
if (dir && !
|
|
6969
|
+
if (dir && !existsSync12(dir)) mkdirSync2(dir, { recursive: true });
|
|
7268
6970
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
7269
6971
|
} catch (e) {
|
|
7270
6972
|
}
|
|
@@ -7391,7 +7093,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
7391
7093
|
}
|
|
7392
7094
|
function resetTokenStats() {
|
|
7393
7095
|
const statsPath = getStatsPath();
|
|
7394
|
-
if (
|
|
7096
|
+
if (existsSync12(statsPath)) {
|
|
7395
7097
|
try {
|
|
7396
7098
|
unlinkSync2(statsPath);
|
|
7397
7099
|
} catch {
|
|
@@ -7648,8 +7350,8 @@ var init_llm_analyzer = __esm({
|
|
|
7648
7350
|
});
|
|
7649
7351
|
|
|
7650
7352
|
// src/brain/rule-store.ts
|
|
7651
|
-
import { existsSync as
|
|
7652
|
-
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";
|
|
7653
7355
|
import { homedir as homedir4 } from "os";
|
|
7654
7356
|
function getRulesPath() {
|
|
7655
7357
|
const envPath = process.env[ENV_RULES_PATH];
|
|
@@ -7668,7 +7370,7 @@ var init_rule_store = __esm({
|
|
|
7668
7370
|
"src/brain/rule-store.ts"() {
|
|
7669
7371
|
"use strict";
|
|
7670
7372
|
init_esm_shims();
|
|
7671
|
-
DEFAULT_RULES_PATH =
|
|
7373
|
+
DEFAULT_RULES_PATH = join11(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
7672
7374
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7673
7375
|
STATUS_DRAFT = "draft";
|
|
7674
7376
|
STATUS_APPROVED = "approved";
|
|
@@ -7685,11 +7387,11 @@ var init_rule_store = __esm({
|
|
|
7685
7387
|
this.data = this.load();
|
|
7686
7388
|
}
|
|
7687
7389
|
load() {
|
|
7688
|
-
if (!
|
|
7390
|
+
if (!existsSync13(this.path)) {
|
|
7689
7391
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
7690
7392
|
}
|
|
7691
7393
|
try {
|
|
7692
|
-
const raw = JSON.parse(
|
|
7394
|
+
const raw = JSON.parse(readFileSync13(this.path, "utf-8"));
|
|
7693
7395
|
return {
|
|
7694
7396
|
version: raw.version || 1,
|
|
7695
7397
|
rules: raw.rules || [],
|
|
@@ -8283,9 +7985,9 @@ var init_reviewer = __esm({
|
|
|
8283
7985
|
});
|
|
8284
7986
|
|
|
8285
7987
|
// src/brain/skill-manager.ts
|
|
8286
|
-
import { existsSync as
|
|
7988
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync14, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8287
7989
|
import { homedir as homedir5 } from "os";
|
|
8288
|
-
import { join as
|
|
7990
|
+
import { join as join12 } from "path";
|
|
8289
7991
|
import { randomUUID } from "crypto";
|
|
8290
7992
|
function getMetaPath(override) {
|
|
8291
7993
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8295,14 +7997,14 @@ var init_skill_manager = __esm({
|
|
|
8295
7997
|
"src/brain/skill-manager.ts"() {
|
|
8296
7998
|
"use strict";
|
|
8297
7999
|
init_esm_shims();
|
|
8298
|
-
DEFAULT_SKILL_META_PATH =
|
|
8000
|
+
DEFAULT_SKILL_META_PATH = join12(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8299
8001
|
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8300
8002
|
BrainSkillManager = class {
|
|
8301
8003
|
constructor(skillsDir, metaPath) {
|
|
8302
8004
|
this.skillsDir = skillsDir;
|
|
8303
8005
|
this.metaPath = getMetaPath(metaPath);
|
|
8304
8006
|
try {
|
|
8305
|
-
mkdirSync4(
|
|
8007
|
+
mkdirSync4(join12(this.metaPath, ".."), { recursive: true });
|
|
8306
8008
|
} catch {
|
|
8307
8009
|
}
|
|
8308
8010
|
this.load();
|
|
@@ -8312,9 +8014,9 @@ var init_skill_manager = __esm({
|
|
|
8312
8014
|
skills = [];
|
|
8313
8015
|
metaPath;
|
|
8314
8016
|
load() {
|
|
8315
|
-
if (!
|
|
8017
|
+
if (!existsSync14(this.metaPath)) return;
|
|
8316
8018
|
try {
|
|
8317
|
-
const data = JSON.parse(
|
|
8019
|
+
const data = JSON.parse(readFileSync14(this.metaPath, "utf-8"));
|
|
8318
8020
|
this.skills = data.skills || [];
|
|
8319
8021
|
} catch {
|
|
8320
8022
|
this.skills = [];
|
|
@@ -8337,7 +8039,7 @@ var init_skill_manager = __esm({
|
|
|
8337
8039
|
let changed = false;
|
|
8338
8040
|
for (const sk of this.skills) {
|
|
8339
8041
|
if (sk.status !== "active") continue;
|
|
8340
|
-
if (!
|
|
8042
|
+
if (!existsSync14(join12(this.skillsDir, `${sk.name}.md`))) {
|
|
8341
8043
|
sk.status = "stale";
|
|
8342
8044
|
changed = true;
|
|
8343
8045
|
}
|
|
@@ -8348,9 +8050,9 @@ var init_skill_manager = __esm({
|
|
|
8348
8050
|
addSkill(skill) {
|
|
8349
8051
|
const id = skill.id || randomUUID();
|
|
8350
8052
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8351
|
-
if (!
|
|
8352
|
-
const mdPath =
|
|
8353
|
-
if (
|
|
8053
|
+
if (!existsSync14(this.skillsDir)) return "";
|
|
8054
|
+
const mdPath = join12(this.skillsDir, `${name}.md`);
|
|
8055
|
+
if (existsSync14(mdPath)) return "";
|
|
8354
8056
|
try {
|
|
8355
8057
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8356
8058
|
} catch {
|
|
@@ -8399,12 +8101,12 @@ var init_skill_manager = __esm({
|
|
|
8399
8101
|
const out = [];
|
|
8400
8102
|
for (const sk of this.skills) {
|
|
8401
8103
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8402
|
-
const mdPath =
|
|
8403
|
-
if (!
|
|
8104
|
+
const mdPath = join12(this.skillsDir, `${sk.name}.md`);
|
|
8105
|
+
if (!existsSync14(mdPath)) continue;
|
|
8404
8106
|
out.push({
|
|
8405
8107
|
name: sk.name,
|
|
8406
8108
|
category: sk.category || "",
|
|
8407
|
-
content:
|
|
8109
|
+
content: readFileSync14(mdPath, "utf-8"),
|
|
8408
8110
|
keywords: sk.keywords || []
|
|
8409
8111
|
});
|
|
8410
8112
|
}
|
|
@@ -8516,7 +8218,7 @@ __export(contributor_exports, {
|
|
|
8516
8218
|
});
|
|
8517
8219
|
import { mkdirSync as mkdirSync5, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8518
8220
|
import { tmpdir } from "os";
|
|
8519
|
-
import { join as
|
|
8221
|
+
import { join as join13 } from "path";
|
|
8520
8222
|
import { spawnSync } from "child_process";
|
|
8521
8223
|
import { dirname as dirname9 } from "path";
|
|
8522
8224
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
@@ -8537,32 +8239,32 @@ function collectSkills(skillMgr) {
|
|
|
8537
8239
|
}
|
|
8538
8240
|
function writeExportFiles(outDir, rules, skills) {
|
|
8539
8241
|
const written = [];
|
|
8540
|
-
const
|
|
8541
|
-
mkdirSync5(
|
|
8242
|
+
const rulesDir2 = join13(outDir, "rules");
|
|
8243
|
+
mkdirSync5(rulesDir2, { recursive: true });
|
|
8542
8244
|
const byLayer = /* @__PURE__ */ new Map();
|
|
8543
8245
|
for (const r of rules) {
|
|
8544
8246
|
if (!byLayer.has(r.layer)) byLayer.set(r.layer, []);
|
|
8545
8247
|
byLayer.get(r.layer).push(r);
|
|
8546
8248
|
}
|
|
8547
8249
|
for (const layer of [...byLayer.keys()].sort((a, b) => a - b)) {
|
|
8548
|
-
const path6 =
|
|
8250
|
+
const path6 = join13(rulesDir2, `l${layer}_brain_rules.json`);
|
|
8549
8251
|
writeFileSync5(path6, JSON.stringify(byLayer.get(layer), null, 2), "utf-8");
|
|
8550
8252
|
written.push(path6);
|
|
8551
8253
|
}
|
|
8552
8254
|
if (rules.length === 0) {
|
|
8553
|
-
const path6 =
|
|
8255
|
+
const path6 = join13(rulesDir2, "empty.txt");
|
|
8554
8256
|
writeFileSync5(path6, "(no brain rules to contribute)", "utf-8");
|
|
8555
8257
|
written.push(path6);
|
|
8556
8258
|
}
|
|
8557
|
-
const skillsDir =
|
|
8259
|
+
const skillsDir = join13(outDir, "skills");
|
|
8558
8260
|
mkdirSync5(skillsDir, { recursive: true });
|
|
8559
8261
|
for (const sk of skills) {
|
|
8560
|
-
const path6 =
|
|
8262
|
+
const path6 = join13(skillsDir, `${sk.name}.md`);
|
|
8561
8263
|
writeFileSync5(path6, sk.content || "", "utf-8");
|
|
8562
8264
|
written.push(path6);
|
|
8563
8265
|
}
|
|
8564
8266
|
if (skills.length === 0) {
|
|
8565
|
-
const path6 =
|
|
8267
|
+
const path6 = join13(skillsDir, "empty.txt");
|
|
8566
8268
|
writeFileSync5(path6, "(no brain skills to contribute)", "utf-8");
|
|
8567
8269
|
written.push(path6);
|
|
8568
8270
|
}
|
|
@@ -8692,7 +8394,7 @@ async function contribute(opts = {}) {
|
|
|
8692
8394
|
console.log(`[Brain] push \u76EE\u6807: ${pushTarget}`);
|
|
8693
8395
|
console.log(`[Brain] MR \u6307\u5411: ${DEFAULT_TARGET_REPO}`);
|
|
8694
8396
|
const store = new BrainRuleStore();
|
|
8695
|
-
const skillsDir = resolveAssetDir(__dirname8, "skills") ??
|
|
8397
|
+
const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join13(__dirname8, "..", "..", "agent_moss", "skills");
|
|
8696
8398
|
const skillMgr = new BrainSkillManager(skillsDir);
|
|
8697
8399
|
let rules;
|
|
8698
8400
|
let skills;
|
|
@@ -8707,9 +8409,9 @@ async function contribute(opts = {}) {
|
|
|
8707
8409
|
return 0;
|
|
8708
8410
|
}
|
|
8709
8411
|
console.log(`[Brain] \u5BFC\u51FA: ${rules.length} \u6761\u89C4\u5219, ${skills.length} \u4E2A Skill`);
|
|
8710
|
-
const workdir = mkdtempSync(
|
|
8412
|
+
const workdir = mkdtempSync(join13(tmpdir(), "agentmoss-contrib-"));
|
|
8711
8413
|
try {
|
|
8712
|
-
writeExportFiles(
|
|
8414
|
+
writeExportFiles(join13(workdir, "agent_moss", "brain_contrib"), rules, skills);
|
|
8713
8415
|
console.log(`[Brain] \u63A2\u6D4B ${pushTarget} \u8BBF\u95EE\u6743\u9650 ...`);
|
|
8714
8416
|
if (!probeAccess(pushTarget)) {
|
|
8715
8417
|
const username = getCurrentUsername();
|
|
@@ -8957,9 +8659,10 @@ var init_decode = __esm({
|
|
|
8957
8659
|
"src/engine/formal-engine/decode.ts"() {
|
|
8958
8660
|
"use strict";
|
|
8959
8661
|
init_esm_shims();
|
|
8662
|
+
init_rules_loader();
|
|
8960
8663
|
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8961
8664
|
QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8962
|
-
OUTPUT_CMDS =
|
|
8665
|
+
OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8963
8666
|
STAGE_PRIORITY = ["EXFIL", "PRIVESC", "PERSIST", "SCAN", "LISTEN"];
|
|
8964
8667
|
PURPOSE_EXEC_RE = /subprocess\.\w+\s*\(|os\.system\s*\(|shell\s*=\s*True|\beval\s*\(|\bexec\s*\(|os\.popen\s*\(|\/exec\b/i;
|
|
8965
8668
|
LISTEN_LEAD_RE = /http\.server|flask\s+run|nohup\b|nc\s+-l|socat\s+TCP-LISTEN/i;
|
|
@@ -9677,7 +9380,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9677
9380
|
}
|
|
9678
9381
|
return violations;
|
|
9679
9382
|
}
|
|
9680
|
-
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;
|
|
9681
9384
|
var init_properties = __esm({
|
|
9682
9385
|
"src/engine/formal-engine/properties.ts"() {
|
|
9683
9386
|
"use strict";
|
|
@@ -9694,11 +9397,12 @@ var init_properties = __esm({
|
|
|
9694
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;
|
|
9695
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;
|
|
9696
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;
|
|
9697
|
-
|
|
9698
|
-
|
|
9400
|
+
FORMAL_RULES = loadFormalRules();
|
|
9401
|
+
PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
9402
|
+
SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
9699
9403
|
TOOL_REGISTRY = null;
|
|
9700
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;
|
|
9701
|
-
STRONG_COMPUTE_TYPES =
|
|
9405
|
+
STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
9702
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;
|
|
9703
9407
|
ALL_PROPERTIES = [
|
|
9704
9408
|
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
@@ -10222,10 +9926,10 @@ var init_stateful = __esm({
|
|
|
10222
9926
|
import * as fs4 from "fs";
|
|
10223
9927
|
import * as os2 from "os";
|
|
10224
9928
|
import * as path4 from "path";
|
|
10225
|
-
import { createHash } from "crypto";
|
|
9929
|
+
import { createHash as createHash2 } from "crypto";
|
|
10226
9930
|
function defaultStateDbPath() {
|
|
10227
9931
|
const dir = process.env.AGENT_MOSS_STATE_DIR || path4.join(os2.homedir(), ".agentmoss");
|
|
10228
|
-
const tag =
|
|
9932
|
+
const tag = createHash2("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
|
|
10229
9933
|
return path4.join(dir, `stateful_chains_${tag}.json`);
|
|
10230
9934
|
}
|
|
10231
9935
|
var ChainStateStore;
|
|
@@ -10485,7 +10189,7 @@ async function runAnalysis(input) {
|
|
|
10485
10189
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
10486
10190
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
10487
10191
|
const lowerDetail = rawDetail.toLowerCase();
|
|
10488
|
-
if (
|
|
10192
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
10489
10193
|
return {
|
|
10490
10194
|
allowed: true,
|
|
10491
10195
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -10528,7 +10232,7 @@ async function runAnalysis(input) {
|
|
|
10528
10232
|
violatedLayers: []
|
|
10529
10233
|
};
|
|
10530
10234
|
}
|
|
10531
|
-
let skipLLM =
|
|
10235
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
10532
10236
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
10533
10237
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
10534
10238
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|
|
@@ -10760,7 +10464,7 @@ async function runAnalysis(input) {
|
|
|
10760
10464
|
}
|
|
10761
10465
|
return llmResult;
|
|
10762
10466
|
}
|
|
10763
|
-
var
|
|
10467
|
+
var TRUSTED_SKILLS_BASE, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
10764
10468
|
var init_coordinator = __esm({
|
|
10765
10469
|
"src/engine/coordinator.ts"() {
|
|
10766
10470
|
"use strict";
|
|
@@ -10783,25 +10487,7 @@ var init_coordinator = __esm({
|
|
|
10783
10487
|
init_asset_dir();
|
|
10784
10488
|
init_runtime_config_bridge();
|
|
10785
10489
|
init_formal_engine();
|
|
10786
|
-
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
10787
|
-
"ask_user_question",
|
|
10788
|
-
"glob",
|
|
10789
|
-
"list_dir",
|
|
10790
|
-
"ls",
|
|
10791
|
-
"count_text_length",
|
|
10792
|
-
"filemgr-globfiles"
|
|
10793
|
-
]);
|
|
10794
10490
|
TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
10795
|
-
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
10796
|
-
"read",
|
|
10797
|
-
"file_read",
|
|
10798
|
-
"read_file",
|
|
10799
|
-
"head",
|
|
10800
|
-
"tail",
|
|
10801
|
-
"grep",
|
|
10802
|
-
"filemgr-readfile",
|
|
10803
|
-
"filemgr-grepfiles"
|
|
10804
|
-
]);
|
|
10805
10491
|
_brainStore = null;
|
|
10806
10492
|
_brainExtractor = null;
|
|
10807
10493
|
_brainSkillManager = null;
|
|
@@ -10814,10 +10500,10 @@ var init_coordinator = __esm({
|
|
|
10814
10500
|
});
|
|
10815
10501
|
|
|
10816
10502
|
// src/infra/policy-cache.ts
|
|
10817
|
-
import { createHash as
|
|
10818
|
-
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";
|
|
10819
10505
|
import { tmpdir as tmpdir2 } from "os";
|
|
10820
|
-
import { join as
|
|
10506
|
+
import { join as join15 } from "path";
|
|
10821
10507
|
function cacheDir() {
|
|
10822
10508
|
return cacheDirOverride ?? CACHE_DIR;
|
|
10823
10509
|
}
|
|
@@ -10831,7 +10517,7 @@ var init_policy_cache = __esm({
|
|
|
10831
10517
|
"use strict";
|
|
10832
10518
|
init_esm_shims();
|
|
10833
10519
|
init_config();
|
|
10834
|
-
CACHE_DIR =
|
|
10520
|
+
CACHE_DIR = join15(tmpdir2(), "agent_moss_policy_cache");
|
|
10835
10521
|
cacheDirOverride = null;
|
|
10836
10522
|
PolicyCache = class {
|
|
10837
10523
|
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
@@ -10844,9 +10530,9 @@ var init_policy_cache = __esm({
|
|
|
10844
10530
|
return `${sessionId}::${promptSession}`;
|
|
10845
10531
|
}
|
|
10846
10532
|
cacheFilePath(sessionId, promptSession) {
|
|
10847
|
-
const promptHash =
|
|
10533
|
+
const promptHash = createHash3("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10848
10534
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10849
|
-
return
|
|
10535
|
+
return join15(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10850
10536
|
}
|
|
10851
10537
|
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10852
10538
|
get(sessionId, promptSession) {
|
|
@@ -10859,9 +10545,9 @@ var init_policy_cache = __esm({
|
|
|
10859
10545
|
return hit;
|
|
10860
10546
|
}
|
|
10861
10547
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10862
|
-
if (
|
|
10548
|
+
if (existsSync16(file)) {
|
|
10863
10549
|
try {
|
|
10864
|
-
const policy =
|
|
10550
|
+
const policy = readFileSync16(file, "utf-8");
|
|
10865
10551
|
this.remember(key, policy);
|
|
10866
10552
|
return policy;
|
|
10867
10553
|
} catch {
|
|
@@ -10875,7 +10561,7 @@ var init_policy_cache = __esm({
|
|
|
10875
10561
|
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10876
10562
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10877
10563
|
try {
|
|
10878
|
-
mkdirSync7(
|
|
10564
|
+
mkdirSync7(join15(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10879
10565
|
writeFileSync7(file, policy, "utf-8");
|
|
10880
10566
|
} catch {
|
|
10881
10567
|
}
|
|
@@ -10901,12 +10587,12 @@ var init_policy_cache = __esm({
|
|
|
10901
10587
|
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10902
10588
|
}
|
|
10903
10589
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10904
|
-
const dir =
|
|
10590
|
+
const dir = join15(cacheDir(), safeSessionId);
|
|
10905
10591
|
try {
|
|
10906
|
-
if (
|
|
10592
|
+
if (existsSync16(dir)) {
|
|
10907
10593
|
for (const f of readdirSync5(dir)) {
|
|
10908
10594
|
try {
|
|
10909
|
-
unlinkSync3(
|
|
10595
|
+
unlinkSync3(join15(dir, f));
|
|
10910
10596
|
} catch {
|
|
10911
10597
|
}
|
|
10912
10598
|
}
|
|
@@ -10918,11 +10604,11 @@ var init_policy_cache = __esm({
|
|
|
10918
10604
|
clear() {
|
|
10919
10605
|
this.memory.clear();
|
|
10920
10606
|
try {
|
|
10921
|
-
if (
|
|
10607
|
+
if (existsSync16(cacheDir())) {
|
|
10922
10608
|
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10923
|
-
const dir =
|
|
10609
|
+
const dir = join15(cacheDir(), sessionDir);
|
|
10924
10610
|
try {
|
|
10925
|
-
for (const f of readdirSync5(dir)) unlinkSync3(
|
|
10611
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join15(dir, f));
|
|
10926
10612
|
rmdirSync(dir);
|
|
10927
10613
|
} catch {
|
|
10928
10614
|
}
|
|
@@ -11018,9 +10704,9 @@ __export(analyzer_exports, {
|
|
|
11018
10704
|
savePolicyToFile: () => savePolicyToFile,
|
|
11019
10705
|
validateToml: () => validateToml
|
|
11020
10706
|
});
|
|
11021
|
-
import { existsSync as
|
|
10707
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
|
|
11022
10708
|
import { tmpdir as tmpdir3 } from "os";
|
|
11023
|
-
import { join as
|
|
10709
|
+
import { join as join16, resolve as resolve10 } from "path";
|
|
11024
10710
|
import { parse as parseToml } from "smol-toml";
|
|
11025
10711
|
function _setPolicyOutputDirForTest(dir) {
|
|
11026
10712
|
policyOutputDirOverride = dir;
|
|
@@ -11034,7 +10720,7 @@ function savePolicyToFile(sessionId, policyToml) {
|
|
|
11034
10720
|
const outDir = policyOutputDir();
|
|
11035
10721
|
mkdirSync8(outDir, { recursive: true });
|
|
11036
10722
|
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
11037
|
-
const filepath =
|
|
10723
|
+
const filepath = join16(outDir, `${safeName}.toml`);
|
|
11038
10724
|
writeFileSync8(filepath, policyToml, "utf-8");
|
|
11039
10725
|
return filepath;
|
|
11040
10726
|
} catch (err) {
|
|
@@ -11073,11 +10759,11 @@ function loadPolicyMappingDoc() {
|
|
|
11073
10759
|
const dir = resolveAssetDir(__dirname, "templates");
|
|
11074
10760
|
if (dir) {
|
|
11075
10761
|
const p = resolve10(dir, "policy_mapping.md");
|
|
11076
|
-
if (
|
|
10762
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
11077
10763
|
}
|
|
11078
10764
|
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
11079
10765
|
const p = resolve10(cand, "policy_mapping.md");
|
|
11080
|
-
if (
|
|
10766
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
11081
10767
|
}
|
|
11082
10768
|
return "";
|
|
11083
10769
|
}
|
|
@@ -11247,7 +10933,7 @@ var init_analyzer = __esm({
|
|
|
11247
10933
|
init_asset_dir();
|
|
11248
10934
|
init_token_stats();
|
|
11249
10935
|
init_logging();
|
|
11250
|
-
POLICY_OUTPUT_DIR =
|
|
10936
|
+
POLICY_OUTPUT_DIR = join16(tmpdir3(), "agent_moss");
|
|
11251
10937
|
policyOutputDirOverride = null;
|
|
11252
10938
|
DEFAULT_READONLY_POLICY = `landlock_optional = false
|
|
11253
10939
|
mount_isolation_fallback = false
|
|
@@ -11470,14 +11156,16 @@ function mineDangerousPatterns(sessions, minSup = 2, maxLen = 4) {
|
|
|
11470
11156
|
out.sort((a, b) => b.support - a.support || b.pattern.length - a.pattern.length);
|
|
11471
11157
|
return out;
|
|
11472
11158
|
}
|
|
11473
|
-
var DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11159
|
+
var _PM, DANGER_STAGES, SENSITIVE_PAIRS;
|
|
11474
11160
|
var init_pattern_miner = __esm({
|
|
11475
11161
|
"src/engine/formal-engine/pattern_miner.ts"() {
|
|
11476
11162
|
"use strict";
|
|
11477
11163
|
init_esm_shims();
|
|
11164
|
+
init_rules_loader();
|
|
11478
11165
|
init_builder();
|
|
11479
|
-
|
|
11480
|
-
|
|
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("|")));
|
|
11481
11169
|
}
|
|
11482
11170
|
});
|
|
11483
11171
|
|
|
@@ -11657,7 +11345,7 @@ var init_observable = __esm({
|
|
|
11657
11345
|
});
|
|
11658
11346
|
|
|
11659
11347
|
// src/routes.ts
|
|
11660
|
-
import { join as
|
|
11348
|
+
import { join as join18, dirname as dirname11 } from "path";
|
|
11661
11349
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
11662
11350
|
async function healthRoute(c) {
|
|
11663
11351
|
return c.json(HealthResponseSchema.parse({
|
|
@@ -11906,7 +11594,7 @@ function getBrainRuleStore() {
|
|
|
11906
11594
|
return new BrainRuleStore();
|
|
11907
11595
|
}
|
|
11908
11596
|
function getBrainSkillManager() {
|
|
11909
|
-
const dir = resolveAssetDir(__dirname9, "skills") ??
|
|
11597
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join18(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11910
11598
|
return new BrainSkillManager(dir);
|
|
11911
11599
|
}
|
|
11912
11600
|
async function brainRulesRoute(c) {
|
|
@@ -12019,8 +11707,8 @@ var init_routes = __esm({
|
|
|
12019
11707
|
|
|
12020
11708
|
// src/console/router.ts
|
|
12021
11709
|
import { Hono } from "hono";
|
|
12022
|
-
import { readFileSync as
|
|
12023
|
-
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";
|
|
12024
11712
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
12025
11713
|
function getConsoleToken() {
|
|
12026
11714
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -12058,9 +11746,9 @@ function createConsoleRouter() {
|
|
|
12058
11746
|
console2.use("/api/*", authMiddleware());
|
|
12059
11747
|
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
12060
11748
|
const serveIndex = (c) => {
|
|
12061
|
-
const indexFile =
|
|
12062
|
-
if (
|
|
12063
|
-
return c.html(
|
|
11749
|
+
const indexFile = join19(STATIC_DIR, "index.html");
|
|
11750
|
+
if (existsSync19(indexFile)) {
|
|
11751
|
+
return c.html(readFileSync19(indexFile, "utf-8"));
|
|
12064
11752
|
}
|
|
12065
11753
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
12066
11754
|
};
|
|
@@ -12255,15 +11943,15 @@ function createConsoleRouter() {
|
|
|
12255
11943
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
12256
11944
|
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
12257
11945
|
}
|
|
12258
|
-
const userPath =
|
|
11946
|
+
const userPath = join19(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
12259
11947
|
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
12260
11948
|
const candidates = [
|
|
12261
11949
|
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
12262
|
-
builtinDir ?
|
|
11950
|
+
builtinDir ? join19(builtinDir, `${skillId}.md`) : ""
|
|
12263
11951
|
].filter(Boolean);
|
|
12264
11952
|
for (const p of candidates) {
|
|
12265
|
-
if (
|
|
12266
|
-
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") });
|
|
12267
11955
|
}
|
|
12268
11956
|
}
|
|
12269
11957
|
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
@@ -12360,7 +12048,7 @@ function createConsoleRouter() {
|
|
|
12360
12048
|
const defaults = generateSourceDefaults();
|
|
12361
12049
|
try {
|
|
12362
12050
|
const p = getRuntimeConfigPath();
|
|
12363
|
-
if (
|
|
12051
|
+
if (existsSync19(p)) unlinkSync4(p);
|
|
12364
12052
|
} catch {
|
|
12365
12053
|
}
|
|
12366
12054
|
return c.json(withL22View(defaults));
|
|
@@ -12397,7 +12085,7 @@ var init_router = __esm({
|
|
|
12397
12085
|
init_asset_dir();
|
|
12398
12086
|
init_rules_mode();
|
|
12399
12087
|
__dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
12400
|
-
STATIC_DIR =
|
|
12088
|
+
STATIC_DIR = join19(__dirname10, "static");
|
|
12401
12089
|
CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
12402
12090
|
}
|
|
12403
12091
|
});
|
|
@@ -12424,7 +12112,7 @@ import { Hono as Hono2 } from "hono";
|
|
|
12424
12112
|
import { cors } from "hono/cors";
|
|
12425
12113
|
import { logger } from "hono/logger";
|
|
12426
12114
|
import net from "net";
|
|
12427
|
-
import { existsSync as
|
|
12115
|
+
import { existsSync as existsSync20 } from "fs";
|
|
12428
12116
|
function logDeploymentSelfCheck() {
|
|
12429
12117
|
try {
|
|
12430
12118
|
const mode = getRulesMode();
|
|
@@ -12451,7 +12139,7 @@ function logDeploymentSelfCheck() {
|
|
|
12451
12139
|
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
12452
12140
|
if (!builtinOnly) {
|
|
12453
12141
|
try {
|
|
12454
|
-
const dirs = rulesDirs().filter((d) =>
|
|
12142
|
+
const dirs = rulesDirs().filter((d) => existsSync20(d));
|
|
12455
12143
|
if (dirs.length > 0) {
|
|
12456
12144
|
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12457
12145
|
}
|
|
@@ -12490,6 +12178,7 @@ function logDeploymentSelfCheck() {
|
|
|
12490
12178
|
}
|
|
12491
12179
|
}
|
|
12492
12180
|
function createApp() {
|
|
12181
|
+
enforceRulesContract();
|
|
12493
12182
|
const app = new Hono2();
|
|
12494
12183
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
12495
12184
|
app.use("*", logger());
|
|
@@ -12578,7 +12267,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12578
12267
|
server = null;
|
|
12579
12268
|
},
|
|
12580
12269
|
serve: async () => {
|
|
12581
|
-
const { mkdirSync: mkdirSync10, existsSync:
|
|
12270
|
+
const { mkdirSync: mkdirSync10, existsSync: existsSync22, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12582
12271
|
const { dirname: dirname14 } = await import("path");
|
|
12583
12272
|
const dir = dirname14(socketPath);
|
|
12584
12273
|
if (dir) {
|
|
@@ -12587,7 +12276,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12587
12276
|
} catch {
|
|
12588
12277
|
}
|
|
12589
12278
|
}
|
|
12590
|
-
if (
|
|
12279
|
+
if (existsSync22(socketPath)) {
|
|
12591
12280
|
const { connect } = await import("net");
|
|
12592
12281
|
const inUse = await new Promise((resolve11) => {
|
|
12593
12282
|
const probe = connect(socketPath);
|
|
@@ -12629,6 +12318,7 @@ var init_server = __esm({
|
|
|
12629
12318
|
"src/server.ts"() {
|
|
12630
12319
|
"use strict";
|
|
12631
12320
|
init_esm_shims();
|
|
12321
|
+
init_rules_contract();
|
|
12632
12322
|
init_routes();
|
|
12633
12323
|
init_console();
|
|
12634
12324
|
init_runtime_config();
|
|
@@ -12652,26 +12342,26 @@ __export(rules_tools_exports, {
|
|
|
12652
12342
|
rulesStatusText: () => rulesStatusText,
|
|
12653
12343
|
templateSourceDir: () => templateSourceDir
|
|
12654
12344
|
});
|
|
12655
|
-
import { existsSync as
|
|
12656
|
-
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";
|
|
12657
12347
|
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
12658
12348
|
function initRulesDir(targetDir, opts = {}) {
|
|
12659
12349
|
mkdirSync9(targetDir, { recursive: true });
|
|
12660
12350
|
const written = [];
|
|
12661
|
-
const extraPath =
|
|
12662
|
-
if (opts.force || !
|
|
12351
|
+
const extraPath = join20(targetDir, EXTRA_FILENAME);
|
|
12352
|
+
if (opts.force || !existsSync21(extraPath)) {
|
|
12663
12353
|
writeFileSync9(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
|
|
12664
12354
|
written.push(extraPath);
|
|
12665
12355
|
}
|
|
12666
|
-
const samplePath =
|
|
12667
|
-
if (opts.force || !
|
|
12356
|
+
const samplePath = join20(targetDir, OVERRIDE_FILENAME);
|
|
12357
|
+
if (opts.force || !existsSync21(samplePath)) {
|
|
12668
12358
|
let payload = OVERRIDE_TEMPLATE;
|
|
12669
12359
|
if (opts.withBuiltin) {
|
|
12670
12360
|
let data = {};
|
|
12671
12361
|
const builtin = resolveRulesFile("l1_rules.json");
|
|
12672
|
-
if (builtin &&
|
|
12362
|
+
if (builtin && existsSync21(builtin)) {
|
|
12673
12363
|
try {
|
|
12674
|
-
data = JSON.parse(
|
|
12364
|
+
data = JSON.parse(readFileSync20(builtin, "utf-8")) || {};
|
|
12675
12365
|
} catch {
|
|
12676
12366
|
data = {};
|
|
12677
12367
|
}
|
|
@@ -12682,17 +12372,17 @@ function initRulesDir(targetDir, opts = {}) {
|
|
|
12682
12372
|
writeFileSync9(samplePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
12683
12373
|
written.push(samplePath);
|
|
12684
12374
|
}
|
|
12685
|
-
const readmeSrc =
|
|
12686
|
-
const readmeDst =
|
|
12687
|
-
if (
|
|
12688
|
-
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");
|
|
12689
12379
|
written.push(readmeDst);
|
|
12690
12380
|
}
|
|
12691
12381
|
return written;
|
|
12692
12382
|
}
|
|
12693
12383
|
function templateSourceDir() {
|
|
12694
|
-
const cands = [
|
|
12695
|
-
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;
|
|
12696
12386
|
return cands[0];
|
|
12697
12387
|
}
|
|
12698
12388
|
function templateFiles(dirs) {
|
|
@@ -12700,7 +12390,7 @@ function templateFiles(dirs) {
|
|
|
12700
12390
|
for (const d of dirs) {
|
|
12701
12391
|
try {
|
|
12702
12392
|
for (const name of readdirSync7(d)) {
|
|
12703
|
-
if (name.endsWith(".json.\u793A\u4F8B")) out.push(
|
|
12393
|
+
if (name.endsWith(".json.\u793A\u4F8B")) out.push(join20(d, name));
|
|
12704
12394
|
}
|
|
12705
12395
|
} catch {
|
|
12706
12396
|
}
|
|
@@ -12709,7 +12399,7 @@ function templateFiles(dirs) {
|
|
|
12709
12399
|
}
|
|
12710
12400
|
function rulesStatusText() {
|
|
12711
12401
|
const lines = [];
|
|
12712
|
-
const existing = rulesDirs().filter((d) =>
|
|
12402
|
+
const existing = rulesDirs().filter((d) => existsSync21(d));
|
|
12713
12403
|
lines.push("\u89C4\u5219\u76EE\u5F55: " + (existing.length > 0 ? existing.join(", ") : "\uFF08\u672A\u8BBE\u7F6E / \u4E0D\u5B58\u5728\uFF09"));
|
|
12714
12404
|
if (existing.length > 0) {
|
|
12715
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`);
|
|
@@ -12861,6 +12551,17 @@ async function analyzeRawCommand(command, useJson = false) {
|
|
|
12861
12551
|
async function main() {
|
|
12862
12552
|
const args = process.argv.slice(2);
|
|
12863
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
|
+
}
|
|
12864
12565
|
if (command === "--version" || command === "-V") {
|
|
12865
12566
|
console.log(`agent-moss ${version}`);
|
|
12866
12567
|
process.exit(0);
|