@kenhkl/agent-moss 0.16.12 → 0.16.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/cli.js +1065 -1216
- package/dist/data/agent_moss.service +4 -0
- package/dist/engine/coordinator.js +514 -872
- package/dist/rules/formal_rules.json +61 -0
- package/dist/rules/heuristic_rules.json +40 -0
- package/dist/rules/inline_rules.json +151 -0
- package/dist/rules/l1_rules.json +115 -49
- package/dist/rules/logic_rules.json +73 -0
- package/dist/rules/manifest.json +6 -0
- package/dist/rules/platform_traits.json +389 -0
- package/dist/rules/script_rules.json +311 -0
- package/dist/server.js +877 -1050
- package/dist/static/index.html +26 -1
- package/package.json +1 -1
|
@@ -25,6 +25,10 @@ var init_esm_shims = __esm({
|
|
|
25
25
|
import { join } from "path";
|
|
26
26
|
import { homedir } from "os";
|
|
27
27
|
import { readFileSync } from "fs";
|
|
28
|
+
function getRulesRoot() {
|
|
29
|
+
const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
|
|
30
|
+
return raw ? raw : null;
|
|
31
|
+
}
|
|
28
32
|
function isRoot() {
|
|
29
33
|
const getuid = process.getuid;
|
|
30
34
|
return typeof getuid === "function" && getuid() === 0;
|
|
@@ -94,7 +98,7 @@ function getTokenStatsPath() {
|
|
|
94
98
|
}
|
|
95
99
|
return join(getDataRoot(), "agent_moss_token_stats.json");
|
|
96
100
|
}
|
|
97
|
-
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT;
|
|
101
|
+
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT;
|
|
98
102
|
var init_paths = __esm({
|
|
99
103
|
"src/infra/paths.ts"() {
|
|
100
104
|
"use strict";
|
|
@@ -102,6 +106,7 @@ var init_paths = __esm({
|
|
|
102
106
|
ENV_DATA_ROOT = "AGENT_MOSS_DATA_DIR";
|
|
103
107
|
ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
|
|
104
108
|
DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
|
|
109
|
+
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
105
110
|
}
|
|
106
111
|
});
|
|
107
112
|
|
|
@@ -149,235 +154,49 @@ var init_shell_split = __esm({
|
|
|
149
154
|
}
|
|
150
155
|
});
|
|
151
156
|
|
|
152
|
-
// src/
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
// src/infra/rules-mode.ts
|
|
158
|
+
function truthy(name) {
|
|
159
|
+
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
160
|
+
}
|
|
161
|
+
function getRulesMode() {
|
|
162
|
+
if (cached !== null) return cached;
|
|
163
|
+
const raw = (process.env[ENV_RULES_MODE] || "").trim().toLowerCase();
|
|
164
|
+
if (raw) {
|
|
165
|
+
if (VALID_MODES.has(raw)) {
|
|
166
|
+
cached = raw;
|
|
167
|
+
} else {
|
|
168
|
+
console.error(
|
|
169
|
+
`[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`
|
|
170
|
+
);
|
|
171
|
+
cached = "builtin";
|
|
163
172
|
}
|
|
173
|
+
return cached;
|
|
164
174
|
}
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
175
|
+
if (truthy(ENV_RULES_BUILTIN_ONLY)) cached = "builtin";
|
|
176
|
+
else if (truthy(ENV_DISABLE_RULE_HOT_RELOAD)) cached = "cold";
|
|
177
|
+
else cached = "hot";
|
|
178
|
+
return cached;
|
|
169
179
|
}
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
let m;
|
|
173
|
-
INLINE_CODE_RE.lastIndex = 0;
|
|
174
|
-
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
175
|
-
const code = m[1] ?? m[2];
|
|
176
|
-
if (code) parts.push(code);
|
|
177
|
-
}
|
|
178
|
-
ECHO_STRING_RE.lastIndex = 0;
|
|
179
|
-
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
180
|
-
const code = m[1] ?? m[2];
|
|
181
|
-
if (code) parts.push(code);
|
|
182
|
-
}
|
|
183
|
-
return parts.join("\n");
|
|
180
|
+
function isHotReloadEnabled() {
|
|
181
|
+
return getRulesMode() === "hot";
|
|
184
182
|
}
|
|
185
|
-
function
|
|
186
|
-
return
|
|
187
|
-
p.lastIndex = 0;
|
|
188
|
-
return p.test(command);
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
function hasDirTraversal(code) {
|
|
192
|
-
const lower = code.toLowerCase();
|
|
193
|
-
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
183
|
+
function isRuntimeConfigDisabled() {
|
|
184
|
+
return getRulesMode() !== "hot";
|
|
194
185
|
}
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
186
|
+
function isBuiltinOnly() {
|
|
187
|
+
return getRulesMode() === "builtin";
|
|
198
188
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
203
|
-
}
|
|
204
|
-
if (hasWriteExecChain(command)) {
|
|
205
|
-
return {
|
|
206
|
-
actionable: true,
|
|
207
|
-
allowed: false,
|
|
208
|
-
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",
|
|
209
|
-
riskLevel: "critical",
|
|
210
|
-
riskType: "script_execution"
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
214
|
-
if (redirMatch) {
|
|
215
|
-
return {
|
|
216
|
-
actionable: true,
|
|
217
|
-
allowed: false,
|
|
218
|
-
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
219
|
-
riskLevel: "critical",
|
|
220
|
-
riskType: "file_access"
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
224
|
-
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
225
|
-
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
226
|
-
}
|
|
227
|
-
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
228
|
-
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
229
|
-
return {
|
|
230
|
-
actionable: true,
|
|
231
|
-
allowed: false,
|
|
232
|
-
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",
|
|
233
|
-
riskLevel: "critical",
|
|
234
|
-
riskType: "file_access"
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
return {
|
|
238
|
-
actionable: true,
|
|
239
|
-
allowed: true,
|
|
240
|
-
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
241
|
-
riskLevel: "low",
|
|
242
|
-
riskType: "file_access"
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
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;
|
|
246
|
-
var init_inline_analyzer = __esm({
|
|
247
|
-
"src/engine/inline-analyzer.ts"() {
|
|
189
|
+
var ENV_RULES_MODE, ENV_RULES_BUILTIN_ONLY, ENV_DISABLE_RULE_HOT_RELOAD, TRUTHY, VALID_MODES, cached;
|
|
190
|
+
var init_rules_mode = __esm({
|
|
191
|
+
"src/infra/rules-mode.ts"() {
|
|
248
192
|
"use strict";
|
|
249
193
|
init_esm_shims();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
"open(",
|
|
257
|
-
"read(",
|
|
258
|
-
"write(",
|
|
259
|
-
"fopen(",
|
|
260
|
-
"system(",
|
|
261
|
-
"exec(",
|
|
262
|
-
"eval(",
|
|
263
|
-
"popen(",
|
|
264
|
-
"load(",
|
|
265
|
-
"dump(",
|
|
266
|
-
// 文件读写(组合形态)
|
|
267
|
-
"readline",
|
|
268
|
-
"readlines",
|
|
269
|
-
"writelines",
|
|
270
|
-
"getpass",
|
|
271
|
-
"pickle.load",
|
|
272
|
-
"yaml.load",
|
|
273
|
-
"json.load",
|
|
274
|
-
"json.read",
|
|
275
|
-
"configparser",
|
|
276
|
-
"fread",
|
|
277
|
-
"fwrite",
|
|
278
|
-
"fgets",
|
|
279
|
-
// 子进程执行(组合形态)
|
|
280
|
-
"os.system",
|
|
281
|
-
"os.popen",
|
|
282
|
-
"subprocess",
|
|
283
|
-
"os.exec",
|
|
284
|
-
"os.spawn",
|
|
285
|
-
"import os",
|
|
286
|
-
"import subprocess",
|
|
287
|
-
"shell=true",
|
|
288
|
-
"check_output",
|
|
289
|
-
"check_call",
|
|
290
|
-
"run(",
|
|
291
|
-
// 绕过变体检测
|
|
292
|
-
"getattr(",
|
|
293
|
-
"__builtins__",
|
|
294
|
-
"globals(",
|
|
295
|
-
"locals(",
|
|
296
|
-
// PHP
|
|
297
|
-
"file_get_contents",
|
|
298
|
-
"file_put_contents",
|
|
299
|
-
"passthru",
|
|
300
|
-
"shell_exec",
|
|
301
|
-
"proc_open",
|
|
302
|
-
// Perl/Ruby
|
|
303
|
-
"<stdin>",
|
|
304
|
-
"open my",
|
|
305
|
-
// Node/JS(2026-09 补,与 Python _READ_WRITE_KEYWORDS 同步):历史缺口 ——
|
|
306
|
-
// 识别表里一条 Node API 都没有,于是 node -e "require('fs').readFileSync('/etc/shadow')"
|
|
307
|
-
// 判为纯文本字面量 → 放行(真漏报)。
|
|
308
|
-
"readfilesync(",
|
|
309
|
-
"writefilesync(",
|
|
310
|
-
"appendfilesync(",
|
|
311
|
-
"unlinksync(",
|
|
312
|
-
"rmsync(",
|
|
313
|
-
"mkdirsync(",
|
|
314
|
-
"execsync(",
|
|
315
|
-
"spawnsync(",
|
|
316
|
-
"execfile",
|
|
317
|
-
"createreadstream",
|
|
318
|
-
"createwritestream",
|
|
319
|
-
"fs.read",
|
|
320
|
-
"fs.write",
|
|
321
|
-
"fs.promises",
|
|
322
|
-
"require('fs')",
|
|
323
|
-
'require("fs")',
|
|
324
|
-
"require('node:fs')",
|
|
325
|
-
'require("node:fs")',
|
|
326
|
-
"require('child_process')",
|
|
327
|
-
'require("child_process")',
|
|
328
|
-
"require('https')",
|
|
329
|
-
'require("https")'
|
|
330
|
-
];
|
|
331
|
-
WRITE_EXEC_CHAIN_PATTERNS = [
|
|
332
|
-
// echo/cat/printf "..." > file && bash/sh/python file
|
|
333
|
-
/(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
|
|
334
|
-
// ... | bash / | sh / | python(管道喂解释器执行)
|
|
335
|
-
/\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
|
|
336
|
-
// eval "$(cat ...)" / eval "$(echo ...)"
|
|
337
|
-
/eval\s+"\$\(/gi,
|
|
338
|
-
// xargs bash / xargs sh
|
|
339
|
-
/\|\s*xargs\s+(?:bash|sh|zsh)/gi
|
|
340
|
-
];
|
|
341
|
-
SHELL_RC_FRAGMENTS = [
|
|
342
|
-
".bashrc",
|
|
343
|
-
".zshrc",
|
|
344
|
-
".profile",
|
|
345
|
-
".bash_profile",
|
|
346
|
-
".zprofile"
|
|
347
|
-
];
|
|
348
|
-
SENSITIVE_PATH_FRAGMENTS = [
|
|
349
|
-
"/etc/shadow",
|
|
350
|
-
"/etc/gshadow",
|
|
351
|
-
"/etc/passwd",
|
|
352
|
-
"/etc/sudoers",
|
|
353
|
-
"/etc/hosts",
|
|
354
|
-
"/etc/crontab",
|
|
355
|
-
"/etc/systemd/",
|
|
356
|
-
"/etc/ssh/sshd_config",
|
|
357
|
-
"/boot/",
|
|
358
|
-
"/proc/sys/",
|
|
359
|
-
"/sys/",
|
|
360
|
-
"/dev/mem",
|
|
361
|
-
"/dev/kmsg",
|
|
362
|
-
".ssh/id_rsa",
|
|
363
|
-
".ssh/id_ed25519",
|
|
364
|
-
".ssh/authorized_keys",
|
|
365
|
-
...SHELL_RC_FRAGMENTS
|
|
366
|
-
];
|
|
367
|
-
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
368
|
-
DIR_TRAVERSAL_KEYWORDS = [
|
|
369
|
-
// Python 目录遍历
|
|
370
|
-
"os.listdir",
|
|
371
|
-
"os.walk",
|
|
372
|
-
"os.scandir",
|
|
373
|
-
"glob.glob",
|
|
374
|
-
"glob.iglob",
|
|
375
|
-
"pathlib.",
|
|
376
|
-
"iterdir",
|
|
377
|
-
"rglob",
|
|
378
|
-
"scandir(",
|
|
379
|
-
"listdir("
|
|
380
|
-
];
|
|
194
|
+
ENV_RULES_MODE = "AGENT_MOSS_RULES_MODE";
|
|
195
|
+
ENV_RULES_BUILTIN_ONLY = "AGENT_MOSS_RULES_BUILTIN_ONLY";
|
|
196
|
+
ENV_DISABLE_RULE_HOT_RELOAD = "AGENT_MOSS_DISABLE_RULE_HOT_RELOAD";
|
|
197
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
198
|
+
VALID_MODES = /* @__PURE__ */ new Set(["hot", "cold", "builtin"]);
|
|
199
|
+
cached = null;
|
|
381
200
|
}
|
|
382
201
|
});
|
|
383
202
|
|
|
@@ -407,23 +226,48 @@ function parseDirEnv(envName) {
|
|
|
407
226
|
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
408
227
|
}
|
|
409
228
|
function rulesDirs() {
|
|
229
|
+
if (isBuiltinOnly()) return [];
|
|
410
230
|
const dirs = [];
|
|
411
231
|
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
412
232
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
413
233
|
return [...new Set(dirs)];
|
|
414
234
|
}
|
|
235
|
+
function rulesDirCands() {
|
|
236
|
+
const root = getRulesRoot();
|
|
237
|
+
if (root) return [join2(root, "rules")];
|
|
238
|
+
const cands = [
|
|
239
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
240
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
241
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
242
|
+
resolve(__dirname2, "../rules"),
|
|
243
|
+
resolve(__dirname2, "rules"),
|
|
244
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
245
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
246
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
247
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
248
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
249
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
250
|
+
];
|
|
251
|
+
return cands.filter((c) => existsSync(c));
|
|
252
|
+
}
|
|
415
253
|
function findRulesFile(name) {
|
|
416
|
-
for (const dir of
|
|
254
|
+
for (const dir of rulesDirCands()) {
|
|
417
255
|
const p = resolve(dir, name);
|
|
418
256
|
if (existsSync(p)) return p;
|
|
419
257
|
}
|
|
420
258
|
return null;
|
|
421
259
|
}
|
|
422
260
|
function loadJson(name) {
|
|
261
|
+
if (!isHotReloadEnabled()) {
|
|
262
|
+
const cached2 = frozenJsonCache.get(name);
|
|
263
|
+
if (cached2 !== void 0) return cached2;
|
|
264
|
+
}
|
|
423
265
|
const p = findRulesFile(name);
|
|
424
266
|
if (!p) return {};
|
|
425
267
|
try {
|
|
426
|
-
|
|
268
|
+
const parsed = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
269
|
+
if (!isHotReloadEnabled()) frozenJsonCache.set(name, parsed);
|
|
270
|
+
return parsed;
|
|
427
271
|
} catch {
|
|
428
272
|
return {};
|
|
429
273
|
}
|
|
@@ -463,6 +307,74 @@ function loadL1() {
|
|
|
463
307
|
function loadL2() {
|
|
464
308
|
return loadJson("l2_rules.json");
|
|
465
309
|
}
|
|
310
|
+
function regexFlags(flags) {
|
|
311
|
+
let out = "";
|
|
312
|
+
for (const ch of String(flags ?? "")) if (RE_FLAG_MAP[ch]) out += RE_FLAG_MAP[ch];
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
315
|
+
function compileRuleRegex(rule, extraFlags = "") {
|
|
316
|
+
return new RegExp(String(rule.pattern), regexFlags(rule.flags) + extraFlags);
|
|
317
|
+
}
|
|
318
|
+
function resolveKeywordSet(group, keywordSets) {
|
|
319
|
+
if (typeof group === "string") return new Set(keywordSets[group.slice(1)] ?? []);
|
|
320
|
+
return new Set(Array.isArray(group) ? group : []);
|
|
321
|
+
}
|
|
322
|
+
function loadFormalRules() {
|
|
323
|
+
return loadJson("formal_rules.json");
|
|
324
|
+
}
|
|
325
|
+
function loadLogicRules() {
|
|
326
|
+
return loadJson("logic_rules.json");
|
|
327
|
+
}
|
|
328
|
+
function loadHeuristicRules() {
|
|
329
|
+
return loadJson("heuristic_rules.json");
|
|
330
|
+
}
|
|
331
|
+
function loadScriptRules() {
|
|
332
|
+
return loadJson("script_rules.json");
|
|
333
|
+
}
|
|
334
|
+
function loadInline() {
|
|
335
|
+
return loadJson("inline_rules.json");
|
|
336
|
+
}
|
|
337
|
+
function loadPlatformTraits(osName) {
|
|
338
|
+
const data = loadJson("platform_traits.json");
|
|
339
|
+
if (!data || Object.keys(data).length === 0) {
|
|
340
|
+
console.error(
|
|
341
|
+
"[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"
|
|
342
|
+
);
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
const name = resolveTraitsOs(osName);
|
|
346
|
+
const entry = data[name];
|
|
347
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
348
|
+
console.error(`[AgentMoss] platform_traits.json \u91CC\u6CA1\u6709\u53EF\u7528\u7684 ${name} \u6761\u76EE \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u4E3A\u7A7A\u3002`);
|
|
349
|
+
return {};
|
|
350
|
+
}
|
|
351
|
+
const out = {};
|
|
352
|
+
for (const f of TRAITS_FIELDS) if (f in entry) out[f] = entry[f];
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
function resolveTraitsOs(osName) {
|
|
356
|
+
const data = loadJson("platform_traits.json");
|
|
357
|
+
let name = String(osName || "").trim().toLowerCase() || TRAITS_OS_FALLBACK;
|
|
358
|
+
const seen = /* @__PURE__ */ new Set();
|
|
359
|
+
for (; ; ) {
|
|
360
|
+
if (seen.has(name)) {
|
|
361
|
+
console.error(`[AgentMoss] platform_traits.json \u7684 inherits \u6210\u73AF\uFF08${name}\uFF09\u2014\u2014 \u56DE\u843D linux\u3002`);
|
|
362
|
+
return TRAITS_OS_FALLBACK;
|
|
363
|
+
}
|
|
364
|
+
seen.add(name);
|
|
365
|
+
const entry = data[name];
|
|
366
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
367
|
+
const nxt = entry.inherits;
|
|
368
|
+
if (typeof nxt === "string" && nxt.trim().toLowerCase() !== name) {
|
|
369
|
+
name = nxt.trim().toLowerCase();
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
return name;
|
|
373
|
+
}
|
|
374
|
+
if (name === TRAITS_OS_FALLBACK) return name;
|
|
375
|
+
name = TRAITS_OS_FALLBACK;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
466
378
|
function loadL3() {
|
|
467
379
|
const data = { ...loadJson("l3_rules.json") };
|
|
468
380
|
try {
|
|
@@ -748,6 +660,7 @@ function scanRuleDirs() {
|
|
|
748
660
|
return { override, extra, loaded, ignored };
|
|
749
661
|
}
|
|
750
662
|
function rulesCached() {
|
|
663
|
+
if (!isHotReloadEnabled() && _RULES_CACHE) return _RULES_CACHE;
|
|
751
664
|
const dirs = rulesDirs();
|
|
752
665
|
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
753
666
|
const files = [];
|
|
@@ -783,40 +696,40 @@ function identicalToBuiltin(override) {
|
|
|
783
696
|
return Object.keys(override).length > 0;
|
|
784
697
|
}
|
|
785
698
|
function loadOverrideRules() {
|
|
699
|
+
if (isBuiltinOnly()) return {};
|
|
786
700
|
return rulesCached().override;
|
|
787
701
|
}
|
|
788
702
|
function loadExtraRules() {
|
|
703
|
+
if (isBuiltinOnly()) return {};
|
|
789
704
|
return rulesCached().extra;
|
|
790
705
|
}
|
|
791
|
-
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS,
|
|
706
|
+
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;
|
|
792
707
|
var init_rules_loader = __esm({
|
|
793
708
|
"src/infra/rules-loader.ts"() {
|
|
794
709
|
"use strict";
|
|
795
710
|
init_esm_shims();
|
|
796
711
|
init_paths();
|
|
712
|
+
init_rules_mode();
|
|
797
713
|
init_runtime_config_bridge();
|
|
798
714
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
799
715
|
__dirname2 = dirname(__filename2);
|
|
800
716
|
OVERRIDE_FILENAME = "override.json";
|
|
801
717
|
EXTRA_FILENAME = "extra.json";
|
|
802
718
|
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
803
|
-
|
|
804
|
-
const cands = [
|
|
805
|
-
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
806
|
-
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
807
|
-
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
808
|
-
resolve(__dirname2, "../rules"),
|
|
809
|
-
resolve(__dirname2, "rules"),
|
|
810
|
-
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
811
|
-
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
812
|
-
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
813
|
-
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
814
|
-
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
815
|
-
resolve(process.cwd(), "agent_moss/rules")
|
|
816
|
-
];
|
|
817
|
-
return cands.filter((c) => existsSync(c));
|
|
818
|
-
})();
|
|
719
|
+
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
819
720
|
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
721
|
+
RE_FLAG_MAP = { i: "i", s: "s", m: "m" };
|
|
722
|
+
TRAITS_FIELDS = [
|
|
723
|
+
"write_keywords",
|
|
724
|
+
"read_keywords",
|
|
725
|
+
"write_command_res",
|
|
726
|
+
"read_only_commands",
|
|
727
|
+
"fully_safe_commands",
|
|
728
|
+
"readonly_sensitive_commands",
|
|
729
|
+
"safe_action_types",
|
|
730
|
+
"readonly_sensitive_action_types"
|
|
731
|
+
];
|
|
732
|
+
TRAITS_OS_FALLBACK = "linux";
|
|
820
733
|
EXTRA_CATEGORIES = {
|
|
821
734
|
dangerous_commands: ["pattern"],
|
|
822
735
|
injection_detection: ["keyword", "lang"],
|
|
@@ -838,7 +751,155 @@ var init_rules_loader = __esm({
|
|
|
838
751
|
}
|
|
839
752
|
});
|
|
840
753
|
|
|
754
|
+
// src/engine/inline-analyzer.ts
|
|
755
|
+
function buildChainRegex(pattern, flags) {
|
|
756
|
+
return new RegExp(pattern, (flags || "") + "g");
|
|
757
|
+
}
|
|
758
|
+
function isLiteralTextCommand(command) {
|
|
759
|
+
const segments = splitCommandSegments(command);
|
|
760
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
761
|
+
if (command.includes(">")) {
|
|
762
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
763
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
764
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
765
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
766
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
function extractInlineCode(command) {
|
|
776
|
+
const parts = [];
|
|
777
|
+
let m;
|
|
778
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
779
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
780
|
+
const code = m[1] ?? m[2];
|
|
781
|
+
if (code) parts.push(code);
|
|
782
|
+
}
|
|
783
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
784
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
785
|
+
const code = m[1] ?? m[2];
|
|
786
|
+
if (code) parts.push(code);
|
|
787
|
+
}
|
|
788
|
+
return parts.join("\n");
|
|
789
|
+
}
|
|
790
|
+
function hasWriteExecChain(command) {
|
|
791
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
792
|
+
p.lastIndex = 0;
|
|
793
|
+
return p.test(command);
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
function hasDirTraversal(code) {
|
|
797
|
+
const lower = code.toLowerCase();
|
|
798
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
799
|
+
}
|
|
800
|
+
function hasReadWriteKeyword(code) {
|
|
801
|
+
const lower = code.toLowerCase();
|
|
802
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
803
|
+
}
|
|
804
|
+
function analyzeInlineScript(command) {
|
|
805
|
+
const code = extractInlineCode(command);
|
|
806
|
+
if (!code) {
|
|
807
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
808
|
+
}
|
|
809
|
+
if (hasWriteExecChain(command)) {
|
|
810
|
+
return {
|
|
811
|
+
actionable: true,
|
|
812
|
+
allowed: false,
|
|
813
|
+
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",
|
|
814
|
+
riskLevel: "critical",
|
|
815
|
+
riskType: "script_execution"
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
819
|
+
if (redirMatch) {
|
|
820
|
+
return {
|
|
821
|
+
actionable: true,
|
|
822
|
+
allowed: false,
|
|
823
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
824
|
+
riskLevel: "critical",
|
|
825
|
+
riskType: "file_access"
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
829
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
830
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
831
|
+
}
|
|
832
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
833
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
834
|
+
return {
|
|
835
|
+
actionable: true,
|
|
836
|
+
allowed: false,
|
|
837
|
+
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",
|
|
838
|
+
riskLevel: "critical",
|
|
839
|
+
riskType: "file_access"
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
actionable: true,
|
|
844
|
+
allowed: true,
|
|
845
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
846
|
+
riskLevel: "low",
|
|
847
|
+
riskType: "file_access"
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
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;
|
|
851
|
+
var init_inline_analyzer = __esm({
|
|
852
|
+
"src/engine/inline-analyzer.ts"() {
|
|
853
|
+
"use strict";
|
|
854
|
+
init_esm_shims();
|
|
855
|
+
init_shell_split();
|
|
856
|
+
init_rules_loader();
|
|
857
|
+
INLINE_RULES = loadInline();
|
|
858
|
+
if (!INLINE_RULES || Object.keys(INLINE_RULES).length === 0) {
|
|
859
|
+
console.error(
|
|
860
|
+
"[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"
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
864
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
865
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
866
|
+
READ_WRITE_KEYWORDS = INLINE_RULES.read_write_keywords ?? [];
|
|
867
|
+
WRITE_EXEC_CHAIN_PATTERNS = (INLINE_RULES.write_exec_chain_patterns ?? []).filter((p) => p && typeof p.pattern === "string").map((p) => buildChainRegex(p.pattern, p.flags ?? ""));
|
|
868
|
+
_REDIRECT_PATHS = INLINE_RULES.redirect_sensitive_paths ?? {};
|
|
869
|
+
SHELL_RC_FRAGMENTS = _REDIRECT_PATHS.shell_rc_fragments ?? [];
|
|
870
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
871
|
+
..._REDIRECT_PATHS.ts_static ?? [],
|
|
872
|
+
...SHELL_RC_FRAGMENTS
|
|
873
|
+
];
|
|
874
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
875
|
+
DIR_TRAVERSAL_KEYWORDS = INLINE_RULES.dir_traversal_keywords ?? [];
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
|
|
841
879
|
// src/engine/platform-traits.ts
|
|
880
|
+
function buildTraits(osName) {
|
|
881
|
+
const t = loadPlatformTraits(osName);
|
|
882
|
+
const strs = (k) => Array.isArray(t[k]) ? t[k] : [];
|
|
883
|
+
return {
|
|
884
|
+
writeKeywords: strs("write_keywords"),
|
|
885
|
+
readKeywords: strs("read_keywords"),
|
|
886
|
+
writeCommandRes: strs("write_command_res").map((p) => new RegExp(p)),
|
|
887
|
+
readOnlyCommands: new Set(strs("read_only_commands")),
|
|
888
|
+
fullySafeCommands: strs("fully_safe_commands").map((p) => new RegExp(p, "i")),
|
|
889
|
+
readonlySensitiveCommands: strs("readonly_sensitive_commands").map((p) => new RegExp(p, "i")),
|
|
890
|
+
safeActionTypes: new Set(strs("safe_action_types")),
|
|
891
|
+
readonlySensitiveActionTypes: new Set(strs("readonly_sensitive_action_types"))
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function traitsFor(key) {
|
|
895
|
+
const resolved = resolveTraitsOs(key);
|
|
896
|
+
let t = traitsCache.get(resolved);
|
|
897
|
+
if (!t) {
|
|
898
|
+
t = buildTraits(resolved);
|
|
899
|
+
traitsCache.set(resolved, t);
|
|
900
|
+
}
|
|
901
|
+
return t;
|
|
902
|
+
}
|
|
842
903
|
function getPlatformTraits(osType) {
|
|
843
904
|
const accepted = acceptedOsValues(osType);
|
|
844
905
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -846,368 +907,18 @@ function getPlatformTraits(osType) {
|
|
|
846
907
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
847
908
|
return LINUX_TRAITS;
|
|
848
909
|
}
|
|
849
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
910
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
850
911
|
var init_platform_traits = __esm({
|
|
851
912
|
"src/engine/platform-traits.ts"() {
|
|
852
913
|
"use strict";
|
|
853
914
|
init_esm_shims();
|
|
854
915
|
init_logic_rules();
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
"modify",
|
|
862
|
-
"\u4FEE\u6539",
|
|
863
|
-
"update",
|
|
864
|
-
"\u66F4\u65B0",
|
|
865
|
-
"delete",
|
|
866
|
-
"\u5220\u9664",
|
|
867
|
-
"remove",
|
|
868
|
-
"\u79FB\u9664",
|
|
869
|
-
"create",
|
|
870
|
-
"\u521B\u5EFA",
|
|
871
|
-
"append",
|
|
872
|
-
"\u8FFD\u52A0",
|
|
873
|
-
"truncate",
|
|
874
|
-
"\u622A\u65AD",
|
|
875
|
-
"overwrite",
|
|
876
|
-
"\u8986\u76D6",
|
|
877
|
-
"replace",
|
|
878
|
-
"\u66FF\u6362",
|
|
879
|
-
"edit"
|
|
880
|
-
],
|
|
881
|
-
readKeywords: [
|
|
882
|
-
"read",
|
|
883
|
-
"\u8BFB\u53D6",
|
|
884
|
-
"cat",
|
|
885
|
-
"head",
|
|
886
|
-
"tail",
|
|
887
|
-
"less",
|
|
888
|
-
"more",
|
|
889
|
-
"view",
|
|
890
|
-
"\u67E5\u770B",
|
|
891
|
-
"open",
|
|
892
|
-
"\u6253\u5F00",
|
|
893
|
-
"load",
|
|
894
|
-
"\u52A0\u8F7D",
|
|
895
|
-
"grep",
|
|
896
|
-
"search",
|
|
897
|
-
"\u641C\u7D22",
|
|
898
|
-
"find",
|
|
899
|
-
"\u67E5\u627E"
|
|
900
|
-
],
|
|
901
|
-
writeCommandRes: [
|
|
902
|
-
/\brm\b/,
|
|
903
|
-
/\bunlink\b/,
|
|
904
|
-
/\brmdir\b/,
|
|
905
|
-
/\bshred\b/,
|
|
906
|
-
/\bcp\b/,
|
|
907
|
-
/\bmv\b/,
|
|
908
|
-
/\binstall\b/,
|
|
909
|
-
/\btee\b/,
|
|
910
|
-
/\bdd\b/,
|
|
911
|
-
/\bmkfs\b/,
|
|
912
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
913
|
-
/\bchmod\b/,
|
|
914
|
-
/\bchown\b/,
|
|
915
|
-
/\btruncate\b/
|
|
916
|
-
],
|
|
917
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
918
|
-
"cat",
|
|
919
|
-
"head",
|
|
920
|
-
"tail",
|
|
921
|
-
"less",
|
|
922
|
-
"more",
|
|
923
|
-
"grep",
|
|
924
|
-
"find",
|
|
925
|
-
"awk",
|
|
926
|
-
"sed",
|
|
927
|
-
"sort",
|
|
928
|
-
"uniq",
|
|
929
|
-
"wc",
|
|
930
|
-
"cut",
|
|
931
|
-
"strings",
|
|
932
|
-
"od",
|
|
933
|
-
"xxd",
|
|
934
|
-
"hexdump",
|
|
935
|
-
"tr",
|
|
936
|
-
"file",
|
|
937
|
-
"stat",
|
|
938
|
-
"du",
|
|
939
|
-
"df",
|
|
940
|
-
"ls",
|
|
941
|
-
"dir",
|
|
942
|
-
"tree",
|
|
943
|
-
"nl",
|
|
944
|
-
"tac",
|
|
945
|
-
"rev",
|
|
946
|
-
"echo",
|
|
947
|
-
"printf",
|
|
948
|
-
"lsblk",
|
|
949
|
-
"blockdev",
|
|
950
|
-
"smartctl",
|
|
951
|
-
"udevadm",
|
|
952
|
-
"dmidecode",
|
|
953
|
-
"lscpu",
|
|
954
|
-
"lspci",
|
|
955
|
-
"lsusb",
|
|
956
|
-
"lsmem",
|
|
957
|
-
"lsns",
|
|
958
|
-
"lsof",
|
|
959
|
-
"hwinfo",
|
|
960
|
-
"inxi",
|
|
961
|
-
"fdisk",
|
|
962
|
-
"parted",
|
|
963
|
-
"dmesg",
|
|
964
|
-
"journalctl",
|
|
965
|
-
"systemctl",
|
|
966
|
-
"hostnamectl",
|
|
967
|
-
"localectl",
|
|
968
|
-
"timedatectl",
|
|
969
|
-
"ps",
|
|
970
|
-
"top",
|
|
971
|
-
"free",
|
|
972
|
-
"vmstat",
|
|
973
|
-
"iostat",
|
|
974
|
-
"mpstat",
|
|
975
|
-
"pidof",
|
|
976
|
-
"pgrep",
|
|
977
|
-
"ip",
|
|
978
|
-
"ifconfig",
|
|
979
|
-
"route",
|
|
980
|
-
"ss",
|
|
981
|
-
"netstat",
|
|
982
|
-
"arp",
|
|
983
|
-
"ethtool",
|
|
984
|
-
"nmcli",
|
|
985
|
-
"uname",
|
|
986
|
-
"arch",
|
|
987
|
-
"nproc",
|
|
988
|
-
"getconf",
|
|
989
|
-
"getent",
|
|
990
|
-
"id",
|
|
991
|
-
"whoami",
|
|
992
|
-
"who",
|
|
993
|
-
"w",
|
|
994
|
-
"last",
|
|
995
|
-
"uptime",
|
|
996
|
-
"lsmod",
|
|
997
|
-
"modinfo",
|
|
998
|
-
"sysctl"
|
|
999
|
-
]),
|
|
1000
|
-
fullySafeCommands: [
|
|
1001
|
-
/^ls(?:\s|$)/i,
|
|
1002
|
-
/^dir(?:\s|$)/i,
|
|
1003
|
-
/^pwd(?:\s|$)/i,
|
|
1004
|
-
/^which\s+/i,
|
|
1005
|
-
/^whereis\s+/i,
|
|
1006
|
-
/^realpath\s+/i,
|
|
1007
|
-
/^basename\s+/i,
|
|
1008
|
-
/^dirname\s+/i,
|
|
1009
|
-
/^file\s+/i,
|
|
1010
|
-
/^stat\s+/i,
|
|
1011
|
-
/^du(?:\s|$)/i,
|
|
1012
|
-
/^echo\s+/i,
|
|
1013
|
-
/^printf\s+/i,
|
|
1014
|
-
/^type\s+/i,
|
|
1015
|
-
/^command\s+/i,
|
|
1016
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
1017
|
-
/^whoami(?:\s|$)/i,
|
|
1018
|
-
/^id(?:\s|$)/i,
|
|
1019
|
-
/^hostname(?:\s|$)/i,
|
|
1020
|
-
/^uname(?:\s|$)/i,
|
|
1021
|
-
/^date(?:\s|$)/i,
|
|
1022
|
-
/^env(?:\s|$)/i,
|
|
1023
|
-
/^printenv(?:\s|$)/i,
|
|
1024
|
-
/^tty(?:\s|$)/i,
|
|
1025
|
-
/^arch(?:\s|$)/i,
|
|
1026
|
-
/^uptime(?:\s|$)/i,
|
|
1027
|
-
/^groups(?:\s|$)/i,
|
|
1028
|
-
/^logname(?:\s|$)/i,
|
|
1029
|
-
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
1030
|
-
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
1031
|
-
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
1032
|
-
/^df(?:\s|$)/i,
|
|
1033
|
-
/^free(?:\s|$)/i,
|
|
1034
|
-
/^vmstat(?:\s|$)/i,
|
|
1035
|
-
/^nproc(?:\s|$)/i,
|
|
1036
|
-
/^lscpu(?:\s|$)/i,
|
|
1037
|
-
/^lsblk(?:\s|$)/i,
|
|
1038
|
-
/^lspci(?:\s|$)/i,
|
|
1039
|
-
/^lsusb(?:\s|$)/i,
|
|
1040
|
-
/^lsmod(?:\s|$)/i
|
|
1041
|
-
],
|
|
1042
|
-
readonlySensitiveCommands: [
|
|
1043
|
-
/^cat\s+/i,
|
|
1044
|
-
/^head(?:\s|$)/i,
|
|
1045
|
-
/^tail(?:\s|$)/i,
|
|
1046
|
-
/^less(?:\s|$)/i,
|
|
1047
|
-
/^more(?:\s|$)/i,
|
|
1048
|
-
/^wc(?:\s|$)/i,
|
|
1049
|
-
/^grep(?:\s|$)/i,
|
|
1050
|
-
/^find\s+/i,
|
|
1051
|
-
/^ag(?:\s|$)/i,
|
|
1052
|
-
/^rg(?:\s|$)/i,
|
|
1053
|
-
/^awk\s+/i,
|
|
1054
|
-
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
1055
|
-
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
1056
|
-
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
1057
|
-
/^cut\s+/i,
|
|
1058
|
-
/^sort(?:\s|$)/i,
|
|
1059
|
-
/^uniq(?:\s|$)/i,
|
|
1060
|
-
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
1061
|
-
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
1062
|
-
/^ps(?:\s|$)/i,
|
|
1063
|
-
/^ss(?:\s|$)/i,
|
|
1064
|
-
/^netstat(?:\s|$)/i,
|
|
1065
|
-
/^lsof(?:\s|$)/i,
|
|
1066
|
-
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
1067
|
-
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
1068
|
-
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
1069
|
-
/^service\s+\S+\s+status\b/i,
|
|
1070
|
-
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
1071
|
-
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
1072
|
-
// 的话只看紧跟 journalctl 的第一个参数,
|
|
1073
|
-
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
1074
|
-
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
1075
|
-
]
|
|
1076
|
-
};
|
|
1077
|
-
WINDOWS_TRAITS = {
|
|
1078
|
-
writeKeywords: [
|
|
1079
|
-
"write",
|
|
1080
|
-
"\u5199\u5165",
|
|
1081
|
-
"save",
|
|
1082
|
-
"\u4FDD\u5B58",
|
|
1083
|
-
"modify",
|
|
1084
|
-
"\u4FEE\u6539",
|
|
1085
|
-
"update",
|
|
1086
|
-
"\u66F4\u65B0",
|
|
1087
|
-
"delete",
|
|
1088
|
-
"\u5220\u9664",
|
|
1089
|
-
"remove",
|
|
1090
|
-
"\u79FB\u9664",
|
|
1091
|
-
"create",
|
|
1092
|
-
"\u521B\u5EFA",
|
|
1093
|
-
"append",
|
|
1094
|
-
"\u8FFD\u52A0",
|
|
1095
|
-
"overwrite",
|
|
1096
|
-
"\u8986\u76D6",
|
|
1097
|
-
"replace",
|
|
1098
|
-
"\u66FF\u6362",
|
|
1099
|
-
"set",
|
|
1100
|
-
"\u8BBE\u7F6E"
|
|
1101
|
-
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
1102
|
-
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
1103
|
-
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
1104
|
-
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
1105
|
-
],
|
|
1106
|
-
readKeywords: [
|
|
1107
|
-
"read",
|
|
1108
|
-
"\u8BFB\u53D6",
|
|
1109
|
-
"type",
|
|
1110
|
-
"findstr",
|
|
1111
|
-
"dir",
|
|
1112
|
-
"view",
|
|
1113
|
-
"\u67E5\u770B",
|
|
1114
|
-
"open",
|
|
1115
|
-
"\u6253\u5F00",
|
|
1116
|
-
"load",
|
|
1117
|
-
"\u52A0\u8F7D",
|
|
1118
|
-
"search",
|
|
1119
|
-
"\u641C\u7D22",
|
|
1120
|
-
"find",
|
|
1121
|
-
"\u67E5\u627E",
|
|
1122
|
-
"get",
|
|
1123
|
-
"\u83B7\u53D6"
|
|
1124
|
-
],
|
|
1125
|
-
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
1126
|
-
writeCommandRes: [
|
|
1127
|
-
/\b(del|erase)\b/,
|
|
1128
|
-
/\brmdir\b/,
|
|
1129
|
-
/\b(rd)\b/,
|
|
1130
|
-
/\bcopy\b/,
|
|
1131
|
-
/\b(xcopy|robocopy)\b/,
|
|
1132
|
-
/\b(move|movefile)\b/,
|
|
1133
|
-
/\breplace\b/,
|
|
1134
|
-
/\bicacls\b/,
|
|
1135
|
-
/\battrib\b/,
|
|
1136
|
-
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
1137
|
-
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
1138
|
-
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
1139
|
-
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
1140
|
-
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
1141
|
-
/\bformat\b/,
|
|
1142
|
-
/\bdiskpart\b/,
|
|
1143
|
-
/\bmkdir\b/,
|
|
1144
|
-
/\bmd\b/,
|
|
1145
|
-
/\bsetx\b/,
|
|
1146
|
-
/\bwmic\b/
|
|
1147
|
-
],
|
|
1148
|
-
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
1149
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1150
|
-
"type",
|
|
1151
|
-
"findstr",
|
|
1152
|
-
"more",
|
|
1153
|
-
"tree",
|
|
1154
|
-
"dir",
|
|
1155
|
-
"where",
|
|
1156
|
-
"ver",
|
|
1157
|
-
"cls",
|
|
1158
|
-
"echo",
|
|
1159
|
-
"help",
|
|
1160
|
-
"fc",
|
|
1161
|
-
"comp",
|
|
1162
|
-
"find",
|
|
1163
|
-
"sort",
|
|
1164
|
-
"powershell",
|
|
1165
|
-
"cmd",
|
|
1166
|
-
"netstat",
|
|
1167
|
-
"ipconfig",
|
|
1168
|
-
"tasklist",
|
|
1169
|
-
"systeminfo",
|
|
1170
|
-
"driverquery",
|
|
1171
|
-
"reg",
|
|
1172
|
-
"wmic",
|
|
1173
|
-
"whoami",
|
|
1174
|
-
"hostname",
|
|
1175
|
-
"getmac",
|
|
1176
|
-
"query",
|
|
1177
|
-
"w32tm",
|
|
1178
|
-
"diskpart"
|
|
1179
|
-
]),
|
|
1180
|
-
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
1181
|
-
fullySafeCommands: [
|
|
1182
|
-
/^dir(?:\s|$)/,
|
|
1183
|
-
/^where\s+/,
|
|
1184
|
-
/^echo\s+/,
|
|
1185
|
-
/^cd(?:\s|$)/,
|
|
1186
|
-
/^tree(?:\s|$)/,
|
|
1187
|
-
/^help(?:\s|$)/,
|
|
1188
|
-
/^cls(?:\s|$)/,
|
|
1189
|
-
/^ver(?:\s|$)/,
|
|
1190
|
-
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
1191
|
-
/^whoami(?:\s|$)/,
|
|
1192
|
-
/^hostname(?:\s|$)/,
|
|
1193
|
-
/^systeminfo(?:\s|$)/,
|
|
1194
|
-
/^tasklist(?:\s|$)/,
|
|
1195
|
-
/^getmac(?:\s|$)/,
|
|
1196
|
-
/^driverquery(?:\s|$)/,
|
|
1197
|
-
/^netstat(?:\s|$)/
|
|
1198
|
-
],
|
|
1199
|
-
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
1200
|
-
readonlySensitiveCommands: [
|
|
1201
|
-
/^type\s+/,
|
|
1202
|
-
/^findstr\s+/,
|
|
1203
|
-
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
1204
|
-
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
1205
|
-
/^sc\s+query\b/,
|
|
1206
|
-
/^reg\s+query\b/
|
|
1207
|
-
]
|
|
1208
|
-
};
|
|
1209
|
-
MACOS_TRAITS = LINUX_TRAITS;
|
|
1210
|
-
HARMONY_TRAITS = LINUX_TRAITS;
|
|
916
|
+
init_rules_loader();
|
|
917
|
+
traitsCache = /* @__PURE__ */ new Map();
|
|
918
|
+
LINUX_TRAITS = traitsFor("linux");
|
|
919
|
+
WINDOWS_TRAITS = traitsFor("windows");
|
|
920
|
+
MACOS_TRAITS = traitsFor("macos");
|
|
921
|
+
HARMONY_TRAITS = traitsFor("harmony");
|
|
1211
922
|
}
|
|
1212
923
|
});
|
|
1213
924
|
|
|
@@ -1366,7 +1077,6 @@ function checkIntentConsistency(input) {
|
|
|
1366
1077
|
const { aNext, promptSession, promptHistory } = input;
|
|
1367
1078
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1368
1079
|
const actionType = aNext.actionType.toLowerCase();
|
|
1369
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1370
1080
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1371
1081
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1372
1082
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1394,7 +1104,6 @@ function runLogicCheck(input, opts) {
|
|
|
1394
1104
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1395
1105
|
const actionType = aNext.actionType.toLowerCase();
|
|
1396
1106
|
const resolvedCwd = cwd || "/";
|
|
1397
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1398
1107
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1399
1108
|
if (!opts?.skipReadBeforeWrite) {
|
|
1400
1109
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1506,7 +1215,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1506
1215
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1507
1216
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1508
1217
|
if (!isWrite) return null;
|
|
1509
|
-
const WRITE_CMDS =
|
|
1218
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1510
1219
|
if (isBashLike) {
|
|
1511
1220
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1512
1221
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1538,7 +1247,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1538
1247
|
}
|
|
1539
1248
|
if (!hasWriteSeg) return null;
|
|
1540
1249
|
}
|
|
1541
|
-
const SOURCE_ARG_CMDS =
|
|
1250
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1542
1251
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1543
1252
|
const targetPaths = [];
|
|
1544
1253
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1586,7 +1295,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1586
1295
|
}
|
|
1587
1296
|
}
|
|
1588
1297
|
}
|
|
1589
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1590
1298
|
let filteredPaths = uniqueTargetPaths;
|
|
1591
1299
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1592
1300
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1687,10 +1395,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1687
1395
|
if (!hasTraversal) {
|
|
1688
1396
|
return null;
|
|
1689
1397
|
}
|
|
1690
|
-
const READ_KEYWORDS =
|
|
1691
|
-
const WRITE_KEYWORDS2 =
|
|
1692
|
-
const EXEC_KEYWORDS =
|
|
1693
|
-
const OPEN_KEYWORDS =
|
|
1398
|
+
const READ_KEYWORDS = IFA_READ_KEYWORDS;
|
|
1399
|
+
const WRITE_KEYWORDS2 = IFA_WRITE_KEYWORDS;
|
|
1400
|
+
const EXEC_KEYWORDS = IFA_EXEC_KEYWORDS;
|
|
1401
|
+
const OPEN_KEYWORDS = IFA_OPEN_KEYWORDS;
|
|
1694
1402
|
const code = extractInlineCode(actionDetail);
|
|
1695
1403
|
if (!code) return null;
|
|
1696
1404
|
const codeLower = code.toLowerCase();
|
|
@@ -1753,7 +1461,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1753
1461
|
violatedLayers: ["L2.1"]
|
|
1754
1462
|
};
|
|
1755
1463
|
}
|
|
1756
|
-
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;
|
|
1464
|
+
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;
|
|
1757
1465
|
var init_logic_rules = __esm({
|
|
1758
1466
|
"src/engine/logic-rules.ts"() {
|
|
1759
1467
|
"use strict";
|
|
@@ -1763,6 +1471,10 @@ var init_logic_rules = __esm({
|
|
|
1763
1471
|
init_rules_loader();
|
|
1764
1472
|
init_platform_traits();
|
|
1765
1473
|
init_shell_split();
|
|
1474
|
+
_LOGIC = loadLogicRules();
|
|
1475
|
+
_HEURISTIC = loadHeuristicRules();
|
|
1476
|
+
EXCLUDED_PREFIXES = _HEURISTIC.sensitive_excluded_prefixes ?? [];
|
|
1477
|
+
TEMP_DIRS = (_HEURISTIC.temp_dirs ?? []).map((d) => d + "/");
|
|
1766
1478
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1767
1479
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1768
1480
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc, dirDelOnly }) => ({ path: path5, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1783,7 +1495,7 @@ var init_logic_rules = __esm({
|
|
|
1783
1495
|
"list_dir",
|
|
1784
1496
|
"count_text_length",
|
|
1785
1497
|
"search",
|
|
1786
|
-
// filemgr 只读三件套(globfiles 在
|
|
1498
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1787
1499
|
"filemgr-globfiles",
|
|
1788
1500
|
"filemgr-readfile",
|
|
1789
1501
|
"filemgr-grepfiles",
|
|
@@ -1795,7 +1507,7 @@ var init_logic_rules = __esm({
|
|
|
1795
1507
|
"tail",
|
|
1796
1508
|
"cat"
|
|
1797
1509
|
]);
|
|
1798
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1510
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1799
1511
|
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"];
|
|
1800
1512
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1801
1513
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1836,25 +1548,24 @@ var init_logic_rules = __esm({
|
|
|
1836
1548
|
}));
|
|
1837
1549
|
})();
|
|
1838
1550
|
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;
|
|
1839
|
-
DIR_TRAVERSAL_KW = [
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
"pathlib.",
|
|
1846
|
-
"iterdir",
|
|
1847
|
-
"rglob",
|
|
1848
|
-
"scandir(",
|
|
1849
|
-
"listdir("
|
|
1850
|
-
];
|
|
1551
|
+
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1552
|
+
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
1553
|
+
IFA_READ_KEYWORDS = _IFA.read_keywords ?? [];
|
|
1554
|
+
IFA_WRITE_KEYWORDS = _IFA.write_keywords ?? [];
|
|
1555
|
+
IFA_EXEC_KEYWORDS = _IFA.exec_keywords ?? [];
|
|
1556
|
+
IFA_OPEN_KEYWORDS = _IFA.open_keywords ?? [];
|
|
1851
1557
|
}
|
|
1852
1558
|
});
|
|
1853
1559
|
|
|
1854
1560
|
// src/engine/asset-dir.ts
|
|
1855
1561
|
import { existsSync as existsSync3 } from "fs";
|
|
1856
|
-
import { resolve as resolve2 } from "path";
|
|
1562
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
1857
1563
|
function assetDirCandidates(moduleDir, name) {
|
|
1564
|
+
const root = getRulesRoot();
|
|
1565
|
+
if (root) {
|
|
1566
|
+
const p = join3(root, name);
|
|
1567
|
+
return [p, p];
|
|
1568
|
+
}
|
|
1858
1569
|
return [
|
|
1859
1570
|
resolve2(moduleDir, name),
|
|
1860
1571
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -1870,6 +1581,7 @@ var init_asset_dir = __esm({
|
|
|
1870
1581
|
"src/engine/asset-dir.ts"() {
|
|
1871
1582
|
"use strict";
|
|
1872
1583
|
init_esm_shims();
|
|
1584
|
+
init_paths();
|
|
1873
1585
|
}
|
|
1874
1586
|
});
|
|
1875
1587
|
|
|
@@ -1898,6 +1610,7 @@ function userSkillsDir() {
|
|
|
1898
1610
|
return resolve3(getDataRoot(), "agent_moss_skills");
|
|
1899
1611
|
}
|
|
1900
1612
|
function runtimeCustomSkillKeywords() {
|
|
1613
|
+
if (isRuntimeConfigDisabled()) return {};
|
|
1901
1614
|
try {
|
|
1902
1615
|
const rt = runtimeConfigBridge.current?.loadRuntimeConfig?.();
|
|
1903
1616
|
if (!rt) return {};
|
|
@@ -1915,6 +1628,7 @@ function runtimeCustomSkillKeywords() {
|
|
|
1915
1628
|
}
|
|
1916
1629
|
}
|
|
1917
1630
|
function loadUserSkill(name) {
|
|
1631
|
+
if (isRuntimeConfigDisabled()) return "";
|
|
1918
1632
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1919
1633
|
try {
|
|
1920
1634
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
@@ -1983,6 +1697,7 @@ var init_skill_loader = __esm({
|
|
|
1983
1697
|
init_esm_shims();
|
|
1984
1698
|
init_rules_loader();
|
|
1985
1699
|
init_paths();
|
|
1700
|
+
init_rules_mode();
|
|
1986
1701
|
init_asset_dir();
|
|
1987
1702
|
init_runtime_config_bridge();
|
|
1988
1703
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -2043,6 +1758,7 @@ __export(runtime_config_exports, {
|
|
|
2043
1758
|
getEnabledScenarioExtensions: () => getEnabledScenarioExtensions,
|
|
2044
1759
|
getEnvOverrides: () => getEnvOverrides,
|
|
2045
1760
|
getProfileOrDetect: () => getProfileOrDetect,
|
|
1761
|
+
getRuleSourceFingerprint: () => getRuleSourceFingerprint,
|
|
2046
1762
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
2047
1763
|
getScenarioRules: () => getScenarioRules,
|
|
2048
1764
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
@@ -2066,7 +1782,7 @@ __export(runtime_config_exports, {
|
|
|
2066
1782
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2067
1783
|
});
|
|
2068
1784
|
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2069
|
-
import { join as
|
|
1785
|
+
import { join as join4 } from "path";
|
|
2070
1786
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2071
1787
|
import { dirname as dirname3 } from "path";
|
|
2072
1788
|
function runtimeDir() {
|
|
@@ -2077,7 +1793,22 @@ function getRuntimeConfigPath() {
|
|
|
2077
1793
|
if (envPath) return envPath;
|
|
2078
1794
|
return RUNTIME_CONFIG_PATH;
|
|
2079
1795
|
}
|
|
1796
|
+
function getRuleSourceFingerprint() {
|
|
1797
|
+
if (isRuntimeConfigDisabled()) return `mode:${getRulesMode()}`;
|
|
1798
|
+
const p = getRuntimeConfigPath();
|
|
1799
|
+
try {
|
|
1800
|
+
const st = statSync2(p);
|
|
1801
|
+
return `${p}|${st.mtimeMs}|${st.size}`;
|
|
1802
|
+
} catch {
|
|
1803
|
+
return `${p}|absent`;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
2080
1806
|
function saveRuntimeConfig(config) {
|
|
1807
|
+
if (isRuntimeConfigDisabled()) {
|
|
1808
|
+
throw new Error(
|
|
1809
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1A\u4FEE\u6539\u89C4\u5219\u4E0D\u4F1A\u751F\u6548\uFF0C\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u3002`
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
2081
1812
|
const p = getRuntimeConfigPath();
|
|
2082
1813
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2083
1814
|
if (dir && !existsSync5(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -2101,6 +1832,18 @@ function invalidateRuntimeConfigCache() {
|
|
|
2101
1832
|
hasRuntimeConfigCache.clear();
|
|
2102
1833
|
}
|
|
2103
1834
|
function loadRuntimeConfig() {
|
|
1835
|
+
if (isRuntimeConfigDisabled()) {
|
|
1836
|
+
return {
|
|
1837
|
+
version: 1,
|
|
1838
|
+
layers: { ...DEFAULT_LAYERS },
|
|
1839
|
+
L1_rules: {},
|
|
1840
|
+
L2_rules: {},
|
|
1841
|
+
L3_skills: {},
|
|
1842
|
+
L2_2: {},
|
|
1843
|
+
agent_rules: {},
|
|
1844
|
+
scenario_rules: {}
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
2104
1847
|
const p = getRuntimeConfigPath();
|
|
2105
1848
|
if (!existsSync5(p)) {
|
|
2106
1849
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
@@ -2147,6 +1890,7 @@ function loadRuntimeConfig() {
|
|
|
2147
1890
|
function isLayerEnabled(layerKey) {
|
|
2148
1891
|
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2149
1892
|
if (disableKey && process.env[disableKey] === "1") return false;
|
|
1893
|
+
if (isRuntimeConfigDisabled()) return true;
|
|
2150
1894
|
const runtime = loadRuntimeConfig();
|
|
2151
1895
|
const layers = runtime.layers || {};
|
|
2152
1896
|
if (layerKey in layers) return !!layers[layerKey];
|
|
@@ -2173,7 +1917,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2173
1917
|
}
|
|
2174
1918
|
function _filterRulesByOs(rules, osType) {
|
|
2175
1919
|
const accepted = acceptedOsValues(osType);
|
|
2176
|
-
return rules.filter((r) =>
|
|
1920
|
+
return rules.filter((r) => {
|
|
1921
|
+
const os3 = String(r.os || "linux").toLowerCase();
|
|
1922
|
+
return os3 === "all" || accepted.includes(os3);
|
|
1923
|
+
});
|
|
2177
1924
|
}
|
|
2178
1925
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2179
1926
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2247,6 +1994,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2247
1994
|
});
|
|
2248
1995
|
}
|
|
2249
1996
|
function hasRuntimeConfig() {
|
|
1997
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
2250
1998
|
const p = getRuntimeConfigPath();
|
|
2251
1999
|
let key;
|
|
2252
2000
|
try {
|
|
@@ -2255,8 +2003,8 @@ function hasRuntimeConfig() {
|
|
|
2255
2003
|
} catch {
|
|
2256
2004
|
return false;
|
|
2257
2005
|
}
|
|
2258
|
-
const
|
|
2259
|
-
if (
|
|
2006
|
+
const cached2 = hasRuntimeConfigCache.get(key);
|
|
2007
|
+
if (cached2 !== void 0) return cached2;
|
|
2260
2008
|
let ok = false;
|
|
2261
2009
|
try {
|
|
2262
2010
|
JSON.parse(readFileSync4(p, "utf-8"));
|
|
@@ -2359,6 +2107,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2359
2107
|
keyword: kw.keyword,
|
|
2360
2108
|
risk_level: kw.riskLevel,
|
|
2361
2109
|
lang: kw.lang || "en",
|
|
2110
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2111
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2112
|
+
os: kw.os,
|
|
2362
2113
|
enabled: true,
|
|
2363
2114
|
builtin: true
|
|
2364
2115
|
}));
|
|
@@ -2476,7 +2227,8 @@ function _l3SkillsToRules() {
|
|
|
2476
2227
|
}
|
|
2477
2228
|
function _scenarioRulesToDefaults() {
|
|
2478
2229
|
const out = {};
|
|
2479
|
-
const
|
|
2230
|
+
const _root = getRulesRoot();
|
|
2231
|
+
const scenDir = _root ? join4(_root, "scenarios") : existsSync5(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync5(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2480
2232
|
if (!existsSync5(scenDir)) return out;
|
|
2481
2233
|
let entries;
|
|
2482
2234
|
try {
|
|
@@ -2485,7 +2237,7 @@ function _scenarioRulesToDefaults() {
|
|
|
2485
2237
|
return out;
|
|
2486
2238
|
}
|
|
2487
2239
|
for (const scenarioId of entries) {
|
|
2488
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2240
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join4(scenDir, scenarioId, "rules.json");
|
|
2489
2241
|
if (!existsSync5(rulesFile)) continue;
|
|
2490
2242
|
let data;
|
|
2491
2243
|
try {
|
|
@@ -2525,7 +2277,8 @@ function _scenarioRulesToDefaults() {
|
|
|
2525
2277
|
}
|
|
2526
2278
|
function _agentRulesToDefaults() {
|
|
2527
2279
|
const out = {};
|
|
2528
|
-
const
|
|
2280
|
+
const _root = getRulesRoot();
|
|
2281
|
+
const agentsDir = _root ? join4(_root, "agents") : existsSync5(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync5(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2529
2282
|
if (!existsSync5(agentsDir)) return out;
|
|
2530
2283
|
let entries;
|
|
2531
2284
|
try {
|
|
@@ -2534,7 +2287,7 @@ function _agentRulesToDefaults() {
|
|
|
2534
2287
|
return out;
|
|
2535
2288
|
}
|
|
2536
2289
|
for (const agentId of entries) {
|
|
2537
|
-
const rulesFile =
|
|
2290
|
+
const rulesFile = join4(agentsDir, agentId, "rules.json");
|
|
2538
2291
|
if (!existsSync5(rulesFile)) continue;
|
|
2539
2292
|
let data;
|
|
2540
2293
|
try {
|
|
@@ -2913,7 +2666,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
2913
2666
|
}
|
|
2914
2667
|
function _filterDefaultsByOs(rules, osType) {
|
|
2915
2668
|
const accepted = acceptedOsValues(osType);
|
|
2916
|
-
return rules.filter((r) =>
|
|
2669
|
+
return rules.filter((r) => {
|
|
2670
|
+
if (!r.os) return true;
|
|
2671
|
+
const os3 = String(r.os).toLowerCase();
|
|
2672
|
+
return os3 === "all" || accepted.includes(os3);
|
|
2673
|
+
});
|
|
2917
2674
|
}
|
|
2918
2675
|
function _hasRulesForOs(rules, osType) {
|
|
2919
2676
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3136,13 +2893,18 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
3136
2893
|
}
|
|
3137
2894
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
3138
2895
|
_validateSkillId(skillId);
|
|
2896
|
+
if (isRuntimeConfigDisabled()) {
|
|
2897
|
+
throw new Error(
|
|
2898
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF0C\u4E0D\u5141\u8BB8\u65B0\u589E\u81EA\u5B9A\u4E49 skill\u3002`
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
3139
2901
|
const runtime = loadRuntimeConfig();
|
|
3140
|
-
const skillsDir =
|
|
2902
|
+
const skillsDir = join4(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3141
2903
|
try {
|
|
3142
2904
|
mkdirSync(skillsDir, { recursive: true });
|
|
3143
2905
|
} catch {
|
|
3144
2906
|
}
|
|
3145
|
-
writeFileSync(
|
|
2907
|
+
writeFileSync(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3146
2908
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3147
2909
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3148
2910
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3157,7 +2919,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3157
2919
|
if (idx >= 0) {
|
|
3158
2920
|
if (skills[idx].builtin) return null;
|
|
3159
2921
|
skills.splice(idx, 1);
|
|
3160
|
-
const skillPath =
|
|
2922
|
+
const skillPath = join4(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3161
2923
|
try {
|
|
3162
2924
|
if (existsSync5(skillPath)) unlinkSync(skillPath);
|
|
3163
2925
|
} catch {
|
|
@@ -3362,10 +3124,11 @@ var init_runtime_config = __esm({
|
|
|
3362
3124
|
init_skill_loader();
|
|
3363
3125
|
init_rules_loader();
|
|
3364
3126
|
init_runtime_config_bridge();
|
|
3127
|
+
init_rules_mode();
|
|
3365
3128
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3366
3129
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3367
3130
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3368
|
-
RUNTIME_CONFIG_PATH =
|
|
3131
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3369
3132
|
LAYER_DISABLE_ENV = {
|
|
3370
3133
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3371
3134
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3393,10 +3156,10 @@ var init_runtime_config = __esm({
|
|
|
3393
3156
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3394
3157
|
};
|
|
3395
3158
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3396
|
-
_AGENTS_DIR_PROD =
|
|
3397
|
-
_AGENTS_DIR_DEV =
|
|
3398
|
-
_SCENARIOS_DIR_PROD =
|
|
3399
|
-
_SCENARIOS_DIR_DEV =
|
|
3159
|
+
_AGENTS_DIR_PROD = join4(__dirname4, "agents");
|
|
3160
|
+
_AGENTS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3161
|
+
_SCENARIOS_DIR_PROD = join4(__dirname4, "scenarios");
|
|
3162
|
+
_SCENARIOS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3400
3163
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3401
3164
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3402
3165
|
registerRuntimeConfigBridge({
|
|
@@ -3433,6 +3196,7 @@ __export(config_exports, {
|
|
|
3433
3196
|
isPolicyGenEnabled: () => isPolicyGenEnabled,
|
|
3434
3197
|
isPolicySaveEnabled: () => isPolicySaveEnabled,
|
|
3435
3198
|
probeLLMConnection: () => probeLLMConnection,
|
|
3199
|
+
reloadCustomRules: () => reloadCustomRules,
|
|
3436
3200
|
updateLLMConfig: () => updateLLMConfig,
|
|
3437
3201
|
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
3438
3202
|
wildcardToRegExp: () => wildcardToRegExp
|
|
@@ -3513,17 +3277,19 @@ function wildcardToRegExp(pattern) {
|
|
|
3513
3277
|
}
|
|
3514
3278
|
function loadCustomRules() {
|
|
3515
3279
|
const rules = [];
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3280
|
+
if (!isBuiltinOnly()) {
|
|
3281
|
+
try {
|
|
3282
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
3283
|
+
if (raw) {
|
|
3284
|
+
const parsed = JSON.parse(raw);
|
|
3285
|
+
if (Array.isArray(parsed)) {
|
|
3286
|
+
for (const r of parsed) {
|
|
3287
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
3288
|
+
}
|
|
3523
3289
|
}
|
|
3524
3290
|
}
|
|
3291
|
+
} catch {
|
|
3525
3292
|
}
|
|
3526
|
-
} catch {
|
|
3527
3293
|
}
|
|
3528
3294
|
for (const rulesPath of USER_RULES_PATHS) {
|
|
3529
3295
|
try {
|
|
@@ -3552,6 +3318,9 @@ function userRuleVariantPattern(pattern) {
|
|
|
3552
3318
|
}
|
|
3553
3319
|
return null;
|
|
3554
3320
|
}
|
|
3321
|
+
function reloadCustomRules() {
|
|
3322
|
+
customRules = loadCustomRules();
|
|
3323
|
+
}
|
|
3555
3324
|
function getLLMConfig() {
|
|
3556
3325
|
return llmConfig;
|
|
3557
3326
|
}
|
|
@@ -3639,6 +3408,7 @@ function layerEnabledOrDefault(key) {
|
|
|
3639
3408
|
}
|
|
3640
3409
|
}
|
|
3641
3410
|
function isBrainEnabled() {
|
|
3411
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
3642
3412
|
return getAgentMossSetting("AGENT_MOSS_BRAIN_ENABLED", "") === "1";
|
|
3643
3413
|
}
|
|
3644
3414
|
function getAgentMossSetting(key, def = "") {
|
|
@@ -3680,6 +3450,7 @@ var init_config = __esm({
|
|
|
3680
3450
|
init_esm_shims();
|
|
3681
3451
|
init_paths();
|
|
3682
3452
|
init_runtime_config();
|
|
3453
|
+
init_rules_mode();
|
|
3683
3454
|
__configModuleDir = (() => {
|
|
3684
3455
|
try {
|
|
3685
3456
|
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
@@ -3727,16 +3498,20 @@ var init_config = __esm({
|
|
|
3727
3498
|
|
|
3728
3499
|
// src/agents/index.ts
|
|
3729
3500
|
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
3730
|
-
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3501
|
+
import { resolve as resolve4, dirname as dirname4, join as join5 } from "path";
|
|
3731
3502
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3732
3503
|
function resolveAgentsDir() {
|
|
3504
|
+
const root = getRulesRoot();
|
|
3505
|
+
if (root) return join5(root, "agents");
|
|
3733
3506
|
if (existsSync6(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3734
3507
|
if (existsSync6(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3735
3508
|
return AGENTS_DIR_PROD;
|
|
3736
3509
|
}
|
|
3737
3510
|
function getAgentExtensions(agentId) {
|
|
3738
3511
|
if (!agentId) return null;
|
|
3739
|
-
|
|
3512
|
+
const fp = getRuleSourceFingerprint();
|
|
3513
|
+
const cached2 = cache.get(agentId);
|
|
3514
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3740
3515
|
let ext = null;
|
|
3741
3516
|
try {
|
|
3742
3517
|
if (hasRuntimeConfig()) {
|
|
@@ -3748,7 +3523,7 @@ function getAgentExtensions(agentId) {
|
|
|
3748
3523
|
const agentsDir = resolveAgentsDir();
|
|
3749
3524
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3750
3525
|
if (!existsSync6(rulesFile)) {
|
|
3751
|
-
cache.set(agentId, null);
|
|
3526
|
+
cache.set(agentId, { fp, ext: null });
|
|
3752
3527
|
return null;
|
|
3753
3528
|
}
|
|
3754
3529
|
try {
|
|
@@ -3756,16 +3531,16 @@ function getAgentExtensions(agentId) {
|
|
|
3756
3531
|
ext = JSON.parse(raw);
|
|
3757
3532
|
if (!ext.agent_id) {
|
|
3758
3533
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3759
|
-
cache.set(agentId, null);
|
|
3534
|
+
cache.set(agentId, { fp, ext: null });
|
|
3760
3535
|
return null;
|
|
3761
3536
|
}
|
|
3762
3537
|
} catch (e) {
|
|
3763
3538
|
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
3764
|
-
cache.set(agentId, null);
|
|
3539
|
+
cache.set(agentId, { fp, ext: null });
|
|
3765
3540
|
return null;
|
|
3766
3541
|
}
|
|
3767
3542
|
}
|
|
3768
|
-
cache.set(agentId, ext);
|
|
3543
|
+
cache.set(agentId, { fp, ext });
|
|
3769
3544
|
console.error(
|
|
3770
3545
|
`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
|
|
3771
3546
|
);
|
|
@@ -3791,6 +3566,7 @@ var init_agents = __esm({
|
|
|
3791
3566
|
"src/agents/index.ts"() {
|
|
3792
3567
|
"use strict";
|
|
3793
3568
|
init_esm_shims();
|
|
3569
|
+
init_paths();
|
|
3794
3570
|
init_runtime_config();
|
|
3795
3571
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3796
3572
|
__dirname5 = dirname4(__filename4);
|
|
@@ -3802,16 +3578,20 @@ var init_agents = __esm({
|
|
|
3802
3578
|
|
|
3803
3579
|
// src/scenarios/index.ts
|
|
3804
3580
|
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3805
|
-
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
3581
|
+
import { resolve as resolve5, dirname as dirname5, join as join6 } from "path";
|
|
3806
3582
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3807
3583
|
function resolveScenariosDir() {
|
|
3584
|
+
const root = getRulesRoot();
|
|
3585
|
+
if (root) return join6(root, "scenarios");
|
|
3808
3586
|
if (existsSync7(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3809
3587
|
if (existsSync7(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3810
3588
|
return SCENARIOS_DIR_PROD;
|
|
3811
3589
|
}
|
|
3812
3590
|
function getScenarioExtensions(scenarioId) {
|
|
3813
3591
|
if (!scenarioId) return null;
|
|
3814
|
-
|
|
3592
|
+
const fp = getRuleSourceFingerprint();
|
|
3593
|
+
const cached2 = cache2.get(scenarioId);
|
|
3594
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3815
3595
|
let ext = null;
|
|
3816
3596
|
let overrideHit = null;
|
|
3817
3597
|
try {
|
|
@@ -3835,7 +3615,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3835
3615
|
);
|
|
3836
3616
|
}
|
|
3837
3617
|
if (!existsSync7(rulesFile)) {
|
|
3838
|
-
cache2.set(scenarioId, null);
|
|
3618
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3839
3619
|
return null;
|
|
3840
3620
|
}
|
|
3841
3621
|
try {
|
|
@@ -3844,7 +3624,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3844
3624
|
const id = data.scenario_id || data.agent_id || "";
|
|
3845
3625
|
if (!id) {
|
|
3846
3626
|
console.warn(`[AgentMoss scenarios] rules.json \u7F3A\u5C11 scenario_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3847
|
-
cache2.set(scenarioId, null);
|
|
3627
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3848
3628
|
return null;
|
|
3849
3629
|
}
|
|
3850
3630
|
ext = {
|
|
@@ -3859,11 +3639,11 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3859
3639
|
};
|
|
3860
3640
|
} catch (e) {
|
|
3861
3641
|
console.error(`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} rules.json \u5931\u8D25:`, e.message);
|
|
3862
|
-
cache2.set(scenarioId, null);
|
|
3642
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3863
3643
|
return null;
|
|
3864
3644
|
}
|
|
3865
3645
|
}
|
|
3866
|
-
cache2.set(scenarioId, ext);
|
|
3646
|
+
cache2.set(scenarioId, { fp, ext });
|
|
3867
3647
|
console.error(
|
|
3868
3648
|
`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths`
|
|
3869
3649
|
);
|
|
@@ -3883,6 +3663,7 @@ var init_scenarios = __esm({
|
|
|
3883
3663
|
"src/scenarios/index.ts"() {
|
|
3884
3664
|
"use strict";
|
|
3885
3665
|
init_esm_shims();
|
|
3666
|
+
init_paths();
|
|
3886
3667
|
init_agents();
|
|
3887
3668
|
init_runtime_config();
|
|
3888
3669
|
init_rules_loader();
|
|
@@ -4289,7 +4070,7 @@ function severityRank(s) {
|
|
|
4289
4070
|
}
|
|
4290
4071
|
function isSearchActionType(actionType) {
|
|
4291
4072
|
const at = actionType.toLowerCase();
|
|
4292
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4073
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4293
4074
|
}
|
|
4294
4075
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4295
4076
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4579,8 +4360,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4579
4360
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4580
4361
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4581
4362
|
const scan = stripHeredocContent(actionDetail);
|
|
4582
|
-
const
|
|
4583
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4363
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4584
4364
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4585
4365
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4586
4366
|
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;
|
|
@@ -4726,7 +4506,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4726
4506
|
}
|
|
4727
4507
|
return null;
|
|
4728
4508
|
}
|
|
4729
|
-
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;
|
|
4509
|
+
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;
|
|
4730
4510
|
var init_heuristic = __esm({
|
|
4731
4511
|
"src/engine/heuristic.ts"() {
|
|
4732
4512
|
"use strict";
|
|
@@ -4743,7 +4523,8 @@ var init_heuristic = __esm({
|
|
|
4743
4523
|
return kws.map((k) => ({
|
|
4744
4524
|
keyword: k.keyword,
|
|
4745
4525
|
riskLevel: k.risk_level,
|
|
4746
|
-
lang: k.lang || "en"
|
|
4526
|
+
lang: k.lang || "en",
|
|
4527
|
+
os: String(k.os || "all").toLowerCase()
|
|
4747
4528
|
}));
|
|
4748
4529
|
})();
|
|
4749
4530
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4751,7 +4532,9 @@ var init_heuristic = __esm({
|
|
|
4751
4532
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4752
4533
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4753
4534
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4754
|
-
|
|
4535
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4536
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4537
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4755
4538
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4756
4539
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4757
4540
|
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;
|
|
@@ -4759,8 +4542,9 @@ var init_heuristic = __esm({
|
|
|
4759
4542
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4760
4543
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4761
4544
|
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4762
|
-
SENSITIVE_EXCLUDED_PREFIXES =
|
|
4763
|
-
SEARCH_ACTION_PREFIXES =
|
|
4545
|
+
SENSITIVE_EXCLUDED_PREFIXES = HEURISTIC_RULES.sensitive_excluded_prefixes ?? [];
|
|
4546
|
+
SEARCH_ACTION_PREFIXES = HEURISTIC_RULES.search_action_prefixes ?? [];
|
|
4547
|
+
SEARCH_ACTION_EXACT = HEURISTIC_RULES.search_action_exact ?? [];
|
|
4764
4548
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4765
4549
|
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;
|
|
4766
4550
|
}
|
|
@@ -4986,87 +4770,25 @@ function loadTemplates() {
|
|
|
4986
4770
|
|
|
4987
4771
|
// src/engine/script-analyzer.ts
|
|
4988
4772
|
init_esm_shims();
|
|
4773
|
+
init_rules_loader();
|
|
4989
4774
|
import { readFileSync as readFileSync8 } from "fs";
|
|
4990
|
-
import { resolve as resolve7, join as
|
|
4991
|
-
var
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
// Sensitive file access
|
|
5008
|
-
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
5009
|
-
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
5010
|
-
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
5011
|
-
// Persistence backdoor
|
|
5012
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
5013
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
5014
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
5015
|
-
];
|
|
5016
|
-
var SCRIPT_EXEC_PATTERNS = [
|
|
5017
|
-
/bash\s+(\S+\.sh)/,
|
|
5018
|
-
/sh\s+(\S+\.sh)/,
|
|
5019
|
-
/\.\/(\S+\.sh)/,
|
|
5020
|
-
/source\s+(\S+\.sh)/,
|
|
5021
|
-
/\.\s+(\S+\.sh)/,
|
|
5022
|
-
/zsh\s+(\S+\.sh)/
|
|
5023
|
-
];
|
|
5024
|
-
var KEYWORD_COMBINATION_RISKS = [
|
|
5025
|
-
{
|
|
5026
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5027
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
5028
|
-
level: "critical",
|
|
5029
|
-
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"
|
|
5030
|
-
},
|
|
5031
|
-
{
|
|
5032
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5033
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
5034
|
-
level: "critical",
|
|
5035
|
-
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"
|
|
5036
|
-
},
|
|
5037
|
-
{
|
|
5038
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
5039
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5040
|
-
level: "high",
|
|
5041
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
5042
|
-
},
|
|
5043
|
-
{
|
|
5044
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
5045
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5046
|
-
level: "high",
|
|
5047
|
-
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"
|
|
5048
|
-
}
|
|
5049
|
-
];
|
|
5050
|
-
var SINGLE_KEYWORD_RISKS = {
|
|
5051
|
-
"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" },
|
|
5052
|
-
"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" },
|
|
5053
|
-
"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" },
|
|
5054
|
-
"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" },
|
|
5055
|
-
"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" },
|
|
5056
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5057
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5058
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5059
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5060
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5061
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5062
|
-
"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" },
|
|
5063
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
5064
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5065
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5066
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5067
|
-
"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" },
|
|
5068
|
-
"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" }
|
|
5069
|
-
};
|
|
4775
|
+
import { resolve as resolve7, join as join7 } from "path";
|
|
4776
|
+
var SCRIPT_RULES = loadScriptRules();
|
|
4777
|
+
if (!SCRIPT_RULES || Object.keys(SCRIPT_RULES).length === 0) {
|
|
4778
|
+
console.error(
|
|
4779
|
+
"[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"
|
|
4780
|
+
);
|
|
4781
|
+
}
|
|
4782
|
+
var SUSPICIOUS_PATTERNS = (SCRIPT_RULES.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
4783
|
+
var SCRIPT_EXEC_PATTERNS = ((SCRIPT_RULES.script_exec_patterns ?? {}).legacy ?? []).map((r) => compileRuleRegex(r));
|
|
4784
|
+
var KEYWORD_SETS = SCRIPT_RULES.keyword_sets ?? {};
|
|
4785
|
+
var KEYWORD_COMBINATION_RISKS = (SCRIPT_RULES.keyword_combination_risks ?? []).map((r) => ({
|
|
4786
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS),
|
|
4787
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS),
|
|
4788
|
+
level: String(r.level),
|
|
4789
|
+
reason: String(r.reason)
|
|
4790
|
+
}));
|
|
4791
|
+
var SINGLE_KEYWORD_RISKS = SCRIPT_RULES.single_keyword_risks ?? {};
|
|
5070
4792
|
var LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5071
4793
|
function emptyResult() {
|
|
5072
4794
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
@@ -5093,10 +4815,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5093
4815
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
5094
4816
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
5095
4817
|
}
|
|
5096
|
-
const networkPostKeywords =
|
|
4818
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
5097
4819
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
5098
4820
|
if (hasNetworkPost) {
|
|
5099
|
-
const envPatterns =
|
|
4821
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
5100
4822
|
const contentLower = scriptContent.toLowerCase();
|
|
5101
4823
|
for (const p of envPatterns) {
|
|
5102
4824
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -5120,7 +4842,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5120
4842
|
}
|
|
5121
4843
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
5122
4844
|
try {
|
|
5123
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
4845
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join7(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
5124
4846
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
5125
4847
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
5126
4848
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -5178,7 +4900,7 @@ init_config();
|
|
|
5178
4900
|
init_logic_rules();
|
|
5179
4901
|
init_inline_analyzer();
|
|
5180
4902
|
import { statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
5181
|
-
import { resolve as resolve9, join as
|
|
4903
|
+
import { resolve as resolve9, join as join9, isAbsolute as isAbsolute2 } from "path";
|
|
5182
4904
|
import { homedir as homedir3 } from "os";
|
|
5183
4905
|
|
|
5184
4906
|
// src/engine/script-engine/tracker.ts
|
|
@@ -6689,6 +6411,7 @@ var RecursiveTracker = class {
|
|
|
6689
6411
|
};
|
|
6690
6412
|
|
|
6691
6413
|
// src/engine/script-content-analyzer.ts
|
|
6414
|
+
init_rules_loader();
|
|
6692
6415
|
function emptyChain() {
|
|
6693
6416
|
return {
|
|
6694
6417
|
nodes: [],
|
|
@@ -6705,87 +6428,22 @@ function emptyChain() {
|
|
|
6705
6428
|
error: ""
|
|
6706
6429
|
};
|
|
6707
6430
|
}
|
|
6708
|
-
var
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
6725
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
6726
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
6727
|
-
];
|
|
6728
|
-
var SCRIPT_EXEC_PATTERNS2 = [
|
|
6729
|
-
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
6730
|
-
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
6731
|
-
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
6732
|
-
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
6733
|
-
/\.\/(\S+)/,
|
|
6734
|
-
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
6735
|
-
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
6736
|
-
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
6737
|
-
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
6738
|
-
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
6739
|
-
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
6740
|
-
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
6741
|
-
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
6742
|
-
];
|
|
6743
|
-
var KEYWORD_COMBINATION_RISKS2 = [
|
|
6744
|
-
{
|
|
6745
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
6746
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
6747
|
-
level: "critical",
|
|
6748
|
-
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"
|
|
6749
|
-
},
|
|
6750
|
-
{
|
|
6751
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
6752
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
6753
|
-
level: "critical",
|
|
6754
|
-
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"
|
|
6755
|
-
},
|
|
6756
|
-
{
|
|
6757
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
6758
|
-
group2: /* @__PURE__ */ new Set(),
|
|
6759
|
-
level: "high",
|
|
6760
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
6761
|
-
},
|
|
6762
|
-
{
|
|
6763
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
6764
|
-
group2: /* @__PURE__ */ new Set(),
|
|
6765
|
-
level: "high",
|
|
6766
|
-
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"
|
|
6767
|
-
}
|
|
6768
|
-
];
|
|
6769
|
-
var SINGLE_KEYWORD_RISKS2 = {
|
|
6770
|
-
"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" },
|
|
6771
|
-
"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" },
|
|
6772
|
-
"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" },
|
|
6773
|
-
"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" },
|
|
6774
|
-
"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" },
|
|
6775
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6776
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6777
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6778
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6779
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
6780
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
6781
|
-
"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" },
|
|
6782
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
6783
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
6784
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
6785
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
6786
|
-
"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" },
|
|
6787
|
-
"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" }
|
|
6788
|
-
};
|
|
6431
|
+
var SCRIPT_RULES2 = loadScriptRules();
|
|
6432
|
+
if (!SCRIPT_RULES2 || Object.keys(SCRIPT_RULES2).length === 0) {
|
|
6433
|
+
console.error(
|
|
6434
|
+
"[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"
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
6437
|
+
var SUSPICIOUS_PATTERNS2 = (SCRIPT_RULES2.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
6438
|
+
var SCRIPT_EXEC_PATTERNS2 = ((SCRIPT_RULES2.script_exec_patterns ?? {}).chain ?? []).map((r) => compileRuleRegex(r));
|
|
6439
|
+
var KEYWORD_SETS2 = SCRIPT_RULES2.keyword_sets ?? {};
|
|
6440
|
+
var KEYWORD_COMBINATION_RISKS2 = (SCRIPT_RULES2.keyword_combination_risks ?? []).map((r) => ({
|
|
6441
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS2),
|
|
6442
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS2),
|
|
6443
|
+
level: String(r.level),
|
|
6444
|
+
reason: String(r.reason)
|
|
6445
|
+
}));
|
|
6446
|
+
var SINGLE_KEYWORD_RISKS2 = SCRIPT_RULES2.single_keyword_risks ?? {};
|
|
6789
6447
|
var LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
6790
6448
|
function scanScriptForKeywords2(content) {
|
|
6791
6449
|
const matched = [];
|
|
@@ -6802,10 +6460,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6802
6460
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6803
6461
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6804
6462
|
}
|
|
6805
|
-
const networkPostKeywords =
|
|
6463
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6806
6464
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6807
6465
|
if (hasNetworkPost) {
|
|
6808
|
-
const envPatterns =
|
|
6466
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6809
6467
|
const contentLower = scriptContent.toLowerCase();
|
|
6810
6468
|
for (const p of envPatterns) {
|
|
6811
6469
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -6952,7 +6610,7 @@ function shlexSplit(s) {
|
|
|
6952
6610
|
function resolvePath2(p) {
|
|
6953
6611
|
let expanded = p;
|
|
6954
6612
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6955
|
-
expanded =
|
|
6613
|
+
expanded = join9(homedir3(), expanded.slice(1));
|
|
6956
6614
|
}
|
|
6957
6615
|
return resolve9(expanded);
|
|
6958
6616
|
}
|
|
@@ -7076,7 +6734,7 @@ function analyzeScriptContent2(input, ext) {
|
|
|
7076
6734
|
isInline = true;
|
|
7077
6735
|
}
|
|
7078
6736
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
7079
|
-
resolvedPath =
|
|
6737
|
+
resolvedPath = join9(input.cwd || process.cwd(), resolvedPath);
|
|
7080
6738
|
}
|
|
7081
6739
|
const l1Adapter = (content) => {
|
|
7082
6740
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7184,10 +6842,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7184
6842
|
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
7185
6843
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
7186
6844
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
7187
|
-
const
|
|
7188
|
-
if (
|
|
6845
|
+
const cached2 = llmCache.get(cacheKey);
|
|
6846
|
+
if (cached2) {
|
|
7189
6847
|
console.log("[AgentMoss] LLM cache hit");
|
|
7190
|
-
return
|
|
6848
|
+
return cached2;
|
|
7191
6849
|
}
|
|
7192
6850
|
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
7193
6851
|
const templates = loadTemplates();
|
|
@@ -7400,9 +7058,9 @@ init_esm_shims();
|
|
|
7400
7058
|
// src/brain/rule-store.ts
|
|
7401
7059
|
init_esm_shims();
|
|
7402
7060
|
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
7403
|
-
import { join as
|
|
7061
|
+
import { join as join10 } from "path";
|
|
7404
7062
|
import { homedir as homedir4 } from "os";
|
|
7405
|
-
var DEFAULT_RULES_PATH =
|
|
7063
|
+
var DEFAULT_RULES_PATH = join10(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
7406
7064
|
var ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7407
7065
|
var STATUS_DRAFT = "draft";
|
|
7408
7066
|
var STATUS_APPROVED = "approved";
|
|
@@ -8017,9 +7675,9 @@ function parseReview(content) {
|
|
|
8017
7675
|
init_esm_shims();
|
|
8018
7676
|
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8019
7677
|
import { homedir as homedir5 } from "os";
|
|
8020
|
-
import { join as
|
|
7678
|
+
import { join as join11 } from "path";
|
|
8021
7679
|
import { randomUUID } from "crypto";
|
|
8022
|
-
var DEFAULT_SKILL_META_PATH =
|
|
7680
|
+
var DEFAULT_SKILL_META_PATH = join11(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8023
7681
|
var ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8024
7682
|
function getMetaPath(override) {
|
|
8025
7683
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8029,7 +7687,7 @@ var BrainSkillManager = class {
|
|
|
8029
7687
|
this.skillsDir = skillsDir;
|
|
8030
7688
|
this.metaPath = getMetaPath(metaPath);
|
|
8031
7689
|
try {
|
|
8032
|
-
mkdirSync4(
|
|
7690
|
+
mkdirSync4(join11(this.metaPath, ".."), { recursive: true });
|
|
8033
7691
|
} catch {
|
|
8034
7692
|
}
|
|
8035
7693
|
this.load();
|
|
@@ -8064,7 +7722,7 @@ var BrainSkillManager = class {
|
|
|
8064
7722
|
let changed = false;
|
|
8065
7723
|
for (const sk of this.skills) {
|
|
8066
7724
|
if (sk.status !== "active") continue;
|
|
8067
|
-
if (!existsSync13(
|
|
7725
|
+
if (!existsSync13(join11(this.skillsDir, `${sk.name}.md`))) {
|
|
8068
7726
|
sk.status = "stale";
|
|
8069
7727
|
changed = true;
|
|
8070
7728
|
}
|
|
@@ -8076,7 +7734,7 @@ var BrainSkillManager = class {
|
|
|
8076
7734
|
const id = skill.id || randomUUID();
|
|
8077
7735
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8078
7736
|
if (!existsSync13(this.skillsDir)) return "";
|
|
8079
|
-
const mdPath =
|
|
7737
|
+
const mdPath = join11(this.skillsDir, `${name}.md`);
|
|
8080
7738
|
if (existsSync13(mdPath)) return "";
|
|
8081
7739
|
try {
|
|
8082
7740
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
@@ -8126,7 +7784,7 @@ var BrainSkillManager = class {
|
|
|
8126
7784
|
const out = [];
|
|
8127
7785
|
for (const sk of this.skills) {
|
|
8128
7786
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8129
|
-
const mdPath =
|
|
7787
|
+
const mdPath = join11(this.skillsDir, `${sk.name}.md`);
|
|
8130
7788
|
if (!existsSync13(mdPath)) continue;
|
|
8131
7789
|
out.push({
|
|
8132
7790
|
name: sk.name,
|
|
@@ -8278,6 +7936,7 @@ init_rules_loader();
|
|
|
8278
7936
|
|
|
8279
7937
|
// src/engine/formal-engine/decode.ts
|
|
8280
7938
|
init_esm_shims();
|
|
7939
|
+
init_rules_loader();
|
|
8281
7940
|
var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8282
7941
|
function isValidBase64Token(tok) {
|
|
8283
7942
|
if (tok.length < 8) return false;
|
|
@@ -8312,7 +7971,7 @@ function decodeBase64Literals(text) {
|
|
|
8312
7971
|
return out;
|
|
8313
7972
|
}
|
|
8314
7973
|
var QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8315
|
-
var OUTPUT_CMDS =
|
|
7974
|
+
var OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8316
7975
|
function stripStringLiterals(text) {
|
|
8317
7976
|
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
8318
7977
|
if (!m) return text;
|
|
@@ -8797,8 +8456,9 @@ function extractInlineShell(s) {
|
|
|
8797
8456
|
}
|
|
8798
8457
|
return out;
|
|
8799
8458
|
}
|
|
8800
|
-
var
|
|
8801
|
-
var
|
|
8459
|
+
var FORMAL_RULES = loadFormalRules();
|
|
8460
|
+
var PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
8461
|
+
var SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
8802
8462
|
function containsPrivesc(shell) {
|
|
8803
8463
|
if (!shell) return false;
|
|
8804
8464
|
for (const seg of shell.split(/\&\&|\|\||;|\n/)) {
|
|
@@ -8971,7 +8631,7 @@ function lookupToolClass(actionType) {
|
|
|
8971
8631
|
return entry.actionClass;
|
|
8972
8632
|
}
|
|
8973
8633
|
var 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;
|
|
8974
|
-
var STRONG_COMPUTE_TYPES =
|
|
8634
|
+
var STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
8975
8635
|
function isComputeNode(n) {
|
|
8976
8636
|
if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
|
|
8977
8637
|
const cls = lookupToolClass(n.actionType);
|
|
@@ -9197,25 +8857,7 @@ import * as path4 from "path";
|
|
|
9197
8857
|
import { createHash } from "crypto";
|
|
9198
8858
|
|
|
9199
8859
|
// src/engine/coordinator.ts
|
|
9200
|
-
var FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
9201
|
-
"ask_user_question",
|
|
9202
|
-
"glob",
|
|
9203
|
-
"list_dir",
|
|
9204
|
-
"ls",
|
|
9205
|
-
"count_text_length",
|
|
9206
|
-
"filemgr-globfiles"
|
|
9207
|
-
]);
|
|
9208
8860
|
var TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
9209
|
-
var READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
9210
|
-
"read",
|
|
9211
|
-
"file_read",
|
|
9212
|
-
"read_file",
|
|
9213
|
-
"head",
|
|
9214
|
-
"tail",
|
|
9215
|
-
"grep",
|
|
9216
|
-
"filemgr-readfile",
|
|
9217
|
-
"filemgr-grepfiles"
|
|
9218
|
-
]);
|
|
9219
8861
|
function whitelistTier(command, fullySafe, readonlySensitive) {
|
|
9220
8862
|
const segments = splitCommandSegments(command);
|
|
9221
8863
|
if (segments.length === 0) return "";
|
|
@@ -9372,7 +9014,7 @@ async function runAnalysis(input) {
|
|
|
9372
9014
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
9373
9015
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
9374
9016
|
const lowerDetail = rawDetail.toLowerCase();
|
|
9375
|
-
if (
|
|
9017
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
9376
9018
|
return {
|
|
9377
9019
|
allowed: true,
|
|
9378
9020
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -9415,7 +9057,7 @@ async function runAnalysis(input) {
|
|
|
9415
9057
|
violatedLayers: []
|
|
9416
9058
|
};
|
|
9417
9059
|
}
|
|
9418
|
-
let skipLLM =
|
|
9060
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
9419
9061
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
9420
9062
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
9421
9063
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|