@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
|
@@ -25,6 +25,10 @@ var init_esm_shims = __esm({
|
|
|
25
25
|
import { join } from "path";
|
|
26
26
|
import { homedir } from "os";
|
|
27
27
|
import { readFileSync } from "fs";
|
|
28
|
+
function getRulesRoot() {
|
|
29
|
+
const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
|
|
30
|
+
return raw ? raw : null;
|
|
31
|
+
}
|
|
28
32
|
function isRoot() {
|
|
29
33
|
const getuid = process.getuid;
|
|
30
34
|
return typeof getuid === "function" && getuid() === 0;
|
|
@@ -94,7 +98,7 @@ function getTokenStatsPath() {
|
|
|
94
98
|
}
|
|
95
99
|
return join(getDataRoot(), "agent_moss_token_stats.json");
|
|
96
100
|
}
|
|
97
|
-
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT;
|
|
101
|
+
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT;
|
|
98
102
|
var init_paths = __esm({
|
|
99
103
|
"src/infra/paths.ts"() {
|
|
100
104
|
"use strict";
|
|
@@ -102,6 +106,7 @@ var init_paths = __esm({
|
|
|
102
106
|
ENV_DATA_ROOT = "AGENT_MOSS_DATA_DIR";
|
|
103
107
|
ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
|
|
104
108
|
DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
|
|
109
|
+
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
105
110
|
}
|
|
106
111
|
});
|
|
107
112
|
|
|
@@ -149,251 +154,6 @@ var init_shell_split = __esm({
|
|
|
149
154
|
}
|
|
150
155
|
});
|
|
151
156
|
|
|
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 };
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
|
|
397
157
|
// src/infra/rules-mode.ts
|
|
398
158
|
function truthy(name) {
|
|
399
159
|
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
@@ -440,6 +200,19 @@ var init_rules_mode = __esm({
|
|
|
440
200
|
}
|
|
441
201
|
});
|
|
442
202
|
|
|
203
|
+
// src/infra/runtime-config-bridge.ts
|
|
204
|
+
function registerRuntimeConfigBridge(bridge) {
|
|
205
|
+
runtimeConfigBridge.current = bridge;
|
|
206
|
+
}
|
|
207
|
+
var runtimeConfigBridge;
|
|
208
|
+
var init_runtime_config_bridge = __esm({
|
|
209
|
+
"src/infra/runtime-config-bridge.ts"() {
|
|
210
|
+
"use strict";
|
|
211
|
+
init_esm_shims();
|
|
212
|
+
runtimeConfigBridge = { current: null };
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
443
216
|
// src/infra/rules-loader.ts
|
|
444
217
|
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
445
218
|
import { resolve, dirname, join as join2 } from "path";
|
|
@@ -459,8 +232,26 @@ function rulesDirs() {
|
|
|
459
232
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
460
233
|
return [...new Set(dirs)];
|
|
461
234
|
}
|
|
235
|
+
function rulesDirCands() {
|
|
236
|
+
const root = getRulesRoot();
|
|
237
|
+
if (root) return [join2(root, "rules")];
|
|
238
|
+
const cands = [
|
|
239
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
240
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
241
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
242
|
+
resolve(__dirname2, "../rules"),
|
|
243
|
+
resolve(__dirname2, "rules"),
|
|
244
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
245
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
246
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
247
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
248
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
249
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
250
|
+
];
|
|
251
|
+
return cands.filter((c) => existsSync(c));
|
|
252
|
+
}
|
|
462
253
|
function findRulesFile(name) {
|
|
463
|
-
for (const dir of
|
|
254
|
+
for (const dir of rulesDirCands()) {
|
|
464
255
|
const p = resolve(dir, name);
|
|
465
256
|
if (existsSync(p)) return p;
|
|
466
257
|
}
|
|
@@ -516,6 +307,74 @@ function loadL1() {
|
|
|
516
307
|
function loadL2() {
|
|
517
308
|
return loadJson("l2_rules.json");
|
|
518
309
|
}
|
|
310
|
+
function regexFlags(flags) {
|
|
311
|
+
let out = "";
|
|
312
|
+
for (const ch of String(flags ?? "")) if (RE_FLAG_MAP[ch]) out += RE_FLAG_MAP[ch];
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
315
|
+
function compileRuleRegex(rule, extraFlags = "") {
|
|
316
|
+
return new RegExp(String(rule.pattern), regexFlags(rule.flags) + extraFlags);
|
|
317
|
+
}
|
|
318
|
+
function resolveKeywordSet(group, keywordSets) {
|
|
319
|
+
if (typeof group === "string") return new Set(keywordSets[group.slice(1)] ?? []);
|
|
320
|
+
return new Set(Array.isArray(group) ? group : []);
|
|
321
|
+
}
|
|
322
|
+
function loadFormalRules() {
|
|
323
|
+
return loadJson("formal_rules.json");
|
|
324
|
+
}
|
|
325
|
+
function loadLogicRules() {
|
|
326
|
+
return loadJson("logic_rules.json");
|
|
327
|
+
}
|
|
328
|
+
function loadHeuristicRules() {
|
|
329
|
+
return loadJson("heuristic_rules.json");
|
|
330
|
+
}
|
|
331
|
+
function loadScriptRules() {
|
|
332
|
+
return loadJson("script_rules.json");
|
|
333
|
+
}
|
|
334
|
+
function loadInline() {
|
|
335
|
+
return loadJson("inline_rules.json");
|
|
336
|
+
}
|
|
337
|
+
function loadPlatformTraits(osName) {
|
|
338
|
+
const data = loadJson("platform_traits.json");
|
|
339
|
+
if (!data || Object.keys(data).length === 0) {
|
|
340
|
+
console.error(
|
|
341
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 platform_traits.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u56DE\u843D\u4E3A\u7A7A\uFF08\u767D\u540D\u5355\u4E3A\u7A7A\uFF0C\u5224\u5B9A\u4F1A\u53D8\u4E25\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
342
|
+
);
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
const name = resolveTraitsOs(osName);
|
|
346
|
+
const entry = data[name];
|
|
347
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
348
|
+
console.error(`[AgentMoss] platform_traits.json \u91CC\u6CA1\u6709\u53EF\u7528\u7684 ${name} \u6761\u76EE \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u4E3A\u7A7A\u3002`);
|
|
349
|
+
return {};
|
|
350
|
+
}
|
|
351
|
+
const out = {};
|
|
352
|
+
for (const f of TRAITS_FIELDS) if (f in entry) out[f] = entry[f];
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
function resolveTraitsOs(osName) {
|
|
356
|
+
const data = loadJson("platform_traits.json");
|
|
357
|
+
let name = String(osName || "").trim().toLowerCase() || TRAITS_OS_FALLBACK;
|
|
358
|
+
const seen = /* @__PURE__ */ new Set();
|
|
359
|
+
for (; ; ) {
|
|
360
|
+
if (seen.has(name)) {
|
|
361
|
+
console.error(`[AgentMoss] platform_traits.json \u7684 inherits \u6210\u73AF\uFF08${name}\uFF09\u2014\u2014 \u56DE\u843D linux\u3002`);
|
|
362
|
+
return TRAITS_OS_FALLBACK;
|
|
363
|
+
}
|
|
364
|
+
seen.add(name);
|
|
365
|
+
const entry = data[name];
|
|
366
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
367
|
+
const nxt = entry.inherits;
|
|
368
|
+
if (typeof nxt === "string" && nxt.trim().toLowerCase() !== name) {
|
|
369
|
+
name = nxt.trim().toLowerCase();
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
return name;
|
|
373
|
+
}
|
|
374
|
+
if (name === TRAITS_OS_FALLBACK) return name;
|
|
375
|
+
name = TRAITS_OS_FALLBACK;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
519
378
|
function loadL3() {
|
|
520
379
|
const data = { ...loadJson("l3_rules.json") };
|
|
521
380
|
try {
|
|
@@ -844,7 +703,7 @@ function loadExtraRules() {
|
|
|
844
703
|
if (isBuiltinOnly()) return {};
|
|
845
704
|
return rulesCached().extra;
|
|
846
705
|
}
|
|
847
|
-
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS,
|
|
706
|
+
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS, frozenJsonCache, SCENARIO_LIST_KEYS, RE_FLAG_MAP, TRAITS_FIELDS, TRAITS_OS_FALLBACK, EXTRA_CATEGORIES, STRUCT_KEYS, EXTRA_FLAT_FIELD, _RULES_CACHE, warnedIgnored;
|
|
848
707
|
var init_rules_loader = __esm({
|
|
849
708
|
"src/infra/rules-loader.ts"() {
|
|
850
709
|
"use strict";
|
|
@@ -857,24 +716,20 @@ var init_rules_loader = __esm({
|
|
|
857
716
|
OVERRIDE_FILENAME = "override.json";
|
|
858
717
|
EXTRA_FILENAME = "extra.json";
|
|
859
718
|
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
860
|
-
RULES_DIR_CANDS = (() => {
|
|
861
|
-
const cands = [
|
|
862
|
-
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
863
|
-
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
864
|
-
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
865
|
-
resolve(__dirname2, "../rules"),
|
|
866
|
-
resolve(__dirname2, "rules"),
|
|
867
|
-
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
868
|
-
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
869
|
-
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
870
|
-
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
871
|
-
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
872
|
-
resolve(process.cwd(), "agent_moss/rules")
|
|
873
|
-
];
|
|
874
|
-
return cands.filter((c) => existsSync(c));
|
|
875
|
-
})();
|
|
876
719
|
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
877
720
|
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
721
|
+
RE_FLAG_MAP = { i: "i", s: "s", m: "m" };
|
|
722
|
+
TRAITS_FIELDS = [
|
|
723
|
+
"write_keywords",
|
|
724
|
+
"read_keywords",
|
|
725
|
+
"write_command_res",
|
|
726
|
+
"read_only_commands",
|
|
727
|
+
"fully_safe_commands",
|
|
728
|
+
"readonly_sensitive_commands",
|
|
729
|
+
"safe_action_types",
|
|
730
|
+
"readonly_sensitive_action_types"
|
|
731
|
+
];
|
|
732
|
+
TRAITS_OS_FALLBACK = "linux";
|
|
878
733
|
EXTRA_CATEGORIES = {
|
|
879
734
|
dangerous_commands: ["pattern"],
|
|
880
735
|
injection_detection: ["keyword", "lang"],
|
|
@@ -896,7 +751,155 @@ var init_rules_loader = __esm({
|
|
|
896
751
|
}
|
|
897
752
|
});
|
|
898
753
|
|
|
754
|
+
// src/engine/inline-analyzer.ts
|
|
755
|
+
function buildChainRegex(pattern, flags) {
|
|
756
|
+
return new RegExp(pattern, (flags || "") + "g");
|
|
757
|
+
}
|
|
758
|
+
function isLiteralTextCommand(command) {
|
|
759
|
+
const segments = splitCommandSegments(command);
|
|
760
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
761
|
+
if (command.includes(">")) {
|
|
762
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
763
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
764
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
765
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
766
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
function extractInlineCode(command) {
|
|
776
|
+
const parts = [];
|
|
777
|
+
let m;
|
|
778
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
779
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
780
|
+
const code = m[1] ?? m[2];
|
|
781
|
+
if (code) parts.push(code);
|
|
782
|
+
}
|
|
783
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
784
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
785
|
+
const code = m[1] ?? m[2];
|
|
786
|
+
if (code) parts.push(code);
|
|
787
|
+
}
|
|
788
|
+
return parts.join("\n");
|
|
789
|
+
}
|
|
790
|
+
function hasWriteExecChain(command) {
|
|
791
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
792
|
+
p.lastIndex = 0;
|
|
793
|
+
return p.test(command);
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
function hasDirTraversal(code) {
|
|
797
|
+
const lower = code.toLowerCase();
|
|
798
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
799
|
+
}
|
|
800
|
+
function hasReadWriteKeyword(code) {
|
|
801
|
+
const lower = code.toLowerCase();
|
|
802
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
803
|
+
}
|
|
804
|
+
function analyzeInlineScript(command) {
|
|
805
|
+
const code = extractInlineCode(command);
|
|
806
|
+
if (!code) {
|
|
807
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
808
|
+
}
|
|
809
|
+
if (hasWriteExecChain(command)) {
|
|
810
|
+
return {
|
|
811
|
+
actionable: true,
|
|
812
|
+
allowed: false,
|
|
813
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
814
|
+
riskLevel: "critical",
|
|
815
|
+
riskType: "script_execution"
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
819
|
+
if (redirMatch) {
|
|
820
|
+
return {
|
|
821
|
+
actionable: true,
|
|
822
|
+
allowed: false,
|
|
823
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
824
|
+
riskLevel: "critical",
|
|
825
|
+
riskType: "file_access"
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
829
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
830
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
831
|
+
}
|
|
832
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
833
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
834
|
+
return {
|
|
835
|
+
actionable: true,
|
|
836
|
+
allowed: false,
|
|
837
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
838
|
+
riskLevel: "critical",
|
|
839
|
+
riskType: "file_access"
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
actionable: true,
|
|
844
|
+
allowed: true,
|
|
845
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
846
|
+
riskLevel: "low",
|
|
847
|
+
riskType: "file_access"
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
var INLINE_RULES, INLINE_CODE_RE, ECHO_STRING_RE, ECHO_SEG_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, _REDIRECT_PATHS, SHELL_RC_FRAGMENTS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
851
|
+
var init_inline_analyzer = __esm({
|
|
852
|
+
"src/engine/inline-analyzer.ts"() {
|
|
853
|
+
"use strict";
|
|
854
|
+
init_esm_shims();
|
|
855
|
+
init_shell_split();
|
|
856
|
+
init_rules_loader();
|
|
857
|
+
INLINE_RULES = loadInline();
|
|
858
|
+
if (!INLINE_RULES || Object.keys(INLINE_RULES).length === 0) {
|
|
859
|
+
console.error(
|
|
860
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 inline_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u5185\u8054\u811A\u672C\u5224\u5B9A\u8BCD\u8868\u4E3A\u7A7A\uFF0C\u5185\u8054\u811A\u672C\u4E2D\u7684\u654F\u611F\u8DEF\u5F84\u8BFB\u5199\u5C06\u6309\u300C\u6587\u672C\u5B57\u9762\u91CF\u300D\u653E\u884C\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
864
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
865
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
866
|
+
READ_WRITE_KEYWORDS = INLINE_RULES.read_write_keywords ?? [];
|
|
867
|
+
WRITE_EXEC_CHAIN_PATTERNS = (INLINE_RULES.write_exec_chain_patterns ?? []).filter((p) => p && typeof p.pattern === "string").map((p) => buildChainRegex(p.pattern, p.flags ?? ""));
|
|
868
|
+
_REDIRECT_PATHS = INLINE_RULES.redirect_sensitive_paths ?? {};
|
|
869
|
+
SHELL_RC_FRAGMENTS = _REDIRECT_PATHS.shell_rc_fragments ?? [];
|
|
870
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
871
|
+
..._REDIRECT_PATHS.ts_static ?? [],
|
|
872
|
+
...SHELL_RC_FRAGMENTS
|
|
873
|
+
];
|
|
874
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
875
|
+
DIR_TRAVERSAL_KEYWORDS = INLINE_RULES.dir_traversal_keywords ?? [];
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
|
|
899
879
|
// src/engine/platform-traits.ts
|
|
880
|
+
function buildTraits(osName) {
|
|
881
|
+
const t = loadPlatformTraits(osName);
|
|
882
|
+
const strs = (k) => Array.isArray(t[k]) ? t[k] : [];
|
|
883
|
+
return {
|
|
884
|
+
writeKeywords: strs("write_keywords"),
|
|
885
|
+
readKeywords: strs("read_keywords"),
|
|
886
|
+
writeCommandRes: strs("write_command_res").map((p) => new RegExp(p)),
|
|
887
|
+
readOnlyCommands: new Set(strs("read_only_commands")),
|
|
888
|
+
fullySafeCommands: strs("fully_safe_commands").map((p) => new RegExp(p, "i")),
|
|
889
|
+
readonlySensitiveCommands: strs("readonly_sensitive_commands").map((p) => new RegExp(p, "i")),
|
|
890
|
+
safeActionTypes: new Set(strs("safe_action_types")),
|
|
891
|
+
readonlySensitiveActionTypes: new Set(strs("readonly_sensitive_action_types"))
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function traitsFor(key) {
|
|
895
|
+
const resolved = resolveTraitsOs(key);
|
|
896
|
+
let t = traitsCache.get(resolved);
|
|
897
|
+
if (!t) {
|
|
898
|
+
t = buildTraits(resolved);
|
|
899
|
+
traitsCache.set(resolved, t);
|
|
900
|
+
}
|
|
901
|
+
return t;
|
|
902
|
+
}
|
|
900
903
|
function getPlatformTraits(osType) {
|
|
901
904
|
const accepted = acceptedOsValues(osType);
|
|
902
905
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -904,368 +907,18 @@ function getPlatformTraits(osType) {
|
|
|
904
907
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
905
908
|
return LINUX_TRAITS;
|
|
906
909
|
}
|
|
907
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
910
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
908
911
|
var init_platform_traits = __esm({
|
|
909
912
|
"src/engine/platform-traits.ts"() {
|
|
910
913
|
"use strict";
|
|
911
914
|
init_esm_shims();
|
|
912
915
|
init_logic_rules();
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
"modify",
|
|
920
|
-
"\u4FEE\u6539",
|
|
921
|
-
"update",
|
|
922
|
-
"\u66F4\u65B0",
|
|
923
|
-
"delete",
|
|
924
|
-
"\u5220\u9664",
|
|
925
|
-
"remove",
|
|
926
|
-
"\u79FB\u9664",
|
|
927
|
-
"create",
|
|
928
|
-
"\u521B\u5EFA",
|
|
929
|
-
"append",
|
|
930
|
-
"\u8FFD\u52A0",
|
|
931
|
-
"truncate",
|
|
932
|
-
"\u622A\u65AD",
|
|
933
|
-
"overwrite",
|
|
934
|
-
"\u8986\u76D6",
|
|
935
|
-
"replace",
|
|
936
|
-
"\u66FF\u6362",
|
|
937
|
-
"edit"
|
|
938
|
-
],
|
|
939
|
-
readKeywords: [
|
|
940
|
-
"read",
|
|
941
|
-
"\u8BFB\u53D6",
|
|
942
|
-
"cat",
|
|
943
|
-
"head",
|
|
944
|
-
"tail",
|
|
945
|
-
"less",
|
|
946
|
-
"more",
|
|
947
|
-
"view",
|
|
948
|
-
"\u67E5\u770B",
|
|
949
|
-
"open",
|
|
950
|
-
"\u6253\u5F00",
|
|
951
|
-
"load",
|
|
952
|
-
"\u52A0\u8F7D",
|
|
953
|
-
"grep",
|
|
954
|
-
"search",
|
|
955
|
-
"\u641C\u7D22",
|
|
956
|
-
"find",
|
|
957
|
-
"\u67E5\u627E"
|
|
958
|
-
],
|
|
959
|
-
writeCommandRes: [
|
|
960
|
-
/\brm\b/,
|
|
961
|
-
/\bunlink\b/,
|
|
962
|
-
/\brmdir\b/,
|
|
963
|
-
/\bshred\b/,
|
|
964
|
-
/\bcp\b/,
|
|
965
|
-
/\bmv\b/,
|
|
966
|
-
/\binstall\b/,
|
|
967
|
-
/\btee\b/,
|
|
968
|
-
/\bdd\b/,
|
|
969
|
-
/\bmkfs\b/,
|
|
970
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
971
|
-
/\bchmod\b/,
|
|
972
|
-
/\bchown\b/,
|
|
973
|
-
/\btruncate\b/
|
|
974
|
-
],
|
|
975
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
976
|
-
"cat",
|
|
977
|
-
"head",
|
|
978
|
-
"tail",
|
|
979
|
-
"less",
|
|
980
|
-
"more",
|
|
981
|
-
"grep",
|
|
982
|
-
"find",
|
|
983
|
-
"awk",
|
|
984
|
-
"sed",
|
|
985
|
-
"sort",
|
|
986
|
-
"uniq",
|
|
987
|
-
"wc",
|
|
988
|
-
"cut",
|
|
989
|
-
"strings",
|
|
990
|
-
"od",
|
|
991
|
-
"xxd",
|
|
992
|
-
"hexdump",
|
|
993
|
-
"tr",
|
|
994
|
-
"file",
|
|
995
|
-
"stat",
|
|
996
|
-
"du",
|
|
997
|
-
"df",
|
|
998
|
-
"ls",
|
|
999
|
-
"dir",
|
|
1000
|
-
"tree",
|
|
1001
|
-
"nl",
|
|
1002
|
-
"tac",
|
|
1003
|
-
"rev",
|
|
1004
|
-
"echo",
|
|
1005
|
-
"printf",
|
|
1006
|
-
"lsblk",
|
|
1007
|
-
"blockdev",
|
|
1008
|
-
"smartctl",
|
|
1009
|
-
"udevadm",
|
|
1010
|
-
"dmidecode",
|
|
1011
|
-
"lscpu",
|
|
1012
|
-
"lspci",
|
|
1013
|
-
"lsusb",
|
|
1014
|
-
"lsmem",
|
|
1015
|
-
"lsns",
|
|
1016
|
-
"lsof",
|
|
1017
|
-
"hwinfo",
|
|
1018
|
-
"inxi",
|
|
1019
|
-
"fdisk",
|
|
1020
|
-
"parted",
|
|
1021
|
-
"dmesg",
|
|
1022
|
-
"journalctl",
|
|
1023
|
-
"systemctl",
|
|
1024
|
-
"hostnamectl",
|
|
1025
|
-
"localectl",
|
|
1026
|
-
"timedatectl",
|
|
1027
|
-
"ps",
|
|
1028
|
-
"top",
|
|
1029
|
-
"free",
|
|
1030
|
-
"vmstat",
|
|
1031
|
-
"iostat",
|
|
1032
|
-
"mpstat",
|
|
1033
|
-
"pidof",
|
|
1034
|
-
"pgrep",
|
|
1035
|
-
"ip",
|
|
1036
|
-
"ifconfig",
|
|
1037
|
-
"route",
|
|
1038
|
-
"ss",
|
|
1039
|
-
"netstat",
|
|
1040
|
-
"arp",
|
|
1041
|
-
"ethtool",
|
|
1042
|
-
"nmcli",
|
|
1043
|
-
"uname",
|
|
1044
|
-
"arch",
|
|
1045
|
-
"nproc",
|
|
1046
|
-
"getconf",
|
|
1047
|
-
"getent",
|
|
1048
|
-
"id",
|
|
1049
|
-
"whoami",
|
|
1050
|
-
"who",
|
|
1051
|
-
"w",
|
|
1052
|
-
"last",
|
|
1053
|
-
"uptime",
|
|
1054
|
-
"lsmod",
|
|
1055
|
-
"modinfo",
|
|
1056
|
-
"sysctl"
|
|
1057
|
-
]),
|
|
1058
|
-
fullySafeCommands: [
|
|
1059
|
-
/^ls(?:\s|$)/i,
|
|
1060
|
-
/^dir(?:\s|$)/i,
|
|
1061
|
-
/^pwd(?:\s|$)/i,
|
|
1062
|
-
/^which\s+/i,
|
|
1063
|
-
/^whereis\s+/i,
|
|
1064
|
-
/^realpath\s+/i,
|
|
1065
|
-
/^basename\s+/i,
|
|
1066
|
-
/^dirname\s+/i,
|
|
1067
|
-
/^file\s+/i,
|
|
1068
|
-
/^stat\s+/i,
|
|
1069
|
-
/^du(?:\s|$)/i,
|
|
1070
|
-
/^echo\s+/i,
|
|
1071
|
-
/^printf\s+/i,
|
|
1072
|
-
/^type\s+/i,
|
|
1073
|
-
/^command\s+/i,
|
|
1074
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
1075
|
-
/^whoami(?:\s|$)/i,
|
|
1076
|
-
/^id(?:\s|$)/i,
|
|
1077
|
-
/^hostname(?:\s|$)/i,
|
|
1078
|
-
/^uname(?:\s|$)/i,
|
|
1079
|
-
/^date(?:\s|$)/i,
|
|
1080
|
-
/^env(?:\s|$)/i,
|
|
1081
|
-
/^printenv(?:\s|$)/i,
|
|
1082
|
-
/^tty(?:\s|$)/i,
|
|
1083
|
-
/^arch(?:\s|$)/i,
|
|
1084
|
-
/^uptime(?:\s|$)/i,
|
|
1085
|
-
/^groups(?:\s|$)/i,
|
|
1086
|
-
/^logname(?:\s|$)/i,
|
|
1087
|
-
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
1088
|
-
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
1089
|
-
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
1090
|
-
/^df(?:\s|$)/i,
|
|
1091
|
-
/^free(?:\s|$)/i,
|
|
1092
|
-
/^vmstat(?:\s|$)/i,
|
|
1093
|
-
/^nproc(?:\s|$)/i,
|
|
1094
|
-
/^lscpu(?:\s|$)/i,
|
|
1095
|
-
/^lsblk(?:\s|$)/i,
|
|
1096
|
-
/^lspci(?:\s|$)/i,
|
|
1097
|
-
/^lsusb(?:\s|$)/i,
|
|
1098
|
-
/^lsmod(?:\s|$)/i
|
|
1099
|
-
],
|
|
1100
|
-
readonlySensitiveCommands: [
|
|
1101
|
-
/^cat\s+/i,
|
|
1102
|
-
/^head(?:\s|$)/i,
|
|
1103
|
-
/^tail(?:\s|$)/i,
|
|
1104
|
-
/^less(?:\s|$)/i,
|
|
1105
|
-
/^more(?:\s|$)/i,
|
|
1106
|
-
/^wc(?:\s|$)/i,
|
|
1107
|
-
/^grep(?:\s|$)/i,
|
|
1108
|
-
/^find\s+/i,
|
|
1109
|
-
/^ag(?:\s|$)/i,
|
|
1110
|
-
/^rg(?:\s|$)/i,
|
|
1111
|
-
/^awk\s+/i,
|
|
1112
|
-
// NEW-FN-P(M03):sed -i/--in-place 是原地写,挡出白名单走 L2 read_before_write。
|
|
1113
|
-
// 前瞻放最前(外层 ^sed\s+ 会消费 -i 前的空格)。与 Python profiles/linux.py 对齐。
|
|
1114
|
-
/^(?!.*(?:\s|['"])-[a-zA-Z]*i[a-zA-Z]*\b)(?!.*(?:\s|['"])--in-place)sed\s+/i,
|
|
1115
|
-
/^cut\s+/i,
|
|
1116
|
-
/^sort(?:\s|$)/i,
|
|
1117
|
-
/^uniq(?:\s|$)/i,
|
|
1118
|
-
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
1119
|
-
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
1120
|
-
/^ps(?:\s|$)/i,
|
|
1121
|
-
/^ss(?:\s|$)/i,
|
|
1122
|
-
/^netstat(?:\s|$)/i,
|
|
1123
|
-
/^lsof(?:\s|$)/i,
|
|
1124
|
-
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
1125
|
-
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
1126
|
-
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
1127
|
-
/^service\s+\S+\s+status\b/i,
|
|
1128
|
-
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
1129
|
-
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
1130
|
-
// 的话只看紧跟 journalctl 的第一个参数,
|
|
1131
|
-
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
1132
|
-
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
1133
|
-
]
|
|
1134
|
-
};
|
|
1135
|
-
WINDOWS_TRAITS = {
|
|
1136
|
-
writeKeywords: [
|
|
1137
|
-
"write",
|
|
1138
|
-
"\u5199\u5165",
|
|
1139
|
-
"save",
|
|
1140
|
-
"\u4FDD\u5B58",
|
|
1141
|
-
"modify",
|
|
1142
|
-
"\u4FEE\u6539",
|
|
1143
|
-
"update",
|
|
1144
|
-
"\u66F4\u65B0",
|
|
1145
|
-
"delete",
|
|
1146
|
-
"\u5220\u9664",
|
|
1147
|
-
"remove",
|
|
1148
|
-
"\u79FB\u9664",
|
|
1149
|
-
"create",
|
|
1150
|
-
"\u521B\u5EFA",
|
|
1151
|
-
"append",
|
|
1152
|
-
"\u8FFD\u52A0",
|
|
1153
|
-
"overwrite",
|
|
1154
|
-
"\u8986\u76D6",
|
|
1155
|
-
"replace",
|
|
1156
|
-
"\u66FF\u6362",
|
|
1157
|
-
"set",
|
|
1158
|
-
"\u8BBE\u7F6E"
|
|
1159
|
-
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
1160
|
-
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
1161
|
-
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
1162
|
-
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
1163
|
-
],
|
|
1164
|
-
readKeywords: [
|
|
1165
|
-
"read",
|
|
1166
|
-
"\u8BFB\u53D6",
|
|
1167
|
-
"type",
|
|
1168
|
-
"findstr",
|
|
1169
|
-
"dir",
|
|
1170
|
-
"view",
|
|
1171
|
-
"\u67E5\u770B",
|
|
1172
|
-
"open",
|
|
1173
|
-
"\u6253\u5F00",
|
|
1174
|
-
"load",
|
|
1175
|
-
"\u52A0\u8F7D",
|
|
1176
|
-
"search",
|
|
1177
|
-
"\u641C\u7D22",
|
|
1178
|
-
"find",
|
|
1179
|
-
"\u67E5\u627E",
|
|
1180
|
-
"get",
|
|
1181
|
-
"\u83B7\u53D6"
|
|
1182
|
-
],
|
|
1183
|
-
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
1184
|
-
writeCommandRes: [
|
|
1185
|
-
/\b(del|erase)\b/,
|
|
1186
|
-
/\brmdir\b/,
|
|
1187
|
-
/\b(rd)\b/,
|
|
1188
|
-
/\bcopy\b/,
|
|
1189
|
-
/\b(xcopy|robocopy)\b/,
|
|
1190
|
-
/\b(move|movefile)\b/,
|
|
1191
|
-
/\breplace\b/,
|
|
1192
|
-
/\bicacls\b/,
|
|
1193
|
-
/\battrib\b/,
|
|
1194
|
-
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
1195
|
-
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
1196
|
-
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
1197
|
-
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
1198
|
-
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
1199
|
-
/\bformat\b/,
|
|
1200
|
-
/\bdiskpart\b/,
|
|
1201
|
-
/\bmkdir\b/,
|
|
1202
|
-
/\bmd\b/,
|
|
1203
|
-
/\bsetx\b/,
|
|
1204
|
-
/\bwmic\b/
|
|
1205
|
-
],
|
|
1206
|
-
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
1207
|
-
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
1208
|
-
"type",
|
|
1209
|
-
"findstr",
|
|
1210
|
-
"more",
|
|
1211
|
-
"tree",
|
|
1212
|
-
"dir",
|
|
1213
|
-
"where",
|
|
1214
|
-
"ver",
|
|
1215
|
-
"cls",
|
|
1216
|
-
"echo",
|
|
1217
|
-
"help",
|
|
1218
|
-
"fc",
|
|
1219
|
-
"comp",
|
|
1220
|
-
"find",
|
|
1221
|
-
"sort",
|
|
1222
|
-
"powershell",
|
|
1223
|
-
"cmd",
|
|
1224
|
-
"netstat",
|
|
1225
|
-
"ipconfig",
|
|
1226
|
-
"tasklist",
|
|
1227
|
-
"systeminfo",
|
|
1228
|
-
"driverquery",
|
|
1229
|
-
"reg",
|
|
1230
|
-
"wmic",
|
|
1231
|
-
"whoami",
|
|
1232
|
-
"hostname",
|
|
1233
|
-
"getmac",
|
|
1234
|
-
"query",
|
|
1235
|
-
"w32tm",
|
|
1236
|
-
"diskpart"
|
|
1237
|
-
]),
|
|
1238
|
-
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
1239
|
-
fullySafeCommands: [
|
|
1240
|
-
/^dir(?:\s|$)/,
|
|
1241
|
-
/^where\s+/,
|
|
1242
|
-
/^echo\s+/,
|
|
1243
|
-
/^cd(?:\s|$)/,
|
|
1244
|
-
/^tree(?:\s|$)/,
|
|
1245
|
-
/^help(?:\s|$)/,
|
|
1246
|
-
/^cls(?:\s|$)/,
|
|
1247
|
-
/^ver(?:\s|$)/,
|
|
1248
|
-
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
1249
|
-
/^whoami(?:\s|$)/,
|
|
1250
|
-
/^hostname(?:\s|$)/,
|
|
1251
|
-
/^systeminfo(?:\s|$)/,
|
|
1252
|
-
/^tasklist(?:\s|$)/,
|
|
1253
|
-
/^getmac(?:\s|$)/,
|
|
1254
|
-
/^driverquery(?:\s|$)/,
|
|
1255
|
-
/^netstat(?:\s|$)/
|
|
1256
|
-
],
|
|
1257
|
-
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
1258
|
-
readonlySensitiveCommands: [
|
|
1259
|
-
/^type\s+/,
|
|
1260
|
-
/^findstr\s+/,
|
|
1261
|
-
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
1262
|
-
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
1263
|
-
/^sc\s+query\b/,
|
|
1264
|
-
/^reg\s+query\b/
|
|
1265
|
-
]
|
|
1266
|
-
};
|
|
1267
|
-
MACOS_TRAITS = LINUX_TRAITS;
|
|
1268
|
-
HARMONY_TRAITS = LINUX_TRAITS;
|
|
916
|
+
init_rules_loader();
|
|
917
|
+
traitsCache = /* @__PURE__ */ new Map();
|
|
918
|
+
LINUX_TRAITS = traitsFor("linux");
|
|
919
|
+
WINDOWS_TRAITS = traitsFor("windows");
|
|
920
|
+
MACOS_TRAITS = traitsFor("macos");
|
|
921
|
+
HARMONY_TRAITS = traitsFor("harmony");
|
|
1269
922
|
}
|
|
1270
923
|
});
|
|
1271
924
|
|
|
@@ -1424,7 +1077,6 @@ function checkIntentConsistency(input) {
|
|
|
1424
1077
|
const { aNext, promptSession, promptHistory } = input;
|
|
1425
1078
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1426
1079
|
const actionType = aNext.actionType.toLowerCase();
|
|
1427
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1428
1080
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1429
1081
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1430
1082
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1452,7 +1104,6 @@ function runLogicCheck(input, opts) {
|
|
|
1452
1104
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1453
1105
|
const actionType = aNext.actionType.toLowerCase();
|
|
1454
1106
|
const resolvedCwd = cwd || "/";
|
|
1455
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1456
1107
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1457
1108
|
if (!opts?.skipReadBeforeWrite) {
|
|
1458
1109
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1564,7 +1215,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1564
1215
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1565
1216
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1566
1217
|
if (!isWrite) return null;
|
|
1567
|
-
const WRITE_CMDS =
|
|
1218
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1568
1219
|
if (isBashLike) {
|
|
1569
1220
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1570
1221
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1596,7 +1247,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1596
1247
|
}
|
|
1597
1248
|
if (!hasWriteSeg) return null;
|
|
1598
1249
|
}
|
|
1599
|
-
const SOURCE_ARG_CMDS =
|
|
1250
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1600
1251
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1601
1252
|
const targetPaths = [];
|
|
1602
1253
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1644,7 +1295,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1644
1295
|
}
|
|
1645
1296
|
}
|
|
1646
1297
|
}
|
|
1647
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1648
1298
|
let filteredPaths = uniqueTargetPaths;
|
|
1649
1299
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1650
1300
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1745,10 +1395,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1745
1395
|
if (!hasTraversal) {
|
|
1746
1396
|
return null;
|
|
1747
1397
|
}
|
|
1748
|
-
const READ_KEYWORDS =
|
|
1749
|
-
const WRITE_KEYWORDS2 =
|
|
1750
|
-
const EXEC_KEYWORDS =
|
|
1751
|
-
const OPEN_KEYWORDS =
|
|
1398
|
+
const READ_KEYWORDS = IFA_READ_KEYWORDS;
|
|
1399
|
+
const WRITE_KEYWORDS2 = IFA_WRITE_KEYWORDS;
|
|
1400
|
+
const EXEC_KEYWORDS = IFA_EXEC_KEYWORDS;
|
|
1401
|
+
const OPEN_KEYWORDS = IFA_OPEN_KEYWORDS;
|
|
1752
1402
|
const code = extractInlineCode(actionDetail);
|
|
1753
1403
|
if (!code) return null;
|
|
1754
1404
|
const codeLower = code.toLowerCase();
|
|
@@ -1811,7 +1461,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1811
1461
|
violatedLayers: ["L2.1"]
|
|
1812
1462
|
};
|
|
1813
1463
|
}
|
|
1814
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1464
|
+
var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
1815
1465
|
var init_logic_rules = __esm({
|
|
1816
1466
|
"src/engine/logic-rules.ts"() {
|
|
1817
1467
|
"use strict";
|
|
@@ -1821,6 +1471,10 @@ var init_logic_rules = __esm({
|
|
|
1821
1471
|
init_rules_loader();
|
|
1822
1472
|
init_platform_traits();
|
|
1823
1473
|
init_shell_split();
|
|
1474
|
+
_LOGIC = loadLogicRules();
|
|
1475
|
+
_HEURISTIC = loadHeuristicRules();
|
|
1476
|
+
EXCLUDED_PREFIXES = _HEURISTIC.sensitive_excluded_prefixes ?? [];
|
|
1477
|
+
TEMP_DIRS = (_HEURISTIC.temp_dirs ?? []).map((d) => d + "/");
|
|
1824
1478
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1825
1479
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1826
1480
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc, dirDelOnly }) => ({ path: path5, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1841,7 +1495,7 @@ var init_logic_rules = __esm({
|
|
|
1841
1495
|
"list_dir",
|
|
1842
1496
|
"count_text_length",
|
|
1843
1497
|
"search",
|
|
1844
|
-
// filemgr 只读三件套(globfiles 在
|
|
1498
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1845
1499
|
"filemgr-globfiles",
|
|
1846
1500
|
"filemgr-readfile",
|
|
1847
1501
|
"filemgr-grepfiles",
|
|
@@ -1853,7 +1507,7 @@ var init_logic_rules = __esm({
|
|
|
1853
1507
|
"tail",
|
|
1854
1508
|
"cat"
|
|
1855
1509
|
]);
|
|
1856
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1510
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1857
1511
|
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362", "edit"];
|
|
1858
1512
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1859
1513
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1894,25 +1548,24 @@ var init_logic_rules = __esm({
|
|
|
1894
1548
|
}));
|
|
1895
1549
|
})();
|
|
1896
1550
|
INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
1897
|
-
DIR_TRAVERSAL_KW = [
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
"pathlib.",
|
|
1904
|
-
"iterdir",
|
|
1905
|
-
"rglob",
|
|
1906
|
-
"scandir(",
|
|
1907
|
-
"listdir("
|
|
1908
|
-
];
|
|
1551
|
+
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1552
|
+
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
1553
|
+
IFA_READ_KEYWORDS = _IFA.read_keywords ?? [];
|
|
1554
|
+
IFA_WRITE_KEYWORDS = _IFA.write_keywords ?? [];
|
|
1555
|
+
IFA_EXEC_KEYWORDS = _IFA.exec_keywords ?? [];
|
|
1556
|
+
IFA_OPEN_KEYWORDS = _IFA.open_keywords ?? [];
|
|
1909
1557
|
}
|
|
1910
1558
|
});
|
|
1911
1559
|
|
|
1912
1560
|
// src/engine/asset-dir.ts
|
|
1913
1561
|
import { existsSync as existsSync3 } from "fs";
|
|
1914
|
-
import { resolve as resolve2 } from "path";
|
|
1562
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
1915
1563
|
function assetDirCandidates(moduleDir, name) {
|
|
1564
|
+
const root = getRulesRoot();
|
|
1565
|
+
if (root) {
|
|
1566
|
+
const p = join3(root, name);
|
|
1567
|
+
return [p, p];
|
|
1568
|
+
}
|
|
1916
1569
|
return [
|
|
1917
1570
|
resolve2(moduleDir, name),
|
|
1918
1571
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -1928,6 +1581,7 @@ var init_asset_dir = __esm({
|
|
|
1928
1581
|
"src/engine/asset-dir.ts"() {
|
|
1929
1582
|
"use strict";
|
|
1930
1583
|
init_esm_shims();
|
|
1584
|
+
init_paths();
|
|
1931
1585
|
}
|
|
1932
1586
|
});
|
|
1933
1587
|
|
|
@@ -2128,7 +1782,7 @@ __export(runtime_config_exports, {
|
|
|
2128
1782
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2129
1783
|
});
|
|
2130
1784
|
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2131
|
-
import { join as
|
|
1785
|
+
import { join as join4 } from "path";
|
|
2132
1786
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2133
1787
|
import { dirname as dirname3 } from "path";
|
|
2134
1788
|
function runtimeDir() {
|
|
@@ -2263,7 +1917,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2263
1917
|
}
|
|
2264
1918
|
function _filterRulesByOs(rules, osType) {
|
|
2265
1919
|
const accepted = acceptedOsValues(osType);
|
|
2266
|
-
return rules.filter((r) =>
|
|
1920
|
+
return rules.filter((r) => {
|
|
1921
|
+
const os3 = String(r.os || "linux").toLowerCase();
|
|
1922
|
+
return os3 === "all" || accepted.includes(os3);
|
|
1923
|
+
});
|
|
2267
1924
|
}
|
|
2268
1925
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2269
1926
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2450,6 +2107,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2450
2107
|
keyword: kw.keyword,
|
|
2451
2108
|
risk_level: kw.riskLevel,
|
|
2452
2109
|
lang: kw.lang || "en",
|
|
2110
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2111
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2112
|
+
os: kw.os,
|
|
2453
2113
|
enabled: true,
|
|
2454
2114
|
builtin: true
|
|
2455
2115
|
}));
|
|
@@ -2567,7 +2227,8 @@ function _l3SkillsToRules() {
|
|
|
2567
2227
|
}
|
|
2568
2228
|
function _scenarioRulesToDefaults() {
|
|
2569
2229
|
const out = {};
|
|
2570
|
-
const
|
|
2230
|
+
const _root = getRulesRoot();
|
|
2231
|
+
const scenDir = _root ? join4(_root, "scenarios") : existsSync5(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync5(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2571
2232
|
if (!existsSync5(scenDir)) return out;
|
|
2572
2233
|
let entries;
|
|
2573
2234
|
try {
|
|
@@ -2576,7 +2237,7 @@ function _scenarioRulesToDefaults() {
|
|
|
2576
2237
|
return out;
|
|
2577
2238
|
}
|
|
2578
2239
|
for (const scenarioId of entries) {
|
|
2579
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2240
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join4(scenDir, scenarioId, "rules.json");
|
|
2580
2241
|
if (!existsSync5(rulesFile)) continue;
|
|
2581
2242
|
let data;
|
|
2582
2243
|
try {
|
|
@@ -2616,7 +2277,8 @@ function _scenarioRulesToDefaults() {
|
|
|
2616
2277
|
}
|
|
2617
2278
|
function _agentRulesToDefaults() {
|
|
2618
2279
|
const out = {};
|
|
2619
|
-
const
|
|
2280
|
+
const _root = getRulesRoot();
|
|
2281
|
+
const agentsDir = _root ? join4(_root, "agents") : existsSync5(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync5(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2620
2282
|
if (!existsSync5(agentsDir)) return out;
|
|
2621
2283
|
let entries;
|
|
2622
2284
|
try {
|
|
@@ -2625,7 +2287,7 @@ function _agentRulesToDefaults() {
|
|
|
2625
2287
|
return out;
|
|
2626
2288
|
}
|
|
2627
2289
|
for (const agentId of entries) {
|
|
2628
|
-
const rulesFile =
|
|
2290
|
+
const rulesFile = join4(agentsDir, agentId, "rules.json");
|
|
2629
2291
|
if (!existsSync5(rulesFile)) continue;
|
|
2630
2292
|
let data;
|
|
2631
2293
|
try {
|
|
@@ -3004,7 +2666,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
3004
2666
|
}
|
|
3005
2667
|
function _filterDefaultsByOs(rules, osType) {
|
|
3006
2668
|
const accepted = acceptedOsValues(osType);
|
|
3007
|
-
return rules.filter((r) =>
|
|
2669
|
+
return rules.filter((r) => {
|
|
2670
|
+
if (!r.os) return true;
|
|
2671
|
+
const os3 = String(r.os).toLowerCase();
|
|
2672
|
+
return os3 === "all" || accepted.includes(os3);
|
|
2673
|
+
});
|
|
3008
2674
|
}
|
|
3009
2675
|
function _hasRulesForOs(rules, osType) {
|
|
3010
2676
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3233,12 +2899,12 @@ function addCustomSkill(skillId, category, keywords, content) {
|
|
|
3233
2899
|
);
|
|
3234
2900
|
}
|
|
3235
2901
|
const runtime = loadRuntimeConfig();
|
|
3236
|
-
const skillsDir =
|
|
2902
|
+
const skillsDir = join4(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3237
2903
|
try {
|
|
3238
2904
|
mkdirSync(skillsDir, { recursive: true });
|
|
3239
2905
|
} catch {
|
|
3240
2906
|
}
|
|
3241
|
-
writeFileSync(
|
|
2907
|
+
writeFileSync(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3242
2908
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3243
2909
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3244
2910
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3253,7 +2919,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3253
2919
|
if (idx >= 0) {
|
|
3254
2920
|
if (skills[idx].builtin) return null;
|
|
3255
2921
|
skills.splice(idx, 1);
|
|
3256
|
-
const skillPath =
|
|
2922
|
+
const skillPath = join4(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3257
2923
|
try {
|
|
3258
2924
|
if (existsSync5(skillPath)) unlinkSync(skillPath);
|
|
3259
2925
|
} catch {
|
|
@@ -3462,7 +3128,7 @@ var init_runtime_config = __esm({
|
|
|
3462
3128
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3463
3129
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3464
3130
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3465
|
-
RUNTIME_CONFIG_PATH =
|
|
3131
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3466
3132
|
LAYER_DISABLE_ENV = {
|
|
3467
3133
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3468
3134
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3490,10 +3156,10 @@ var init_runtime_config = __esm({
|
|
|
3490
3156
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3491
3157
|
};
|
|
3492
3158
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3493
|
-
_AGENTS_DIR_PROD =
|
|
3494
|
-
_AGENTS_DIR_DEV =
|
|
3495
|
-
_SCENARIOS_DIR_PROD =
|
|
3496
|
-
_SCENARIOS_DIR_DEV =
|
|
3159
|
+
_AGENTS_DIR_PROD = join4(__dirname4, "agents");
|
|
3160
|
+
_AGENTS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3161
|
+
_SCENARIOS_DIR_PROD = join4(__dirname4, "scenarios");
|
|
3162
|
+
_SCENARIOS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3497
3163
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3498
3164
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3499
3165
|
registerRuntimeConfigBridge({
|
|
@@ -3832,9 +3498,11 @@ var init_config = __esm({
|
|
|
3832
3498
|
|
|
3833
3499
|
// src/agents/index.ts
|
|
3834
3500
|
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
3835
|
-
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3501
|
+
import { resolve as resolve4, dirname as dirname4, join as join5 } from "path";
|
|
3836
3502
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3837
3503
|
function resolveAgentsDir() {
|
|
3504
|
+
const root = getRulesRoot();
|
|
3505
|
+
if (root) return join5(root, "agents");
|
|
3838
3506
|
if (existsSync6(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3839
3507
|
if (existsSync6(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3840
3508
|
return AGENTS_DIR_PROD;
|
|
@@ -3898,6 +3566,7 @@ var init_agents = __esm({
|
|
|
3898
3566
|
"src/agents/index.ts"() {
|
|
3899
3567
|
"use strict";
|
|
3900
3568
|
init_esm_shims();
|
|
3569
|
+
init_paths();
|
|
3901
3570
|
init_runtime_config();
|
|
3902
3571
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3903
3572
|
__dirname5 = dirname4(__filename4);
|
|
@@ -3909,9 +3578,11 @@ var init_agents = __esm({
|
|
|
3909
3578
|
|
|
3910
3579
|
// src/scenarios/index.ts
|
|
3911
3580
|
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3912
|
-
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
3581
|
+
import { resolve as resolve5, dirname as dirname5, join as join6 } from "path";
|
|
3913
3582
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3914
3583
|
function resolveScenariosDir() {
|
|
3584
|
+
const root = getRulesRoot();
|
|
3585
|
+
if (root) return join6(root, "scenarios");
|
|
3915
3586
|
if (existsSync7(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3916
3587
|
if (existsSync7(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3917
3588
|
return SCENARIOS_DIR_PROD;
|
|
@@ -3992,6 +3663,7 @@ var init_scenarios = __esm({
|
|
|
3992
3663
|
"src/scenarios/index.ts"() {
|
|
3993
3664
|
"use strict";
|
|
3994
3665
|
init_esm_shims();
|
|
3666
|
+
init_paths();
|
|
3995
3667
|
init_agents();
|
|
3996
3668
|
init_runtime_config();
|
|
3997
3669
|
init_rules_loader();
|
|
@@ -4398,7 +4070,7 @@ function severityRank(s) {
|
|
|
4398
4070
|
}
|
|
4399
4071
|
function isSearchActionType(actionType) {
|
|
4400
4072
|
const at = actionType.toLowerCase();
|
|
4401
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4073
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4402
4074
|
}
|
|
4403
4075
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4404
4076
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4688,8 +4360,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4688
4360
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4689
4361
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4690
4362
|
const scan = stripHeredocContent(actionDetail);
|
|
4691
|
-
const
|
|
4692
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4363
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4693
4364
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4694
4365
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4695
4366
|
const wildcardDeleteRe = /(?:\brm\b|\b(r?mdir|del|delete|remove|rmdir|unlink)\b|\b删除\b|\b移除\b)[\s\S]{0,40}?[*?]|[*?][\s\S]{0,40}?\b(?:rm|del|delete|remove|rmdir|unlink)\b|rm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\S*[*?]/i;
|
|
@@ -4835,7 +4506,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4835
4506
|
}
|
|
4836
4507
|
return null;
|
|
4837
4508
|
}
|
|
4838
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, LOOPBACK_URL_RE, LOOPBACK_HOSTS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, PY_EXEC_IO_RE, PY_PRINT_RE, PY_BIGNUM_RE, PY_INLINE_C_RE, PY_CMD_SUBST_C_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4509
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, LOOPBACK_URL_RE, HEURISTIC_RULES, LOOPBACK_HOSTS, TEMP_DIRS2, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, PY_EXEC_IO_RE, PY_PRINT_RE, PY_BIGNUM_RE, PY_INLINE_C_RE, PY_CMD_SUBST_C_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, SEARCH_ACTION_EXACT, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
4839
4510
|
var init_heuristic = __esm({
|
|
4840
4511
|
"src/engine/heuristic.ts"() {
|
|
4841
4512
|
"use strict";
|
|
@@ -4852,7 +4523,8 @@ var init_heuristic = __esm({
|
|
|
4852
4523
|
return kws.map((k) => ({
|
|
4853
4524
|
keyword: k.keyword,
|
|
4854
4525
|
riskLevel: k.risk_level,
|
|
4855
|
-
lang: k.lang || "en"
|
|
4526
|
+
lang: k.lang || "en",
|
|
4527
|
+
os: String(k.os || "all").toLowerCase()
|
|
4856
4528
|
}));
|
|
4857
4529
|
})();
|
|
4858
4530
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4860,7 +4532,9 @@ var init_heuristic = __esm({
|
|
|
4860
4532
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4861
4533
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4862
4534
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4863
|
-
|
|
4535
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4536
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4537
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4864
4538
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4865
4539
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4866
4540
|
PY_EXEC_IO_RE = /os\.system|os\.popen|subprocess|eval\s*\(|exec\s*\(|__import__|open\s*\(|socket|urllib|requests\.|shutil|os\.(?:remove|unlink|rename|makedirs?|listdir|scandir|walk|chmod|chown|kill|environ)|iterdir|glob\s*\(|path\(/i;
|
|
@@ -4868,8 +4542,9 @@ var init_heuristic = __esm({
|
|
|
4868
4542
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4869
4543
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4870
4544
|
PY_CMD_SUBST_C_RE = /\$\(\s*(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"'|'([^']*)')\s*\)/i;
|
|
4871
|
-
SENSITIVE_EXCLUDED_PREFIXES =
|
|
4872
|
-
SEARCH_ACTION_PREFIXES =
|
|
4545
|
+
SENSITIVE_EXCLUDED_PREFIXES = HEURISTIC_RULES.sensitive_excluded_prefixes ?? [];
|
|
4546
|
+
SEARCH_ACTION_PREFIXES = HEURISTIC_RULES.search_action_prefixes ?? [];
|
|
4547
|
+
SEARCH_ACTION_EXACT = HEURISTIC_RULES.search_action_exact ?? [];
|
|
4873
4548
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4874
4549
|
HOME_ASSIGN_RE = /([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(~[A-Za-z0-9_./-]*|\$HOME[A-Za-z0-9_./-]*|\$\{HOME\}[A-Za-z0-9_./-]*|\/home\/[^\s;|&]+|\$\((?:[^()]|\([^()]*\))*\))(?=\s|;|$|\||&)/gi;
|
|
4875
4550
|
}
|
|
@@ -5095,87 +4770,25 @@ function loadTemplates() {
|
|
|
5095
4770
|
|
|
5096
4771
|
// src/engine/script-analyzer.ts
|
|
5097
4772
|
init_esm_shims();
|
|
4773
|
+
init_rules_loader();
|
|
5098
4774
|
import { readFileSync as readFileSync8 } from "fs";
|
|
5099
|
-
import { resolve as resolve7, join as
|
|
5100
|
-
var
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
// Sensitive file access
|
|
5117
|
-
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
5118
|
-
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
5119
|
-
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
5120
|
-
// Persistence backdoor
|
|
5121
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
5122
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
5123
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
5124
|
-
];
|
|
5125
|
-
var SCRIPT_EXEC_PATTERNS = [
|
|
5126
|
-
/bash\s+(\S+\.sh)/,
|
|
5127
|
-
/sh\s+(\S+\.sh)/,
|
|
5128
|
-
/\.\/(\S+\.sh)/,
|
|
5129
|
-
/source\s+(\S+\.sh)/,
|
|
5130
|
-
/\.\s+(\S+\.sh)/,
|
|
5131
|
-
/zsh\s+(\S+\.sh)/
|
|
5132
|
-
];
|
|
5133
|
-
var KEYWORD_COMBINATION_RISKS = [
|
|
5134
|
-
{
|
|
5135
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5136
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
5137
|
-
level: "critical",
|
|
5138
|
-
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"
|
|
5139
|
-
},
|
|
5140
|
-
{
|
|
5141
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
5142
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
5143
|
-
level: "critical",
|
|
5144
|
-
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"
|
|
5145
|
-
},
|
|
5146
|
-
{
|
|
5147
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
5148
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5149
|
-
level: "high",
|
|
5150
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
5151
|
-
},
|
|
5152
|
-
{
|
|
5153
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
5154
|
-
group2: /* @__PURE__ */ new Set(),
|
|
5155
|
-
level: "high",
|
|
5156
|
-
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"
|
|
5157
|
-
}
|
|
5158
|
-
];
|
|
5159
|
-
var SINGLE_KEYWORD_RISKS = {
|
|
5160
|
-
"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" },
|
|
5161
|
-
"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" },
|
|
5162
|
-
"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" },
|
|
5163
|
-
"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" },
|
|
5164
|
-
"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" },
|
|
5165
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5166
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5167
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5168
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
5169
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5170
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
5171
|
-
"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" },
|
|
5172
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
5173
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5174
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
5175
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
5176
|
-
"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" },
|
|
5177
|
-
"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" }
|
|
5178
|
-
};
|
|
4775
|
+
import { resolve as resolve7, join as join7 } from "path";
|
|
4776
|
+
var SCRIPT_RULES = loadScriptRules();
|
|
4777
|
+
if (!SCRIPT_RULES || Object.keys(SCRIPT_RULES).length === 0) {
|
|
4778
|
+
console.error(
|
|
4779
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 script_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u811A\u672C\u5173\u952E\u8BCD\u626B\u63CF\u5C06\u4E0D\u547D\u4E2D\u4EFB\u4F55\u6A21\u5F0F\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
4780
|
+
);
|
|
4781
|
+
}
|
|
4782
|
+
var SUSPICIOUS_PATTERNS = (SCRIPT_RULES.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
4783
|
+
var SCRIPT_EXEC_PATTERNS = ((SCRIPT_RULES.script_exec_patterns ?? {}).legacy ?? []).map((r) => compileRuleRegex(r));
|
|
4784
|
+
var KEYWORD_SETS = SCRIPT_RULES.keyword_sets ?? {};
|
|
4785
|
+
var KEYWORD_COMBINATION_RISKS = (SCRIPT_RULES.keyword_combination_risks ?? []).map((r) => ({
|
|
4786
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS),
|
|
4787
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS),
|
|
4788
|
+
level: String(r.level),
|
|
4789
|
+
reason: String(r.reason)
|
|
4790
|
+
}));
|
|
4791
|
+
var SINGLE_KEYWORD_RISKS = SCRIPT_RULES.single_keyword_risks ?? {};
|
|
5179
4792
|
var LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5180
4793
|
function emptyResult() {
|
|
5181
4794
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
@@ -5202,10 +4815,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5202
4815
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
5203
4816
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
5204
4817
|
}
|
|
5205
|
-
const networkPostKeywords =
|
|
4818
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
5206
4819
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
5207
4820
|
if (hasNetworkPost) {
|
|
5208
|
-
const envPatterns =
|
|
4821
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
5209
4822
|
const contentLower = scriptContent.toLowerCase();
|
|
5210
4823
|
for (const p of envPatterns) {
|
|
5211
4824
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -5229,7 +4842,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5229
4842
|
}
|
|
5230
4843
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
5231
4844
|
try {
|
|
5232
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
4845
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join7(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
5233
4846
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
5234
4847
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
5235
4848
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -5287,7 +4900,7 @@ init_config();
|
|
|
5287
4900
|
init_logic_rules();
|
|
5288
4901
|
init_inline_analyzer();
|
|
5289
4902
|
import { statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
5290
|
-
import { resolve as resolve9, join as
|
|
4903
|
+
import { resolve as resolve9, join as join9, isAbsolute as isAbsolute2 } from "path";
|
|
5291
4904
|
import { homedir as homedir3 } from "os";
|
|
5292
4905
|
|
|
5293
4906
|
// src/engine/script-engine/tracker.ts
|
|
@@ -6798,6 +6411,7 @@ var RecursiveTracker = class {
|
|
|
6798
6411
|
};
|
|
6799
6412
|
|
|
6800
6413
|
// src/engine/script-content-analyzer.ts
|
|
6414
|
+
init_rules_loader();
|
|
6801
6415
|
function emptyChain() {
|
|
6802
6416
|
return {
|
|
6803
6417
|
nodes: [],
|
|
@@ -6814,87 +6428,22 @@ function emptyChain() {
|
|
|
6814
6428
|
error: ""
|
|
6815
6429
|
};
|
|
6816
6430
|
}
|
|
6817
|
-
var
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
6834
|
-
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
6835
|
-
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
6836
|
-
];
|
|
6837
|
-
var SCRIPT_EXEC_PATTERNS2 = [
|
|
6838
|
-
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
6839
|
-
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
6840
|
-
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
6841
|
-
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
6842
|
-
/\.\/(\S+)/,
|
|
6843
|
-
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
6844
|
-
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
6845
|
-
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
6846
|
-
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
6847
|
-
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
6848
|
-
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
6849
|
-
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
6850
|
-
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
6851
|
-
];
|
|
6852
|
-
var KEYWORD_COMBINATION_RISKS2 = [
|
|
6853
|
-
{
|
|
6854
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
6855
|
-
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
6856
|
-
level: "critical",
|
|
6857
|
-
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"
|
|
6858
|
-
},
|
|
6859
|
-
{
|
|
6860
|
-
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
6861
|
-
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
6862
|
-
level: "critical",
|
|
6863
|
-
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"
|
|
6864
|
-
},
|
|
6865
|
-
{
|
|
6866
|
-
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
6867
|
-
group2: /* @__PURE__ */ new Set(),
|
|
6868
|
-
level: "high",
|
|
6869
|
-
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
6870
|
-
},
|
|
6871
|
-
{
|
|
6872
|
-
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
6873
|
-
group2: /* @__PURE__ */ new Set(),
|
|
6874
|
-
level: "high",
|
|
6875
|
-
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"
|
|
6876
|
-
}
|
|
6877
|
-
];
|
|
6878
|
-
var SINGLE_KEYWORD_RISKS2 = {
|
|
6879
|
-
"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" },
|
|
6880
|
-
"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" },
|
|
6881
|
-
"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" },
|
|
6882
|
-
"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" },
|
|
6883
|
-
"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" },
|
|
6884
|
-
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6885
|
-
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6886
|
-
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6887
|
-
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
6888
|
-
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
6889
|
-
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
6890
|
-
"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" },
|
|
6891
|
-
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
6892
|
-
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
6893
|
-
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
6894
|
-
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
6895
|
-
"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" },
|
|
6896
|
-
"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" }
|
|
6897
|
-
};
|
|
6431
|
+
var SCRIPT_RULES2 = loadScriptRules();
|
|
6432
|
+
if (!SCRIPT_RULES2 || Object.keys(SCRIPT_RULES2).length === 0) {
|
|
6433
|
+
console.error(
|
|
6434
|
+
"[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 script_rules.json \u7F3A\u5931\u6216\u635F\u574F \u2014\u2014 \u811A\u672C\u5185\u5BB9\u5173\u952E\u8BCD\u626B\u63CF\u5C06\u4E0D\u547D\u4E2D\u4EFB\u4F55\u6A21\u5F0F\uFF08\u6BD4\u6B63\u5E38\u66F4\u677E\uFF09\u3002\u8BF7\u68C0\u67E5\u5305\u5B8C\u6574\u6027\u3002"
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
6437
|
+
var SUSPICIOUS_PATTERNS2 = (SCRIPT_RULES2.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
6438
|
+
var SCRIPT_EXEC_PATTERNS2 = ((SCRIPT_RULES2.script_exec_patterns ?? {}).chain ?? []).map((r) => compileRuleRegex(r));
|
|
6439
|
+
var KEYWORD_SETS2 = SCRIPT_RULES2.keyword_sets ?? {};
|
|
6440
|
+
var KEYWORD_COMBINATION_RISKS2 = (SCRIPT_RULES2.keyword_combination_risks ?? []).map((r) => ({
|
|
6441
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS2),
|
|
6442
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS2),
|
|
6443
|
+
level: String(r.level),
|
|
6444
|
+
reason: String(r.reason)
|
|
6445
|
+
}));
|
|
6446
|
+
var SINGLE_KEYWORD_RISKS2 = SCRIPT_RULES2.single_keyword_risks ?? {};
|
|
6898
6447
|
var LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
6899
6448
|
function scanScriptForKeywords2(content) {
|
|
6900
6449
|
const matched = [];
|
|
@@ -6911,10 +6460,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6911
6460
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6912
6461
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6913
6462
|
}
|
|
6914
|
-
const networkPostKeywords =
|
|
6463
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6915
6464
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6916
6465
|
if (hasNetworkPost) {
|
|
6917
|
-
const envPatterns =
|
|
6466
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6918
6467
|
const contentLower = scriptContent.toLowerCase();
|
|
6919
6468
|
for (const p of envPatterns) {
|
|
6920
6469
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -7061,7 +6610,7 @@ function shlexSplit(s) {
|
|
|
7061
6610
|
function resolvePath2(p) {
|
|
7062
6611
|
let expanded = p;
|
|
7063
6612
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
7064
|
-
expanded =
|
|
6613
|
+
expanded = join9(homedir3(), expanded.slice(1));
|
|
7065
6614
|
}
|
|
7066
6615
|
return resolve9(expanded);
|
|
7067
6616
|
}
|
|
@@ -7185,7 +6734,7 @@ function analyzeScriptContent2(input, ext) {
|
|
|
7185
6734
|
isInline = true;
|
|
7186
6735
|
}
|
|
7187
6736
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
7188
|
-
resolvedPath =
|
|
6737
|
+
resolvedPath = join9(input.cwd || process.cwd(), resolvedPath);
|
|
7189
6738
|
}
|
|
7190
6739
|
const l1Adapter = (content) => {
|
|
7191
6740
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7509,9 +7058,9 @@ init_esm_shims();
|
|
|
7509
7058
|
// src/brain/rule-store.ts
|
|
7510
7059
|
init_esm_shims();
|
|
7511
7060
|
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
7512
|
-
import { join as
|
|
7061
|
+
import { join as join10 } from "path";
|
|
7513
7062
|
import { homedir as homedir4 } from "os";
|
|
7514
|
-
var DEFAULT_RULES_PATH =
|
|
7063
|
+
var DEFAULT_RULES_PATH = join10(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
7515
7064
|
var ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7516
7065
|
var STATUS_DRAFT = "draft";
|
|
7517
7066
|
var STATUS_APPROVED = "approved";
|
|
@@ -8126,9 +7675,9 @@ function parseReview(content) {
|
|
|
8126
7675
|
init_esm_shims();
|
|
8127
7676
|
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8128
7677
|
import { homedir as homedir5 } from "os";
|
|
8129
|
-
import { join as
|
|
7678
|
+
import { join as join11 } from "path";
|
|
8130
7679
|
import { randomUUID } from "crypto";
|
|
8131
|
-
var DEFAULT_SKILL_META_PATH =
|
|
7680
|
+
var DEFAULT_SKILL_META_PATH = join11(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8132
7681
|
var ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8133
7682
|
function getMetaPath(override) {
|
|
8134
7683
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8138,7 +7687,7 @@ var BrainSkillManager = class {
|
|
|
8138
7687
|
this.skillsDir = skillsDir;
|
|
8139
7688
|
this.metaPath = getMetaPath(metaPath);
|
|
8140
7689
|
try {
|
|
8141
|
-
mkdirSync4(
|
|
7690
|
+
mkdirSync4(join11(this.metaPath, ".."), { recursive: true });
|
|
8142
7691
|
} catch {
|
|
8143
7692
|
}
|
|
8144
7693
|
this.load();
|
|
@@ -8173,7 +7722,7 @@ var BrainSkillManager = class {
|
|
|
8173
7722
|
let changed = false;
|
|
8174
7723
|
for (const sk of this.skills) {
|
|
8175
7724
|
if (sk.status !== "active") continue;
|
|
8176
|
-
if (!existsSync13(
|
|
7725
|
+
if (!existsSync13(join11(this.skillsDir, `${sk.name}.md`))) {
|
|
8177
7726
|
sk.status = "stale";
|
|
8178
7727
|
changed = true;
|
|
8179
7728
|
}
|
|
@@ -8185,7 +7734,7 @@ var BrainSkillManager = class {
|
|
|
8185
7734
|
const id = skill.id || randomUUID();
|
|
8186
7735
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8187
7736
|
if (!existsSync13(this.skillsDir)) return "";
|
|
8188
|
-
const mdPath =
|
|
7737
|
+
const mdPath = join11(this.skillsDir, `${name}.md`);
|
|
8189
7738
|
if (existsSync13(mdPath)) return "";
|
|
8190
7739
|
try {
|
|
8191
7740
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
@@ -8235,7 +7784,7 @@ var BrainSkillManager = class {
|
|
|
8235
7784
|
const out = [];
|
|
8236
7785
|
for (const sk of this.skills) {
|
|
8237
7786
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8238
|
-
const mdPath =
|
|
7787
|
+
const mdPath = join11(this.skillsDir, `${sk.name}.md`);
|
|
8239
7788
|
if (!existsSync13(mdPath)) continue;
|
|
8240
7789
|
out.push({
|
|
8241
7790
|
name: sk.name,
|
|
@@ -8387,6 +7936,7 @@ init_rules_loader();
|
|
|
8387
7936
|
|
|
8388
7937
|
// src/engine/formal-engine/decode.ts
|
|
8389
7938
|
init_esm_shims();
|
|
7939
|
+
init_rules_loader();
|
|
8390
7940
|
var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8391
7941
|
function isValidBase64Token(tok) {
|
|
8392
7942
|
if (tok.length < 8) return false;
|
|
@@ -8421,7 +7971,7 @@ function decodeBase64Literals(text) {
|
|
|
8421
7971
|
return out;
|
|
8422
7972
|
}
|
|
8423
7973
|
var QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8424
|
-
var OUTPUT_CMDS =
|
|
7974
|
+
var OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8425
7975
|
function stripStringLiterals(text) {
|
|
8426
7976
|
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
8427
7977
|
if (!m) return text;
|
|
@@ -8906,8 +8456,9 @@ function extractInlineShell(s) {
|
|
|
8906
8456
|
}
|
|
8907
8457
|
return out;
|
|
8908
8458
|
}
|
|
8909
|
-
var
|
|
8910
|
-
var
|
|
8459
|
+
var FORMAL_RULES = loadFormalRules();
|
|
8460
|
+
var PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
8461
|
+
var SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
8911
8462
|
function containsPrivesc(shell) {
|
|
8912
8463
|
if (!shell) return false;
|
|
8913
8464
|
for (const seg of shell.split(/\&\&|\|\||;|\n/)) {
|
|
@@ -9080,7 +8631,7 @@ function lookupToolClass(actionType) {
|
|
|
9080
8631
|
return entry.actionClass;
|
|
9081
8632
|
}
|
|
9082
8633
|
var COMPUTE_RE = /subprocess|os\.system|shell\s*=\s*True|\beval\(|\bexec\(|bash\s+[^\s]|sh\s+-c|python3?\s+|node\s+|\btimeout\s+|nohup\s+|\b\.\/[\w./-]+/i;
|
|
9083
|
-
var STRONG_COMPUTE_TYPES =
|
|
8634
|
+
var STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
9084
8635
|
function isComputeNode(n) {
|
|
9085
8636
|
if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
|
|
9086
8637
|
const cls = lookupToolClass(n.actionType);
|
|
@@ -9306,25 +8857,7 @@ import * as path4 from "path";
|
|
|
9306
8857
|
import { createHash } from "crypto";
|
|
9307
8858
|
|
|
9308
8859
|
// src/engine/coordinator.ts
|
|
9309
|
-
var FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
9310
|
-
"ask_user_question",
|
|
9311
|
-
"glob",
|
|
9312
|
-
"list_dir",
|
|
9313
|
-
"ls",
|
|
9314
|
-
"count_text_length",
|
|
9315
|
-
"filemgr-globfiles"
|
|
9316
|
-
]);
|
|
9317
8860
|
var TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
9318
|
-
var READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
9319
|
-
"read",
|
|
9320
|
-
"file_read",
|
|
9321
|
-
"read_file",
|
|
9322
|
-
"head",
|
|
9323
|
-
"tail",
|
|
9324
|
-
"grep",
|
|
9325
|
-
"filemgr-readfile",
|
|
9326
|
-
"filemgr-grepfiles"
|
|
9327
|
-
]);
|
|
9328
8861
|
function whitelistTier(command, fullySafe, readonlySensitive) {
|
|
9329
8862
|
const segments = splitCommandSegments(command);
|
|
9330
8863
|
if (segments.length === 0) return "";
|
|
@@ -9481,7 +9014,7 @@ async function runAnalysis(input) {
|
|
|
9481
9014
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
9482
9015
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
9483
9016
|
const lowerDetail = rawDetail.toLowerCase();
|
|
9484
|
-
if (
|
|
9017
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
9485
9018
|
return {
|
|
9486
9019
|
allowed: true,
|
|
9487
9020
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -9524,7 +9057,7 @@ async function runAnalysis(input) {
|
|
|
9524
9057
|
violatedLayers: []
|
|
9525
9058
|
};
|
|
9526
9059
|
}
|
|
9527
|
-
let skipLLM =
|
|
9060
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
9528
9061
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
9529
9062
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
9530
9063
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|