@kenhkl/agent-moss 0.16.13 → 0.16.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +958 -1257
- package/dist/engine/coordinator.js +405 -872
- package/dist/rules/formal_rules.json +61 -0
- package/dist/rules/heuristic_rules.json +40 -0
- package/dist/rules/inline_rules.json +151 -0
- package/dist/rules/l1_rules.json +115 -49
- package/dist/rules/logic_rules.json +73 -0
- package/dist/rules/manifest.json +6 -0
- package/dist/rules/platform_traits.json +389 -0
- package/dist/rules/script_rules.json +311 -0
- package/dist/server.js +692 -1013
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -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,295 +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");
|
|
105
|
-
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// src/engine/shell-split.ts
|
|
109
|
-
function hasCommandSubstitution(text) {
|
|
110
|
-
return CMD_SUBSTITUTION.test(text);
|
|
111
|
-
}
|
|
112
|
-
function splitCommandSegments(command) {
|
|
113
|
-
const segments = [];
|
|
114
|
-
let cur = "";
|
|
115
|
-
let quote = null;
|
|
116
|
-
for (let i = 0; i < command.length; i++) {
|
|
117
|
-
const ch = command[i];
|
|
118
|
-
if (quote) {
|
|
119
|
-
cur += ch;
|
|
120
|
-
if (ch === quote) quote = null;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (ch === "'" || ch === '"') {
|
|
124
|
-
quote = ch;
|
|
125
|
-
cur += ch;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (/\||&|;|\n/.test(ch)) {
|
|
129
|
-
segments.push(cur);
|
|
130
|
-
segments.push(ch);
|
|
131
|
-
cur = "";
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
cur += ch;
|
|
135
|
-
}
|
|
136
|
-
if (cur) segments.push(cur);
|
|
137
|
-
return segments.map((s) => s.trim()).filter((s) => s.length > 0 && !/^(?:\|\||&&|[|;&])$/.test(s));
|
|
138
|
-
}
|
|
139
|
-
function extractFirstCommand(command) {
|
|
140
|
-
const segments = splitCommandSegments(command);
|
|
141
|
-
return segments.length ? segments[0] : "";
|
|
142
|
-
}
|
|
143
|
-
var CMD_SUBSTITUTION;
|
|
144
|
-
var init_shell_split = __esm({
|
|
145
|
-
"src/engine/shell-split.ts"() {
|
|
146
|
-
"use strict";
|
|
147
|
-
init_esm_shims();
|
|
148
|
-
CMD_SUBSTITUTION = /\$\(|`|</;
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// src/engine/inline-analyzer.ts
|
|
153
|
-
function isLiteralTextCommand(command) {
|
|
154
|
-
const segments = splitCommandSegments(command);
|
|
155
|
-
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
156
|
-
if (command.includes(">")) {
|
|
157
|
-
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
158
|
-
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
159
|
-
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
160
|
-
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
161
|
-
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
function extractInlineCode(command) {
|
|
171
|
-
const parts = [];
|
|
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");
|
|
184
|
-
}
|
|
185
|
-
function hasWriteExecChain(command) {
|
|
186
|
-
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
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));
|
|
194
|
-
}
|
|
195
|
-
function hasReadWriteKeyword(code) {
|
|
196
|
-
const lower = code.toLowerCase();
|
|
197
|
-
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
198
|
-
}
|
|
199
|
-
function analyzeInlineScript(command) {
|
|
200
|
-
const code = extractInlineCode(command);
|
|
201
|
-
if (!code) {
|
|
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"() {
|
|
248
|
-
"use strict";
|
|
249
|
-
init_esm_shims();
|
|
250
|
-
init_shell_split();
|
|
251
|
-
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
252
|
-
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
253
|
-
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
254
|
-
READ_WRITE_KEYWORDS = [
|
|
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
|
-
];
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// src/infra/runtime-config-bridge.ts
|
|
385
|
-
function registerRuntimeConfigBridge(bridge) {
|
|
386
|
-
runtimeConfigBridge.current = bridge;
|
|
387
|
-
}
|
|
388
|
-
var runtimeConfigBridge;
|
|
389
|
-
var init_runtime_config_bridge = __esm({
|
|
390
|
-
"src/infra/runtime-config-bridge.ts"() {
|
|
391
|
-
"use strict";
|
|
392
|
-
init_esm_shims();
|
|
393
|
-
runtimeConfigBridge = { current: null };
|
|
109
|
+
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
394
110
|
}
|
|
395
111
|
});
|
|
396
112
|
|
|
@@ -440,6 +156,19 @@ var init_rules_mode = __esm({
|
|
|
440
156
|
}
|
|
441
157
|
});
|
|
442
158
|
|
|
159
|
+
// src/infra/runtime-config-bridge.ts
|
|
160
|
+
function registerRuntimeConfigBridge(bridge) {
|
|
161
|
+
runtimeConfigBridge.current = bridge;
|
|
162
|
+
}
|
|
163
|
+
var runtimeConfigBridge;
|
|
164
|
+
var init_runtime_config_bridge = __esm({
|
|
165
|
+
"src/infra/runtime-config-bridge.ts"() {
|
|
166
|
+
"use strict";
|
|
167
|
+
init_esm_shims();
|
|
168
|
+
runtimeConfigBridge = { current: null };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
443
172
|
// src/infra/rules-loader.ts
|
|
444
173
|
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
445
174
|
import { resolve, dirname, join as join2 } from "path";
|
|
@@ -459,13 +188,34 @@ function rulesDirs() {
|
|
|
459
188
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
460
189
|
return [...new Set(dirs)];
|
|
461
190
|
}
|
|
191
|
+
function rulesDirCands() {
|
|
192
|
+
const root = getRulesRoot();
|
|
193
|
+
if (root) return [join2(root, "rules")];
|
|
194
|
+
const cands = [
|
|
195
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
196
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
197
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
198
|
+
resolve(__dirname2, "../rules"),
|
|
199
|
+
resolve(__dirname2, "rules"),
|
|
200
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
201
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
202
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
203
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
204
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
205
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
206
|
+
];
|
|
207
|
+
return cands.filter((c) => existsSync(c));
|
|
208
|
+
}
|
|
462
209
|
function findRulesFile(name) {
|
|
463
|
-
for (const dir of
|
|
210
|
+
for (const dir of rulesDirCands()) {
|
|
464
211
|
const p = resolve(dir, name);
|
|
465
212
|
if (existsSync(p)) return p;
|
|
466
213
|
}
|
|
467
214
|
return null;
|
|
468
215
|
}
|
|
216
|
+
function resolveRulesFile(name) {
|
|
217
|
+
return findRulesFile(name);
|
|
218
|
+
}
|
|
469
219
|
function loadJson(name) {
|
|
470
220
|
if (!isHotReloadEnabled()) {
|
|
471
221
|
const cached2 = frozenJsonCache.get(name);
|
|
@@ -516,6 +266,74 @@ function loadL1() {
|
|
|
516
266
|
function loadL2() {
|
|
517
267
|
return loadJson("l2_rules.json");
|
|
518
268
|
}
|
|
269
|
+
function regexFlags(flags) {
|
|
270
|
+
let out = "";
|
|
271
|
+
for (const ch of String(flags ?? "")) if (RE_FLAG_MAP[ch]) out += RE_FLAG_MAP[ch];
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
function compileRuleRegex(rule, extraFlags = "") {
|
|
275
|
+
return new RegExp(String(rule.pattern), regexFlags(rule.flags) + extraFlags);
|
|
276
|
+
}
|
|
277
|
+
function resolveKeywordSet(group, keywordSets) {
|
|
278
|
+
if (typeof group === "string") return new Set(keywordSets[group.slice(1)] ?? []);
|
|
279
|
+
return new Set(Array.isArray(group) ? group : []);
|
|
280
|
+
}
|
|
281
|
+
function loadFormalRules() {
|
|
282
|
+
return loadJson("formal_rules.json");
|
|
283
|
+
}
|
|
284
|
+
function loadLogicRules() {
|
|
285
|
+
return loadJson("logic_rules.json");
|
|
286
|
+
}
|
|
287
|
+
function loadHeuristicRules() {
|
|
288
|
+
return loadJson("heuristic_rules.json");
|
|
289
|
+
}
|
|
290
|
+
function loadScriptRules() {
|
|
291
|
+
return loadJson("script_rules.json");
|
|
292
|
+
}
|
|
293
|
+
function loadInline() {
|
|
294
|
+
return loadJson("inline_rules.json");
|
|
295
|
+
}
|
|
296
|
+
function loadPlatformTraits(osName) {
|
|
297
|
+
const data = loadJson("platform_traits.json");
|
|
298
|
+
if (!data || Object.keys(data).length === 0) {
|
|
299
|
+
console.error(
|
|
300
|
+
"[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"
|
|
301
|
+
);
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
const name = resolveTraitsOs(osName);
|
|
305
|
+
const entry = data[name];
|
|
306
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
307
|
+
console.error(`[AgentMoss] platform_traits.json \u91CC\u6CA1\u6709\u53EF\u7528\u7684 ${name} \u6761\u76EE \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u4E3A\u7A7A\u3002`);
|
|
308
|
+
return {};
|
|
309
|
+
}
|
|
310
|
+
const out = {};
|
|
311
|
+
for (const f of TRAITS_FIELDS) if (f in entry) out[f] = entry[f];
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
function resolveTraitsOs(osName) {
|
|
315
|
+
const data = loadJson("platform_traits.json");
|
|
316
|
+
let name = String(osName || "").trim().toLowerCase() || TRAITS_OS_FALLBACK;
|
|
317
|
+
const seen = /* @__PURE__ */ new Set();
|
|
318
|
+
for (; ; ) {
|
|
319
|
+
if (seen.has(name)) {
|
|
320
|
+
console.error(`[AgentMoss] platform_traits.json \u7684 inherits \u6210\u73AF\uFF08${name}\uFF09\u2014\u2014 \u56DE\u843D linux\u3002`);
|
|
321
|
+
return TRAITS_OS_FALLBACK;
|
|
322
|
+
}
|
|
323
|
+
seen.add(name);
|
|
324
|
+
const entry = data[name];
|
|
325
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
326
|
+
const nxt = entry.inherits;
|
|
327
|
+
if (typeof nxt === "string" && nxt.trim().toLowerCase() !== name) {
|
|
328
|
+
name = nxt.trim().toLowerCase();
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
return name;
|
|
332
|
+
}
|
|
333
|
+
if (name === TRAITS_OS_FALLBACK) return name;
|
|
334
|
+
name = TRAITS_OS_FALLBACK;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
519
337
|
function loadL3() {
|
|
520
338
|
const data = { ...loadJson("l3_rules.json") };
|
|
521
339
|
try {
|
|
@@ -850,7 +668,7 @@ function ruleFilesLoaded() {
|
|
|
850
668
|
function ignoredRuleFiles() {
|
|
851
669
|
return rulesCached().ignored;
|
|
852
670
|
}
|
|
853
|
-
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS,
|
|
671
|
+
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;
|
|
854
672
|
var init_rules_loader = __esm({
|
|
855
673
|
"src/infra/rules-loader.ts"() {
|
|
856
674
|
"use strict";
|
|
@@ -863,24 +681,20 @@ var init_rules_loader = __esm({
|
|
|
863
681
|
OVERRIDE_FILENAME = "override.json";
|
|
864
682
|
EXTRA_FILENAME = "extra.json";
|
|
865
683
|
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
866
|
-
RULES_DIR_CANDS = (() => {
|
|
867
|
-
const cands = [
|
|
868
|
-
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
869
|
-
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
870
|
-
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
871
|
-
resolve(__dirname2, "../rules"),
|
|
872
|
-
resolve(__dirname2, "rules"),
|
|
873
|
-
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
874
|
-
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
875
|
-
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
876
|
-
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
877
|
-
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
878
|
-
resolve(process.cwd(), "agent_moss/rules")
|
|
879
|
-
];
|
|
880
|
-
return cands.filter((c) => existsSync(c));
|
|
881
|
-
})();
|
|
882
684
|
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
883
685
|
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
686
|
+
RE_FLAG_MAP = { i: "i", s: "s", m: "m" };
|
|
687
|
+
TRAITS_FIELDS = [
|
|
688
|
+
"write_keywords",
|
|
689
|
+
"read_keywords",
|
|
690
|
+
"write_command_res",
|
|
691
|
+
"read_only_commands",
|
|
692
|
+
"fully_safe_commands",
|
|
693
|
+
"readonly_sensitive_commands",
|
|
694
|
+
"safe_action_types",
|
|
695
|
+
"readonly_sensitive_action_types"
|
|
696
|
+
];
|
|
697
|
+
TRAITS_OS_FALLBACK = "linux";
|
|
884
698
|
EXTRA_CATEGORIES = {
|
|
885
699
|
dangerous_commands: ["pattern"],
|
|
886
700
|
injection_detection: ["keyword", "lang"],
|
|
@@ -902,7 +716,199 @@ var init_rules_loader = __esm({
|
|
|
902
716
|
}
|
|
903
717
|
});
|
|
904
718
|
|
|
719
|
+
// src/engine/shell-split.ts
|
|
720
|
+
function hasCommandSubstitution(text) {
|
|
721
|
+
return CMD_SUBSTITUTION.test(text);
|
|
722
|
+
}
|
|
723
|
+
function splitCommandSegments(command) {
|
|
724
|
+
const segments = [];
|
|
725
|
+
let cur = "";
|
|
726
|
+
let quote = null;
|
|
727
|
+
for (let i = 0; i < command.length; i++) {
|
|
728
|
+
const ch = command[i];
|
|
729
|
+
if (quote) {
|
|
730
|
+
cur += ch;
|
|
731
|
+
if (ch === quote) quote = null;
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
if (ch === "'" || ch === '"') {
|
|
735
|
+
quote = ch;
|
|
736
|
+
cur += ch;
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (/\||&|;|\n/.test(ch)) {
|
|
740
|
+
segments.push(cur);
|
|
741
|
+
segments.push(ch);
|
|
742
|
+
cur = "";
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
cur += ch;
|
|
746
|
+
}
|
|
747
|
+
if (cur) segments.push(cur);
|
|
748
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0 && !/^(?:\|\||&&|[|;&])$/.test(s));
|
|
749
|
+
}
|
|
750
|
+
function extractFirstCommand(command) {
|
|
751
|
+
const segments = splitCommandSegments(command);
|
|
752
|
+
return segments.length ? segments[0] : "";
|
|
753
|
+
}
|
|
754
|
+
var CMD_SUBSTITUTION;
|
|
755
|
+
var init_shell_split = __esm({
|
|
756
|
+
"src/engine/shell-split.ts"() {
|
|
757
|
+
"use strict";
|
|
758
|
+
init_esm_shims();
|
|
759
|
+
CMD_SUBSTITUTION = /\$\(|`|</;
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// src/engine/inline-analyzer.ts
|
|
764
|
+
function buildChainRegex(pattern, flags) {
|
|
765
|
+
return new RegExp(pattern, (flags || "") + "g");
|
|
766
|
+
}
|
|
767
|
+
function isLiteralTextCommand(command) {
|
|
768
|
+
const segments = splitCommandSegments(command);
|
|
769
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
770
|
+
if (command.includes(">")) {
|
|
771
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
772
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
773
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
774
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
775
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
function extractInlineCode(command) {
|
|
785
|
+
const parts = [];
|
|
786
|
+
let m;
|
|
787
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
788
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
789
|
+
const code = m[1] ?? m[2];
|
|
790
|
+
if (code) parts.push(code);
|
|
791
|
+
}
|
|
792
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
793
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
794
|
+
const code = m[1] ?? m[2];
|
|
795
|
+
if (code) parts.push(code);
|
|
796
|
+
}
|
|
797
|
+
return parts.join("\n");
|
|
798
|
+
}
|
|
799
|
+
function hasWriteExecChain(command) {
|
|
800
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
801
|
+
p.lastIndex = 0;
|
|
802
|
+
return p.test(command);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
function hasDirTraversal(code) {
|
|
806
|
+
const lower = code.toLowerCase();
|
|
807
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
808
|
+
}
|
|
809
|
+
function hasReadWriteKeyword(code) {
|
|
810
|
+
const lower = code.toLowerCase();
|
|
811
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
812
|
+
}
|
|
813
|
+
function analyzeInlineScript(command) {
|
|
814
|
+
const code = extractInlineCode(command);
|
|
815
|
+
if (!code) {
|
|
816
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
817
|
+
}
|
|
818
|
+
if (hasWriteExecChain(command)) {
|
|
819
|
+
return {
|
|
820
|
+
actionable: true,
|
|
821
|
+
allowed: false,
|
|
822
|
+
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",
|
|
823
|
+
riskLevel: "critical",
|
|
824
|
+
riskType: "script_execution"
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
828
|
+
if (redirMatch) {
|
|
829
|
+
return {
|
|
830
|
+
actionable: true,
|
|
831
|
+
allowed: false,
|
|
832
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
833
|
+
riskLevel: "critical",
|
|
834
|
+
riskType: "file_access"
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
838
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
839
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
840
|
+
}
|
|
841
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
842
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
843
|
+
return {
|
|
844
|
+
actionable: true,
|
|
845
|
+
allowed: false,
|
|
846
|
+
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",
|
|
847
|
+
riskLevel: "critical",
|
|
848
|
+
riskType: "file_access"
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
actionable: true,
|
|
853
|
+
allowed: true,
|
|
854
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
855
|
+
riskLevel: "low",
|
|
856
|
+
riskType: "file_access"
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
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;
|
|
860
|
+
var init_inline_analyzer = __esm({
|
|
861
|
+
"src/engine/inline-analyzer.ts"() {
|
|
862
|
+
"use strict";
|
|
863
|
+
init_esm_shims();
|
|
864
|
+
init_shell_split();
|
|
865
|
+
init_rules_loader();
|
|
866
|
+
INLINE_RULES = loadInline();
|
|
867
|
+
if (!INLINE_RULES || Object.keys(INLINE_RULES).length === 0) {
|
|
868
|
+
console.error(
|
|
869
|
+
"[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"
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
873
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
874
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
875
|
+
READ_WRITE_KEYWORDS = INLINE_RULES.read_write_keywords ?? [];
|
|
876
|
+
WRITE_EXEC_CHAIN_PATTERNS = (INLINE_RULES.write_exec_chain_patterns ?? []).filter((p) => p && typeof p.pattern === "string").map((p) => buildChainRegex(p.pattern, p.flags ?? ""));
|
|
877
|
+
_REDIRECT_PATHS = INLINE_RULES.redirect_sensitive_paths ?? {};
|
|
878
|
+
SHELL_RC_FRAGMENTS = _REDIRECT_PATHS.shell_rc_fragments ?? [];
|
|
879
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
880
|
+
..._REDIRECT_PATHS.ts_static ?? [],
|
|
881
|
+
...SHELL_RC_FRAGMENTS
|
|
882
|
+
];
|
|
883
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
884
|
+
DIR_TRAVERSAL_KEYWORDS = INLINE_RULES.dir_traversal_keywords ?? [];
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
|
|
905
888
|
// src/engine/platform-traits.ts
|
|
889
|
+
function buildTraits(osName) {
|
|
890
|
+
const t = loadPlatformTraits(osName);
|
|
891
|
+
const strs = (k) => Array.isArray(t[k]) ? t[k] : [];
|
|
892
|
+
return {
|
|
893
|
+
writeKeywords: strs("write_keywords"),
|
|
894
|
+
readKeywords: strs("read_keywords"),
|
|
895
|
+
writeCommandRes: strs("write_command_res").map((p) => new RegExp(p)),
|
|
896
|
+
readOnlyCommands: new Set(strs("read_only_commands")),
|
|
897
|
+
fullySafeCommands: strs("fully_safe_commands").map((p) => new RegExp(p, "i")),
|
|
898
|
+
readonlySensitiveCommands: strs("readonly_sensitive_commands").map((p) => new RegExp(p, "i")),
|
|
899
|
+
safeActionTypes: new Set(strs("safe_action_types")),
|
|
900
|
+
readonlySensitiveActionTypes: new Set(strs("readonly_sensitive_action_types"))
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function traitsFor(key) {
|
|
904
|
+
const resolved = resolveTraitsOs(key);
|
|
905
|
+
let t = traitsCache.get(resolved);
|
|
906
|
+
if (!t) {
|
|
907
|
+
t = buildTraits(resolved);
|
|
908
|
+
traitsCache.set(resolved, t);
|
|
909
|
+
}
|
|
910
|
+
return t;
|
|
911
|
+
}
|
|
906
912
|
function getPlatformTraits(osType) {
|
|
907
913
|
const accepted = acceptedOsValues(osType);
|
|
908
914
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -910,368 +916,18 @@ function getPlatformTraits(osType) {
|
|
|
910
916
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
911
917
|
return LINUX_TRAITS;
|
|
912
918
|
}
|
|
913
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
919
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
914
920
|
var init_platform_traits = __esm({
|
|
915
921
|
"src/engine/platform-traits.ts"() {
|
|
916
922
|
"use strict";
|
|
917
923
|
init_esm_shims();
|
|
918
924
|
init_logic_rules();
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
"modify",
|
|
926
|
-
"\u4FEE\u6539",
|
|
927
|
-
"update",
|
|
928
|
-
"\u66F4\u65B0",
|
|
929
|
-
"delete",
|
|
930
|
-
"\u5220\u9664",
|
|
931
|
-
"remove",
|
|
932
|
-
"\u79FB\u9664",
|
|
933
|
-
"create",
|
|
934
|
-
"\u521B\u5EFA",
|
|
935
|
-
"append",
|
|
936
|
-
"\u8FFD\u52A0",
|
|
937
|
-
"truncate",
|
|
938
|
-
"\u622A\u65AD",
|
|
939
|
-
"overwrite",
|
|
940
|
-
"\u8986\u76D6",
|
|
941
|
-
"replace",
|
|
942
|
-
"\u66FF\u6362",
|
|
943
|
-
"edit"
|
|
944
|
-
],
|
|
945
|
-
readKeywords: [
|
|
946
|
-
"read",
|
|
947
|
-
"\u8BFB\u53D6",
|
|
948
|
-
"cat",
|
|
949
|
-
"head",
|
|
950
|
-
"tail",
|
|
951
|
-
"less",
|
|
952
|
-
"more",
|
|
953
|
-
"view",
|
|
954
|
-
"\u67E5\u770B",
|
|
955
|
-
"open",
|
|
956
|
-
"\u6253\u5F00",
|
|
957
|
-
"load",
|
|
958
|
-
"\u52A0\u8F7D",
|
|
959
|
-
"grep",
|
|
960
|
-
"search",
|
|
961
|
-
"\u641C\u7D22",
|
|
962
|
-
"find",
|
|
963
|
-
"\u67E5\u627E"
|
|
964
|
-
],
|
|
965
|
-
writeCommandRes: [
|
|
966
|
-
/\brm\b/,
|
|
967
|
-
/\bunlink\b/,
|
|
968
|
-
/\brmdir\b/,
|
|
969
|
-
/\bshred\b/,
|
|
970
|
-
/\bcp\b/,
|
|
971
|
-
/\bmv\b/,
|
|
972
|
-
/\binstall\b/,
|
|
973
|
-
/\btee\b/,
|
|
974
|
-
/\bdd\b/,
|
|
975
|
-
/\bmkfs\b/,
|
|
976
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
977
|
-
/\bchmod\b/,
|
|
978
|
-
/\bchown\b/,
|
|
979
|
-
/\btruncate\b/
|
|
980
|
-
],
|
|
981
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
982
|
-
"cat",
|
|
983
|
-
"head",
|
|
984
|
-
"tail",
|
|
985
|
-
"less",
|
|
986
|
-
"more",
|
|
987
|
-
"grep",
|
|
988
|
-
"find",
|
|
989
|
-
"awk",
|
|
990
|
-
"sed",
|
|
991
|
-
"sort",
|
|
992
|
-
"uniq",
|
|
993
|
-
"wc",
|
|
994
|
-
"cut",
|
|
995
|
-
"strings",
|
|
996
|
-
"od",
|
|
997
|
-
"xxd",
|
|
998
|
-
"hexdump",
|
|
999
|
-
"tr",
|
|
1000
|
-
"file",
|
|
1001
|
-
"stat",
|
|
1002
|
-
"du",
|
|
1003
|
-
"df",
|
|
1004
|
-
"ls",
|
|
1005
|
-
"dir",
|
|
1006
|
-
"tree",
|
|
1007
|
-
"nl",
|
|
1008
|
-
"tac",
|
|
1009
|
-
"rev",
|
|
1010
|
-
"echo",
|
|
1011
|
-
"printf",
|
|
1012
|
-
"lsblk",
|
|
1013
|
-
"blockdev",
|
|
1014
|
-
"smartctl",
|
|
1015
|
-
"udevadm",
|
|
1016
|
-
"dmidecode",
|
|
1017
|
-
"lscpu",
|
|
1018
|
-
"lspci",
|
|
1019
|
-
"lsusb",
|
|
1020
|
-
"lsmem",
|
|
1021
|
-
"lsns",
|
|
1022
|
-
"lsof",
|
|
1023
|
-
"hwinfo",
|
|
1024
|
-
"inxi",
|
|
1025
|
-
"fdisk",
|
|
1026
|
-
"parted",
|
|
1027
|
-
"dmesg",
|
|
1028
|
-
"journalctl",
|
|
1029
|
-
"systemctl",
|
|
1030
|
-
"hostnamectl",
|
|
1031
|
-
"localectl",
|
|
1032
|
-
"timedatectl",
|
|
1033
|
-
"ps",
|
|
1034
|
-
"top",
|
|
1035
|
-
"free",
|
|
1036
|
-
"vmstat",
|
|
1037
|
-
"iostat",
|
|
1038
|
-
"mpstat",
|
|
1039
|
-
"pidof",
|
|
1040
|
-
"pgrep",
|
|
1041
|
-
"ip",
|
|
1042
|
-
"ifconfig",
|
|
1043
|
-
"route",
|
|
1044
|
-
"ss",
|
|
1045
|
-
"netstat",
|
|
1046
|
-
"arp",
|
|
1047
|
-
"ethtool",
|
|
1048
|
-
"nmcli",
|
|
1049
|
-
"uname",
|
|
1050
|
-
"arch",
|
|
1051
|
-
"nproc",
|
|
1052
|
-
"getconf",
|
|
1053
|
-
"getent",
|
|
1054
|
-
"id",
|
|
1055
|
-
"whoami",
|
|
1056
|
-
"who",
|
|
1057
|
-
"w",
|
|
1058
|
-
"last",
|
|
1059
|
-
"uptime",
|
|
1060
|
-
"lsmod",
|
|
1061
|
-
"modinfo",
|
|
1062
|
-
"sysctl"
|
|
1063
|
-
]),
|
|
1064
|
-
fullySafeCommands: [
|
|
1065
|
-
/^ls(?:\s|$)/i,
|
|
1066
|
-
/^dir(?:\s|$)/i,
|
|
1067
|
-
/^pwd(?:\s|$)/i,
|
|
1068
|
-
/^which\s+/i,
|
|
1069
|
-
/^whereis\s+/i,
|
|
1070
|
-
/^realpath\s+/i,
|
|
1071
|
-
/^basename\s+/i,
|
|
1072
|
-
/^dirname\s+/i,
|
|
1073
|
-
/^file\s+/i,
|
|
1074
|
-
/^stat\s+/i,
|
|
1075
|
-
/^du(?:\s|$)/i,
|
|
1076
|
-
/^echo\s+/i,
|
|
1077
|
-
/^printf\s+/i,
|
|
1078
|
-
/^type\s+/i,
|
|
1079
|
-
/^command\s+/i,
|
|
1080
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
1081
|
-
/^whoami(?:\s|$)/i,
|
|
1082
|
-
/^id(?:\s|$)/i,
|
|
1083
|
-
/^hostname(?:\s|$)/i,
|
|
1084
|
-
/^uname(?:\s|$)/i,
|
|
1085
|
-
/^date(?:\s|$)/i,
|
|
1086
|
-
/^env(?:\s|$)/i,
|
|
1087
|
-
/^printenv(?:\s|$)/i,
|
|
1088
|
-
/^tty(?:\s|$)/i,
|
|
1089
|
-
/^arch(?:\s|$)/i,
|
|
1090
|
-
/^uptime(?:\s|$)/i,
|
|
1091
|
-
/^groups(?:\s|$)/i,
|
|
1092
|
-
/^logname(?:\s|$)/i,
|
|
1093
|
-
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
1094
|
-
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
1095
|
-
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
1096
|
-
/^df(?:\s|$)/i,
|
|
1097
|
-
/^free(?:\s|$)/i,
|
|
1098
|
-
/^vmstat(?:\s|$)/i,
|
|
1099
|
-
/^nproc(?:\s|$)/i,
|
|
1100
|
-
/^lscpu(?:\s|$)/i,
|
|
1101
|
-
/^lsblk(?:\s|$)/i,
|
|
1102
|
-
/^lspci(?:\s|$)/i,
|
|
1103
|
-
/^lsusb(?:\s|$)/i,
|
|
1104
|
-
/^lsmod(?:\s|$)/i
|
|
1105
|
-
],
|
|
1106
|
-
readonlySensitiveCommands: [
|
|
1107
|
-
/^cat\s+/i,
|
|
1108
|
-
/^head(?:\s|$)/i,
|
|
1109
|
-
/^tail(?:\s|$)/i,
|
|
1110
|
-
/^less(?:\s|$)/i,
|
|
1111
|
-
/^more(?:\s|$)/i,
|
|
1112
|
-
/^wc(?:\s|$)/i,
|
|
1113
|
-
/^grep(?:\s|$)/i,
|
|
1114
|
-
/^find\s+/i,
|
|
1115
|
-
/^ag(?:\s|$)/i,
|
|
1116
|
-
/^rg(?:\s|$)/i,
|
|
1117
|
-
/^awk\s+/i,
|
|
1118
|
-
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
1119
|
-
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
1120
|
-
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
1121
|
-
/^cut\s+/i,
|
|
1122
|
-
/^sort(?:\s|$)/i,
|
|
1123
|
-
/^uniq(?:\s|$)/i,
|
|
1124
|
-
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
1125
|
-
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
1126
|
-
/^ps(?:\s|$)/i,
|
|
1127
|
-
/^ss(?:\s|$)/i,
|
|
1128
|
-
/^netstat(?:\s|$)/i,
|
|
1129
|
-
/^lsof(?:\s|$)/i,
|
|
1130
|
-
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
1131
|
-
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
1132
|
-
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
1133
|
-
/^service\s+\S+\s+status\b/i,
|
|
1134
|
-
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
1135
|
-
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
1136
|
-
// 的话只看紧跟 journalctl 的第一个参数,
|
|
1137
|
-
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
1138
|
-
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
1139
|
-
]
|
|
1140
|
-
};
|
|
1141
|
-
WINDOWS_TRAITS = {
|
|
1142
|
-
writeKeywords: [
|
|
1143
|
-
"write",
|
|
1144
|
-
"\u5199\u5165",
|
|
1145
|
-
"save",
|
|
1146
|
-
"\u4FDD\u5B58",
|
|
1147
|
-
"modify",
|
|
1148
|
-
"\u4FEE\u6539",
|
|
1149
|
-
"update",
|
|
1150
|
-
"\u66F4\u65B0",
|
|
1151
|
-
"delete",
|
|
1152
|
-
"\u5220\u9664",
|
|
1153
|
-
"remove",
|
|
1154
|
-
"\u79FB\u9664",
|
|
1155
|
-
"create",
|
|
1156
|
-
"\u521B\u5EFA",
|
|
1157
|
-
"append",
|
|
1158
|
-
"\u8FFD\u52A0",
|
|
1159
|
-
"overwrite",
|
|
1160
|
-
"\u8986\u76D6",
|
|
1161
|
-
"replace",
|
|
1162
|
-
"\u66FF\u6362",
|
|
1163
|
-
"set",
|
|
1164
|
-
"\u8BBE\u7F6E"
|
|
1165
|
-
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
1166
|
-
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
1167
|
-
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
1168
|
-
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
1169
|
-
],
|
|
1170
|
-
readKeywords: [
|
|
1171
|
-
"read",
|
|
1172
|
-
"\u8BFB\u53D6",
|
|
1173
|
-
"type",
|
|
1174
|
-
"findstr",
|
|
1175
|
-
"dir",
|
|
1176
|
-
"view",
|
|
1177
|
-
"\u67E5\u770B",
|
|
1178
|
-
"open",
|
|
1179
|
-
"\u6253\u5F00",
|
|
1180
|
-
"load",
|
|
1181
|
-
"\u52A0\u8F7D",
|
|
1182
|
-
"search",
|
|
1183
|
-
"\u641C\u7D22",
|
|
1184
|
-
"find",
|
|
1185
|
-
"\u67E5\u627E",
|
|
1186
|
-
"get",
|
|
1187
|
-
"\u83B7\u53D6"
|
|
1188
|
-
],
|
|
1189
|
-
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
1190
|
-
writeCommandRes: [
|
|
1191
|
-
/\b(del|erase)\b/,
|
|
1192
|
-
/\brmdir\b/,
|
|
1193
|
-
/\b(rd)\b/,
|
|
1194
|
-
/\bcopy\b/,
|
|
1195
|
-
/\b(xcopy|robocopy)\b/,
|
|
1196
|
-
/\b(move|movefile)\b/,
|
|
1197
|
-
/\breplace\b/,
|
|
1198
|
-
/\bicacls\b/,
|
|
1199
|
-
/\battrib\b/,
|
|
1200
|
-
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
1201
|
-
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
1202
|
-
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
1203
|
-
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
1204
|
-
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
1205
|
-
/\bformat\b/,
|
|
1206
|
-
/\bdiskpart\b/,
|
|
1207
|
-
/\bmkdir\b/,
|
|
1208
|
-
/\bmd\b/,
|
|
1209
|
-
/\bsetx\b/,
|
|
1210
|
-
/\bwmic\b/
|
|
1211
|
-
],
|
|
1212
|
-
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
1213
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1214
|
-
"type",
|
|
1215
|
-
"findstr",
|
|
1216
|
-
"more",
|
|
1217
|
-
"tree",
|
|
1218
|
-
"dir",
|
|
1219
|
-
"where",
|
|
1220
|
-
"ver",
|
|
1221
|
-
"cls",
|
|
1222
|
-
"echo",
|
|
1223
|
-
"help",
|
|
1224
|
-
"fc",
|
|
1225
|
-
"comp",
|
|
1226
|
-
"find",
|
|
1227
|
-
"sort",
|
|
1228
|
-
"powershell",
|
|
1229
|
-
"cmd",
|
|
1230
|
-
"netstat",
|
|
1231
|
-
"ipconfig",
|
|
1232
|
-
"tasklist",
|
|
1233
|
-
"systeminfo",
|
|
1234
|
-
"driverquery",
|
|
1235
|
-
"reg",
|
|
1236
|
-
"wmic",
|
|
1237
|
-
"whoami",
|
|
1238
|
-
"hostname",
|
|
1239
|
-
"getmac",
|
|
1240
|
-
"query",
|
|
1241
|
-
"w32tm",
|
|
1242
|
-
"diskpart"
|
|
1243
|
-
]),
|
|
1244
|
-
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
1245
|
-
fullySafeCommands: [
|
|
1246
|
-
/^dir(?:\s|$)/,
|
|
1247
|
-
/^where\s+/,
|
|
1248
|
-
/^echo\s+/,
|
|
1249
|
-
/^cd(?:\s|$)/,
|
|
1250
|
-
/^tree(?:\s|$)/,
|
|
1251
|
-
/^help(?:\s|$)/,
|
|
1252
|
-
/^cls(?:\s|$)/,
|
|
1253
|
-
/^ver(?:\s|$)/,
|
|
1254
|
-
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
1255
|
-
/^whoami(?:\s|$)/,
|
|
1256
|
-
/^hostname(?:\s|$)/,
|
|
1257
|
-
/^systeminfo(?:\s|$)/,
|
|
1258
|
-
/^tasklist(?:\s|$)/,
|
|
1259
|
-
/^getmac(?:\s|$)/,
|
|
1260
|
-
/^driverquery(?:\s|$)/,
|
|
1261
|
-
/^netstat(?:\s|$)/
|
|
1262
|
-
],
|
|
1263
|
-
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
1264
|
-
readonlySensitiveCommands: [
|
|
1265
|
-
/^type\s+/,
|
|
1266
|
-
/^findstr\s+/,
|
|
1267
|
-
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
1268
|
-
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
1269
|
-
/^sc\s+query\b/,
|
|
1270
|
-
/^reg\s+query\b/
|
|
1271
|
-
]
|
|
1272
|
-
};
|
|
1273
|
-
MACOS_TRAITS = LINUX_TRAITS;
|
|
1274
|
-
HARMONY_TRAITS = LINUX_TRAITS;
|
|
925
|
+
init_rules_loader();
|
|
926
|
+
traitsCache = /* @__PURE__ */ new Map();
|
|
927
|
+
LINUX_TRAITS = traitsFor("linux");
|
|
928
|
+
WINDOWS_TRAITS = traitsFor("windows");
|
|
929
|
+
MACOS_TRAITS = traitsFor("macos");
|
|
930
|
+
HARMONY_TRAITS = traitsFor("harmony");
|
|
1275
931
|
}
|
|
1276
932
|
});
|
|
1277
933
|
|
|
@@ -1430,7 +1086,6 @@ function checkIntentConsistency(input) {
|
|
|
1430
1086
|
const { aNext, promptSession, promptHistory } = input;
|
|
1431
1087
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1432
1088
|
const actionType = aNext.actionType.toLowerCase();
|
|
1433
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1434
1089
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1435
1090
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1436
1091
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1458,7 +1113,6 @@ function runLogicCheck(input, opts) {
|
|
|
1458
1113
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1459
1114
|
const actionType = aNext.actionType.toLowerCase();
|
|
1460
1115
|
const resolvedCwd = cwd || "/";
|
|
1461
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1462
1116
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1463
1117
|
if (!opts?.skipReadBeforeWrite) {
|
|
1464
1118
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1570,7 +1224,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1570
1224
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1571
1225
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1572
1226
|
if (!isWrite) return null;
|
|
1573
|
-
const WRITE_CMDS =
|
|
1227
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1574
1228
|
if (isBashLike) {
|
|
1575
1229
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1576
1230
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1602,7 +1256,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1602
1256
|
}
|
|
1603
1257
|
if (!hasWriteSeg) return null;
|
|
1604
1258
|
}
|
|
1605
|
-
const SOURCE_ARG_CMDS =
|
|
1259
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1606
1260
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1607
1261
|
const targetPaths = [];
|
|
1608
1262
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1650,7 +1304,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1650
1304
|
}
|
|
1651
1305
|
}
|
|
1652
1306
|
}
|
|
1653
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1654
1307
|
let filteredPaths = uniqueTargetPaths;
|
|
1655
1308
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1656
1309
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1751,10 +1404,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1751
1404
|
if (!hasTraversal) {
|
|
1752
1405
|
return null;
|
|
1753
1406
|
}
|
|
1754
|
-
const READ_KEYWORDS =
|
|
1755
|
-
const WRITE_KEYWORDS2 =
|
|
1756
|
-
const EXEC_KEYWORDS =
|
|
1757
|
-
const OPEN_KEYWORDS =
|
|
1407
|
+
const READ_KEYWORDS = IFA_READ_KEYWORDS;
|
|
1408
|
+
const WRITE_KEYWORDS2 = IFA_WRITE_KEYWORDS;
|
|
1409
|
+
const EXEC_KEYWORDS = IFA_EXEC_KEYWORDS;
|
|
1410
|
+
const OPEN_KEYWORDS = IFA_OPEN_KEYWORDS;
|
|
1758
1411
|
const code = extractInlineCode(actionDetail);
|
|
1759
1412
|
if (!code) return null;
|
|
1760
1413
|
const codeLower = code.toLowerCase();
|
|
@@ -1817,7 +1470,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1817
1470
|
violatedLayers: ["L2.1"]
|
|
1818
1471
|
};
|
|
1819
1472
|
}
|
|
1820
|
-
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;
|
|
1473
|
+
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;
|
|
1821
1474
|
var init_logic_rules = __esm({
|
|
1822
1475
|
"src/engine/logic-rules.ts"() {
|
|
1823
1476
|
"use strict";
|
|
@@ -1827,6 +1480,10 @@ var init_logic_rules = __esm({
|
|
|
1827
1480
|
init_rules_loader();
|
|
1828
1481
|
init_platform_traits();
|
|
1829
1482
|
init_shell_split();
|
|
1483
|
+
_LOGIC = loadLogicRules();
|
|
1484
|
+
_HEURISTIC = loadHeuristicRules();
|
|
1485
|
+
EXCLUDED_PREFIXES = _HEURISTIC.sensitive_excluded_prefixes ?? [];
|
|
1486
|
+
TEMP_DIRS = (_HEURISTIC.temp_dirs ?? []).map((d) => d + "/");
|
|
1830
1487
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1831
1488
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1832
1489
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1847,7 +1504,7 @@ var init_logic_rules = __esm({
|
|
|
1847
1504
|
"list_dir",
|
|
1848
1505
|
"count_text_length",
|
|
1849
1506
|
"search",
|
|
1850
|
-
// filemgr 只读三件套(globfiles 在
|
|
1507
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1851
1508
|
"filemgr-globfiles",
|
|
1852
1509
|
"filemgr-readfile",
|
|
1853
1510
|
"filemgr-grepfiles",
|
|
@@ -1859,7 +1516,7 @@ var init_logic_rules = __esm({
|
|
|
1859
1516
|
"tail",
|
|
1860
1517
|
"cat"
|
|
1861
1518
|
]);
|
|
1862
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1519
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1863
1520
|
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"];
|
|
1864
1521
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1865
1522
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1900,25 +1557,24 @@ var init_logic_rules = __esm({
|
|
|
1900
1557
|
}));
|
|
1901
1558
|
})();
|
|
1902
1559
|
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;
|
|
1903
|
-
DIR_TRAVERSAL_KW = [
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
"pathlib.",
|
|
1910
|
-
"iterdir",
|
|
1911
|
-
"rglob",
|
|
1912
|
-
"scandir(",
|
|
1913
|
-
"listdir("
|
|
1914
|
-
];
|
|
1560
|
+
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1561
|
+
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
1562
|
+
IFA_READ_KEYWORDS = _IFA.read_keywords ?? [];
|
|
1563
|
+
IFA_WRITE_KEYWORDS = _IFA.write_keywords ?? [];
|
|
1564
|
+
IFA_EXEC_KEYWORDS = _IFA.exec_keywords ?? [];
|
|
1565
|
+
IFA_OPEN_KEYWORDS = _IFA.open_keywords ?? [];
|
|
1915
1566
|
}
|
|
1916
1567
|
});
|
|
1917
1568
|
|
|
1918
1569
|
// src/engine/asset-dir.ts
|
|
1919
|
-
import { existsSync as
|
|
1920
|
-
import { resolve as resolve2 } from "path";
|
|
1570
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1571
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
1921
1572
|
function assetDirCandidates(moduleDir, name) {
|
|
1573
|
+
const root = getRulesRoot();
|
|
1574
|
+
if (root) {
|
|
1575
|
+
const p = join4(root, name);
|
|
1576
|
+
return [p, p];
|
|
1577
|
+
}
|
|
1922
1578
|
return [
|
|
1923
1579
|
resolve2(moduleDir, name),
|
|
1924
1580
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -1926,7 +1582,7 @@ function assetDirCandidates(moduleDir, name) {
|
|
|
1926
1582
|
}
|
|
1927
1583
|
function resolveAssetDir(moduleDir, name) {
|
|
1928
1584
|
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
1929
|
-
if (
|
|
1585
|
+
if (existsSync4(dir)) return dir;
|
|
1930
1586
|
}
|
|
1931
1587
|
return null;
|
|
1932
1588
|
}
|
|
@@ -1934,11 +1590,12 @@ var init_asset_dir = __esm({
|
|
|
1934
1590
|
"src/engine/asset-dir.ts"() {
|
|
1935
1591
|
"use strict";
|
|
1936
1592
|
init_esm_shims();
|
|
1593
|
+
init_paths();
|
|
1937
1594
|
}
|
|
1938
1595
|
});
|
|
1939
1596
|
|
|
1940
1597
|
// src/engine/skill-loader.ts
|
|
1941
|
-
import { readFileSync as
|
|
1598
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3, existsSync as existsSync5 } from "fs";
|
|
1942
1599
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1943
1600
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1944
1601
|
function loadAllSkills() {
|
|
@@ -1953,7 +1610,7 @@ function loadAllSkills() {
|
|
|
1953
1610
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
|
|
1954
1611
|
for (const f of files) {
|
|
1955
1612
|
const name = f.replace(".md", "");
|
|
1956
|
-
cachedSkills[name] =
|
|
1613
|
+
cachedSkills[name] = readFileSync4(resolve3(dir, f), "utf-8");
|
|
1957
1614
|
}
|
|
1958
1615
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1959
1616
|
return cachedSkills;
|
|
@@ -1984,7 +1641,7 @@ function loadUserSkill(name) {
|
|
|
1984
1641
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1985
1642
|
try {
|
|
1986
1643
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
1987
|
-
userSkillCache[name] =
|
|
1644
|
+
userSkillCache[name] = existsSync5(p) ? readFileSync4(p, "utf-8") : "";
|
|
1988
1645
|
} catch {
|
|
1989
1646
|
userSkillCache[name] = "";
|
|
1990
1647
|
}
|
|
@@ -2133,8 +1790,8 @@ __export(runtime_config_exports, {
|
|
|
2133
1790
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
2134
1791
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2135
1792
|
});
|
|
2136
|
-
import { readFileSync as
|
|
2137
|
-
import { join as
|
|
1793
|
+
import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
1794
|
+
import { join as join5 } from "path";
|
|
2138
1795
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2139
1796
|
import { dirname as dirname3 } from "path";
|
|
2140
1797
|
function runtimeDir() {
|
|
@@ -2163,7 +1820,7 @@ function saveRuntimeConfig(config) {
|
|
|
2163
1820
|
}
|
|
2164
1821
|
const p = getRuntimeConfigPath();
|
|
2165
1822
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
2166
|
-
if (dir && !
|
|
1823
|
+
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
2167
1824
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
2168
1825
|
invalidateRuntimeConfigCache();
|
|
2169
1826
|
}
|
|
@@ -2197,7 +1854,7 @@ function loadRuntimeConfig() {
|
|
|
2197
1854
|
};
|
|
2198
1855
|
}
|
|
2199
1856
|
const p = getRuntimeConfigPath();
|
|
2200
|
-
if (!
|
|
1857
|
+
if (!existsSync6(p)) {
|
|
2201
1858
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
2202
1859
|
try {
|
|
2203
1860
|
saveRuntimeConfig(seeded);
|
|
@@ -2211,7 +1868,7 @@ function loadRuntimeConfig() {
|
|
|
2211
1868
|
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
2212
1869
|
return runtimeConfigCache.value;
|
|
2213
1870
|
}
|
|
2214
|
-
const data = JSON.parse(
|
|
1871
|
+
const data = JSON.parse(readFileSync5(p, "utf-8"));
|
|
2215
1872
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
2216
1873
|
const result = {
|
|
2217
1874
|
version: data.version || 1,
|
|
@@ -2269,7 +1926,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2269
1926
|
}
|
|
2270
1927
|
function _filterRulesByOs(rules, osType) {
|
|
2271
1928
|
const accepted = acceptedOsValues(osType);
|
|
2272
|
-
return rules.filter((r) =>
|
|
1929
|
+
return rules.filter((r) => {
|
|
1930
|
+
const os4 = String(r.os || "linux").toLowerCase();
|
|
1931
|
+
return os4 === "all" || accepted.includes(os4);
|
|
1932
|
+
});
|
|
2273
1933
|
}
|
|
2274
1934
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2275
1935
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2356,7 +2016,7 @@ function hasRuntimeConfig() {
|
|
|
2356
2016
|
if (cached2 !== void 0) return cached2;
|
|
2357
2017
|
let ok = false;
|
|
2358
2018
|
try {
|
|
2359
|
-
JSON.parse(
|
|
2019
|
+
JSON.parse(readFileSync5(p, "utf-8"));
|
|
2360
2020
|
ok = true;
|
|
2361
2021
|
} catch {
|
|
2362
2022
|
ok = false;
|
|
@@ -2456,6 +2116,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2456
2116
|
keyword: kw.keyword,
|
|
2457
2117
|
risk_level: kw.riskLevel,
|
|
2458
2118
|
lang: kw.lang || "en",
|
|
2119
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2120
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2121
|
+
os: kw.os,
|
|
2459
2122
|
enabled: true,
|
|
2460
2123
|
builtin: true
|
|
2461
2124
|
}));
|
|
@@ -2573,8 +2236,9 @@ function _l3SkillsToRules() {
|
|
|
2573
2236
|
}
|
|
2574
2237
|
function _scenarioRulesToDefaults() {
|
|
2575
2238
|
const out = {};
|
|
2576
|
-
const
|
|
2577
|
-
|
|
2239
|
+
const _root = getRulesRoot();
|
|
2240
|
+
const scenDir = _root ? join5(_root, "scenarios") : existsSync6(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync6(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2241
|
+
if (!existsSync6(scenDir)) return out;
|
|
2578
2242
|
let entries;
|
|
2579
2243
|
try {
|
|
2580
2244
|
entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2582,11 +2246,11 @@ function _scenarioRulesToDefaults() {
|
|
|
2582
2246
|
return out;
|
|
2583
2247
|
}
|
|
2584
2248
|
for (const scenarioId of entries) {
|
|
2585
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2586
|
-
if (!
|
|
2249
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join5(scenDir, scenarioId, "rules.json");
|
|
2250
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2587
2251
|
let data;
|
|
2588
2252
|
try {
|
|
2589
|
-
data = JSON.parse(
|
|
2253
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2590
2254
|
} catch {
|
|
2591
2255
|
continue;
|
|
2592
2256
|
}
|
|
@@ -2622,8 +2286,9 @@ function _scenarioRulesToDefaults() {
|
|
|
2622
2286
|
}
|
|
2623
2287
|
function _agentRulesToDefaults() {
|
|
2624
2288
|
const out = {};
|
|
2625
|
-
const
|
|
2626
|
-
|
|
2289
|
+
const _root = getRulesRoot();
|
|
2290
|
+
const agentsDir = _root ? join5(_root, "agents") : existsSync6(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync6(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2291
|
+
if (!existsSync6(agentsDir)) return out;
|
|
2627
2292
|
let entries;
|
|
2628
2293
|
try {
|
|
2629
2294
|
entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2631,11 +2296,11 @@ function _agentRulesToDefaults() {
|
|
|
2631
2296
|
return out;
|
|
2632
2297
|
}
|
|
2633
2298
|
for (const agentId of entries) {
|
|
2634
|
-
const rulesFile =
|
|
2635
|
-
if (!
|
|
2299
|
+
const rulesFile = join5(agentsDir, agentId, "rules.json");
|
|
2300
|
+
if (!existsSync6(rulesFile)) continue;
|
|
2636
2301
|
let data;
|
|
2637
2302
|
try {
|
|
2638
|
-
data = JSON.parse(
|
|
2303
|
+
data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
|
|
2639
2304
|
} catch {
|
|
2640
2305
|
continue;
|
|
2641
2306
|
}
|
|
@@ -3010,7 +2675,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
3010
2675
|
}
|
|
3011
2676
|
function _filterDefaultsByOs(rules, osType) {
|
|
3012
2677
|
const accepted = acceptedOsValues(osType);
|
|
3013
|
-
return rules.filter((r) =>
|
|
2678
|
+
return rules.filter((r) => {
|
|
2679
|
+
if (!r.os) return true;
|
|
2680
|
+
const os4 = String(r.os).toLowerCase();
|
|
2681
|
+
return os4 === "all" || accepted.includes(os4);
|
|
2682
|
+
});
|
|
3014
2683
|
}
|
|
3015
2684
|
function _hasRulesForOs(rules, osType) {
|
|
3016
2685
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3239,12 +2908,12 @@ function addCustomSkill(skillId, category, keywords, content) {
|
|
|
3239
2908
|
);
|
|
3240
2909
|
}
|
|
3241
2910
|
const runtime = loadRuntimeConfig();
|
|
3242
|
-
const skillsDir =
|
|
2911
|
+
const skillsDir = join5(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3243
2912
|
try {
|
|
3244
2913
|
mkdirSync(skillsDir, { recursive: true });
|
|
3245
2914
|
} catch {
|
|
3246
2915
|
}
|
|
3247
|
-
writeFileSync(
|
|
2916
|
+
writeFileSync(join5(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3248
2917
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3249
2918
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3250
2919
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3259,9 +2928,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3259
2928
|
if (idx >= 0) {
|
|
3260
2929
|
if (skills[idx].builtin) return null;
|
|
3261
2930
|
skills.splice(idx, 1);
|
|
3262
|
-
const skillPath =
|
|
2931
|
+
const skillPath = join5(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3263
2932
|
try {
|
|
3264
|
-
if (
|
|
2933
|
+
if (existsSync6(skillPath)) unlinkSync(skillPath);
|
|
3265
2934
|
} catch {
|
|
3266
2935
|
}
|
|
3267
2936
|
saveRuntimeConfig(runtime);
|
|
@@ -3468,7 +3137,7 @@ var init_runtime_config = __esm({
|
|
|
3468
3137
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3469
3138
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3470
3139
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3471
|
-
RUNTIME_CONFIG_PATH =
|
|
3140
|
+
RUNTIME_CONFIG_PATH = join5(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3472
3141
|
LAYER_DISABLE_ENV = {
|
|
3473
3142
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3474
3143
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3496,10 +3165,10 @@ var init_runtime_config = __esm({
|
|
|
3496
3165
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3497
3166
|
};
|
|
3498
3167
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3499
|
-
_AGENTS_DIR_PROD =
|
|
3500
|
-
_AGENTS_DIR_DEV =
|
|
3501
|
-
_SCENARIOS_DIR_PROD =
|
|
3502
|
-
_SCENARIOS_DIR_DEV =
|
|
3168
|
+
_AGENTS_DIR_PROD = join5(__dirname4, "agents");
|
|
3169
|
+
_AGENTS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3170
|
+
_SCENARIOS_DIR_PROD = join5(__dirname4, "scenarios");
|
|
3171
|
+
_SCENARIOS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3503
3172
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3504
3173
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3505
3174
|
registerRuntimeConfigBridge({
|
|
@@ -3837,12 +3506,14 @@ var init_config = __esm({
|
|
|
3837
3506
|
});
|
|
3838
3507
|
|
|
3839
3508
|
// src/agents/index.ts
|
|
3840
|
-
import { readFileSync as
|
|
3841
|
-
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3509
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3510
|
+
import { resolve as resolve4, dirname as dirname4, join as join6 } from "path";
|
|
3842
3511
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3843
3512
|
function resolveAgentsDir() {
|
|
3844
|
-
|
|
3845
|
-
if (
|
|
3513
|
+
const root = getRulesRoot();
|
|
3514
|
+
if (root) return join6(root, "agents");
|
|
3515
|
+
if (existsSync7(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3516
|
+
if (existsSync7(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3846
3517
|
return AGENTS_DIR_PROD;
|
|
3847
3518
|
}
|
|
3848
3519
|
function getAgentExtensions(agentId) {
|
|
@@ -3860,12 +3531,12 @@ function getAgentExtensions(agentId) {
|
|
|
3860
3531
|
if (ext === null) {
|
|
3861
3532
|
const agentsDir = resolveAgentsDir();
|
|
3862
3533
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3863
|
-
if (!
|
|
3534
|
+
if (!existsSync7(rulesFile)) {
|
|
3864
3535
|
cache.set(agentId, { fp, ext: null });
|
|
3865
3536
|
return null;
|
|
3866
3537
|
}
|
|
3867
3538
|
try {
|
|
3868
|
-
const raw =
|
|
3539
|
+
const raw = readFileSync6(rulesFile, "utf-8");
|
|
3869
3540
|
ext = JSON.parse(raw);
|
|
3870
3541
|
if (!ext.agent_id) {
|
|
3871
3542
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
@@ -3904,6 +3575,7 @@ var init_agents = __esm({
|
|
|
3904
3575
|
"src/agents/index.ts"() {
|
|
3905
3576
|
"use strict";
|
|
3906
3577
|
init_esm_shims();
|
|
3578
|
+
init_paths();
|
|
3907
3579
|
init_runtime_config();
|
|
3908
3580
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3909
3581
|
__dirname5 = dirname4(__filename4);
|
|
@@ -3914,12 +3586,14 @@ var init_agents = __esm({
|
|
|
3914
3586
|
});
|
|
3915
3587
|
|
|
3916
3588
|
// src/scenarios/index.ts
|
|
3917
|
-
import { readFileSync as
|
|
3918
|
-
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
3589
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
3590
|
+
import { resolve as resolve5, dirname as dirname5, join as join7 } from "path";
|
|
3919
3591
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3920
3592
|
function resolveScenariosDir() {
|
|
3921
|
-
|
|
3922
|
-
if (
|
|
3593
|
+
const root = getRulesRoot();
|
|
3594
|
+
if (root) return join7(root, "scenarios");
|
|
3595
|
+
if (existsSync8(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3596
|
+
if (existsSync8(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3923
3597
|
return SCENARIOS_DIR_PROD;
|
|
3924
3598
|
}
|
|
3925
3599
|
function getScenarioExtensions(scenarioId) {
|
|
@@ -3949,12 +3623,12 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3949
3623
|
`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${overrideHit}\uFF08\u5185\u7F6E\u9ED8\u8BA4 ${builtinFile} \u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C\u5185\u7F6E\u573A\u666F\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\u3002`
|
|
3950
3624
|
);
|
|
3951
3625
|
}
|
|
3952
|
-
if (!
|
|
3626
|
+
if (!existsSync8(rulesFile)) {
|
|
3953
3627
|
cache2.set(scenarioId, { fp, ext: null });
|
|
3954
3628
|
return null;
|
|
3955
3629
|
}
|
|
3956
3630
|
try {
|
|
3957
|
-
const raw =
|
|
3631
|
+
const raw = readFileSync7(rulesFile, "utf-8");
|
|
3958
3632
|
const data = JSON.parse(raw);
|
|
3959
3633
|
const id = data.scenario_id || data.agent_id || "";
|
|
3960
3634
|
if (!id) {
|
|
@@ -3998,6 +3672,7 @@ var init_scenarios = __esm({
|
|
|
3998
3672
|
"src/scenarios/index.ts"() {
|
|
3999
3673
|
"use strict";
|
|
4000
3674
|
init_esm_shims();
|
|
3675
|
+
init_paths();
|
|
4001
3676
|
init_agents();
|
|
4002
3677
|
init_runtime_config();
|
|
4003
3678
|
init_rules_loader();
|
|
@@ -4404,7 +4079,7 @@ function severityRank(s) {
|
|
|
4404
4079
|
}
|
|
4405
4080
|
function isSearchActionType(actionType) {
|
|
4406
4081
|
const at = actionType.toLowerCase();
|
|
4407
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4082
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4408
4083
|
}
|
|
4409
4084
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4410
4085
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4694,8 +4369,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4694
4369
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4695
4370
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4696
4371
|
const scan = stripHeredocContent(actionDetail);
|
|
4697
|
-
const
|
|
4698
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4372
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4699
4373
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4700
4374
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4701
4375
|
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;
|
|
@@ -4841,7 +4515,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4841
4515
|
}
|
|
4842
4516
|
return null;
|
|
4843
4517
|
}
|
|
4844
|
-
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;
|
|
4518
|
+
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;
|
|
4845
4519
|
var init_heuristic = __esm({
|
|
4846
4520
|
"src/engine/heuristic.ts"() {
|
|
4847
4521
|
"use strict";
|
|
@@ -4858,7 +4532,8 @@ var init_heuristic = __esm({
|
|
|
4858
4532
|
return kws.map((k) => ({
|
|
4859
4533
|
keyword: k.keyword,
|
|
4860
4534
|
riskLevel: k.risk_level,
|
|
4861
|
-
lang: k.lang || "en"
|
|
4535
|
+
lang: k.lang || "en",
|
|
4536
|
+
os: String(k.os || "all").toLowerCase()
|
|
4862
4537
|
}));
|
|
4863
4538
|
})();
|
|
4864
4539
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4866,7 +4541,9 @@ var init_heuristic = __esm({
|
|
|
4866
4541
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4867
4542
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4868
4543
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4869
|
-
|
|
4544
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4545
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4546
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4870
4547
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4871
4548
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4872
4549
|
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;
|
|
@@ -4874,15 +4551,16 @@ var init_heuristic = __esm({
|
|
|
4874
4551
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4875
4552
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4876
4553
|
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4877
|
-
SENSITIVE_EXCLUDED_PREFIXES =
|
|
4878
|
-
SEARCH_ACTION_PREFIXES =
|
|
4554
|
+
SENSITIVE_EXCLUDED_PREFIXES = HEURISTIC_RULES.sensitive_excluded_prefixes ?? [];
|
|
4555
|
+
SEARCH_ACTION_PREFIXES = HEURISTIC_RULES.search_action_prefixes ?? [];
|
|
4556
|
+
SEARCH_ACTION_EXACT = HEURISTIC_RULES.search_action_exact ?? [];
|
|
4879
4557
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4880
4558
|
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;
|
|
4881
4559
|
}
|
|
4882
4560
|
});
|
|
4883
4561
|
|
|
4884
4562
|
// src/engine/template-loader.ts
|
|
4885
|
-
import { readFileSync as
|
|
4563
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4886
4564
|
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
4887
4565
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
4888
4566
|
function loadTemplates() {
|
|
@@ -4896,9 +4574,9 @@ function loadTemplates() {
|
|
|
4896
4574
|
}
|
|
4897
4575
|
for (const file of TEMPLATE_FILES) {
|
|
4898
4576
|
const filePath = resolve6(dir, file);
|
|
4899
|
-
if (
|
|
4577
|
+
if (existsSync9(filePath)) {
|
|
4900
4578
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
4901
|
-
cachedTemplates[key] =
|
|
4579
|
+
cachedTemplates[key] = readFileSync8(filePath, "utf-8");
|
|
4902
4580
|
}
|
|
4903
4581
|
}
|
|
4904
4582
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -4918,8 +4596,8 @@ var init_template_loader = __esm({
|
|
|
4918
4596
|
});
|
|
4919
4597
|
|
|
4920
4598
|
// src/engine/script-analyzer.ts
|
|
4921
|
-
import { readFileSync as
|
|
4922
|
-
import { resolve as resolve7, join as
|
|
4599
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4600
|
+
import { resolve as resolve7, join as join8 } from "path";
|
|
4923
4601
|
function emptyResult() {
|
|
4924
4602
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
4925
4603
|
}
|
|
@@ -4945,10 +4623,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4945
4623
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
4946
4624
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
4947
4625
|
}
|
|
4948
|
-
const networkPostKeywords =
|
|
4626
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
4949
4627
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
4950
4628
|
if (hasNetworkPost) {
|
|
4951
|
-
const envPatterns =
|
|
4629
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
4952
4630
|
const contentLower = scriptContent.toLowerCase();
|
|
4953
4631
|
for (const p of envPatterns) {
|
|
4954
4632
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -4972,8 +4650,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4972
4650
|
}
|
|
4973
4651
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4974
4652
|
try {
|
|
4975
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
4976
|
-
const raw =
|
|
4653
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join8(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
4654
|
+
const raw = readFileSync9(expandedPath, "utf-8");
|
|
4977
4655
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4978
4656
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
4979
4657
|
} catch (e) {
|
|
@@ -5022,90 +4700,28 @@ ${result.scriptContent}
|
|
|
5022
4700
|
\`\`\`
|
|
5023
4701
|
`;
|
|
5024
4702
|
}
|
|
5025
|
-
var SUSPICIOUS_PATTERNS, SCRIPT_EXEC_PATTERNS, KEYWORD_COMBINATION_RISKS, SINGLE_KEYWORD_RISKS, LEVEL_ORDER;
|
|
4703
|
+
var SCRIPT_RULES, SUSPICIOUS_PATTERNS, SCRIPT_EXEC_PATTERNS, KEYWORD_SETS, KEYWORD_COMBINATION_RISKS, SINGLE_KEYWORD_RISKS, LEVEL_ORDER;
|
|
5026
4704
|
var init_script_analyzer = __esm({
|
|
5027
4705
|
"src/engine/script-analyzer.ts"() {
|
|
5028
4706
|
"use strict";
|
|
5029
4707
|
init_esm_shims();
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
5048
|
-
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
5049
|
-
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
5050
|
-
// Persistence backdoor
|
|
5051
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
5052
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
5053
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
5054
|
-
];
|
|
5055
|
-
SCRIPT_EXEC_PATTERNS = [
|
|
5056
|
-
/bash\s+(\S+\.sh)/,
|
|
5057
|
-
/sh\s+(\S+\.sh)/,
|
|
5058
|
-
/\.\/(\S+\.sh)/,
|
|
5059
|
-
/source\s+(\S+\.sh)/,
|
|
5060
|
-
/\.\s+(\S+\.sh)/,
|
|
5061
|
-
/zsh\s+(\S+\.sh)/
|
|
5062
|
-
];
|
|
5063
|
-
KEYWORD_COMBINATION_RISKS = [
|
|
5064
|
-
{
|
|
5065
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5066
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
5067
|
-
level: "critical",
|
|
5068
|
-
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"
|
|
5069
|
-
},
|
|
5070
|
-
{
|
|
5071
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5072
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
5073
|
-
level: "critical",
|
|
5074
|
-
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"
|
|
5075
|
-
},
|
|
5076
|
-
{
|
|
5077
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
5078
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5079
|
-
level: "high",
|
|
5080
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
5081
|
-
},
|
|
5082
|
-
{
|
|
5083
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
5084
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5085
|
-
level: "high",
|
|
5086
|
-
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"
|
|
5087
|
-
}
|
|
5088
|
-
];
|
|
5089
|
-
SINGLE_KEYWORD_RISKS = {
|
|
5090
|
-
"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" },
|
|
5091
|
-
"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" },
|
|
5092
|
-
"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" },
|
|
5093
|
-
"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" },
|
|
5094
|
-
"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" },
|
|
5095
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5096
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5097
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5098
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5099
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5100
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5101
|
-
"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" },
|
|
5102
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
5103
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5104
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5105
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5106
|
-
"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" },
|
|
5107
|
-
"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" }
|
|
5108
|
-
};
|
|
4708
|
+
init_rules_loader();
|
|
4709
|
+
SCRIPT_RULES = loadScriptRules();
|
|
4710
|
+
if (!SCRIPT_RULES || Object.keys(SCRIPT_RULES).length === 0) {
|
|
4711
|
+
console.error(
|
|
4712
|
+
"[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"
|
|
4713
|
+
);
|
|
4714
|
+
}
|
|
4715
|
+
SUSPICIOUS_PATTERNS = (SCRIPT_RULES.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
4716
|
+
SCRIPT_EXEC_PATTERNS = ((SCRIPT_RULES.script_exec_patterns ?? {}).legacy ?? []).map((r) => compileRuleRegex(r));
|
|
4717
|
+
KEYWORD_SETS = SCRIPT_RULES.keyword_sets ?? {};
|
|
4718
|
+
KEYWORD_COMBINATION_RISKS = (SCRIPT_RULES.keyword_combination_risks ?? []).map((r) => ({
|
|
4719
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS),
|
|
4720
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS),
|
|
4721
|
+
level: String(r.level),
|
|
4722
|
+
reason: String(r.reason)
|
|
4723
|
+
}));
|
|
4724
|
+
SINGLE_KEYWORD_RISKS = SCRIPT_RULES.single_keyword_risks ?? {};
|
|
5109
4725
|
LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5110
4726
|
}
|
|
5111
4727
|
});
|
|
@@ -6649,8 +6265,8 @@ var init_tracker = __esm({
|
|
|
6649
6265
|
});
|
|
6650
6266
|
|
|
6651
6267
|
// src/engine/script-content-analyzer.ts
|
|
6652
|
-
import { statSync as statSync4, existsSync as
|
|
6653
|
-
import { resolve as resolve9, join as
|
|
6268
|
+
import { statSync as statSync4, existsSync as existsSync11 } from "fs";
|
|
6269
|
+
import { resolve as resolve9, join as join10, isAbsolute as isAbsolute2 } from "path";
|
|
6654
6270
|
import { homedir as homedir3 } from "os";
|
|
6655
6271
|
function emptyChain() {
|
|
6656
6272
|
return {
|
|
@@ -6683,10 +6299,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6683
6299
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6684
6300
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6685
6301
|
}
|
|
6686
|
-
const networkPostKeywords =
|
|
6302
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6687
6303
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6688
6304
|
if (hasNetworkPost) {
|
|
6689
|
-
const envPatterns =
|
|
6305
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6690
6306
|
const contentLower = scriptContent.toLowerCase();
|
|
6691
6307
|
for (const p of envPatterns) {
|
|
6692
6308
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -6762,7 +6378,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6762
6378
|
if (looksLikeScriptPath(tok)) {
|
|
6763
6379
|
try {
|
|
6764
6380
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6765
|
-
if (
|
|
6381
|
+
if (existsSync11(resolved)) {
|
|
6766
6382
|
const stat = statSync4(resolved);
|
|
6767
6383
|
if (stat.isFile()) return resolved;
|
|
6768
6384
|
}
|
|
@@ -6776,7 +6392,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6776
6392
|
const p = m[1];
|
|
6777
6393
|
try {
|
|
6778
6394
|
const resolved = resolvePath2(p);
|
|
6779
|
-
if (
|
|
6395
|
+
if (existsSync11(resolved)) {
|
|
6780
6396
|
const stat = statSync4(resolved);
|
|
6781
6397
|
if (stat.isFile()) return resolved;
|
|
6782
6398
|
}
|
|
@@ -6792,7 +6408,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6792
6408
|
let resolved;
|
|
6793
6409
|
try {
|
|
6794
6410
|
resolved = resolvePath2(p);
|
|
6795
|
-
if (
|
|
6411
|
+
if (existsSync11(resolved)) {
|
|
6796
6412
|
const stat = statSync4(resolved);
|
|
6797
6413
|
if (stat.isFile()) return resolved;
|
|
6798
6414
|
}
|
|
@@ -6833,7 +6449,7 @@ function shlexSplit(s) {
|
|
|
6833
6449
|
function resolvePath2(p) {
|
|
6834
6450
|
let expanded = p;
|
|
6835
6451
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6836
|
-
expanded =
|
|
6452
|
+
expanded = join10(homedir3(), expanded.slice(1));
|
|
6837
6453
|
}
|
|
6838
6454
|
return resolve9(expanded);
|
|
6839
6455
|
}
|
|
@@ -6957,7 +6573,7 @@ function analyzeScriptContent2(input, ext) {
|
|
|
6957
6573
|
isInline = true;
|
|
6958
6574
|
}
|
|
6959
6575
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
6960
|
-
resolvedPath =
|
|
6576
|
+
resolvedPath = join10(input.cwd || process.cwd(), resolvedPath);
|
|
6961
6577
|
}
|
|
6962
6578
|
const l1Adapter = (content) => {
|
|
6963
6579
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7042,7 +6658,7 @@ ${contentPreview}
|
|
|
7042
6658
|
if (chain.maxSizeReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u7D2F\u8BA1\u6587\u4EF6\u5927\u5C0F\u89E6\u8FBE\u4E0A\u9650\uFF0C\u90E8\u5206\u811A\u672C\u53EF\u80FD\u672A\u88AB\u5B8C\u6574\u8BFB\u53D6\u3002\n";
|
|
7043
6659
|
return header + parts.join("\n---\n\n");
|
|
7044
6660
|
}
|
|
7045
|
-
var SUSPICIOUS_PATTERNS2, SCRIPT_EXEC_PATTERNS2, KEYWORD_COMBINATION_RISKS2, SINGLE_KEYWORD_RISKS2, LEVEL_ORDER2;
|
|
6661
|
+
var SCRIPT_RULES2, SUSPICIOUS_PATTERNS2, SCRIPT_EXEC_PATTERNS2, KEYWORD_SETS2, KEYWORD_COMBINATION_RISKS2, SINGLE_KEYWORD_RISKS2, LEVEL_ORDER2;
|
|
7046
6662
|
var init_script_content_analyzer = __esm({
|
|
7047
6663
|
"src/engine/script-content-analyzer.ts"() {
|
|
7048
6664
|
"use strict";
|
|
@@ -7052,87 +6668,23 @@ var init_script_content_analyzer = __esm({
|
|
|
7052
6668
|
init_logic_rules();
|
|
7053
6669
|
init_inline_analyzer();
|
|
7054
6670
|
init_tracker();
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
7073
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
7074
|
-
];
|
|
7075
|
-
SCRIPT_EXEC_PATTERNS2 = [
|
|
7076
|
-
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
7077
|
-
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
7078
|
-
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
7079
|
-
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
7080
|
-
/\.\/(\S+)/,
|
|
7081
|
-
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
7082
|
-
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
7083
|
-
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
7084
|
-
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
7085
|
-
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
7086
|
-
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
7087
|
-
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
7088
|
-
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
7089
|
-
];
|
|
7090
|
-
KEYWORD_COMBINATION_RISKS2 = [
|
|
7091
|
-
{
|
|
7092
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7093
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
7094
|
-
level: "critical",
|
|
7095
|
-
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"
|
|
7096
|
-
},
|
|
7097
|
-
{
|
|
7098
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
7099
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
7100
|
-
level: "critical",
|
|
7101
|
-
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"
|
|
7102
|
-
},
|
|
7103
|
-
{
|
|
7104
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
7105
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7106
|
-
level: "high",
|
|
7107
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
7108
|
-
},
|
|
7109
|
-
{
|
|
7110
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
7111
|
-
group2: /* @__PURE__ */ new Set(),
|
|
7112
|
-
level: "high",
|
|
7113
|
-
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"
|
|
7114
|
-
}
|
|
7115
|
-
];
|
|
7116
|
-
SINGLE_KEYWORD_RISKS2 = {
|
|
7117
|
-
"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" },
|
|
7118
|
-
"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" },
|
|
7119
|
-
"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" },
|
|
7120
|
-
"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" },
|
|
7121
|
-
"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" },
|
|
7122
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7123
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7124
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7125
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
7126
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7127
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
7128
|
-
"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" },
|
|
7129
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
7130
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7131
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
7132
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
7133
|
-
"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" },
|
|
7134
|
-
"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" }
|
|
7135
|
-
};
|
|
6671
|
+
init_rules_loader();
|
|
6672
|
+
SCRIPT_RULES2 = loadScriptRules();
|
|
6673
|
+
if (!SCRIPT_RULES2 || Object.keys(SCRIPT_RULES2).length === 0) {
|
|
6674
|
+
console.error(
|
|
6675
|
+
"[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"
|
|
6676
|
+
);
|
|
6677
|
+
}
|
|
6678
|
+
SUSPICIOUS_PATTERNS2 = (SCRIPT_RULES2.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
6679
|
+
SCRIPT_EXEC_PATTERNS2 = ((SCRIPT_RULES2.script_exec_patterns ?? {}).chain ?? []).map((r) => compileRuleRegex(r));
|
|
6680
|
+
KEYWORD_SETS2 = SCRIPT_RULES2.keyword_sets ?? {};
|
|
6681
|
+
KEYWORD_COMBINATION_RISKS2 = (SCRIPT_RULES2.keyword_combination_risks ?? []).map((r) => ({
|
|
6682
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS2),
|
|
6683
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS2),
|
|
6684
|
+
level: String(r.level),
|
|
6685
|
+
reason: String(r.reason)
|
|
6686
|
+
}));
|
|
6687
|
+
SINGLE_KEYWORD_RISKS2 = SCRIPT_RULES2.single_keyword_risks ?? {};
|
|
7136
6688
|
LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
7137
6689
|
}
|
|
7138
6690
|
});
|
|
@@ -7147,16 +6699,16 @@ __export(token_stats_exports, {
|
|
|
7147
6699
|
recordTokenUsage: () => recordTokenUsage,
|
|
7148
6700
|
resetTokenStats: () => resetTokenStats
|
|
7149
6701
|
});
|
|
7150
|
-
import { readFileSync as
|
|
6702
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
7151
6703
|
function getStatsPath() {
|
|
7152
6704
|
const envPath = process.env[ENV_STATS_PATH];
|
|
7153
6705
|
if (envPath) return envPath;
|
|
7154
6706
|
return getTokenStatsPath();
|
|
7155
6707
|
}
|
|
7156
6708
|
function loadAllRecords(statsPath) {
|
|
7157
|
-
if (!
|
|
6709
|
+
if (!existsSync12(statsPath)) return [];
|
|
7158
6710
|
try {
|
|
7159
|
-
const data = JSON.parse(
|
|
6711
|
+
const data = JSON.parse(readFileSync12(statsPath, "utf-8"));
|
|
7160
6712
|
if (Array.isArray(data)) return data;
|
|
7161
6713
|
if (data && Array.isArray(data.records)) return data.records;
|
|
7162
6714
|
return [];
|
|
@@ -7167,7 +6719,7 @@ function loadAllRecords(statsPath) {
|
|
|
7167
6719
|
function saveRecords(statsPath, records) {
|
|
7168
6720
|
try {
|
|
7169
6721
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
7170
|
-
if (dir && !
|
|
6722
|
+
if (dir && !existsSync12(dir)) mkdirSync2(dir, { recursive: true });
|
|
7171
6723
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
7172
6724
|
} catch (e) {
|
|
7173
6725
|
}
|
|
@@ -7294,7 +6846,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
7294
6846
|
}
|
|
7295
6847
|
function resetTokenStats() {
|
|
7296
6848
|
const statsPath = getStatsPath();
|
|
7297
|
-
if (
|
|
6849
|
+
if (existsSync12(statsPath)) {
|
|
7298
6850
|
try {
|
|
7299
6851
|
unlinkSync2(statsPath);
|
|
7300
6852
|
} catch {
|
|
@@ -7551,8 +7103,8 @@ var init_llm_analyzer = __esm({
|
|
|
7551
7103
|
});
|
|
7552
7104
|
|
|
7553
7105
|
// src/brain/rule-store.ts
|
|
7554
|
-
import { existsSync as
|
|
7555
|
-
import { join as
|
|
7106
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
7107
|
+
import { join as join11 } from "path";
|
|
7556
7108
|
import { homedir as homedir4 } from "os";
|
|
7557
7109
|
function getRulesPath() {
|
|
7558
7110
|
const envPath = process.env[ENV_RULES_PATH];
|
|
@@ -7571,7 +7123,7 @@ var init_rule_store = __esm({
|
|
|
7571
7123
|
"src/brain/rule-store.ts"() {
|
|
7572
7124
|
"use strict";
|
|
7573
7125
|
init_esm_shims();
|
|
7574
|
-
DEFAULT_RULES_PATH =
|
|
7126
|
+
DEFAULT_RULES_PATH = join11(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
7575
7127
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7576
7128
|
STATUS_DRAFT = "draft";
|
|
7577
7129
|
STATUS_APPROVED = "approved";
|
|
@@ -7588,11 +7140,11 @@ var init_rule_store = __esm({
|
|
|
7588
7140
|
this.data = this.load();
|
|
7589
7141
|
}
|
|
7590
7142
|
load() {
|
|
7591
|
-
if (!
|
|
7143
|
+
if (!existsSync13(this.path)) {
|
|
7592
7144
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
7593
7145
|
}
|
|
7594
7146
|
try {
|
|
7595
|
-
const raw = JSON.parse(
|
|
7147
|
+
const raw = JSON.parse(readFileSync13(this.path, "utf-8"));
|
|
7596
7148
|
return {
|
|
7597
7149
|
version: raw.version || 1,
|
|
7598
7150
|
rules: raw.rules || [],
|
|
@@ -8186,9 +7738,9 @@ var init_reviewer = __esm({
|
|
|
8186
7738
|
});
|
|
8187
7739
|
|
|
8188
7740
|
// src/brain/skill-manager.ts
|
|
8189
|
-
import { existsSync as
|
|
7741
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync14, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8190
7742
|
import { homedir as homedir5 } from "os";
|
|
8191
|
-
import { join as
|
|
7743
|
+
import { join as join12 } from "path";
|
|
8192
7744
|
import { randomUUID } from "crypto";
|
|
8193
7745
|
function getMetaPath(override) {
|
|
8194
7746
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8198,14 +7750,14 @@ var init_skill_manager = __esm({
|
|
|
8198
7750
|
"src/brain/skill-manager.ts"() {
|
|
8199
7751
|
"use strict";
|
|
8200
7752
|
init_esm_shims();
|
|
8201
|
-
DEFAULT_SKILL_META_PATH =
|
|
7753
|
+
DEFAULT_SKILL_META_PATH = join12(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8202
7754
|
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8203
7755
|
BrainSkillManager = class {
|
|
8204
7756
|
constructor(skillsDir, metaPath) {
|
|
8205
7757
|
this.skillsDir = skillsDir;
|
|
8206
7758
|
this.metaPath = getMetaPath(metaPath);
|
|
8207
7759
|
try {
|
|
8208
|
-
mkdirSync4(
|
|
7760
|
+
mkdirSync4(join12(this.metaPath, ".."), { recursive: true });
|
|
8209
7761
|
} catch {
|
|
8210
7762
|
}
|
|
8211
7763
|
this.load();
|
|
@@ -8215,9 +7767,9 @@ var init_skill_manager = __esm({
|
|
|
8215
7767
|
skills = [];
|
|
8216
7768
|
metaPath;
|
|
8217
7769
|
load() {
|
|
8218
|
-
if (!
|
|
7770
|
+
if (!existsSync14(this.metaPath)) return;
|
|
8219
7771
|
try {
|
|
8220
|
-
const data = JSON.parse(
|
|
7772
|
+
const data = JSON.parse(readFileSync14(this.metaPath, "utf-8"));
|
|
8221
7773
|
this.skills = data.skills || [];
|
|
8222
7774
|
} catch {
|
|
8223
7775
|
this.skills = [];
|
|
@@ -8240,7 +7792,7 @@ var init_skill_manager = __esm({
|
|
|
8240
7792
|
let changed = false;
|
|
8241
7793
|
for (const sk of this.skills) {
|
|
8242
7794
|
if (sk.status !== "active") continue;
|
|
8243
|
-
if (!
|
|
7795
|
+
if (!existsSync14(join12(this.skillsDir, `${sk.name}.md`))) {
|
|
8244
7796
|
sk.status = "stale";
|
|
8245
7797
|
changed = true;
|
|
8246
7798
|
}
|
|
@@ -8251,9 +7803,9 @@ var init_skill_manager = __esm({
|
|
|
8251
7803
|
addSkill(skill) {
|
|
8252
7804
|
const id = skill.id || randomUUID();
|
|
8253
7805
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8254
|
-
if (!
|
|
8255
|
-
const mdPath =
|
|
8256
|
-
if (
|
|
7806
|
+
if (!existsSync14(this.skillsDir)) return "";
|
|
7807
|
+
const mdPath = join12(this.skillsDir, `${name}.md`);
|
|
7808
|
+
if (existsSync14(mdPath)) return "";
|
|
8257
7809
|
try {
|
|
8258
7810
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8259
7811
|
} catch {
|
|
@@ -8302,12 +7854,12 @@ var init_skill_manager = __esm({
|
|
|
8302
7854
|
const out = [];
|
|
8303
7855
|
for (const sk of this.skills) {
|
|
8304
7856
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8305
|
-
const mdPath =
|
|
8306
|
-
if (!
|
|
7857
|
+
const mdPath = join12(this.skillsDir, `${sk.name}.md`);
|
|
7858
|
+
if (!existsSync14(mdPath)) continue;
|
|
8307
7859
|
out.push({
|
|
8308
7860
|
name: sk.name,
|
|
8309
7861
|
category: sk.category || "",
|
|
8310
|
-
content:
|
|
7862
|
+
content: readFileSync14(mdPath, "utf-8"),
|
|
8311
7863
|
keywords: sk.keywords || []
|
|
8312
7864
|
});
|
|
8313
7865
|
}
|
|
@@ -8595,9 +8147,10 @@ var init_decode = __esm({
|
|
|
8595
8147
|
"src/engine/formal-engine/decode.ts"() {
|
|
8596
8148
|
"use strict";
|
|
8597
8149
|
init_esm_shims();
|
|
8150
|
+
init_rules_loader();
|
|
8598
8151
|
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8599
8152
|
QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8600
|
-
OUTPUT_CMDS =
|
|
8153
|
+
OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8601
8154
|
STAGE_PRIORITY = ["EXFIL", "PRIVESC", "PERSIST", "SCAN", "LISTEN"];
|
|
8602
8155
|
PURPOSE_EXEC_RE = /subprocess\.\w+\s*\(|os\.system\s*\(|shell\s*=\s*True|\beval\s*\(|\bexec\s*\(|os\.popen\s*\(|\/exec\b/i;
|
|
8603
8156
|
LISTEN_LEAD_RE = /http\.server|flask\s+run|nohup\b|nc\s+-l|socat\s+TCP-LISTEN/i;
|
|
@@ -9315,7 +8868,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9315
8868
|
}
|
|
9316
8869
|
return violations;
|
|
9317
8870
|
}
|
|
9318
|
-
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
8871
|
+
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
9319
8872
|
var init_properties = __esm({
|
|
9320
8873
|
"src/engine/formal-engine/properties.ts"() {
|
|
9321
8874
|
"use strict";
|
|
@@ -9332,11 +8885,12 @@ var init_properties = __esm({
|
|
|
9332
8885
|
EXFIL_ACTION_RE = /\bcurl\b|\bwget\b|\bscp\b|\brsync\b|\brclone\b|\bftp\b|connect\s+.*\d+\.\d+\.\d+\.\d+|open\s+.*tcp|base64\s+.*\-o/i;
|
|
9333
8886
|
CRED_STRICT_RE = /(known_hosts|\.ssh|id_rsa|\.pem|\b\.key\b|\/etc\/shadow|\/etc\/passwd|\.aws\/credentials|credentials\.json|netrc|password|secrets?\.ya?ml|credentials?\s*(file|path)?\s*(=|\:)|\bconfig.*(password|token))/i;
|
|
9334
8887
|
INLINE_SHELL_RE = /(?:os\.system|os\.popen)\s*\(\s*['"]([^'"]+)['"]|subprocess\.[a-z_]+\(\s*\[([^\]]+)\]|subprocess\.[a-z_]+\(\s*['"]([^'"]+)['"]|(?:bash|sh|zsh)\s+-c\s*['"]([^'"]+)['"]/gi;
|
|
9335
|
-
|
|
9336
|
-
|
|
8888
|
+
FORMAL_RULES = loadFormalRules();
|
|
8889
|
+
PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
8890
|
+
SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
9337
8891
|
TOOL_REGISTRY = null;
|
|
9338
8892
|
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;
|
|
9339
|
-
STRONG_COMPUTE_TYPES =
|
|
8893
|
+
STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
9340
8894
|
DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
|
|
9341
8895
|
ALL_PROPERTIES = [
|
|
9342
8896
|
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
@@ -9860,10 +9414,10 @@ var init_stateful = __esm({
|
|
|
9860
9414
|
import * as fs4 from "fs";
|
|
9861
9415
|
import * as os2 from "os";
|
|
9862
9416
|
import * as path4 from "path";
|
|
9863
|
-
import { createHash } from "crypto";
|
|
9417
|
+
import { createHash as createHash2 } from "crypto";
|
|
9864
9418
|
function defaultStateDbPath() {
|
|
9865
9419
|
const dir = process.env.AGENT_MOSS_STATE_DIR || path4.join(os2.homedir(), ".agentmoss");
|
|
9866
|
-
const tag =
|
|
9420
|
+
const tag = createHash2("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
|
|
9867
9421
|
return path4.join(dir, `stateful_chains_${tag}.json`);
|
|
9868
9422
|
}
|
|
9869
9423
|
var ChainStateStore;
|
|
@@ -10123,7 +9677,7 @@ async function runAnalysis(input) {
|
|
|
10123
9677
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
10124
9678
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
10125
9679
|
const lowerDetail = rawDetail.toLowerCase();
|
|
10126
|
-
if (
|
|
9680
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
10127
9681
|
return {
|
|
10128
9682
|
allowed: true,
|
|
10129
9683
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -10166,7 +9720,7 @@ async function runAnalysis(input) {
|
|
|
10166
9720
|
violatedLayers: []
|
|
10167
9721
|
};
|
|
10168
9722
|
}
|
|
10169
|
-
let skipLLM =
|
|
9723
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
10170
9724
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
10171
9725
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
10172
9726
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|
|
@@ -10398,7 +9952,7 @@ async function runAnalysis(input) {
|
|
|
10398
9952
|
}
|
|
10399
9953
|
return llmResult;
|
|
10400
9954
|
}
|
|
10401
|
-
var
|
|
9955
|
+
var TRUSTED_SKILLS_BASE, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
10402
9956
|
var init_coordinator = __esm({
|
|
10403
9957
|
"src/engine/coordinator.ts"() {
|
|
10404
9958
|
"use strict";
|
|
@@ -10421,25 +9975,7 @@ var init_coordinator = __esm({
|
|
|
10421
9975
|
init_asset_dir();
|
|
10422
9976
|
init_runtime_config_bridge();
|
|
10423
9977
|
init_formal_engine();
|
|
10424
|
-
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
10425
|
-
"ask_user_question",
|
|
10426
|
-
"glob",
|
|
10427
|
-
"list_dir",
|
|
10428
|
-
"ls",
|
|
10429
|
-
"count_text_length",
|
|
10430
|
-
"filemgr-globfiles"
|
|
10431
|
-
]);
|
|
10432
9978
|
TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
10433
|
-
READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
10434
|
-
"read",
|
|
10435
|
-
"file_read",
|
|
10436
|
-
"read_file",
|
|
10437
|
-
"head",
|
|
10438
|
-
"tail",
|
|
10439
|
-
"grep",
|
|
10440
|
-
"filemgr-readfile",
|
|
10441
|
-
"filemgr-grepfiles"
|
|
10442
|
-
]);
|
|
10443
9979
|
_brainStore = null;
|
|
10444
9980
|
_brainExtractor = null;
|
|
10445
9981
|
_brainSkillManager = null;
|
|
@@ -10454,10 +9990,150 @@ var init_coordinator = __esm({
|
|
|
10454
9990
|
// src/server.ts
|
|
10455
9991
|
init_esm_shims();
|
|
10456
9992
|
import { Hono as Hono2 } from "hono";
|
|
9993
|
+
|
|
9994
|
+
// src/infra/rules-contract.ts
|
|
9995
|
+
init_esm_shims();
|
|
9996
|
+
init_paths();
|
|
9997
|
+
init_rules_loader();
|
|
9998
|
+
import { createHash } from "crypto";
|
|
9999
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
10000
|
+
import { join as join3 } from "path";
|
|
10001
|
+
var RULES_SCHEMA_VERSION = 1;
|
|
10002
|
+
var REQUIRED_RULE_FILES = {
|
|
10003
|
+
"l1_rules.json": ["dangerous_commands", "injection_detection", "sensitive_path_access"],
|
|
10004
|
+
"l2_rules.json": ["intent_consistency", "password_modify_consent", "user_deletion_consent"],
|
|
10005
|
+
"l3_rules.json": [],
|
|
10006
|
+
"mc_rules.json": ["rules", "ap_patterns"],
|
|
10007
|
+
"tool_registry.json": ["tools"],
|
|
10008
|
+
"platform_traits.json": ["linux", "windows"],
|
|
10009
|
+
"inline_rules.json": ["read_write_keywords", "dir_traversal_keywords", "write_exec_chain_patterns"],
|
|
10010
|
+
"script_rules.json": [
|
|
10011
|
+
"suspicious_patterns",
|
|
10012
|
+
"keyword_combination_risks",
|
|
10013
|
+
"single_keyword_risks",
|
|
10014
|
+
"script_exec_patterns"
|
|
10015
|
+
],
|
|
10016
|
+
"heuristic_rules.json": [
|
|
10017
|
+
"sensitive_excluded_prefixes",
|
|
10018
|
+
"search_action_prefixes",
|
|
10019
|
+
"loopback_hosts",
|
|
10020
|
+
"temp_dirs"
|
|
10021
|
+
],
|
|
10022
|
+
"logic_rules.json": ["indirect_file_access", "write_commands", "source_arg_commands"],
|
|
10023
|
+
"formal_rules.json": ["privesc", "compute_action_types", "pattern_miner", "output_commands"]
|
|
10024
|
+
};
|
|
10025
|
+
var MANIFEST_FILE = "manifest.json";
|
|
10026
|
+
function sha256Short(data, n = 12) {
|
|
10027
|
+
return createHash("sha256").update(data).digest("hex").slice(0, n);
|
|
10028
|
+
}
|
|
10029
|
+
function rulesDir() {
|
|
10030
|
+
if (getRulesRoot()) return join3(getRulesRoot(), "rules");
|
|
10031
|
+
const probe = resolveRulesFile("l1_rules.json");
|
|
10032
|
+
return probe ? probe.slice(0, probe.length - "l1_rules.json".length - 1) : null;
|
|
10033
|
+
}
|
|
10034
|
+
function checkRulesAssets() {
|
|
10035
|
+
const rep = {
|
|
10036
|
+
ok: true,
|
|
10037
|
+
schemaVersion: 0,
|
|
10038
|
+
fingerprint: "",
|
|
10039
|
+
fileHashes: {},
|
|
10040
|
+
errors: [],
|
|
10041
|
+
external: getRulesRoot() !== null
|
|
10042
|
+
};
|
|
10043
|
+
const dir = rulesDir();
|
|
10044
|
+
if (!dir) {
|
|
10045
|
+
rep.errors.push("\u627E\u4E0D\u5230\u89C4\u5219\u76EE\u5F55\uFF08\u5305\u5185\u89C4\u5219\u6587\u4EF6\u7F3A\u5931\uFF0C\u4E14\u672A\u8BBE AGENT_MOSS_RULES_ROOT\uFF09");
|
|
10046
|
+
rep.ok = false;
|
|
10047
|
+
return rep;
|
|
10048
|
+
}
|
|
10049
|
+
const manifestPath = join3(dir, MANIFEST_FILE);
|
|
10050
|
+
if (!existsSync2(manifestPath)) {
|
|
10051
|
+
rep.errors.push(`\u7F3A\u5C11 ${MANIFEST_FILE}\uFF08\u89C4\u5219\u5305\u5FC5\u987B\u58F0\u660E schema \u7248\u672C\uFF09`);
|
|
10052
|
+
} else {
|
|
10053
|
+
try {
|
|
10054
|
+
const m = JSON.parse(readFileSync3(manifestPath, "utf-8")) || {};
|
|
10055
|
+
const ver = m.schema_version;
|
|
10056
|
+
if (typeof ver !== "number" || !Number.isInteger(ver)) {
|
|
10057
|
+
rep.errors.push(`${MANIFEST_FILE} \u7F3A\u5C11\u6574\u6570 schema_version`);
|
|
10058
|
+
} else {
|
|
10059
|
+
rep.schemaVersion = ver;
|
|
10060
|
+
if (ver !== RULES_SCHEMA_VERSION) {
|
|
10061
|
+
rep.errors.push(
|
|
10062
|
+
`schema \u7248\u672C\u4E0D\u5339\u914D\uFF1A\u89C4\u5219\u5305\u662F ${ver}\uFF0C\u672C\u5F15\u64CE\u8BA4\u8BC6 ${RULES_SCHEMA_VERSION}\u3002\u8BF7\u5347\u7EA7\u5F15\u64CE\u6216\u6539\u7528\u5BF9\u5E94\u7248\u672C\u7684\u89C4\u5219\u5305\u3002`
|
|
10063
|
+
);
|
|
10064
|
+
}
|
|
10065
|
+
}
|
|
10066
|
+
} catch (e) {
|
|
10067
|
+
rep.errors.push(`${MANIFEST_FILE} \u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${String(e)}`);
|
|
10068
|
+
}
|
|
10069
|
+
}
|
|
10070
|
+
const fpParts = [];
|
|
10071
|
+
for (const [name, requiredKeys] of Object.entries(REQUIRED_RULE_FILES)) {
|
|
10072
|
+
const p = join3(dir, name);
|
|
10073
|
+
if (!existsSync2(p)) {
|
|
10074
|
+
rep.errors.push(`\u7F3A\u5C11\u89C4\u5219\u6587\u4EF6 ${name}`);
|
|
10075
|
+
rep.fileHashes[name] = "MISSING";
|
|
10076
|
+
fpParts.push(`${name}:MISSING`);
|
|
10077
|
+
continue;
|
|
10078
|
+
}
|
|
10079
|
+
let raw = null;
|
|
10080
|
+
try {
|
|
10081
|
+
raw = readFileSync3(p);
|
|
10082
|
+
} catch (e) {
|
|
10083
|
+
rep.errors.push(`${name} \u8BFB\u53D6\u5931\u8D25: ${String(e)}`);
|
|
10084
|
+
rep.fileHashes[name] = "UNREADABLE";
|
|
10085
|
+
fpParts.push(`${name}:UNREADABLE`);
|
|
10086
|
+
continue;
|
|
10087
|
+
}
|
|
10088
|
+
rep.fileHashes[name] = sha256Short(raw);
|
|
10089
|
+
fpParts.push(`${name}:${rep.fileHashes[name]}`);
|
|
10090
|
+
let data = null;
|
|
10091
|
+
try {
|
|
10092
|
+
data = JSON.parse(raw.toString("utf-8")) || {};
|
|
10093
|
+
} catch (e) {
|
|
10094
|
+
rep.errors.push(`${name} \u89E3\u6790\u5931\u8D25: ${String(e)}`);
|
|
10095
|
+
rep.fileHashes[name] = "BADJSON";
|
|
10096
|
+
continue;
|
|
10097
|
+
}
|
|
10098
|
+
if (typeof data !== "object" || Array.isArray(data)) {
|
|
10099
|
+
rep.errors.push(`${name} \u9876\u5C42\u5FC5\u987B\u662F JSON \u5BF9\u8C61`);
|
|
10100
|
+
continue;
|
|
10101
|
+
}
|
|
10102
|
+
const missing = requiredKeys.filter((k) => !(k in data));
|
|
10103
|
+
if (missing.length) rep.errors.push(`${name} \u7F3A\u5C11\u9876\u5C42\u952E: ${missing.join(", ")}`);
|
|
10104
|
+
}
|
|
10105
|
+
rep.fingerprint = sha256Short([...fpParts].sort().join("\n"));
|
|
10106
|
+
rep.ok = rep.errors.length === 0;
|
|
10107
|
+
return rep;
|
|
10108
|
+
}
|
|
10109
|
+
function formatRulesIntegrity(rep) {
|
|
10110
|
+
const src = rep.external ? `\u5916\u90E8\u89C4\u5219\u5305 ${getRulesRoot()}` : "\u5305\u5185\u89C4\u5219";
|
|
10111
|
+
const head = `\u89C4\u5219\u5305: ${src} | schema v${rep.schemaVersion} | \u6307\u7EB9 ${rep.fingerprint || "-"}`;
|
|
10112
|
+
if (rep.ok) return head;
|
|
10113
|
+
return `${head} | \u274C ${rep.errors.length} \u4E2A\u95EE\u9898: ${rep.errors.slice(0, 5).join("\uFF1B")}`;
|
|
10114
|
+
}
|
|
10115
|
+
function enforceRulesContract() {
|
|
10116
|
+
const rep = checkRulesAssets();
|
|
10117
|
+
if (rep.ok) {
|
|
10118
|
+
console.log(`[AgentMoss] ${formatRulesIntegrity(rep)}`);
|
|
10119
|
+
return rep;
|
|
10120
|
+
}
|
|
10121
|
+
const msg = formatRulesIntegrity(rep);
|
|
10122
|
+
if (rep.external) {
|
|
10123
|
+
console.error(`[AgentMoss] \u274C ${msg}`);
|
|
10124
|
+
throw new Error(
|
|
10125
|
+
`\u89C4\u5219\u5305\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u542F\u52A8\uFF08AGENT_MOSS_RULES_ROOT \u5DF2\u663E\u5F0F\u6307\u5B9A\uFF0C\u4E0D\u5141\u8BB8\u5E26\u7740\u6B8B\u7F3A\u89C4\u5219\u8FD0\u884C\uFF09\uFF1A${rep.errors.join("\uFF1B")}`
|
|
10126
|
+
);
|
|
10127
|
+
}
|
|
10128
|
+
console.error(`[AgentMoss] \u26A0\uFE0F ${msg}\uFF08\u4F7F\u7528\u5305\u5185\u89C4\u5219\uFF0C\u7167\u5E38\u542F\u52A8\uFF1B\u82E5\u662F npm \u5B89\u88C5\u8BF7\u91CD\u88C5\uFF09`);
|
|
10129
|
+
return rep;
|
|
10130
|
+
}
|
|
10131
|
+
|
|
10132
|
+
// src/server.ts
|
|
10457
10133
|
import { cors } from "hono/cors";
|
|
10458
10134
|
import { logger } from "hono/logger";
|
|
10459
10135
|
import net from "net";
|
|
10460
|
-
import { existsSync as
|
|
10136
|
+
import { existsSync as existsSync20 } from "fs";
|
|
10461
10137
|
|
|
10462
10138
|
// src/routes.ts
|
|
10463
10139
|
init_esm_shims();
|
|
@@ -10547,18 +10223,18 @@ var ReviewResponseSchema = z.object({
|
|
|
10547
10223
|
// src/engine/analyzer.ts
|
|
10548
10224
|
init_esm_shims();
|
|
10549
10225
|
init_coordinator();
|
|
10550
|
-
import { existsSync as
|
|
10226
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync17, writeFileSync as writeFileSync7 } from "fs";
|
|
10551
10227
|
import { tmpdir as tmpdir2 } from "os";
|
|
10552
|
-
import { join as
|
|
10228
|
+
import { join as join15, resolve as resolve10 } from "path";
|
|
10553
10229
|
|
|
10554
10230
|
// src/infra/policy-cache.ts
|
|
10555
10231
|
init_esm_shims();
|
|
10556
10232
|
init_config();
|
|
10557
|
-
import { createHash as
|
|
10558
|
-
import { existsSync as
|
|
10233
|
+
import { createHash as createHash3 } from "crypto";
|
|
10234
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync16, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
10559
10235
|
import { tmpdir } from "os";
|
|
10560
|
-
import { join as
|
|
10561
|
-
var CACHE_DIR =
|
|
10236
|
+
import { join as join14 } from "path";
|
|
10237
|
+
var CACHE_DIR = join14(tmpdir(), "agent_moss_policy_cache");
|
|
10562
10238
|
var cacheDirOverride = null;
|
|
10563
10239
|
function cacheDir() {
|
|
10564
10240
|
return cacheDirOverride ?? CACHE_DIR;
|
|
@@ -10574,9 +10250,9 @@ var PolicyCache = class {
|
|
|
10574
10250
|
return `${sessionId}::${promptSession}`;
|
|
10575
10251
|
}
|
|
10576
10252
|
cacheFilePath(sessionId, promptSession) {
|
|
10577
|
-
const promptHash =
|
|
10253
|
+
const promptHash = createHash3("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10578
10254
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10579
|
-
return
|
|
10255
|
+
return join14(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10580
10256
|
}
|
|
10581
10257
|
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10582
10258
|
get(sessionId, promptSession) {
|
|
@@ -10589,9 +10265,9 @@ var PolicyCache = class {
|
|
|
10589
10265
|
return hit;
|
|
10590
10266
|
}
|
|
10591
10267
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10592
|
-
if (
|
|
10268
|
+
if (existsSync16(file)) {
|
|
10593
10269
|
try {
|
|
10594
|
-
const policy =
|
|
10270
|
+
const policy = readFileSync16(file, "utf-8");
|
|
10595
10271
|
this.remember(key, policy);
|
|
10596
10272
|
return policy;
|
|
10597
10273
|
} catch {
|
|
@@ -10605,7 +10281,7 @@ var PolicyCache = class {
|
|
|
10605
10281
|
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10606
10282
|
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10607
10283
|
try {
|
|
10608
|
-
mkdirSync6(
|
|
10284
|
+
mkdirSync6(join14(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10609
10285
|
writeFileSync6(file, policy, "utf-8");
|
|
10610
10286
|
} catch {
|
|
10611
10287
|
}
|
|
@@ -10631,12 +10307,12 @@ var PolicyCache = class {
|
|
|
10631
10307
|
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10632
10308
|
}
|
|
10633
10309
|
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10634
|
-
const dir =
|
|
10310
|
+
const dir = join14(cacheDir(), safeSessionId);
|
|
10635
10311
|
try {
|
|
10636
|
-
if (
|
|
10312
|
+
if (existsSync16(dir)) {
|
|
10637
10313
|
for (const f of readdirSync5(dir)) {
|
|
10638
10314
|
try {
|
|
10639
|
-
unlinkSync3(
|
|
10315
|
+
unlinkSync3(join14(dir, f));
|
|
10640
10316
|
} catch {
|
|
10641
10317
|
}
|
|
10642
10318
|
}
|
|
@@ -10648,11 +10324,11 @@ var PolicyCache = class {
|
|
|
10648
10324
|
clear() {
|
|
10649
10325
|
this.memory.clear();
|
|
10650
10326
|
try {
|
|
10651
|
-
if (
|
|
10327
|
+
if (existsSync16(cacheDir())) {
|
|
10652
10328
|
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10653
|
-
const dir =
|
|
10329
|
+
const dir = join14(cacheDir(), sessionDir);
|
|
10654
10330
|
try {
|
|
10655
|
-
for (const f of readdirSync5(dir)) unlinkSync3(
|
|
10331
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join14(dir, f));
|
|
10656
10332
|
rmdirSync(dir);
|
|
10657
10333
|
} catch {
|
|
10658
10334
|
}
|
|
@@ -10739,7 +10415,7 @@ ${safe(policy, 2e3)}`);
|
|
|
10739
10415
|
|
|
10740
10416
|
// src/engine/analyzer.ts
|
|
10741
10417
|
import { parse as parseToml } from "smol-toml";
|
|
10742
|
-
var POLICY_OUTPUT_DIR =
|
|
10418
|
+
var POLICY_OUTPUT_DIR = join15(tmpdir2(), "agent_moss");
|
|
10743
10419
|
var policyOutputDirOverride = null;
|
|
10744
10420
|
function policyOutputDir() {
|
|
10745
10421
|
return policyOutputDirOverride ?? POLICY_OUTPUT_DIR;
|
|
@@ -10775,7 +10451,7 @@ function savePolicyToFile(sessionId, policyToml) {
|
|
|
10775
10451
|
const outDir = policyOutputDir();
|
|
10776
10452
|
mkdirSync7(outDir, { recursive: true });
|
|
10777
10453
|
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10778
|
-
const filepath =
|
|
10454
|
+
const filepath = join15(outDir, `${safeName}.toml`);
|
|
10779
10455
|
writeFileSync7(filepath, policyToml, "utf-8");
|
|
10780
10456
|
return filepath;
|
|
10781
10457
|
} catch (err) {
|
|
@@ -10814,11 +10490,11 @@ function loadPolicyMappingDoc() {
|
|
|
10814
10490
|
const dir = resolveAssetDir(__dirname, "templates");
|
|
10815
10491
|
if (dir) {
|
|
10816
10492
|
const p = resolve10(dir, "policy_mapping.md");
|
|
10817
|
-
if (
|
|
10493
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
10818
10494
|
}
|
|
10819
10495
|
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
10820
10496
|
const p = resolve10(cand, "policy_mapping.md");
|
|
10821
|
-
if (
|
|
10497
|
+
if (existsSync17(p)) return readFileSync17(p, "utf-8");
|
|
10822
10498
|
}
|
|
10823
10499
|
return "";
|
|
10824
10500
|
}
|
|
@@ -11093,9 +10769,11 @@ init_formal_engine();
|
|
|
11093
10769
|
|
|
11094
10770
|
// src/engine/formal-engine/pattern_miner.ts
|
|
11095
10771
|
init_esm_shims();
|
|
10772
|
+
init_rules_loader();
|
|
11096
10773
|
init_builder();
|
|
11097
|
-
var
|
|
11098
|
-
var
|
|
10774
|
+
var _PM = loadFormalRules().pattern_miner ?? {};
|
|
10775
|
+
var DANGER_STAGES = new Set(_PM.danger_stages ?? []);
|
|
10776
|
+
var SENSITIVE_PAIRS = new Set((_PM.sensitive_pairs ?? []).map((p) => p.join("|")));
|
|
11099
10777
|
function projectStages(actionHistory) {
|
|
11100
10778
|
const stages = [];
|
|
11101
10779
|
let prev = null;
|
|
@@ -11335,7 +11013,7 @@ function normalizeObservablePayload(body) {
|
|
|
11335
11013
|
init_rule_store();
|
|
11336
11014
|
init_skill_manager();
|
|
11337
11015
|
init_asset_dir();
|
|
11338
|
-
import { join as
|
|
11016
|
+
import { join as join17, dirname as dirname11 } from "path";
|
|
11339
11017
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
11340
11018
|
var __dirname9 = dirname11(fileURLToPath10(import.meta.url));
|
|
11341
11019
|
async function healthRoute(c) {
|
|
@@ -11586,7 +11264,7 @@ function getBrainRuleStore() {
|
|
|
11586
11264
|
return new BrainRuleStore();
|
|
11587
11265
|
}
|
|
11588
11266
|
function getBrainSkillManager() {
|
|
11589
|
-
const dir = resolveAssetDir(__dirname9, "skills") ??
|
|
11267
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join17(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11590
11268
|
return new BrainSkillManager(dir);
|
|
11591
11269
|
}
|
|
11592
11270
|
async function brainRulesRoute(c) {
|
|
@@ -11689,11 +11367,11 @@ init_rules_loader();
|
|
|
11689
11367
|
init_asset_dir();
|
|
11690
11368
|
init_rules_mode();
|
|
11691
11369
|
import { Hono } from "hono";
|
|
11692
|
-
import { readFileSync as
|
|
11693
|
-
import { join as
|
|
11370
|
+
import { readFileSync as readFileSync19, existsSync as existsSync19, unlinkSync as unlinkSync4 } from "fs";
|
|
11371
|
+
import { join as join18, dirname as dirname12 } from "path";
|
|
11694
11372
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
11695
11373
|
var __dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
11696
|
-
var STATIC_DIR =
|
|
11374
|
+
var STATIC_DIR = join18(__dirname10, "static");
|
|
11697
11375
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
11698
11376
|
function getConsoleToken() {
|
|
11699
11377
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -11731,9 +11409,9 @@ function createConsoleRouter() {
|
|
|
11731
11409
|
console2.use("/api/*", authMiddleware());
|
|
11732
11410
|
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
11733
11411
|
const serveIndex = (c) => {
|
|
11734
|
-
const indexFile =
|
|
11735
|
-
if (
|
|
11736
|
-
return c.html(
|
|
11412
|
+
const indexFile = join18(STATIC_DIR, "index.html");
|
|
11413
|
+
if (existsSync19(indexFile)) {
|
|
11414
|
+
return c.html(readFileSync19(indexFile, "utf-8"));
|
|
11737
11415
|
}
|
|
11738
11416
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
11739
11417
|
};
|
|
@@ -11928,15 +11606,15 @@ function createConsoleRouter() {
|
|
|
11928
11606
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
11929
11607
|
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
11930
11608
|
}
|
|
11931
|
-
const userPath =
|
|
11609
|
+
const userPath = join18(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
11932
11610
|
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
11933
11611
|
const candidates = [
|
|
11934
11612
|
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
11935
|
-
builtinDir ?
|
|
11613
|
+
builtinDir ? join18(builtinDir, `${skillId}.md`) : ""
|
|
11936
11614
|
].filter(Boolean);
|
|
11937
11615
|
for (const p of candidates) {
|
|
11938
|
-
if (
|
|
11939
|
-
return c.json({ skill_id: skillId, path: p, content:
|
|
11616
|
+
if (existsSync19(p)) {
|
|
11617
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync19(p, "utf-8") });
|
|
11940
11618
|
}
|
|
11941
11619
|
}
|
|
11942
11620
|
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
@@ -12033,7 +11711,7 @@ function createConsoleRouter() {
|
|
|
12033
11711
|
const defaults = generateSourceDefaults();
|
|
12034
11712
|
try {
|
|
12035
11713
|
const p = getRuntimeConfigPath();
|
|
12036
|
-
if (
|
|
11714
|
+
if (existsSync19(p)) unlinkSync4(p);
|
|
12037
11715
|
} catch {
|
|
12038
11716
|
}
|
|
12039
11717
|
return c.json(withL22View(defaults));
|
|
@@ -12090,7 +11768,7 @@ function logDeploymentSelfCheck() {
|
|
|
12090
11768
|
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
12091
11769
|
if (!builtinOnly) {
|
|
12092
11770
|
try {
|
|
12093
|
-
const dirs = rulesDirs().filter((d) =>
|
|
11771
|
+
const dirs = rulesDirs().filter((d) => existsSync20(d));
|
|
12094
11772
|
if (dirs.length > 0) {
|
|
12095
11773
|
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12096
11774
|
}
|
|
@@ -12129,6 +11807,7 @@ function logDeploymentSelfCheck() {
|
|
|
12129
11807
|
}
|
|
12130
11808
|
}
|
|
12131
11809
|
function createApp() {
|
|
11810
|
+
enforceRulesContract();
|
|
12132
11811
|
const app = new Hono2();
|
|
12133
11812
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
12134
11813
|
app.use("*", logger());
|
|
@@ -12223,7 +11902,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12223
11902
|
server = null;
|
|
12224
11903
|
},
|
|
12225
11904
|
serve: async () => {
|
|
12226
|
-
const { mkdirSync: mkdirSync8, existsSync:
|
|
11905
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync21, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12227
11906
|
const { dirname: dirname13 } = await import("path");
|
|
12228
11907
|
const dir = dirname13(socketPath);
|
|
12229
11908
|
if (dir) {
|
|
@@ -12232,7 +11911,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
12232
11911
|
} catch {
|
|
12233
11912
|
}
|
|
12234
11913
|
}
|
|
12235
|
-
if (
|
|
11914
|
+
if (existsSync21(socketPath)) {
|
|
12236
11915
|
const { connect } = await import("net");
|
|
12237
11916
|
const inUse = await new Promise((resolve11) => {
|
|
12238
11917
|
const probe = connect(socketPath);
|