@kenhkl/agent-moss 0.16.13 → 0.17.0
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 +1900 -1418
- package/dist/engine/coordinator.js +723 -1015
- 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 +166 -51
- package/dist/rules/l2_rules.json +2 -1
- 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 +1678 -1218
- package/package.json +1 -1
|
@@ -25,6 +25,14 @@ 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 isSafeId(value) {
|
|
29
|
+
if (!value || value.length > ID_MAX_LEN) return false;
|
|
30
|
+
return ID_PATTERN.test(value);
|
|
31
|
+
}
|
|
32
|
+
function getRulesRoot() {
|
|
33
|
+
const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
|
|
34
|
+
return raw ? raw : null;
|
|
35
|
+
}
|
|
28
36
|
function isRoot() {
|
|
29
37
|
const getuid = process.getuid;
|
|
30
38
|
return typeof getuid === "function" && getuid() === 0;
|
|
@@ -94,7 +102,7 @@ function getTokenStatsPath() {
|
|
|
94
102
|
}
|
|
95
103
|
return join(getDataRoot(), "agent_moss_token_stats.json");
|
|
96
104
|
}
|
|
97
|
-
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT;
|
|
105
|
+
var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT, ID_MAX_LEN, ID_PATTERN;
|
|
98
106
|
var init_paths = __esm({
|
|
99
107
|
"src/infra/paths.ts"() {
|
|
100
108
|
"use strict";
|
|
@@ -102,6 +110,9 @@ var init_paths = __esm({
|
|
|
102
110
|
ENV_DATA_ROOT = "AGENT_MOSS_DATA_DIR";
|
|
103
111
|
ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
|
|
104
112
|
DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
|
|
113
|
+
ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
|
|
114
|
+
ID_MAX_LEN = 64;
|
|
115
|
+
ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
105
116
|
}
|
|
106
117
|
});
|
|
107
118
|
|
|
@@ -149,251 +160,6 @@ var init_shell_split = __esm({
|
|
|
149
160
|
}
|
|
150
161
|
});
|
|
151
162
|
|
|
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
163
|
// src/infra/rules-mode.ts
|
|
398
164
|
function truthy(name) {
|
|
399
165
|
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
@@ -440,6 +206,19 @@ var init_rules_mode = __esm({
|
|
|
440
206
|
}
|
|
441
207
|
});
|
|
442
208
|
|
|
209
|
+
// src/infra/runtime-config-bridge.ts
|
|
210
|
+
function registerRuntimeConfigBridge(bridge) {
|
|
211
|
+
runtimeConfigBridge.current = bridge;
|
|
212
|
+
}
|
|
213
|
+
var runtimeConfigBridge;
|
|
214
|
+
var init_runtime_config_bridge = __esm({
|
|
215
|
+
"src/infra/runtime-config-bridge.ts"() {
|
|
216
|
+
"use strict";
|
|
217
|
+
init_esm_shims();
|
|
218
|
+
runtimeConfigBridge = { current: null };
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
443
222
|
// src/infra/rules-loader.ts
|
|
444
223
|
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
445
224
|
import { resolve, dirname, join as join2 } from "path";
|
|
@@ -455,12 +234,30 @@ function parseDirEnv(envName) {
|
|
|
455
234
|
function rulesDirs() {
|
|
456
235
|
if (isBuiltinOnly()) return [];
|
|
457
236
|
const dirs = [];
|
|
458
|
-
for (const
|
|
237
|
+
for (const env2 of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env2));
|
|
459
238
|
dirs.push(resolve(dataRootDir(), "rules"));
|
|
460
239
|
return [...new Set(dirs)];
|
|
461
240
|
}
|
|
241
|
+
function rulesDirCands() {
|
|
242
|
+
const root = getRulesRoot();
|
|
243
|
+
if (root) return [join2(root, "rules")];
|
|
244
|
+
const cands = [
|
|
245
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
246
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
247
|
+
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
248
|
+
resolve(__dirname2, "../rules"),
|
|
249
|
+
resolve(__dirname2, "rules"),
|
|
250
|
+
// dev src: __dirname=ts/src/infra/ → ts/src/infra/../../../agent_moss/rules = 仓库根 agent_moss/rules
|
|
251
|
+
resolve(__dirname2, "../../../agent_moss/rules"),
|
|
252
|
+
// dist 但 __dirname 指向 dist/engine 再往上两级的仓库根 agent_moss/rules
|
|
253
|
+
resolve(__dirname2, "../../../../agent_moss/rules"),
|
|
254
|
+
// 相对当前工作目录的仓库根(兜底,兼容以仓库根为 cwd 运行)
|
|
255
|
+
resolve(process.cwd(), "agent_moss/rules")
|
|
256
|
+
];
|
|
257
|
+
return cands.filter((c) => existsSync(c));
|
|
258
|
+
}
|
|
462
259
|
function findRulesFile(name) {
|
|
463
|
-
for (const dir of
|
|
260
|
+
for (const dir of rulesDirCands()) {
|
|
464
261
|
const p = resolve(dir, name);
|
|
465
262
|
if (existsSync(p)) return p;
|
|
466
263
|
}
|
|
@@ -482,6 +279,7 @@ function loadJson(name) {
|
|
|
482
279
|
}
|
|
483
280
|
}
|
|
484
281
|
function resolveScenarioRulesFile(scenarioId) {
|
|
282
|
+
if (!isSafeId(scenarioId)) return null;
|
|
485
283
|
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
486
284
|
for (const dir of rulesDirs()) {
|
|
487
285
|
const p = resolve(dir, rel);
|
|
@@ -516,6 +314,74 @@ function loadL1() {
|
|
|
516
314
|
function loadL2() {
|
|
517
315
|
return loadJson("l2_rules.json");
|
|
518
316
|
}
|
|
317
|
+
function regexFlags(flags) {
|
|
318
|
+
let out = "";
|
|
319
|
+
for (const ch of String(flags ?? "")) if (RE_FLAG_MAP[ch]) out += RE_FLAG_MAP[ch];
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
function compileRuleRegex(rule, extraFlags = "") {
|
|
323
|
+
return new RegExp(String(rule.pattern), regexFlags(rule.flags) + extraFlags);
|
|
324
|
+
}
|
|
325
|
+
function resolveKeywordSet(group, keywordSets) {
|
|
326
|
+
if (typeof group === "string") return new Set(keywordSets[group.slice(1)] ?? []);
|
|
327
|
+
return new Set(Array.isArray(group) ? group : []);
|
|
328
|
+
}
|
|
329
|
+
function loadFormalRules() {
|
|
330
|
+
return loadJson("formal_rules.json");
|
|
331
|
+
}
|
|
332
|
+
function loadLogicRules() {
|
|
333
|
+
return loadJson("logic_rules.json");
|
|
334
|
+
}
|
|
335
|
+
function loadHeuristicRules() {
|
|
336
|
+
return loadJson("heuristic_rules.json");
|
|
337
|
+
}
|
|
338
|
+
function loadScriptRules() {
|
|
339
|
+
return loadJson("script_rules.json");
|
|
340
|
+
}
|
|
341
|
+
function loadInline() {
|
|
342
|
+
return loadJson("inline_rules.json");
|
|
343
|
+
}
|
|
344
|
+
function loadPlatformTraits(osName) {
|
|
345
|
+
const data = loadJson("platform_traits.json");
|
|
346
|
+
if (!data || Object.keys(data).length === 0) {
|
|
347
|
+
console.error(
|
|
348
|
+
"[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"
|
|
349
|
+
);
|
|
350
|
+
return {};
|
|
351
|
+
}
|
|
352
|
+
const name = resolveTraitsOs(osName);
|
|
353
|
+
const entry = data[name];
|
|
354
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
355
|
+
console.error(`[AgentMoss] platform_traits.json \u91CC\u6CA1\u6709\u53EF\u7528\u7684 ${name} \u6761\u76EE \u2014\u2014 \u5E73\u53F0\u7279\u5F81\u4E3A\u7A7A\u3002`);
|
|
356
|
+
return {};
|
|
357
|
+
}
|
|
358
|
+
const out = {};
|
|
359
|
+
for (const f of TRAITS_FIELDS) if (f in entry) out[f] = entry[f];
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
362
|
+
function resolveTraitsOs(osName) {
|
|
363
|
+
const data = loadJson("platform_traits.json");
|
|
364
|
+
let name = String(osName || "").trim().toLowerCase() || TRAITS_OS_FALLBACK;
|
|
365
|
+
const seen = /* @__PURE__ */ new Set();
|
|
366
|
+
for (; ; ) {
|
|
367
|
+
if (seen.has(name)) {
|
|
368
|
+
console.error(`[AgentMoss] platform_traits.json \u7684 inherits \u6210\u73AF\uFF08${name}\uFF09\u2014\u2014 \u56DE\u843D linux\u3002`);
|
|
369
|
+
return TRAITS_OS_FALLBACK;
|
|
370
|
+
}
|
|
371
|
+
seen.add(name);
|
|
372
|
+
const entry = data[name];
|
|
373
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
374
|
+
const nxt = entry.inherits;
|
|
375
|
+
if (typeof nxt === "string" && nxt.trim().toLowerCase() !== name) {
|
|
376
|
+
name = nxt.trim().toLowerCase();
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
return name;
|
|
380
|
+
}
|
|
381
|
+
if (name === TRAITS_OS_FALLBACK) return name;
|
|
382
|
+
name = TRAITS_OS_FALLBACK;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
519
385
|
function loadL3() {
|
|
520
386
|
const data = { ...loadJson("l3_rules.json") };
|
|
521
387
|
try {
|
|
@@ -844,7 +710,7 @@ function loadExtraRules() {
|
|
|
844
710
|
if (isBuiltinOnly()) return {};
|
|
845
711
|
return rulesCached().extra;
|
|
846
712
|
}
|
|
847
|
-
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS,
|
|
713
|
+
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
714
|
var init_rules_loader = __esm({
|
|
849
715
|
"src/infra/rules-loader.ts"() {
|
|
850
716
|
"use strict";
|
|
@@ -857,24 +723,20 @@ var init_rules_loader = __esm({
|
|
|
857
723
|
OVERRIDE_FILENAME = "override.json";
|
|
858
724
|
EXTRA_FILENAME = "extra.json";
|
|
859
725
|
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
726
|
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
877
727
|
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
728
|
+
RE_FLAG_MAP = { i: "i", s: "s", m: "m" };
|
|
729
|
+
TRAITS_FIELDS = [
|
|
730
|
+
"write_keywords",
|
|
731
|
+
"read_keywords",
|
|
732
|
+
"write_command_res",
|
|
733
|
+
"read_only_commands",
|
|
734
|
+
"fully_safe_commands",
|
|
735
|
+
"readonly_sensitive_commands",
|
|
736
|
+
"safe_action_types",
|
|
737
|
+
"readonly_sensitive_action_types"
|
|
738
|
+
];
|
|
739
|
+
TRAITS_OS_FALLBACK = "linux";
|
|
878
740
|
EXTRA_CATEGORIES = {
|
|
879
741
|
dangerous_commands: ["pattern"],
|
|
880
742
|
injection_detection: ["keyword", "lang"],
|
|
@@ -896,7 +758,155 @@ var init_rules_loader = __esm({
|
|
|
896
758
|
}
|
|
897
759
|
});
|
|
898
760
|
|
|
761
|
+
// src/engine/inline-analyzer.ts
|
|
762
|
+
function buildChainRegex(pattern, flags) {
|
|
763
|
+
return new RegExp(pattern, (flags || "") + "g");
|
|
764
|
+
}
|
|
765
|
+
function isLiteralTextCommand(command) {
|
|
766
|
+
const segments = splitCommandSegments(command);
|
|
767
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
768
|
+
if (command.includes(">")) {
|
|
769
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
770
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
771
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
772
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
773
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
function extractInlineCode(command) {
|
|
783
|
+
const parts = [];
|
|
784
|
+
let m;
|
|
785
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
786
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
787
|
+
const code = m[1] ?? m[2];
|
|
788
|
+
if (code) parts.push(code);
|
|
789
|
+
}
|
|
790
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
791
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
792
|
+
const code = m[1] ?? m[2];
|
|
793
|
+
if (code) parts.push(code);
|
|
794
|
+
}
|
|
795
|
+
return parts.join("\n");
|
|
796
|
+
}
|
|
797
|
+
function hasWriteExecChain(command) {
|
|
798
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
799
|
+
p.lastIndex = 0;
|
|
800
|
+
return p.test(command);
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
function hasDirTraversal(code) {
|
|
804
|
+
const lower = code.toLowerCase();
|
|
805
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
806
|
+
}
|
|
807
|
+
function hasReadWriteKeyword(code) {
|
|
808
|
+
const lower = code.toLowerCase();
|
|
809
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
810
|
+
}
|
|
811
|
+
function analyzeInlineScript(command) {
|
|
812
|
+
const code = extractInlineCode(command);
|
|
813
|
+
if (!code) {
|
|
814
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
815
|
+
}
|
|
816
|
+
if (hasWriteExecChain(command)) {
|
|
817
|
+
return {
|
|
818
|
+
actionable: true,
|
|
819
|
+
allowed: false,
|
|
820
|
+
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",
|
|
821
|
+
riskLevel: "critical",
|
|
822
|
+
riskType: "script_execution"
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
826
|
+
if (redirMatch) {
|
|
827
|
+
return {
|
|
828
|
+
actionable: true,
|
|
829
|
+
allowed: false,
|
|
830
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
831
|
+
riskLevel: "critical",
|
|
832
|
+
riskType: "file_access"
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
836
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
837
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
838
|
+
}
|
|
839
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
840
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
841
|
+
return {
|
|
842
|
+
actionable: true,
|
|
843
|
+
allowed: false,
|
|
844
|
+
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",
|
|
845
|
+
riskLevel: "critical",
|
|
846
|
+
riskType: "file_access"
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
actionable: true,
|
|
851
|
+
allowed: true,
|
|
852
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
853
|
+
riskLevel: "low",
|
|
854
|
+
riskType: "file_access"
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
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;
|
|
858
|
+
var init_inline_analyzer = __esm({
|
|
859
|
+
"src/engine/inline-analyzer.ts"() {
|
|
860
|
+
"use strict";
|
|
861
|
+
init_esm_shims();
|
|
862
|
+
init_shell_split();
|
|
863
|
+
init_rules_loader();
|
|
864
|
+
INLINE_RULES = loadInline();
|
|
865
|
+
if (!INLINE_RULES || Object.keys(INLINE_RULES).length === 0) {
|
|
866
|
+
console.error(
|
|
867
|
+
"[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"
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
871
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
872
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
873
|
+
READ_WRITE_KEYWORDS = INLINE_RULES.read_write_keywords ?? [];
|
|
874
|
+
WRITE_EXEC_CHAIN_PATTERNS = (INLINE_RULES.write_exec_chain_patterns ?? []).filter((p) => p && typeof p.pattern === "string").map((p) => buildChainRegex(p.pattern, p.flags ?? ""));
|
|
875
|
+
_REDIRECT_PATHS = INLINE_RULES.redirect_sensitive_paths ?? {};
|
|
876
|
+
SHELL_RC_FRAGMENTS = _REDIRECT_PATHS.shell_rc_fragments ?? [];
|
|
877
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
878
|
+
..._REDIRECT_PATHS.ts_static ?? [],
|
|
879
|
+
...SHELL_RC_FRAGMENTS
|
|
880
|
+
];
|
|
881
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
882
|
+
DIR_TRAVERSAL_KEYWORDS = INLINE_RULES.dir_traversal_keywords ?? [];
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
899
886
|
// src/engine/platform-traits.ts
|
|
887
|
+
function buildTraits(osName) {
|
|
888
|
+
const t = loadPlatformTraits(osName);
|
|
889
|
+
const strs = (k) => Array.isArray(t[k]) ? t[k] : [];
|
|
890
|
+
return {
|
|
891
|
+
writeKeywords: strs("write_keywords"),
|
|
892
|
+
readKeywords: strs("read_keywords"),
|
|
893
|
+
writeCommandRes: strs("write_command_res").map((p) => new RegExp(p)),
|
|
894
|
+
readOnlyCommands: new Set(strs("read_only_commands")),
|
|
895
|
+
fullySafeCommands: strs("fully_safe_commands").map((p) => new RegExp(p, "i")),
|
|
896
|
+
readonlySensitiveCommands: strs("readonly_sensitive_commands").map((p) => new RegExp(p, "i")),
|
|
897
|
+
safeActionTypes: new Set(strs("safe_action_types")),
|
|
898
|
+
readonlySensitiveActionTypes: new Set(strs("readonly_sensitive_action_types"))
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function traitsFor(key) {
|
|
902
|
+
const resolved = resolveTraitsOs(key);
|
|
903
|
+
let t = traitsCache.get(resolved);
|
|
904
|
+
if (!t) {
|
|
905
|
+
t = buildTraits(resolved);
|
|
906
|
+
traitsCache.set(resolved, t);
|
|
907
|
+
}
|
|
908
|
+
return t;
|
|
909
|
+
}
|
|
900
910
|
function getPlatformTraits(osType) {
|
|
901
911
|
const accepted = acceptedOsValues(osType);
|
|
902
912
|
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
@@ -904,368 +914,18 @@ function getPlatformTraits(osType) {
|
|
|
904
914
|
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
905
915
|
return LINUX_TRAITS;
|
|
906
916
|
}
|
|
907
|
-
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
917
|
+
var traitsCache, LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
908
918
|
var init_platform_traits = __esm({
|
|
909
919
|
"src/engine/platform-traits.ts"() {
|
|
910
920
|
"use strict";
|
|
911
921
|
init_esm_shims();
|
|
912
922
|
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;
|
|
923
|
+
init_rules_loader();
|
|
924
|
+
traitsCache = /* @__PURE__ */ new Map();
|
|
925
|
+
LINUX_TRAITS = traitsFor("linux");
|
|
926
|
+
WINDOWS_TRAITS = traitsFor("windows");
|
|
927
|
+
MACOS_TRAITS = traitsFor("macos");
|
|
928
|
+
HARMONY_TRAITS = traitsFor("harmony");
|
|
1269
929
|
}
|
|
1270
930
|
});
|
|
1271
931
|
|
|
@@ -1424,7 +1084,6 @@ function checkIntentConsistency(input) {
|
|
|
1424
1084
|
const { aNext, promptSession, promptHistory } = input;
|
|
1425
1085
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1426
1086
|
const actionType = aNext.actionType.toLowerCase();
|
|
1427
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1428
1087
|
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1429
1088
|
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1430
1089
|
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
@@ -1452,7 +1111,6 @@ function runLogicCheck(input, opts) {
|
|
|
1452
1111
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1453
1112
|
const actionType = aNext.actionType.toLowerCase();
|
|
1454
1113
|
const resolvedCwd = cwd || "/";
|
|
1455
|
-
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1456
1114
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
1457
1115
|
if (!opts?.skipReadBeforeWrite) {
|
|
1458
1116
|
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
@@ -1564,7 +1222,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1564
1222
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
1565
1223
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
1566
1224
|
if (!isWrite) return null;
|
|
1567
|
-
const WRITE_CMDS =
|
|
1225
|
+
const WRITE_CMDS = new Set(_LOGIC.write_commands ?? []);
|
|
1568
1226
|
if (isBashLike) {
|
|
1569
1227
|
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
1570
1228
|
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
@@ -1596,7 +1254,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1596
1254
|
}
|
|
1597
1255
|
if (!hasWriteSeg) return null;
|
|
1598
1256
|
}
|
|
1599
|
-
const SOURCE_ARG_CMDS =
|
|
1257
|
+
const SOURCE_ARG_CMDS = new Set(_LOGIC.source_arg_commands ?? []);
|
|
1600
1258
|
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
1601
1259
|
const targetPaths = [];
|
|
1602
1260
|
for (const seg of splitCommandSegments(actionDetail)) {
|
|
@@ -1631,7 +1289,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1631
1289
|
targetPaths.push(...segPathsFiltered);
|
|
1632
1290
|
}
|
|
1633
1291
|
}
|
|
1634
|
-
const uniqueTargetPaths = [...new Set(targetPaths)];
|
|
1292
|
+
const uniqueTargetPaths = [...new Set(targetPaths)].filter((p) => !PSEUDO_FS_PREFIXES.some((pfx) => p.startsWith(pfx)));
|
|
1635
1293
|
if (uniqueTargetPaths.length === 0) return null;
|
|
1636
1294
|
const historyReadPaths = /* @__PURE__ */ new Set();
|
|
1637
1295
|
for (const action of actionHistory) {
|
|
@@ -1644,7 +1302,6 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1644
1302
|
}
|
|
1645
1303
|
}
|
|
1646
1304
|
}
|
|
1647
|
-
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1648
1305
|
let filteredPaths = uniqueTargetPaths;
|
|
1649
1306
|
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1650
1307
|
filteredPaths = uniqueTargetPaths.filter(
|
|
@@ -1745,10 +1402,10 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1745
1402
|
if (!hasTraversal) {
|
|
1746
1403
|
return null;
|
|
1747
1404
|
}
|
|
1748
|
-
const READ_KEYWORDS =
|
|
1749
|
-
const WRITE_KEYWORDS2 =
|
|
1750
|
-
const EXEC_KEYWORDS =
|
|
1751
|
-
const OPEN_KEYWORDS =
|
|
1405
|
+
const READ_KEYWORDS = IFA_READ_KEYWORDS;
|
|
1406
|
+
const WRITE_KEYWORDS2 = IFA_WRITE_KEYWORDS;
|
|
1407
|
+
const EXEC_KEYWORDS = IFA_EXEC_KEYWORDS;
|
|
1408
|
+
const OPEN_KEYWORDS = IFA_OPEN_KEYWORDS;
|
|
1752
1409
|
const code = extractInlineCode(actionDetail);
|
|
1753
1410
|
if (!code) return null;
|
|
1754
1411
|
const codeLower = code.toLowerCase();
|
|
@@ -1811,7 +1468,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1811
1468
|
violatedLayers: ["L2.1"]
|
|
1812
1469
|
};
|
|
1813
1470
|
}
|
|
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;
|
|
1471
|
+
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, PSEUDO_FS_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
1472
|
var init_logic_rules = __esm({
|
|
1816
1473
|
"src/engine/logic-rules.ts"() {
|
|
1817
1474
|
"use strict";
|
|
@@ -1821,6 +1478,10 @@ var init_logic_rules = __esm({
|
|
|
1821
1478
|
init_rules_loader();
|
|
1822
1479
|
init_platform_traits();
|
|
1823
1480
|
init_shell_split();
|
|
1481
|
+
_LOGIC = loadLogicRules();
|
|
1482
|
+
_HEURISTIC = loadHeuristicRules();
|
|
1483
|
+
EXCLUDED_PREFIXES = _HEURISTIC.sensitive_excluded_prefixes ?? [];
|
|
1484
|
+
TEMP_DIRS = (_HEURISTIC.temp_dirs ?? []).map((d) => d + "/");
|
|
1824
1485
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1825
1486
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1826
1487
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc, dirDelOnly }) => ({ path: path5, riskLevel, desc, credential: true, dirDelOnly }));
|
|
@@ -1841,7 +1502,7 @@ var init_logic_rules = __esm({
|
|
|
1841
1502
|
"list_dir",
|
|
1842
1503
|
"count_text_length",
|
|
1843
1504
|
"search",
|
|
1844
|
-
// filemgr 只读三件套(globfiles 在
|
|
1505
|
+
// filemgr 只读三件套(globfiles 在 platform-traits 的 safeActionTypes 提前放行,此处兜底防御非 coordinator 入口)
|
|
1845
1506
|
"filemgr-globfiles",
|
|
1846
1507
|
"filemgr-readfile",
|
|
1847
1508
|
"filemgr-grepfiles",
|
|
@@ -1853,7 +1514,8 @@ var init_logic_rules = __esm({
|
|
|
1853
1514
|
"tail",
|
|
1854
1515
|
"cat"
|
|
1855
1516
|
]);
|
|
1856
|
-
NON_WRITE_TOOL_PREFIXES =
|
|
1517
|
+
NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
|
|
1518
|
+
PSEUDO_FS_PREFIXES = ["/sys/", "/proc/", "/dev/", "/run/"];
|
|
1857
1519
|
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
1520
|
SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
|
|
1859
1521
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
@@ -1894,25 +1556,24 @@ var init_logic_rules = __esm({
|
|
|
1894
1556
|
}));
|
|
1895
1557
|
})();
|
|
1896
1558
|
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
|
-
];
|
|
1559
|
+
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1560
|
+
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
1561
|
+
IFA_READ_KEYWORDS = _IFA.read_keywords ?? [];
|
|
1562
|
+
IFA_WRITE_KEYWORDS = _IFA.write_keywords ?? [];
|
|
1563
|
+
IFA_EXEC_KEYWORDS = _IFA.exec_keywords ?? [];
|
|
1564
|
+
IFA_OPEN_KEYWORDS = _IFA.open_keywords ?? [];
|
|
1909
1565
|
}
|
|
1910
1566
|
});
|
|
1911
1567
|
|
|
1912
1568
|
// src/engine/asset-dir.ts
|
|
1913
1569
|
import { existsSync as existsSync3 } from "fs";
|
|
1914
|
-
import { resolve as resolve2 } from "path";
|
|
1570
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
1915
1571
|
function assetDirCandidates(moduleDir, name) {
|
|
1572
|
+
const root = getRulesRoot();
|
|
1573
|
+
if (root) {
|
|
1574
|
+
const p = join3(root, name);
|
|
1575
|
+
return [p, p];
|
|
1576
|
+
}
|
|
1916
1577
|
return [
|
|
1917
1578
|
resolve2(moduleDir, name),
|
|
1918
1579
|
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
@@ -1928,6 +1589,7 @@ var init_asset_dir = __esm({
|
|
|
1928
1589
|
"src/engine/asset-dir.ts"() {
|
|
1929
1590
|
"use strict";
|
|
1930
1591
|
init_esm_shims();
|
|
1592
|
+
init_paths();
|
|
1931
1593
|
}
|
|
1932
1594
|
});
|
|
1933
1595
|
|
|
@@ -2128,7 +1790,7 @@ __export(runtime_config_exports, {
|
|
|
2128
1790
|
updateSkillEnabled: () => updateSkillEnabled
|
|
2129
1791
|
});
|
|
2130
1792
|
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
2131
|
-
import { join as
|
|
1793
|
+
import { join as join4 } from "path";
|
|
2132
1794
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2133
1795
|
import { dirname as dirname3 } from "path";
|
|
2134
1796
|
function runtimeDir() {
|
|
@@ -2263,7 +1925,10 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
2263
1925
|
}
|
|
2264
1926
|
function _filterRulesByOs(rules, osType) {
|
|
2265
1927
|
const accepted = acceptedOsValues(osType);
|
|
2266
|
-
return rules.filter((r) =>
|
|
1928
|
+
return rules.filter((r) => {
|
|
1929
|
+
const os3 = String(r.os || "linux").toLowerCase();
|
|
1930
|
+
return os3 === "all" || accepted.includes(os3);
|
|
1931
|
+
});
|
|
2267
1932
|
}
|
|
2268
1933
|
function _userCustomRules(runtime, layers, requiredKey) {
|
|
2269
1934
|
const list = Array.isArray(layers) ? layers : [layers];
|
|
@@ -2450,6 +2115,9 @@ function _l1InjectionKeywordsToRules() {
|
|
|
2450
2115
|
keyword: kw.keyword,
|
|
2451
2116
|
risk_level: kw.riskLevel,
|
|
2452
2117
|
lang: kw.lang || "en",
|
|
2118
|
+
// 带上条目自己的 os(`all` = 全平台通用),别丢 —— 丢了 Windows 会拿到
|
|
2119
|
+
// 本不该给它的条目。与 Python `_l1_injection_keywords_to_rules` 对齐。
|
|
2120
|
+
os: kw.os,
|
|
2453
2121
|
enabled: true,
|
|
2454
2122
|
builtin: true
|
|
2455
2123
|
}));
|
|
@@ -2462,7 +2130,7 @@ function _l2SensitivePathsToRules() {
|
|
|
2462
2130
|
const all = [...root, ...home, ...credential];
|
|
2463
2131
|
for (const sp of all) {
|
|
2464
2132
|
const os3 = String(sp.os || "linux").toLowerCase();
|
|
2465
|
-
const denyMode = _deriveDenyMode({
|
|
2133
|
+
const denyMode = sp.denyMode || _deriveDenyMode({
|
|
2466
2134
|
deny_mode: sp.deny_mode,
|
|
2467
2135
|
credential: sp.credential,
|
|
2468
2136
|
read_only: sp.read_only
|
|
@@ -2567,7 +2235,8 @@ function _l3SkillsToRules() {
|
|
|
2567
2235
|
}
|
|
2568
2236
|
function _scenarioRulesToDefaults() {
|
|
2569
2237
|
const out = {};
|
|
2570
|
-
const
|
|
2238
|
+
const _root = getRulesRoot();
|
|
2239
|
+
const scenDir = _root ? join4(_root, "scenarios") : existsSync5(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync5(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2571
2240
|
if (!existsSync5(scenDir)) return out;
|
|
2572
2241
|
let entries;
|
|
2573
2242
|
try {
|
|
@@ -2576,7 +2245,7 @@ function _scenarioRulesToDefaults() {
|
|
|
2576
2245
|
return out;
|
|
2577
2246
|
}
|
|
2578
2247
|
for (const scenarioId of entries) {
|
|
2579
|
-
const rulesFile = resolveScenarioRulesFile(scenarioId) ??
|
|
2248
|
+
const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join4(scenDir, scenarioId, "rules.json");
|
|
2580
2249
|
if (!existsSync5(rulesFile)) continue;
|
|
2581
2250
|
let data;
|
|
2582
2251
|
try {
|
|
@@ -2616,7 +2285,8 @@ function _scenarioRulesToDefaults() {
|
|
|
2616
2285
|
}
|
|
2617
2286
|
function _agentRulesToDefaults() {
|
|
2618
2287
|
const out = {};
|
|
2619
|
-
const
|
|
2288
|
+
const _root = getRulesRoot();
|
|
2289
|
+
const agentsDir = _root ? join4(_root, "agents") : existsSync5(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync5(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2620
2290
|
if (!existsSync5(agentsDir)) return out;
|
|
2621
2291
|
let entries;
|
|
2622
2292
|
try {
|
|
@@ -2625,7 +2295,7 @@ function _agentRulesToDefaults() {
|
|
|
2625
2295
|
return out;
|
|
2626
2296
|
}
|
|
2627
2297
|
for (const agentId of entries) {
|
|
2628
|
-
const rulesFile =
|
|
2298
|
+
const rulesFile = join4(agentsDir, agentId, "rules.json");
|
|
2629
2299
|
if (!existsSync5(rulesFile)) continue;
|
|
2630
2300
|
let data;
|
|
2631
2301
|
try {
|
|
@@ -3004,7 +2674,11 @@ function getDisabledSkillIds(runtime) {
|
|
|
3004
2674
|
}
|
|
3005
2675
|
function _filterDefaultsByOs(rules, osType) {
|
|
3006
2676
|
const accepted = acceptedOsValues(osType);
|
|
3007
|
-
return rules.filter((r) =>
|
|
2677
|
+
return rules.filter((r) => {
|
|
2678
|
+
if (!r.os) return true;
|
|
2679
|
+
const os3 = String(r.os).toLowerCase();
|
|
2680
|
+
return os3 === "all" || accepted.includes(os3);
|
|
2681
|
+
});
|
|
3008
2682
|
}
|
|
3009
2683
|
function _hasRulesForOs(rules, osType) {
|
|
3010
2684
|
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
@@ -3233,12 +2907,12 @@ function addCustomSkill(skillId, category, keywords, content) {
|
|
|
3233
2907
|
);
|
|
3234
2908
|
}
|
|
3235
2909
|
const runtime = loadRuntimeConfig();
|
|
3236
|
-
const skillsDir =
|
|
2910
|
+
const skillsDir = join4(getRuntimeConfigDir(), "agent_moss_skills");
|
|
3237
2911
|
try {
|
|
3238
2912
|
mkdirSync(skillsDir, { recursive: true });
|
|
3239
2913
|
} catch {
|
|
3240
2914
|
}
|
|
3241
|
-
writeFileSync(
|
|
2915
|
+
writeFileSync(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
3242
2916
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
3243
2917
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
3244
2918
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -3253,7 +2927,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
3253
2927
|
if (idx >= 0) {
|
|
3254
2928
|
if (skills[idx].builtin) return null;
|
|
3255
2929
|
skills.splice(idx, 1);
|
|
3256
|
-
const skillPath =
|
|
2930
|
+
const skillPath = join4(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
3257
2931
|
try {
|
|
3258
2932
|
if (existsSync5(skillPath)) unlinkSync(skillPath);
|
|
3259
2933
|
} catch {
|
|
@@ -3462,7 +3136,7 @@ var init_runtime_config = __esm({
|
|
|
3462
3136
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3463
3137
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3464
3138
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
3465
|
-
RUNTIME_CONFIG_PATH =
|
|
3139
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
3466
3140
|
LAYER_DISABLE_ENV = {
|
|
3467
3141
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
3468
3142
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
@@ -3490,10 +3164,10 @@ var init_runtime_config = __esm({
|
|
|
3490
3164
|
intent_consistency: ["intent_keywords", "reason"]
|
|
3491
3165
|
};
|
|
3492
3166
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3493
|
-
_AGENTS_DIR_PROD =
|
|
3494
|
-
_AGENTS_DIR_DEV =
|
|
3495
|
-
_SCENARIOS_DIR_PROD =
|
|
3496
|
-
_SCENARIOS_DIR_DEV =
|
|
3167
|
+
_AGENTS_DIR_PROD = join4(__dirname4, "agents");
|
|
3168
|
+
_AGENTS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
3169
|
+
_SCENARIOS_DIR_PROD = join4(__dirname4, "scenarios");
|
|
3170
|
+
_SCENARIOS_DIR_DEV = join4(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3497
3171
|
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3498
3172
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3499
3173
|
registerRuntimeConfigBridge({
|
|
@@ -3832,15 +3506,22 @@ var init_config = __esm({
|
|
|
3832
3506
|
|
|
3833
3507
|
// src/agents/index.ts
|
|
3834
3508
|
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
3835
|
-
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
3509
|
+
import { resolve as resolve4, dirname as dirname4, join as join5 } from "path";
|
|
3836
3510
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
3837
3511
|
function resolveAgentsDir() {
|
|
3512
|
+
const root = getRulesRoot();
|
|
3513
|
+
if (root) return join5(root, "agents");
|
|
3838
3514
|
if (existsSync6(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3839
3515
|
if (existsSync6(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
3840
3516
|
return AGENTS_DIR_PROD;
|
|
3841
3517
|
}
|
|
3842
3518
|
function getAgentExtensions(agentId) {
|
|
3843
3519
|
if (!agentId) return null;
|
|
3520
|
+
if (!isSafeId(agentId)) {
|
|
3521
|
+
console.warn(`[AgentMoss agents] \u975E\u6CD5 agent_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${agentId.slice(0, 120)}`);
|
|
3522
|
+
return null;
|
|
3523
|
+
}
|
|
3524
|
+
if (cache.size >= CACHE_MAX) cache.clear();
|
|
3844
3525
|
const fp = getRuleSourceFingerprint();
|
|
3845
3526
|
const cached2 = cache.get(agentId);
|
|
3846
3527
|
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
@@ -3893,31 +3574,40 @@ function mergeAgentExtensions(...exts) {
|
|
|
3893
3574
|
extra_skills: nonNull.flatMap((e) => e.extra_skills || [])
|
|
3894
3575
|
};
|
|
3895
3576
|
}
|
|
3896
|
-
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
3577
|
+
var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache, CACHE_MAX;
|
|
3897
3578
|
var init_agents = __esm({
|
|
3898
3579
|
"src/agents/index.ts"() {
|
|
3899
3580
|
"use strict";
|
|
3900
3581
|
init_esm_shims();
|
|
3582
|
+
init_paths();
|
|
3901
3583
|
init_runtime_config();
|
|
3902
3584
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
3903
3585
|
__dirname5 = dirname4(__filename4);
|
|
3904
3586
|
AGENTS_DIR_PROD = resolve4(__dirname5, "agents");
|
|
3905
3587
|
AGENTS_DIR_DEV_FALLBACK = resolve4(__dirname5, "../../../agent_moss/agents");
|
|
3906
3588
|
cache = /* @__PURE__ */ new Map();
|
|
3589
|
+
CACHE_MAX = 512;
|
|
3907
3590
|
}
|
|
3908
3591
|
});
|
|
3909
3592
|
|
|
3910
3593
|
// src/scenarios/index.ts
|
|
3911
3594
|
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3912
|
-
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
3595
|
+
import { resolve as resolve5, dirname as dirname5, join as join6 } from "path";
|
|
3913
3596
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
3914
3597
|
function resolveScenariosDir() {
|
|
3598
|
+
const root = getRulesRoot();
|
|
3599
|
+
if (root) return join6(root, "scenarios");
|
|
3915
3600
|
if (existsSync7(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3916
3601
|
if (existsSync7(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
3917
3602
|
return SCENARIOS_DIR_PROD;
|
|
3918
3603
|
}
|
|
3919
3604
|
function getScenarioExtensions(scenarioId) {
|
|
3920
3605
|
if (!scenarioId) return null;
|
|
3606
|
+
if (!isSafeId(scenarioId)) {
|
|
3607
|
+
console.warn(`[AgentMoss scenarios] \u975E\u6CD5 scenario_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${scenarioId.slice(0, 120)}`);
|
|
3608
|
+
return null;
|
|
3609
|
+
}
|
|
3610
|
+
if (cache2.size >= CACHE_MAX2) cache2.clear();
|
|
3921
3611
|
const fp = getRuleSourceFingerprint();
|
|
3922
3612
|
const cached2 = cache2.get(scenarioId);
|
|
3923
3613
|
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
@@ -3987,11 +3677,12 @@ function getScenarioExtensionsMulti(scenarioSpec) {
|
|
|
3987
3677
|
if (nonNull.length === 0) return null;
|
|
3988
3678
|
return mergeAgentExtensions(...nonNull);
|
|
3989
3679
|
}
|
|
3990
|
-
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
|
|
3680
|
+
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2, CACHE_MAX2;
|
|
3991
3681
|
var init_scenarios = __esm({
|
|
3992
3682
|
"src/scenarios/index.ts"() {
|
|
3993
3683
|
"use strict";
|
|
3994
3684
|
init_esm_shims();
|
|
3685
|
+
init_paths();
|
|
3995
3686
|
init_agents();
|
|
3996
3687
|
init_runtime_config();
|
|
3997
3688
|
init_rules_loader();
|
|
@@ -4000,6 +3691,7 @@ var init_scenarios = __esm({
|
|
|
4000
3691
|
SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
|
|
4001
3692
|
SCENARIOS_DIR_DEV_FALLBACK = resolve5(__dirname6, "../../../agent_moss/scenarios");
|
|
4002
3693
|
cache2 = /* @__PURE__ */ new Map();
|
|
3694
|
+
CACHE_MAX2 = 512;
|
|
4003
3695
|
}
|
|
4004
3696
|
});
|
|
4005
3697
|
|
|
@@ -4398,7 +4090,7 @@ function severityRank(s) {
|
|
|
4398
4090
|
}
|
|
4399
4091
|
function isSearchActionType(actionType) {
|
|
4400
4092
|
const at = actionType.toLowerCase();
|
|
4401
|
-
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) ||
|
|
4093
|
+
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || SEARCH_ACTION_EXACT.includes(at);
|
|
4402
4094
|
}
|
|
4403
4095
|
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
4404
4096
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
@@ -4688,8 +4380,7 @@ function isSshKeygenGenerate(command) {
|
|
|
4688
4380
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
4689
4381
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
4690
4382
|
const scan = stripHeredocContent(actionDetail);
|
|
4691
|
-
const
|
|
4692
|
-
const isTempPath = TEMP_DIRS.some(
|
|
4383
|
+
const isTempPath = TEMP_DIRS2.some(
|
|
4693
4384
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
4694
4385
|
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
4695
4386
|
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 +4526,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
4835
4526
|
}
|
|
4836
4527
|
return null;
|
|
4837
4528
|
}
|
|
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;
|
|
4529
|
+
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
4530
|
var init_heuristic = __esm({
|
|
4840
4531
|
"src/engine/heuristic.ts"() {
|
|
4841
4532
|
"use strict";
|
|
@@ -4852,7 +4543,8 @@ var init_heuristic = __esm({
|
|
|
4852
4543
|
return kws.map((k) => ({
|
|
4853
4544
|
keyword: k.keyword,
|
|
4854
4545
|
riskLevel: k.risk_level,
|
|
4855
|
-
lang: k.lang || "en"
|
|
4546
|
+
lang: k.lang || "en",
|
|
4547
|
+
os: String(k.os || "all").toLowerCase()
|
|
4856
4548
|
}));
|
|
4857
4549
|
})();
|
|
4858
4550
|
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
@@ -4860,7 +4552,9 @@ var init_heuristic = __esm({
|
|
|
4860
4552
|
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4861
4553
|
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
4862
4554
|
LOOPBACK_URL_RE = /https?:\/\/([^\/\s;'"|&)]+)/i;
|
|
4863
|
-
|
|
4555
|
+
HEURISTIC_RULES = loadHeuristicRules();
|
|
4556
|
+
LOOPBACK_HOSTS = new Set(HEURISTIC_RULES.loopback_hosts ?? []);
|
|
4557
|
+
TEMP_DIRS2 = HEURISTIC_RULES.temp_dirs ?? [];
|
|
4864
4558
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
4865
4559
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
4866
4560
|
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 +4562,9 @@ var init_heuristic = __esm({
|
|
|
4868
4562
|
PY_BIGNUM_RE = /\d{6,}/;
|
|
4869
4563
|
PY_INLINE_C_RE = /(python(?:3(?:\.\d+)?)?|perl5?|ruby|node|php|lua)\s+(?:-[a-zA-Z]+\s+)*-c\s+(?:"([^"]*)"|'([^']*)')/i;
|
|
4870
4564
|
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 =
|
|
4565
|
+
SENSITIVE_EXCLUDED_PREFIXES = HEURISTIC_RULES.sensitive_excluded_prefixes ?? [];
|
|
4566
|
+
SEARCH_ACTION_PREFIXES = HEURISTIC_RULES.search_action_prefixes ?? [];
|
|
4567
|
+
SEARCH_ACTION_EXACT = HEURISTIC_RULES.search_action_exact ?? [];
|
|
4873
4568
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
4874
4569
|
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
4570
|
}
|
|
@@ -4885,16 +4580,16 @@ __export(token_stats_exports, {
|
|
|
4885
4580
|
recordTokenUsage: () => recordTokenUsage,
|
|
4886
4581
|
resetTokenStats: () => resetTokenStats
|
|
4887
4582
|
});
|
|
4888
|
-
import { readFileSync as
|
|
4583
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
4889
4584
|
function getStatsPath() {
|
|
4890
4585
|
const envPath = process.env[ENV_STATS_PATH];
|
|
4891
4586
|
if (envPath) return envPath;
|
|
4892
4587
|
return getTokenStatsPath();
|
|
4893
4588
|
}
|
|
4894
4589
|
function loadAllRecords(statsPath) {
|
|
4895
|
-
if (!
|
|
4590
|
+
if (!existsSync12(statsPath)) return [];
|
|
4896
4591
|
try {
|
|
4897
|
-
const data = JSON.parse(
|
|
4592
|
+
const data = JSON.parse(readFileSync12(statsPath, "utf-8"));
|
|
4898
4593
|
if (Array.isArray(data)) return data;
|
|
4899
4594
|
if (data && Array.isArray(data.records)) return data.records;
|
|
4900
4595
|
return [];
|
|
@@ -4905,7 +4600,7 @@ function loadAllRecords(statsPath) {
|
|
|
4905
4600
|
function saveRecords(statsPath, records) {
|
|
4906
4601
|
try {
|
|
4907
4602
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
4908
|
-
if (dir && !
|
|
4603
|
+
if (dir && !existsSync12(dir)) mkdirSync3(dir, { recursive: true });
|
|
4909
4604
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
4910
4605
|
} catch (e) {
|
|
4911
4606
|
}
|
|
@@ -5032,7 +4727,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
5032
4727
|
}
|
|
5033
4728
|
function resetTokenStats() {
|
|
5034
4729
|
const statsPath = getStatsPath();
|
|
5035
|
-
if (
|
|
4730
|
+
if (existsSync12(statsPath)) {
|
|
5036
4731
|
try {
|
|
5037
4732
|
unlinkSync2(statsPath);
|
|
5038
4733
|
} catch {
|
|
@@ -5060,17 +4755,113 @@ init_inline_analyzer();
|
|
|
5060
4755
|
|
|
5061
4756
|
// src/engine/llm-analyzer.ts
|
|
5062
4757
|
init_esm_shims();
|
|
4758
|
+
|
|
4759
|
+
// src/infra/security-policy.ts
|
|
4760
|
+
init_esm_shims();
|
|
4761
|
+
init_paths();
|
|
4762
|
+
import { chmodSync, closeSync, existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync7, readSync, realpathSync, writeSync } from "fs";
|
|
4763
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
4764
|
+
import { basename, isAbsolute, join as join7, relative, resolve as resolve6, dirname as dirname6 } from "path";
|
|
4765
|
+
var TRUTHY2 = ["1", "true", "yes", "on"];
|
|
4766
|
+
function env(name, dflt = "") {
|
|
4767
|
+
return (process.env[name] ?? dflt).trim();
|
|
4768
|
+
}
|
|
4769
|
+
function splitList(raw) {
|
|
4770
|
+
return raw.replace(/;/g, ",").split(",").map((x) => x.trim()).filter(Boolean);
|
|
4771
|
+
}
|
|
4772
|
+
function homeDir() {
|
|
4773
|
+
try {
|
|
4774
|
+
return homedir2();
|
|
4775
|
+
} catch {
|
|
4776
|
+
return tmpdir();
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
function logRoots() {
|
|
4780
|
+
const raw = env("AGENT_MOSS_LOG_ROOTS");
|
|
4781
|
+
if (raw) return splitList(raw);
|
|
4782
|
+
return [.../* @__PURE__ */ new Set([homeDir(), "/tmp", tmpdir()])];
|
|
4783
|
+
}
|
|
4784
|
+
function realpathLoose(p) {
|
|
4785
|
+
const abs = resolve6(p.replace(/^~(?=$|\/)/, homeDir()));
|
|
4786
|
+
try {
|
|
4787
|
+
return realpathSync(abs);
|
|
4788
|
+
} catch {
|
|
4789
|
+
}
|
|
4790
|
+
const parts = [];
|
|
4791
|
+
let cur = abs;
|
|
4792
|
+
for (; ; ) {
|
|
4793
|
+
const parent = dirname6(cur);
|
|
4794
|
+
if (parent === cur) return null;
|
|
4795
|
+
parts.unshift(basename(cur));
|
|
4796
|
+
cur = parent;
|
|
4797
|
+
try {
|
|
4798
|
+
return join7(realpathSync(cur), ...parts);
|
|
4799
|
+
} catch {
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
function pathWithin(p, roots) {
|
|
4804
|
+
if (!p) return true;
|
|
4805
|
+
const target = realpathLoose(p);
|
|
4806
|
+
if (target === null) return false;
|
|
4807
|
+
for (const root of roots) {
|
|
4808
|
+
const rootReal = realpathLoose(root);
|
|
4809
|
+
if (rootReal === null) continue;
|
|
4810
|
+
const rel = relative(rootReal, target);
|
|
4811
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return true;
|
|
4812
|
+
}
|
|
4813
|
+
return false;
|
|
4814
|
+
}
|
|
4815
|
+
var BLOCKED_HOSTS = ["169.254.169.254", "metadata.google.internal", "metadata.tencentyun.com"];
|
|
4816
|
+
function validateLlmBaseUrl(url, allowPrivate) {
|
|
4817
|
+
if (!url) return [true, ""];
|
|
4818
|
+
let u;
|
|
4819
|
+
try {
|
|
4820
|
+
u = new URL(url);
|
|
4821
|
+
} catch {
|
|
4822
|
+
return [false, "baseUrl \u4E0D\u662F\u5408\u6CD5 URL"];
|
|
4823
|
+
}
|
|
4824
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
4825
|
+
return [false, `baseUrl \u534F\u8BAE\u4E0D\u53D7\u652F\u6301: ${u.protocol}`];
|
|
4826
|
+
}
|
|
4827
|
+
const host = (u.hostname || "").toLowerCase();
|
|
4828
|
+
if (!host) return [false, "baseUrl \u7F3A\u5C11\u4E3B\u673A\u540D"];
|
|
4829
|
+
if (BLOCKED_HOSTS.includes(host)) return [false, `baseUrl \u6307\u5411\u4E91\u5143\u6570\u636E\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
4830
|
+
if (/^169\.254\.\d{1,3}\.\d{1,3}$/.test(host)) {
|
|
4831
|
+
return [false, `baseUrl \u6307\u5411\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
4832
|
+
}
|
|
4833
|
+
const allowlist = splitList(env("AGENT_MOSS_LLM_HOST_ALLOWLIST")).map((h) => h.toLowerCase());
|
|
4834
|
+
if (allowlist.length && !allowlist.includes(host)) {
|
|
4835
|
+
return [false, `baseUrl \u4E3B\u673A\u4E0D\u5728\u5141\u8BB8\u5217\u8868: ${host}`];
|
|
4836
|
+
}
|
|
4837
|
+
if (allowPrivate === void 0) {
|
|
4838
|
+
allowPrivate = TRUTHY2.includes(env("AGENT_MOSS_TRUST_PRIVATE_LLM", "1").toLowerCase());
|
|
4839
|
+
}
|
|
4840
|
+
if (!allowPrivate) {
|
|
4841
|
+
if (host === "localhost" || host === "::1") return [false, "baseUrl \u6307\u5411\u672C\u673A\uFF0C\u5DF2\u62D2\u7EDD"];
|
|
4842
|
+
const m = host.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
|
|
4843
|
+
if (m) {
|
|
4844
|
+
const [a, b] = [Number(m[1]), Number(m[2])];
|
|
4845
|
+
if (a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 127 || a === 169 && b === 254) {
|
|
4846
|
+
return [false, `baseUrl \u6307\u5411\u5185\u7F51\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
return [true, ""];
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
// src/engine/llm-analyzer.ts
|
|
5063
4854
|
init_config();
|
|
5064
4855
|
init_skill_loader();
|
|
5065
4856
|
|
|
5066
4857
|
// src/engine/template-loader.ts
|
|
5067
4858
|
init_esm_shims();
|
|
5068
4859
|
init_asset_dir();
|
|
5069
|
-
import { readFileSync as
|
|
5070
|
-
import { resolve as
|
|
4860
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4861
|
+
import { resolve as resolve7, dirname as dirname7 } from "path";
|
|
5071
4862
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
5072
4863
|
var __filename6 = fileURLToPath8(import.meta.url);
|
|
5073
|
-
var __dirname7 =
|
|
4864
|
+
var __dirname7 = dirname7(__filename6);
|
|
5074
4865
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
5075
4866
|
var cachedTemplates = null;
|
|
5076
4867
|
function loadTemplates() {
|
|
@@ -5083,10 +4874,10 @@ function loadTemplates() {
|
|
|
5083
4874
|
return cachedTemplates;
|
|
5084
4875
|
}
|
|
5085
4876
|
for (const file of TEMPLATE_FILES) {
|
|
5086
|
-
const filePath =
|
|
5087
|
-
if (
|
|
4877
|
+
const filePath = resolve7(dir, file);
|
|
4878
|
+
if (existsSync9(filePath)) {
|
|
5088
4879
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
5089
|
-
cachedTemplates[key] =
|
|
4880
|
+
cachedTemplates[key] = readFileSync8(filePath, "utf-8");
|
|
5090
4881
|
}
|
|
5091
4882
|
}
|
|
5092
4883
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
@@ -5095,87 +4886,25 @@ function loadTemplates() {
|
|
|
5095
4886
|
|
|
5096
4887
|
// src/engine/script-analyzer.ts
|
|
5097
4888
|
init_esm_shims();
|
|
5098
|
-
|
|
5099
|
-
import {
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
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
|
-
};
|
|
4889
|
+
init_rules_loader();
|
|
4890
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4891
|
+
import { resolve as resolve8, join as join8 } from "path";
|
|
4892
|
+
var SCRIPT_RULES = loadScriptRules();
|
|
4893
|
+
if (!SCRIPT_RULES || Object.keys(SCRIPT_RULES).length === 0) {
|
|
4894
|
+
console.error(
|
|
4895
|
+
"[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"
|
|
4896
|
+
);
|
|
4897
|
+
}
|
|
4898
|
+
var SUSPICIOUS_PATTERNS = (SCRIPT_RULES.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
4899
|
+
var SCRIPT_EXEC_PATTERNS = ((SCRIPT_RULES.script_exec_patterns ?? {}).legacy ?? []).map((r) => compileRuleRegex(r));
|
|
4900
|
+
var KEYWORD_SETS = SCRIPT_RULES.keyword_sets ?? {};
|
|
4901
|
+
var KEYWORD_COMBINATION_RISKS = (SCRIPT_RULES.keyword_combination_risks ?? []).map((r) => ({
|
|
4902
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS),
|
|
4903
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS),
|
|
4904
|
+
level: String(r.level),
|
|
4905
|
+
reason: String(r.reason)
|
|
4906
|
+
}));
|
|
4907
|
+
var SINGLE_KEYWORD_RISKS = SCRIPT_RULES.single_keyword_risks ?? {};
|
|
5179
4908
|
var LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
5180
4909
|
function emptyResult() {
|
|
5181
4910
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
@@ -5202,10 +4931,10 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5202
4931
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
5203
4932
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
5204
4933
|
}
|
|
5205
|
-
const networkPostKeywords =
|
|
4934
|
+
const networkPostKeywords = new Set(KEYWORD_SETS.network_post ?? []);
|
|
5206
4935
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
5207
4936
|
if (hasNetworkPost) {
|
|
5208
|
-
const envPatterns =
|
|
4937
|
+
const envPatterns = SCRIPT_RULES.env_exfil_patterns ?? [];
|
|
5209
4938
|
const contentLower = scriptContent.toLowerCase();
|
|
5210
4939
|
for (const p of envPatterns) {
|
|
5211
4940
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -5229,8 +4958,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
5229
4958
|
}
|
|
5230
4959
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
5231
4960
|
try {
|
|
5232
|
-
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ?
|
|
5233
|
-
const raw =
|
|
4961
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join8(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve8(scriptPath);
|
|
4962
|
+
const raw = readFileSync9(expandedPath, "utf-8");
|
|
5234
4963
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
5235
4964
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
5236
4965
|
} catch (e) {
|
|
@@ -5286,9 +5015,9 @@ init_heuristic();
|
|
|
5286
5015
|
init_config();
|
|
5287
5016
|
init_logic_rules();
|
|
5288
5017
|
init_inline_analyzer();
|
|
5289
|
-
import { statSync as statSync4, existsSync as
|
|
5290
|
-
import { resolve as
|
|
5291
|
-
import { homedir as
|
|
5018
|
+
import { statSync as statSync4, existsSync as existsSync11 } from "fs";
|
|
5019
|
+
import { resolve as resolve10, join as join10, isAbsolute as isAbsolute3 } from "path";
|
|
5020
|
+
import { homedir as homedir4 } from "os";
|
|
5292
5021
|
|
|
5293
5022
|
// src/engine/script-engine/tracker.ts
|
|
5294
5023
|
init_esm_shims();
|
|
@@ -5398,14 +5127,14 @@ function isSysArgv(node) {
|
|
|
5398
5127
|
const prop = node.getChild("PropertyName");
|
|
5399
5128
|
return prop !== null && srcSlice(prop) === "argv";
|
|
5400
5129
|
}
|
|
5401
|
-
function evalExpr(node,
|
|
5130
|
+
function evalExpr(node, env2) {
|
|
5402
5131
|
if (!node) return null;
|
|
5403
5132
|
const name = node.name;
|
|
5404
5133
|
if (name === "String") return stringValue(srcSlice(node));
|
|
5405
5134
|
if (name === "Number") return srcSlice(node);
|
|
5406
5135
|
if (name === "VariableName") {
|
|
5407
5136
|
const id = srcSlice(node);
|
|
5408
|
-
return id in
|
|
5137
|
+
return id in env2.vars ? env2.vars[id] : null;
|
|
5409
5138
|
}
|
|
5410
5139
|
if (name === "MemberExpression") {
|
|
5411
5140
|
const bracket = node.getChild("[");
|
|
@@ -5415,13 +5144,13 @@ function evalExpr(node, env) {
|
|
|
5415
5144
|
const idxNode = bracket.nextSibling;
|
|
5416
5145
|
if (idxNode && idxNode.name === "Number") {
|
|
5417
5146
|
const idx = parseInt(srcSlice(idxNode), 10);
|
|
5418
|
-
if (idx >= 1 && idx <=
|
|
5147
|
+
if (idx >= 1 && idx <= env2.argv.length) return env2.argv[idx - 1];
|
|
5419
5148
|
return null;
|
|
5420
5149
|
}
|
|
5421
5150
|
}
|
|
5422
5151
|
return null;
|
|
5423
5152
|
}
|
|
5424
|
-
if (isSysArgv(node)) return
|
|
5153
|
+
if (isSysArgv(node)) return env2.argv[0] ?? null;
|
|
5425
5154
|
return null;
|
|
5426
5155
|
}
|
|
5427
5156
|
if (name === "CallExpression") {
|
|
@@ -5429,7 +5158,7 @@ function evalExpr(node, env) {
|
|
|
5429
5158
|
if (func && func.name === "VariableName" && srcSlice(func) === "len") {
|
|
5430
5159
|
const argList = node.getChild("ArgList");
|
|
5431
5160
|
const inner = argList ? firstNamedChild(argList) : null;
|
|
5432
|
-
if (inner && isSysArgv(inner)) return String(
|
|
5161
|
+
if (inner && isSysArgv(inner)) return String(env2.argv.length + 1);
|
|
5433
5162
|
}
|
|
5434
5163
|
return null;
|
|
5435
5164
|
}
|
|
@@ -5441,16 +5170,16 @@ function evalExpr(node, env) {
|
|
|
5441
5170
|
const condNode = ifKw.nextSibling;
|
|
5442
5171
|
const orelseNode = elseKw.nextSibling;
|
|
5443
5172
|
if (!thenNode || !condNode || !orelseNode) return null;
|
|
5444
|
-
const cond = evalCondition(condNode,
|
|
5445
|
-
if (cond === true) return evalExpr(thenNode,
|
|
5446
|
-
if (cond === false) return evalExpr(orelseNode,
|
|
5173
|
+
const cond = evalCondition(condNode, env2);
|
|
5174
|
+
if (cond === true) return evalExpr(thenNode, env2);
|
|
5175
|
+
if (cond === false) return evalExpr(orelseNode, env2);
|
|
5447
5176
|
return null;
|
|
5448
5177
|
}
|
|
5449
5178
|
if (name === "BinaryExpression") {
|
|
5450
5179
|
const op = findOp(node);
|
|
5451
5180
|
if (op === "+") {
|
|
5452
|
-
const left = evalExpr(node.firstChild,
|
|
5453
|
-
const right = evalExpr(lastNamedChild(node),
|
|
5181
|
+
const left = evalExpr(node.firstChild, env2);
|
|
5182
|
+
const right = evalExpr(lastNamedChild(node), env2);
|
|
5454
5183
|
if (left !== null && right !== null) return left + right;
|
|
5455
5184
|
}
|
|
5456
5185
|
return null;
|
|
@@ -5483,7 +5212,7 @@ function findOp(node) {
|
|
|
5483
5212
|
}
|
|
5484
5213
|
return null;
|
|
5485
5214
|
}
|
|
5486
|
-
function evalCondition(node,
|
|
5215
|
+
function evalCondition(node, env2) {
|
|
5487
5216
|
if (!node) return null;
|
|
5488
5217
|
const name = node.name;
|
|
5489
5218
|
if (name === "Boolean") {
|
|
@@ -5494,7 +5223,7 @@ function evalCondition(node, env) {
|
|
|
5494
5223
|
const op = findOp(node);
|
|
5495
5224
|
if (op === "and" || op === "or") {
|
|
5496
5225
|
const parts2 = namedChildren(node);
|
|
5497
|
-
const vals = parts2.map((p) => evalCondition(p,
|
|
5226
|
+
const vals = parts2.map((p) => evalCondition(p, env2));
|
|
5498
5227
|
if (op === "and") {
|
|
5499
5228
|
if (vals.some((v) => v === false)) return false;
|
|
5500
5229
|
if (vals.every((v) => v === true)) return true;
|
|
@@ -5508,13 +5237,13 @@ function evalCondition(node, env) {
|
|
|
5508
5237
|
if (parts.length !== 3) return null;
|
|
5509
5238
|
const [left, opNode, right] = parts;
|
|
5510
5239
|
const opText = srcSlice(opNode).trim();
|
|
5511
|
-
return evalCompare(left, opText, right,
|
|
5240
|
+
return evalCompare(left, opText, right, env2);
|
|
5512
5241
|
}
|
|
5513
5242
|
if (name === "UnaryExpression") {
|
|
5514
5243
|
const kw = node.getChild("not");
|
|
5515
5244
|
if (kw) {
|
|
5516
5245
|
const operand = kw.nextSibling;
|
|
5517
|
-
const v = evalCondition(operand,
|
|
5246
|
+
const v = evalCondition(operand, env2);
|
|
5518
5247
|
return v === null ? null : !v;
|
|
5519
5248
|
}
|
|
5520
5249
|
return null;
|
|
@@ -5535,39 +5264,39 @@ function namedChildren(node) {
|
|
|
5535
5264
|
}
|
|
5536
5265
|
return out;
|
|
5537
5266
|
}
|
|
5538
|
-
function evalCompare(left, opText, right,
|
|
5267
|
+
function evalCompare(left, opText, right, env2) {
|
|
5539
5268
|
if (opText === "==" && left.name === "VariableName" && srcSlice(left) === "__name__") {
|
|
5540
|
-
const rv = evalExpr(right,
|
|
5269
|
+
const rv = evalExpr(right, env2);
|
|
5541
5270
|
if (rv !== null) return rv === "__main__";
|
|
5542
5271
|
return null;
|
|
5543
5272
|
}
|
|
5544
5273
|
if (opText === "==" || opText === "!=") {
|
|
5545
|
-
const lv = evalExpr(left,
|
|
5546
|
-
const rv = evalExpr(right,
|
|
5274
|
+
const lv = evalExpr(left, env2);
|
|
5275
|
+
const rv = evalExpr(right, env2);
|
|
5547
5276
|
if (lv !== null && rv !== null) {
|
|
5548
5277
|
return opText === "==" ? lv === rv : lv !== rv;
|
|
5549
5278
|
}
|
|
5550
5279
|
return null;
|
|
5551
5280
|
}
|
|
5552
5281
|
if (opText === "in") {
|
|
5553
|
-
const lv = evalExpr(left,
|
|
5282
|
+
const lv = evalExpr(left, env2);
|
|
5554
5283
|
if (lv === null) return null;
|
|
5555
5284
|
const rightName = right.name;
|
|
5556
5285
|
if (rightName === "TupleExpression" || rightName === "ArrayExpression" || rightName === "SetExpression" || rightName === "ParenthesizedExpression") {
|
|
5557
5286
|
const container = rightName === "ParenthesizedExpression" && right.firstChild && right.firstChild.name === "TupleExpression" ? right.firstChild : right;
|
|
5558
5287
|
for (const elt of namedChildren(container)) {
|
|
5559
|
-
const ev = evalExpr(elt,
|
|
5288
|
+
const ev = evalExpr(elt, env2);
|
|
5560
5289
|
if (ev !== null && ev === lv) return true;
|
|
5561
5290
|
}
|
|
5562
5291
|
return false;
|
|
5563
5292
|
}
|
|
5564
|
-
const rv = evalExpr(right,
|
|
5293
|
+
const rv = evalExpr(right, env2);
|
|
5565
5294
|
if (rv !== null) return rv.includes(lv);
|
|
5566
5295
|
return null;
|
|
5567
5296
|
}
|
|
5568
5297
|
if (opText === ">" || opText === ">=" || opText === "<" || opText === "<=") {
|
|
5569
|
-
const lv = evalExpr(left,
|
|
5570
|
-
const rv = evalExpr(right,
|
|
5298
|
+
const lv = evalExpr(left, env2);
|
|
5299
|
+
const rv = evalExpr(right, env2);
|
|
5571
5300
|
if (lv === null || rv === null) return null;
|
|
5572
5301
|
const ln = parseInt(lv, 10);
|
|
5573
5302
|
const rn = parseInt(rv, 10);
|
|
@@ -5579,71 +5308,71 @@ function evalCompare(left, opText, right, env) {
|
|
|
5579
5308
|
}
|
|
5580
5309
|
return null;
|
|
5581
5310
|
}
|
|
5582
|
-
function processStmts(stmts,
|
|
5583
|
-
for (const s of stmts) processStmt(s,
|
|
5311
|
+
function processStmts(stmts, env2, lineStarts, lines, branches) {
|
|
5312
|
+
for (const s of stmts) processStmt(s, env2, lineStarts, lines, branches);
|
|
5584
5313
|
}
|
|
5585
|
-
function invalidateEnvOnCalls(node,
|
|
5314
|
+
function invalidateEnvOnCalls(node, env2) {
|
|
5586
5315
|
const cursor = node.cursor();
|
|
5587
5316
|
cursor.iterate((n) => {
|
|
5588
5317
|
const nd = n.node;
|
|
5589
5318
|
if (nd.name !== "CallExpression") return true;
|
|
5590
5319
|
const func = nd.firstChild;
|
|
5591
5320
|
if (func && func.name === "VariableName" && !PURE_BUILTINS.has(srcSlice(func))) {
|
|
5592
|
-
for (const k of Object.keys(
|
|
5321
|
+
for (const k of Object.keys(env2.vars)) env2.vars[k] = null;
|
|
5593
5322
|
return false;
|
|
5594
5323
|
}
|
|
5595
5324
|
return true;
|
|
5596
5325
|
});
|
|
5597
5326
|
}
|
|
5598
|
-
function processStmt(stmt,
|
|
5327
|
+
function processStmt(stmt, env2, lineStarts, lines, branches) {
|
|
5599
5328
|
const name = stmt.name;
|
|
5600
5329
|
if (name === "AssignStatement") {
|
|
5601
5330
|
const target = stmt.firstChild;
|
|
5602
5331
|
if (target && target.name === "VariableName") {
|
|
5603
5332
|
const valNode = target.nextSibling ? target.nextSibling.nextSibling : null;
|
|
5604
|
-
invalidateEnvOnCalls(stmt,
|
|
5605
|
-
const val = evalExpr(valNode,
|
|
5606
|
-
|
|
5333
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
5334
|
+
const val = evalExpr(valNode, env2);
|
|
5335
|
+
env2.vars[srcSlice(target)] = val;
|
|
5607
5336
|
}
|
|
5608
5337
|
markNodeLines(stmt, lines, lineStarts);
|
|
5609
5338
|
return;
|
|
5610
5339
|
}
|
|
5611
5340
|
if (name === "UpdateStatement") {
|
|
5612
5341
|
const target = stmt.firstChild;
|
|
5613
|
-
if (target && target.name === "VariableName")
|
|
5614
|
-
invalidateEnvOnCalls(stmt,
|
|
5342
|
+
if (target && target.name === "VariableName") env2.vars[srcSlice(target)] = null;
|
|
5343
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
5615
5344
|
markNodeLines(stmt, lines, lineStarts);
|
|
5616
5345
|
return;
|
|
5617
5346
|
}
|
|
5618
5347
|
if (name === "IfStatement") {
|
|
5619
|
-
processIf(stmt,
|
|
5348
|
+
processIf(stmt, env2, lineStarts, lines, branches);
|
|
5620
5349
|
return;
|
|
5621
5350
|
}
|
|
5622
5351
|
if (name === "ForStatement" || name === "WhileStatement") {
|
|
5623
5352
|
markNodeLines(stmt, lines, lineStarts);
|
|
5624
5353
|
for (const body of stmt.getChildren("Body")) {
|
|
5625
|
-
processStmts(bodyStmts(body), { argv:
|
|
5354
|
+
processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5626
5355
|
}
|
|
5627
5356
|
return;
|
|
5628
5357
|
}
|
|
5629
5358
|
if (name === "TryStatement") {
|
|
5630
5359
|
markNodeLines(stmt, lines, lineStarts);
|
|
5631
5360
|
for (const body of stmt.getChildren("Body")) {
|
|
5632
|
-
processStmts(bodyStmts(body), { argv:
|
|
5361
|
+
processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5633
5362
|
}
|
|
5634
5363
|
return;
|
|
5635
5364
|
}
|
|
5636
5365
|
if (name === "WithStatement") {
|
|
5637
5366
|
markNodeLines(stmt, lines, lineStarts);
|
|
5638
5367
|
const body = stmt.getChild("Body");
|
|
5639
|
-
if (body) processStmts(bodyStmts(body), { argv:
|
|
5368
|
+
if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5640
5369
|
return;
|
|
5641
5370
|
}
|
|
5642
5371
|
if (name === "MatchStatement") {
|
|
5643
5372
|
markNodeLines(stmt, lines, lineStarts);
|
|
5644
5373
|
for (const clause of stmt.getChildren("MatchClause")) {
|
|
5645
5374
|
const body = clause.getChild("Body");
|
|
5646
|
-
if (body) processStmts(bodyStmts(body), { argv:
|
|
5375
|
+
if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
|
|
5647
5376
|
}
|
|
5648
5377
|
return;
|
|
5649
5378
|
}
|
|
@@ -5652,7 +5381,7 @@ function processStmt(stmt, env, lineStarts, lines, branches) {
|
|
|
5652
5381
|
return;
|
|
5653
5382
|
}
|
|
5654
5383
|
if (name === "ExpressionStatement") {
|
|
5655
|
-
invalidateEnvOnCalls(stmt,
|
|
5384
|
+
invalidateEnvOnCalls(stmt, env2);
|
|
5656
5385
|
}
|
|
5657
5386
|
markNodeLines(stmt, lines, lineStarts);
|
|
5658
5387
|
}
|
|
@@ -5672,7 +5401,7 @@ function bodyStmts(body) {
|
|
|
5672
5401
|
}
|
|
5673
5402
|
return out;
|
|
5674
5403
|
}
|
|
5675
|
-
function processIf(stmt,
|
|
5404
|
+
function processIf(stmt, env2, lineStarts, lines, branches) {
|
|
5676
5405
|
const sections = [];
|
|
5677
5406
|
let c = stmt.firstChild;
|
|
5678
5407
|
while (c) {
|
|
@@ -5687,17 +5416,17 @@ function processIf(stmt, env, lineStarts, lines, branches) {
|
|
|
5687
5416
|
}
|
|
5688
5417
|
c = c.nextSibling;
|
|
5689
5418
|
}
|
|
5690
|
-
processIfSections(stmt, sections, 0,
|
|
5419
|
+
processIfSections(stmt, sections, 0, env2, lineStarts, lines, branches);
|
|
5691
5420
|
}
|
|
5692
|
-
function processIfSections(_stmt, sections, idx,
|
|
5421
|
+
function processIfSections(_stmt, sections, idx, env2, lineStarts, lines, branches) {
|
|
5693
5422
|
if (idx >= sections.length) return;
|
|
5694
5423
|
const s = sections[idx];
|
|
5695
5424
|
if (s.cond) markNodeLines(s.cond, lines, lineStarts);
|
|
5696
5425
|
if (s.isElse) {
|
|
5697
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5426
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5698
5427
|
return;
|
|
5699
5428
|
}
|
|
5700
|
-
const cond = s.cond ? evalCondition(s.cond,
|
|
5429
|
+
const cond = s.cond ? evalCondition(s.cond, env2) : null;
|
|
5701
5430
|
branches.push({
|
|
5702
5431
|
kind: "if",
|
|
5703
5432
|
label: s.cond ? srcSlice(s.cond) : "",
|
|
@@ -5705,18 +5434,18 @@ function processIfSections(_stmt, sections, idx, env, lineStarts, lines, branche
|
|
|
5705
5434
|
reachable: cond !== false
|
|
5706
5435
|
});
|
|
5707
5436
|
if (cond === true) {
|
|
5708
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5437
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5709
5438
|
return;
|
|
5710
5439
|
}
|
|
5711
5440
|
if (cond === false) {
|
|
5712
|
-
processIfSections(_stmt, sections, idx + 1,
|
|
5441
|
+
processIfSections(_stmt, sections, idx + 1, env2, lineStarts, lines, branches);
|
|
5713
5442
|
return;
|
|
5714
5443
|
}
|
|
5715
|
-
if (s.body) processStmts(bodyStmts(s.body),
|
|
5444
|
+
if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
|
|
5716
5445
|
for (let j = idx + 1; j < sections.length; j++) {
|
|
5717
5446
|
const sj = sections[j];
|
|
5718
5447
|
if (sj.cond) markNodeLines(sj.cond, lines, lineStarts);
|
|
5719
|
-
if (sj.body) processStmts(bodyStmts(sj.body),
|
|
5448
|
+
if (sj.body) processStmts(bodyStmts(sj.body), env2, lineStarts, lines, branches);
|
|
5720
5449
|
}
|
|
5721
5450
|
}
|
|
5722
5451
|
function parseShellString(s) {
|
|
@@ -5839,7 +5568,7 @@ var PythonBackend = class {
|
|
|
5839
5568
|
SOURCE = c;
|
|
5840
5569
|
const sourceLines = c.split("\n");
|
|
5841
5570
|
const lineStarts = buildLineStarts(c);
|
|
5842
|
-
const
|
|
5571
|
+
const env2 = makeEnv(entryArgs || []);
|
|
5843
5572
|
const lines = /* @__PURE__ */ new Set();
|
|
5844
5573
|
const branches = [];
|
|
5845
5574
|
if (sourceLines.length > MAX_LINES) {
|
|
@@ -5866,7 +5595,7 @@ var PythonBackend = class {
|
|
|
5866
5595
|
};
|
|
5867
5596
|
}
|
|
5868
5597
|
const stmts = bodyStmts(tree);
|
|
5869
|
-
processStmts(stmts,
|
|
5598
|
+
processStmts(stmts, env2, lineStarts, lines, branches);
|
|
5870
5599
|
const reachable = reconstruct(sourceLines, lines);
|
|
5871
5600
|
let calls = extractCallsFromTree(tree, lines);
|
|
5872
5601
|
if (calls.length === 0) calls = extractCallsRegex(c);
|
|
@@ -6116,7 +5845,7 @@ function tokenize(content) {
|
|
|
6116
5845
|
return stmts;
|
|
6117
5846
|
}
|
|
6118
5847
|
var EXPR_SPLIT_RE = /(\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$\d+)/;
|
|
6119
|
-
function evalExpr2(text,
|
|
5848
|
+
function evalExpr2(text, env2) {
|
|
6120
5849
|
const isSingleQuoted = text.length >= 2 && text[0] === "'" && text[text.length - 1] === "'";
|
|
6121
5850
|
if (text.length >= 2 && (text[0] === '"' || text[0] === "'") && text[text.length - 1] === text[0]) {
|
|
6122
5851
|
text = text.slice(1, -1);
|
|
@@ -6132,19 +5861,19 @@ function evalExpr2(text, env) {
|
|
|
6132
5861
|
const dm = inner.match(/^([A-Za-z_][A-Za-z0-9_]*|\d+):?-(.*)$/s);
|
|
6133
5862
|
if (dm) {
|
|
6134
5863
|
const key = /^\d+$/.test(dm[1]) ? `$${dm[1]}` : dm[1];
|
|
6135
|
-
const bound =
|
|
5864
|
+
const bound = env2.get(key);
|
|
6136
5865
|
if (bound && !(bound.kind === "concrete" && !bound.value)) {
|
|
6137
5866
|
result.push(bound);
|
|
6138
5867
|
} else {
|
|
6139
|
-
result.push(evalExpr2(dm[2],
|
|
5868
|
+
result.push(evalExpr2(dm[2], env2));
|
|
6140
5869
|
}
|
|
6141
5870
|
continue;
|
|
6142
5871
|
}
|
|
6143
5872
|
if (/^\d+$/.test(inner)) {
|
|
6144
|
-
const v =
|
|
5873
|
+
const v = env2.get(`$${inner}`);
|
|
6145
5874
|
result.push(v || svalSym(`$${inner}`));
|
|
6146
5875
|
} else {
|
|
6147
|
-
const v =
|
|
5876
|
+
const v = env2.get(inner);
|
|
6148
5877
|
result.push(v || svalSym(inner));
|
|
6149
5878
|
}
|
|
6150
5879
|
continue;
|
|
@@ -6153,7 +5882,7 @@ function evalExpr2(text, env) {
|
|
|
6153
5882
|
if (bare) {
|
|
6154
5883
|
const varName = bare[1];
|
|
6155
5884
|
const key = /^\d+$/.test(varName) ? `$${varName}` : varName;
|
|
6156
|
-
const v =
|
|
5885
|
+
const v = env2.get(key);
|
|
6157
5886
|
result.push(v || svalSym(p));
|
|
6158
5887
|
continue;
|
|
6159
5888
|
}
|
|
@@ -6163,13 +5892,13 @@ function evalExpr2(text, env) {
|
|
|
6163
5892
|
return fold(svalExpr(result));
|
|
6164
5893
|
}
|
|
6165
5894
|
function initEnv(entryArgs) {
|
|
6166
|
-
const
|
|
5895
|
+
const env2 = /* @__PURE__ */ new Map();
|
|
6167
5896
|
if (entryArgs && entryArgs.length) {
|
|
6168
|
-
entryArgs.forEach((a, idx) =>
|
|
6169
|
-
|
|
6170
|
-
|
|
5897
|
+
entryArgs.forEach((a, idx) => env2.set(`$${idx + 1}`, svalConc(a)));
|
|
5898
|
+
env2.set("$@", svalConc(entryArgs.join(" ")));
|
|
5899
|
+
env2.set("$*", svalConc(entryArgs.join(" ")));
|
|
6171
5900
|
}
|
|
6172
|
-
return
|
|
5901
|
+
return env2;
|
|
6173
5902
|
}
|
|
6174
5903
|
var HAS_GLOB = /[*?[]/;
|
|
6175
5904
|
function globToRegex(pattern) {
|
|
@@ -6228,12 +5957,12 @@ function evalNumericCompare(op, left, right) {
|
|
|
6228
5957
|
return null;
|
|
6229
5958
|
}
|
|
6230
5959
|
}
|
|
6231
|
-
function resolveVarSide(tok,
|
|
5960
|
+
function resolveVarSide(tok, env2) {
|
|
6232
5961
|
if (!tok.startsWith("$")) return tok;
|
|
6233
|
-
const ev = evalExpr2(tok,
|
|
5962
|
+
const ev = evalExpr2(tok, env2);
|
|
6234
5963
|
return ev.kind === "concrete" ? ev.value : null;
|
|
6235
5964
|
}
|
|
6236
|
-
function evalCondition2(cond,
|
|
5965
|
+
function evalCondition2(cond, env2) {
|
|
6237
5966
|
let inner = cond.trim().replace(/^\[\[?|\]\]?$/g, "").trim();
|
|
6238
5967
|
if (/^-[A-Za-z]\s/.test(inner) || inner.startsWith("-d") || inner.startsWith("-f")) return null;
|
|
6239
5968
|
const innerQ = inner.replace(/"/g, "").replace(/'/g, "");
|
|
@@ -6243,8 +5972,8 @@ function evalCondition2(cond, env) {
|
|
|
6243
5972
|
if (!opm) continue;
|
|
6244
5973
|
const left2 = innerQ.slice(0, opm.index).trim();
|
|
6245
5974
|
const right2 = innerQ.slice(opm.index + opm[0].length).trim();
|
|
6246
|
-
const lv2 = resolveVarSide(left2,
|
|
6247
|
-
const rv2 = resolveVarSide(right2,
|
|
5975
|
+
const lv2 = resolveVarSide(left2, env2);
|
|
5976
|
+
const rv2 = resolveVarSide(right2, env2);
|
|
6248
5977
|
if (lv2 === null || rv2 === null) return null;
|
|
6249
5978
|
return evalNumericCompare(op2, lv2, rv2);
|
|
6250
5979
|
}
|
|
@@ -6255,7 +5984,7 @@ function evalCondition2(cond, env) {
|
|
|
6255
5984
|
let right = sm[3].trim();
|
|
6256
5985
|
if (op === "=~") {
|
|
6257
5986
|
if (right.startsWith("/") && right.endsWith("/")) right = right.slice(1, -1);
|
|
6258
|
-
const lv2 = resolveVarSide(left,
|
|
5987
|
+
const lv2 = resolveVarSide(left, env2);
|
|
6259
5988
|
if (lv2 === null) return null;
|
|
6260
5989
|
try {
|
|
6261
5990
|
return new RegExp(right).test(lv2);
|
|
@@ -6263,8 +5992,8 @@ function evalCondition2(cond, env) {
|
|
|
6263
5992
|
return null;
|
|
6264
5993
|
}
|
|
6265
5994
|
}
|
|
6266
|
-
const lv = resolveVarSide(left,
|
|
6267
|
-
const rv = resolveVarSide(right,
|
|
5995
|
+
const lv = resolveVarSide(left, env2);
|
|
5996
|
+
const rv = resolveVarSide(right, env2);
|
|
6268
5997
|
if (lv === null || rv === null) return null;
|
|
6269
5998
|
if (op === "!=") return lv !== rv;
|
|
6270
5999
|
return lv === rv;
|
|
@@ -6274,7 +6003,7 @@ function symbolicProcess(content, entryArgs) {
|
|
|
6274
6003
|
const stmts = tokenize(content);
|
|
6275
6004
|
if (!stmts.some((s) => s.kind === "case" || s.kind === "if" || s.kind === "assign")) return null;
|
|
6276
6005
|
if (stmts.some((s) => !s.modelable)) return null;
|
|
6277
|
-
const
|
|
6006
|
+
const env2 = initEnv(entryArgs);
|
|
6278
6007
|
let linesBudget = 0;
|
|
6279
6008
|
let branchesBudget = 0;
|
|
6280
6009
|
let modelable = false;
|
|
@@ -6284,9 +6013,9 @@ function symbolicProcess(content, entryArgs) {
|
|
|
6284
6013
|
if (linesBudget > MAX_LINES2) return null;
|
|
6285
6014
|
if (s.kind === "assign") {
|
|
6286
6015
|
modelable = true;
|
|
6287
|
-
if (
|
|
6288
|
-
const v = evalExpr2(s.rhs,
|
|
6289
|
-
|
|
6016
|
+
if (env2.size >= MAX_VARS) return null;
|
|
6017
|
+
const v = evalExpr2(s.rhs, env2);
|
|
6018
|
+
env2.set(s.name, fold(v));
|
|
6290
6019
|
out.push(s.line);
|
|
6291
6020
|
continue;
|
|
6292
6021
|
}
|
|
@@ -6294,7 +6023,7 @@ function symbolicProcess(content, entryArgs) {
|
|
|
6294
6023
|
modelable = true;
|
|
6295
6024
|
branchesBudget += s.branches.length;
|
|
6296
6025
|
if (branchesBudget > MAX_BRANCHES) return null;
|
|
6297
|
-
const selVal = fold(evalExpr2(s.selector,
|
|
6026
|
+
const selVal = fold(evalExpr2(s.selector, env2));
|
|
6298
6027
|
if (selVal.kind !== "concrete") return null;
|
|
6299
6028
|
const concrete = selVal.value || "";
|
|
6300
6029
|
let selected = null;
|
|
@@ -6321,7 +6050,7 @@ esac`);
|
|
|
6321
6050
|
modelable = true;
|
|
6322
6051
|
branchesBudget++;
|
|
6323
6052
|
if (branchesBudget > MAX_BRANCHES) return null;
|
|
6324
|
-
const truth = evalCondition2(s.cond,
|
|
6053
|
+
const truth = evalCondition2(s.cond, env2);
|
|
6325
6054
|
if (truth === null) return null;
|
|
6326
6055
|
const chosen = truth ? s.thenBody : s.elseBody;
|
|
6327
6056
|
if (chosen === null) return null;
|
|
@@ -6798,6 +6527,7 @@ var RecursiveTracker = class {
|
|
|
6798
6527
|
};
|
|
6799
6528
|
|
|
6800
6529
|
// src/engine/script-content-analyzer.ts
|
|
6530
|
+
init_rules_loader();
|
|
6801
6531
|
function emptyChain() {
|
|
6802
6532
|
return {
|
|
6803
6533
|
nodes: [],
|
|
@@ -6814,87 +6544,22 @@ function emptyChain() {
|
|
|
6814
6544
|
error: ""
|
|
6815
6545
|
};
|
|
6816
6546
|
}
|
|
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
|
-
};
|
|
6547
|
+
var SCRIPT_RULES2 = loadScriptRules();
|
|
6548
|
+
if (!SCRIPT_RULES2 || Object.keys(SCRIPT_RULES2).length === 0) {
|
|
6549
|
+
console.error(
|
|
6550
|
+
"[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"
|
|
6551
|
+
);
|
|
6552
|
+
}
|
|
6553
|
+
var SUSPICIOUS_PATTERNS2 = (SCRIPT_RULES2.suspicious_patterns ?? []).map((r) => ({ name: r.name, pattern: compileRuleRegex(r) }));
|
|
6554
|
+
var SCRIPT_EXEC_PATTERNS2 = ((SCRIPT_RULES2.script_exec_patterns ?? {}).chain ?? []).map((r) => compileRuleRegex(r));
|
|
6555
|
+
var KEYWORD_SETS2 = SCRIPT_RULES2.keyword_sets ?? {};
|
|
6556
|
+
var KEYWORD_COMBINATION_RISKS2 = (SCRIPT_RULES2.keyword_combination_risks ?? []).map((r) => ({
|
|
6557
|
+
group1: resolveKeywordSet(r.group1, KEYWORD_SETS2),
|
|
6558
|
+
group2: resolveKeywordSet(r.group2, KEYWORD_SETS2),
|
|
6559
|
+
level: String(r.level),
|
|
6560
|
+
reason: String(r.reason)
|
|
6561
|
+
}));
|
|
6562
|
+
var SINGLE_KEYWORD_RISKS2 = SCRIPT_RULES2.single_keyword_risks ?? {};
|
|
6898
6563
|
var LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
6899
6564
|
function scanScriptForKeywords2(content) {
|
|
6900
6565
|
const matched = [];
|
|
@@ -6911,10 +6576,10 @@ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
|
6911
6576
|
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
6912
6577
|
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
6913
6578
|
}
|
|
6914
|
-
const networkPostKeywords =
|
|
6579
|
+
const networkPostKeywords = new Set(KEYWORD_SETS2.network_post ?? []);
|
|
6915
6580
|
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
6916
6581
|
if (hasNetworkPost) {
|
|
6917
|
-
const envPatterns =
|
|
6582
|
+
const envPatterns = SCRIPT_RULES2.env_exfil_patterns ?? [];
|
|
6918
6583
|
const contentLower = scriptContent.toLowerCase();
|
|
6919
6584
|
for (const p of envPatterns) {
|
|
6920
6585
|
if (contentLower.includes(p.toLowerCase())) {
|
|
@@ -6990,7 +6655,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6990
6655
|
if (looksLikeScriptPath(tok)) {
|
|
6991
6656
|
try {
|
|
6992
6657
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
6993
|
-
if (
|
|
6658
|
+
if (existsSync11(resolved)) {
|
|
6994
6659
|
const stat = statSync4(resolved);
|
|
6995
6660
|
if (stat.isFile()) return resolved;
|
|
6996
6661
|
}
|
|
@@ -7004,7 +6669,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
7004
6669
|
const p = m[1];
|
|
7005
6670
|
try {
|
|
7006
6671
|
const resolved = resolvePath2(p);
|
|
7007
|
-
if (
|
|
6672
|
+
if (existsSync11(resolved)) {
|
|
7008
6673
|
const stat = statSync4(resolved);
|
|
7009
6674
|
if (stat.isFile()) return resolved;
|
|
7010
6675
|
}
|
|
@@ -7020,7 +6685,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
7020
6685
|
let resolved;
|
|
7021
6686
|
try {
|
|
7022
6687
|
resolved = resolvePath2(p);
|
|
7023
|
-
if (
|
|
6688
|
+
if (existsSync11(resolved)) {
|
|
7024
6689
|
const stat = statSync4(resolved);
|
|
7025
6690
|
if (stat.isFile()) return resolved;
|
|
7026
6691
|
}
|
|
@@ -7061,9 +6726,9 @@ function shlexSplit(s) {
|
|
|
7061
6726
|
function resolvePath2(p) {
|
|
7062
6727
|
let expanded = p;
|
|
7063
6728
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
7064
|
-
expanded =
|
|
6729
|
+
expanded = join10(homedir4(), expanded.slice(1));
|
|
7065
6730
|
}
|
|
7066
|
-
return
|
|
6731
|
+
return resolve10(expanded);
|
|
7067
6732
|
}
|
|
7068
6733
|
function scanExtensionPatterns(ext, content) {
|
|
7069
6734
|
const pats = ext?.extra_command_patterns;
|
|
@@ -7184,8 +6849,8 @@ function analyzeScriptContent2(input, ext) {
|
|
|
7184
6849
|
resolvedPath = inline;
|
|
7185
6850
|
isInline = true;
|
|
7186
6851
|
}
|
|
7187
|
-
if (resolvedPath && !
|
|
7188
|
-
resolvedPath =
|
|
6852
|
+
if (resolvedPath && !isAbsolute3(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
6853
|
+
resolvedPath = join10(input.cwd || process.cwd(), resolvedPath);
|
|
7189
6854
|
}
|
|
7190
6855
|
const l1Adapter = (content) => {
|
|
7191
6856
|
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
@@ -7272,7 +6937,7 @@ ${contentPreview}
|
|
|
7272
6937
|
}
|
|
7273
6938
|
|
|
7274
6939
|
// src/engine/llm-analyzer.ts
|
|
7275
|
-
import {
|
|
6940
|
+
import { openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2, constants as fsConstants } from "fs";
|
|
7276
6941
|
import OpenAI from "openai";
|
|
7277
6942
|
var llmCache = /* @__PURE__ */ new Map();
|
|
7278
6943
|
var LLM_CACHE_MAX = 200;
|
|
@@ -7290,7 +6955,15 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7290
6955
|
};
|
|
7291
6956
|
}
|
|
7292
6957
|
const globalConfig = getLLMConfig();
|
|
7293
|
-
|
|
6958
|
+
let perRequest = input.llmConfig;
|
|
6959
|
+
if (perRequest?.apiKey) {
|
|
6960
|
+
const [okUrl, why] = validateLlmBaseUrl(String(perRequest.baseUrl || ""));
|
|
6961
|
+
if (!okUrl) {
|
|
6962
|
+
console.warn("[AgentMoss] \u5FFD\u7565\u4E0D\u88AB\u5141\u8BB8\u7684 per-request llmConfig.baseUrl:", why);
|
|
6963
|
+
perRequest = { ...perRequest, baseUrl: void 0 };
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
const config = perRequest?.apiKey ? { ...globalConfig, ...perRequest } : globalConfig;
|
|
7294
6967
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
7295
6968
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
7296
6969
|
const cached2 = llmCache.get(cacheKey);
|
|
@@ -7319,6 +6992,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7319
6992
|
}
|
|
7320
6993
|
function logPrompt(prompt, aNext, logPath) {
|
|
7321
6994
|
if (!logPath) return;
|
|
6995
|
+
if (!pathWithin(logPath, logRoots())) {
|
|
6996
|
+
console.warn("[AgentMoss] \u5FFD\u7565\u8D8A\u754C\u7684 llm_log_path\uFF08\u4E0D\u5728\u5141\u8BB8\u6839\u76EE\u5F55\u5185\uFF09:", logPath);
|
|
6997
|
+
return;
|
|
6998
|
+
}
|
|
7322
6999
|
try {
|
|
7323
7000
|
const actionType = aNext.actionType || "unknown";
|
|
7324
7001
|
const actionDetail = (aNext.actionDetail || "").slice(0, 100);
|
|
@@ -7326,7 +7003,14 @@ function logPrompt(prompt, aNext, logPath) {
|
|
|
7326
7003
|
${prompt}
|
|
7327
7004
|
--- END PROMPT ---
|
|
7328
7005
|
`;
|
|
7329
|
-
|
|
7006
|
+
let flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_APPEND;
|
|
7007
|
+
if (fsConstants.O_NOFOLLOW) flags |= fsConstants.O_NOFOLLOW;
|
|
7008
|
+
const fd = openSync2(logPath, flags, 384);
|
|
7009
|
+
try {
|
|
7010
|
+
writeSync2(fd, line);
|
|
7011
|
+
} finally {
|
|
7012
|
+
closeSync2(fd);
|
|
7013
|
+
}
|
|
7330
7014
|
} catch (e) {
|
|
7331
7015
|
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
7332
7016
|
}
|
|
@@ -7485,7 +7169,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
7485
7169
|
}
|
|
7486
7170
|
if (attempt < maxRetries) {
|
|
7487
7171
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
7488
|
-
await new Promise((
|
|
7172
|
+
await new Promise((resolve11) => setTimeout(resolve11, retryCfg.retryInterval * 1e3));
|
|
7489
7173
|
} else {
|
|
7490
7174
|
clearTimeout(totalTimeoutTimer);
|
|
7491
7175
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -7508,10 +7192,10 @@ init_esm_shims();
|
|
|
7508
7192
|
|
|
7509
7193
|
// src/brain/rule-store.ts
|
|
7510
7194
|
init_esm_shims();
|
|
7511
|
-
import { existsSync as
|
|
7512
|
-
import { join as
|
|
7513
|
-
import { homedir as
|
|
7514
|
-
var DEFAULT_RULES_PATH =
|
|
7195
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
7196
|
+
import { join as join11 } from "path";
|
|
7197
|
+
import { homedir as homedir5 } from "os";
|
|
7198
|
+
var DEFAULT_RULES_PATH = join11(homedir5(), ".agentmoss", "brain", "rules.json");
|
|
7515
7199
|
var ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
7516
7200
|
var STATUS_DRAFT = "draft";
|
|
7517
7201
|
var STATUS_APPROVED = "approved";
|
|
@@ -7540,11 +7224,11 @@ var BrainRuleStore = class {
|
|
|
7540
7224
|
this.data = this.load();
|
|
7541
7225
|
}
|
|
7542
7226
|
load() {
|
|
7543
|
-
if (!
|
|
7227
|
+
if (!existsSync13(this.path)) {
|
|
7544
7228
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
7545
7229
|
}
|
|
7546
7230
|
try {
|
|
7547
|
-
const raw = JSON.parse(
|
|
7231
|
+
const raw = JSON.parse(readFileSync13(this.path, "utf-8"));
|
|
7548
7232
|
return {
|
|
7549
7233
|
version: raw.version || 1,
|
|
7550
7234
|
rules: raw.rules || [],
|
|
@@ -7556,7 +7240,7 @@ var BrainRuleStore = class {
|
|
|
7556
7240
|
}
|
|
7557
7241
|
save() {
|
|
7558
7242
|
const dir = this.path.substring(0, this.path.lastIndexOf("/"));
|
|
7559
|
-
if (dir)
|
|
7243
|
+
if (dir) mkdirSync4(dir, { recursive: true });
|
|
7560
7244
|
writeFileSync3(this.path, JSON.stringify(this.data, null, 2), "utf-8");
|
|
7561
7245
|
}
|
|
7562
7246
|
addRule(rule) {
|
|
@@ -8124,11 +7808,11 @@ function parseReview(content) {
|
|
|
8124
7808
|
|
|
8125
7809
|
// src/brain/skill-manager.ts
|
|
8126
7810
|
init_esm_shims();
|
|
8127
|
-
import { existsSync as
|
|
8128
|
-
import { homedir as
|
|
8129
|
-
import { join as
|
|
7811
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync14, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
7812
|
+
import { homedir as homedir6 } from "os";
|
|
7813
|
+
import { join as join12 } from "path";
|
|
8130
7814
|
import { randomUUID } from "crypto";
|
|
8131
|
-
var DEFAULT_SKILL_META_PATH =
|
|
7815
|
+
var DEFAULT_SKILL_META_PATH = join12(homedir6(), ".agentmoss", "brain", "skill_meta.json");
|
|
8132
7816
|
var ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8133
7817
|
function getMetaPath(override) {
|
|
8134
7818
|
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
@@ -8138,7 +7822,7 @@ var BrainSkillManager = class {
|
|
|
8138
7822
|
this.skillsDir = skillsDir;
|
|
8139
7823
|
this.metaPath = getMetaPath(metaPath);
|
|
8140
7824
|
try {
|
|
8141
|
-
|
|
7825
|
+
mkdirSync5(join12(this.metaPath, ".."), { recursive: true });
|
|
8142
7826
|
} catch {
|
|
8143
7827
|
}
|
|
8144
7828
|
this.load();
|
|
@@ -8148,9 +7832,9 @@ var BrainSkillManager = class {
|
|
|
8148
7832
|
skills = [];
|
|
8149
7833
|
metaPath;
|
|
8150
7834
|
load() {
|
|
8151
|
-
if (!
|
|
7835
|
+
if (!existsSync14(this.metaPath)) return;
|
|
8152
7836
|
try {
|
|
8153
|
-
const data = JSON.parse(
|
|
7837
|
+
const data = JSON.parse(readFileSync14(this.metaPath, "utf-8"));
|
|
8154
7838
|
this.skills = data.skills || [];
|
|
8155
7839
|
} catch {
|
|
8156
7840
|
this.skills = [];
|
|
@@ -8173,7 +7857,7 @@ var BrainSkillManager = class {
|
|
|
8173
7857
|
let changed = false;
|
|
8174
7858
|
for (const sk of this.skills) {
|
|
8175
7859
|
if (sk.status !== "active") continue;
|
|
8176
|
-
if (!
|
|
7860
|
+
if (!existsSync14(join12(this.skillsDir, `${sk.name}.md`))) {
|
|
8177
7861
|
sk.status = "stale";
|
|
8178
7862
|
changed = true;
|
|
8179
7863
|
}
|
|
@@ -8184,9 +7868,9 @@ var BrainSkillManager = class {
|
|
|
8184
7868
|
addSkill(skill) {
|
|
8185
7869
|
const id = skill.id || randomUUID();
|
|
8186
7870
|
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8187
|
-
if (!
|
|
8188
|
-
const mdPath =
|
|
8189
|
-
if (
|
|
7871
|
+
if (!existsSync14(this.skillsDir)) return "";
|
|
7872
|
+
const mdPath = join12(this.skillsDir, `${name}.md`);
|
|
7873
|
+
if (existsSync14(mdPath)) return "";
|
|
8190
7874
|
try {
|
|
8191
7875
|
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8192
7876
|
} catch {
|
|
@@ -8235,12 +7919,12 @@ var BrainSkillManager = class {
|
|
|
8235
7919
|
const out = [];
|
|
8236
7920
|
for (const sk of this.skills) {
|
|
8237
7921
|
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8238
|
-
const mdPath =
|
|
8239
|
-
if (!
|
|
7922
|
+
const mdPath = join12(this.skillsDir, `${sk.name}.md`);
|
|
7923
|
+
if (!existsSync14(mdPath)) continue;
|
|
8240
7924
|
out.push({
|
|
8241
7925
|
name: sk.name,
|
|
8242
7926
|
category: sk.category || "",
|
|
8243
|
-
content:
|
|
7927
|
+
content: readFileSync14(mdPath, "utf-8"),
|
|
8244
7928
|
keywords: sk.keywords || []
|
|
8245
7929
|
});
|
|
8246
7930
|
}
|
|
@@ -8321,9 +8005,9 @@ var BrainCurator = class {
|
|
|
8321
8005
|
// src/brain/contributor.ts
|
|
8322
8006
|
init_esm_shims();
|
|
8323
8007
|
init_asset_dir();
|
|
8324
|
-
import { dirname as
|
|
8008
|
+
import { dirname as dirname10 } from "path";
|
|
8325
8009
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8326
|
-
var __dirname8 =
|
|
8010
|
+
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
8327
8011
|
|
|
8328
8012
|
// src/brain/index.ts
|
|
8329
8013
|
function checkBrainRules(store, opts) {
|
|
@@ -8387,6 +8071,7 @@ init_rules_loader();
|
|
|
8387
8071
|
|
|
8388
8072
|
// src/engine/formal-engine/decode.ts
|
|
8389
8073
|
init_esm_shims();
|
|
8074
|
+
init_rules_loader();
|
|
8390
8075
|
var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
8391
8076
|
function isValidBase64Token(tok) {
|
|
8392
8077
|
if (tok.length < 8) return false;
|
|
@@ -8421,7 +8106,7 @@ function decodeBase64Literals(text) {
|
|
|
8421
8106
|
return out;
|
|
8422
8107
|
}
|
|
8423
8108
|
var QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
8424
|
-
var OUTPUT_CMDS =
|
|
8109
|
+
var OUTPUT_CMDS = new Set(loadFormalRules().output_commands ?? []);
|
|
8425
8110
|
function stripStringLiterals(text) {
|
|
8426
8111
|
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
8427
8112
|
if (!m) return text;
|
|
@@ -8675,7 +8360,7 @@ function markIntegrity(actionDetail) {
|
|
|
8675
8360
|
}
|
|
8676
8361
|
return { integrity: "medium", isLowSource: false };
|
|
8677
8362
|
}
|
|
8678
|
-
function initNode(sessionId, seq, actionType, actionDetail) {
|
|
8363
|
+
function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
|
|
8679
8364
|
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
8680
8365
|
return {
|
|
8681
8366
|
sessionId,
|
|
@@ -8685,7 +8370,8 @@ function initNode(sessionId, seq, actionType, actionDetail) {
|
|
|
8685
8370
|
stage: markStage(actionDetail),
|
|
8686
8371
|
resources: extractResources(actionDetail),
|
|
8687
8372
|
integrity,
|
|
8688
|
-
isLowSource
|
|
8373
|
+
isLowSource,
|
|
8374
|
+
timestamp: timestamp || void 0
|
|
8689
8375
|
};
|
|
8690
8376
|
}
|
|
8691
8377
|
function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
@@ -8695,13 +8381,21 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
|
8695
8381
|
const details = [];
|
|
8696
8382
|
for (const a of actionHistory) {
|
|
8697
8383
|
const ad = String(a.actionDetail || "");
|
|
8698
|
-
if (ad) details.push({
|
|
8384
|
+
if (ad) details.push({
|
|
8385
|
+
actionType: String(a.actionType || ""),
|
|
8386
|
+
actionDetail: ad,
|
|
8387
|
+
calledAt: a.calledAt || void 0
|
|
8388
|
+
});
|
|
8699
8389
|
}
|
|
8700
8390
|
if (aNext) {
|
|
8701
|
-
details.push({
|
|
8391
|
+
details.push({
|
|
8392
|
+
actionType: String(aNext.actionType || ""),
|
|
8393
|
+
actionDetail: String(aNext.actionDetail || ""),
|
|
8394
|
+
calledAt: aNext.calledAt || void 0
|
|
8395
|
+
});
|
|
8702
8396
|
}
|
|
8703
8397
|
details.forEach((d, seq) => {
|
|
8704
|
-
const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
|
|
8398
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
|
|
8705
8399
|
nodes.push(node);
|
|
8706
8400
|
if (node.stage) stageFlags.add(node.stage);
|
|
8707
8401
|
});
|
|
@@ -8727,7 +8421,8 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
8727
8421
|
last.sessionId,
|
|
8728
8422
|
last.nodes.length,
|
|
8729
8423
|
String(aNext.actionType || ""),
|
|
8730
|
-
String(aNext.actionDetail || "")
|
|
8424
|
+
String(aNext.actionDetail || ""),
|
|
8425
|
+
aNext.calledAt || void 0
|
|
8731
8426
|
));
|
|
8732
8427
|
if (last.nodes.length > 1) {
|
|
8733
8428
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
@@ -8775,6 +8470,27 @@ function allNodesFlat(mg) {
|
|
|
8775
8470
|
// src/engine/formal-engine/properties.ts
|
|
8776
8471
|
init_esm_shims();
|
|
8777
8472
|
init_rules_loader();
|
|
8473
|
+
function parseTs(ts) {
|
|
8474
|
+
if (!ts) return null;
|
|
8475
|
+
const t = Date.parse(String(ts).trim());
|
|
8476
|
+
return Number.isFinite(t) ? t : null;
|
|
8477
|
+
}
|
|
8478
|
+
function privescExfilWindowSec() {
|
|
8479
|
+
const raw = (process.env.AGENT_MOSS_PRIVESC_EXFIL_WINDOW_SEC || "").trim();
|
|
8480
|
+
if (!raw) return 1800;
|
|
8481
|
+
const n = parseInt(raw, 10);
|
|
8482
|
+
return Number.isFinite(n) ? Math.max(0, n) : 1800;
|
|
8483
|
+
}
|
|
8484
|
+
function privescInWindow(nodes, indexes, exfilIdx, windowSec) {
|
|
8485
|
+
const before = indexes.filter((i) => i < exfilIdx);
|
|
8486
|
+
if (windowSec <= 0) return before;
|
|
8487
|
+
const exfilTs = parseTs(nodes[exfilIdx].timestamp);
|
|
8488
|
+
if (exfilTs === null) return before;
|
|
8489
|
+
return before.filter((i) => {
|
|
8490
|
+
const ts = parseTs(nodes[i].timestamp);
|
|
8491
|
+
return ts === null || exfilTs - ts <= windowSec * 1e3;
|
|
8492
|
+
});
|
|
8493
|
+
}
|
|
8778
8494
|
var CROSS_SESSION_CHAINS = null;
|
|
8779
8495
|
function loadCrossSessionChains() {
|
|
8780
8496
|
if (CROSS_SESSION_CHAINS) return CROSS_SESSION_CHAINS;
|
|
@@ -8906,8 +8622,9 @@ function extractInlineShell(s) {
|
|
|
8906
8622
|
}
|
|
8907
8623
|
return out;
|
|
8908
8624
|
}
|
|
8909
|
-
var
|
|
8910
|
-
var
|
|
8625
|
+
var FORMAL_RULES = loadFormalRules();
|
|
8626
|
+
var PKG_MGR = new Set(FORMAL_RULES.privesc?.package_managers ?? []);
|
|
8627
|
+
var SHELLS = new Set(FORMAL_RULES.privesc?.bare_shells ?? []);
|
|
8911
8628
|
function containsPrivesc(shell) {
|
|
8912
8629
|
if (!shell) return false;
|
|
8913
8630
|
for (const seg of shell.split(/\&\&|\|\||;|\n/)) {
|
|
@@ -8976,6 +8693,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8976
8693
|
const nodes = allNodes(graph);
|
|
8977
8694
|
if (nodes.length < 2) return violations;
|
|
8978
8695
|
const witness = graph.witnessIdx;
|
|
8696
|
+
const windowSec = privescExfilWindowSec();
|
|
8979
8697
|
if (witness !== void 0) {
|
|
8980
8698
|
const evIdx = [];
|
|
8981
8699
|
nodes.forEach((n, i) => {
|
|
@@ -8983,15 +8701,17 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8983
8701
|
});
|
|
8984
8702
|
if (evIdx.length === 0) return violations;
|
|
8985
8703
|
const firstEv = Math.min(...evIdx);
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
if (isPrivescNode(
|
|
8989
|
-
}
|
|
8990
|
-
|
|
8704
|
+
const privescIdx = [];
|
|
8705
|
+
nodes.forEach((n, i) => {
|
|
8706
|
+
if (isPrivescNode(n)) privescIdx.push(i);
|
|
8707
|
+
});
|
|
8708
|
+
const candidates = privescInWindow(nodes, privescIdx, firstEv, windowSec);
|
|
8709
|
+
if (candidates.length > 0) {
|
|
8710
|
+
const pre = nodes[candidates[candidates.length - 1]];
|
|
8991
8711
|
const post = nodes[firstEv];
|
|
8992
8712
|
return [{
|
|
8993
8713
|
propertyId: "no-exfil-after-privesc",
|
|
8994
|
-
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\
|
|
8714
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
8995
8715
|
riskLevel: "critical",
|
|
8996
8716
|
path: [
|
|
8997
8717
|
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
@@ -9004,18 +8724,18 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
9004
8724
|
for (let i = 0; i < nodes.length; i++) {
|
|
9005
8725
|
if (!isPrivescNode(nodes[i])) continue;
|
|
9006
8726
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
9007
|
-
if (isExfilNode(nodes[j]))
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
]
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
8727
|
+
if (!isExfilNode(nodes[j])) continue;
|
|
8728
|
+
if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
|
|
8729
|
+
violations.push({
|
|
8730
|
+
propertyId: "no-exfil-after-privesc",
|
|
8731
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
8732
|
+
riskLevel: "critical",
|
|
8733
|
+
path: [
|
|
8734
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
8735
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
8736
|
+
]
|
|
8737
|
+
});
|
|
8738
|
+
return violations;
|
|
9019
8739
|
}
|
|
9020
8740
|
}
|
|
9021
8741
|
return violations;
|
|
@@ -9080,7 +8800,7 @@ function lookupToolClass(actionType) {
|
|
|
9080
8800
|
return entry.actionClass;
|
|
9081
8801
|
}
|
|
9082
8802
|
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 =
|
|
8803
|
+
var STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
|
|
9084
8804
|
function isComputeNode(n) {
|
|
9085
8805
|
if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
|
|
9086
8806
|
const cls = lookupToolClass(n.actionType);
|
|
@@ -9182,6 +8902,11 @@ function checkCrossSessionReachability(graph) {
|
|
|
9182
8902
|
}
|
|
9183
8903
|
return violations;
|
|
9184
8904
|
}
|
|
8905
|
+
var DATA_SOURCE_READ_RE = /\b(?:cat|head|tail|less|more|sed|awk|grep|strings|base64|xxd|od|dd)\b|\b(?:open|read_text|read_bytes)\s*\(/i;
|
|
8906
|
+
function isDataSourceNode(n) {
|
|
8907
|
+
if (n.stage === "CRED" || n.isLowSource) return true;
|
|
8908
|
+
return DATA_SOURCE_READ_RE.test(n.actionDetail);
|
|
8909
|
+
}
|
|
9185
8910
|
function checkSensitiveDataAggregation(graph) {
|
|
9186
8911
|
const violations = [];
|
|
9187
8912
|
const nodes = allNodes(graph);
|
|
@@ -9201,6 +8926,7 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9201
8926
|
for (const r of n.resources) {
|
|
9202
8927
|
for (const j of resIndex.get(r) || []) {
|
|
9203
8928
|
if (j === exfilIdx) continue;
|
|
8929
|
+
if (!isDataSourceNode(nodes[j])) continue;
|
|
9204
8930
|
srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
|
|
9205
8931
|
}
|
|
9206
8932
|
}
|
|
@@ -9306,25 +9032,7 @@ import * as path4 from "path";
|
|
|
9306
9032
|
import { createHash } from "crypto";
|
|
9307
9033
|
|
|
9308
9034
|
// 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
9035
|
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
9036
|
function whitelistTier(command, fullySafe, readonlySensitive) {
|
|
9329
9037
|
const segments = splitCommandSegments(command);
|
|
9330
9038
|
if (segments.length === 0) return "";
|
|
@@ -9481,7 +9189,7 @@ async function runAnalysis(input) {
|
|
|
9481
9189
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
9482
9190
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
9483
9191
|
const lowerDetail = rawDetail.toLowerCase();
|
|
9484
|
-
if (
|
|
9192
|
+
if (getPlatformTraits(input.osType).safeActionTypes.has(actionType)) {
|
|
9485
9193
|
return {
|
|
9486
9194
|
allowed: true,
|
|
9487
9195
|
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
@@ -9524,7 +9232,7 @@ async function runAnalysis(input) {
|
|
|
9524
9232
|
violatedLayers: []
|
|
9525
9233
|
};
|
|
9526
9234
|
}
|
|
9527
|
-
let skipLLM =
|
|
9235
|
+
let skipLLM = getPlatformTraits(input.osType).readonlySensitiveActionTypes.has(actionType);
|
|
9528
9236
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
9529
9237
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
9530
9238
|
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|