@kenhkl/agent-moss 0.10.5 → 0.11.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 +472 -82
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.js +463 -73
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/user_rules.json +77 -0
- package/dist/server.js +467 -77
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -56,11 +56,13 @@ __export(config_exports, {
|
|
|
56
56
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
57
57
|
isLLMAvailable: () => isLLMAvailable,
|
|
58
58
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
59
|
+
probeLLMConnection: () => probeLLMConnection,
|
|
59
60
|
updateLLMConfig: () => updateLLMConfig
|
|
60
61
|
});
|
|
61
62
|
import fs from "fs";
|
|
62
63
|
import path2 from "path";
|
|
63
64
|
import os from "os";
|
|
65
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
64
66
|
function loadConfigFile(filePath) {
|
|
65
67
|
try {
|
|
66
68
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
@@ -123,16 +125,38 @@ function getTimeoutConfig() {
|
|
|
123
125
|
function getRetryConfig() {
|
|
124
126
|
return retryConfig;
|
|
125
127
|
}
|
|
128
|
+
function wildcardToRegExp(pattern) {
|
|
129
|
+
return pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
130
|
+
}
|
|
126
131
|
function loadCustomRules() {
|
|
132
|
+
const rules = [];
|
|
127
133
|
try {
|
|
128
134
|
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
if (raw) {
|
|
136
|
+
const parsed = JSON.parse(raw);
|
|
137
|
+
if (Array.isArray(parsed)) {
|
|
138
|
+
for (const r of parsed) {
|
|
139
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
133
143
|
} catch {
|
|
134
|
-
return [];
|
|
135
144
|
}
|
|
145
|
+
for (const rulesPath of USER_RULES_PATHS) {
|
|
146
|
+
try {
|
|
147
|
+
if (!fs.existsSync(rulesPath)) continue;
|
|
148
|
+
const data = JSON.parse(fs.readFileSync(rulesPath, "utf-8"));
|
|
149
|
+
const actions = Array.isArray(data?.sensitive_actions) ? data.sensitive_actions : [];
|
|
150
|
+
for (const r of actions) {
|
|
151
|
+
const p = r?.pattern;
|
|
152
|
+
if (!p) continue;
|
|
153
|
+
rules.push({ pattern: wildcardToRegExp(p), action: "Deny", severity: "high" });
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return rules;
|
|
136
160
|
}
|
|
137
161
|
function getLLMConfig() {
|
|
138
162
|
return llmConfig;
|
|
@@ -147,6 +171,30 @@ function isLLMAvailable() {
|
|
|
147
171
|
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
148
172
|
return llmConfig.apiKey.length > 0;
|
|
149
173
|
}
|
|
174
|
+
async function probeLLMConnection(timeoutMs = 8e3) {
|
|
175
|
+
if (!isLLMAvailable()) return false;
|
|
176
|
+
const OpenAI2 = (await import("openai")).default;
|
|
177
|
+
const config = getLLMConfig();
|
|
178
|
+
const provider = detectProvider(config.baseUrl, config.provider);
|
|
179
|
+
const extraHeaders = getProviderHeaders(provider);
|
|
180
|
+
const client = new OpenAI2({
|
|
181
|
+
baseURL: config.baseUrl,
|
|
182
|
+
apiKey: config.apiKey,
|
|
183
|
+
timeout: timeoutMs,
|
|
184
|
+
maxRetries: 0,
|
|
185
|
+
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
const resp = await client.chat.completions.create({
|
|
189
|
+
model: config.model,
|
|
190
|
+
messages: [{ role: "user", content: "ping" }],
|
|
191
|
+
max_tokens: 1
|
|
192
|
+
});
|
|
193
|
+
return !!(resp && resp.choices && resp.choices[0]);
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
150
198
|
function getLLMFailMode() {
|
|
151
199
|
const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
|
|
152
200
|
return val === "fail_closed" ? "fail_closed" : "fail_open";
|
|
@@ -190,7 +238,7 @@ function isHeuristicEnabled() {
|
|
|
190
238
|
function isLogicRulesEnabled() {
|
|
191
239
|
return process.env.AGENT_MOSS_DISABLE_LOGIC_RULES !== "1";
|
|
192
240
|
}
|
|
193
|
-
var timeoutConfig, retryConfig, llmConfig, customRules, PROVIDER_HEADERS;
|
|
241
|
+
var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
194
242
|
var init_config = __esm({
|
|
195
243
|
"src/config.ts"() {
|
|
196
244
|
"use strict";
|
|
@@ -212,6 +260,21 @@ var init_config = __esm({
|
|
|
212
260
|
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
213
261
|
};
|
|
214
262
|
})();
|
|
263
|
+
__configModuleDir = (() => {
|
|
264
|
+
try {
|
|
265
|
+
return path2.dirname(fileURLToPath2(import.meta.url));
|
|
266
|
+
} catch {
|
|
267
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
268
|
+
}
|
|
269
|
+
})();
|
|
270
|
+
USER_RULES_PATHS = [
|
|
271
|
+
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
272
|
+
// 源在 ts/src/rules(一般不存在)
|
|
273
|
+
path2.join(__configModuleDir, "..", "rules", "user_rules.json"),
|
|
274
|
+
// dist: dist/engine/../rules = dist/rules ✅
|
|
275
|
+
path2.join(__configModuleDir, "..", "..", "agent_moss", "rules", "user_rules.json")
|
|
276
|
+
// dev src: ts/src/../../agent_moss
|
|
277
|
+
];
|
|
215
278
|
customRules = loadCustomRules();
|
|
216
279
|
PROVIDER_HEADERS = {
|
|
217
280
|
"openrouter": {
|
|
@@ -228,7 +291,17 @@ var init_config = __esm({
|
|
|
228
291
|
// src/engine/inline-analyzer.ts
|
|
229
292
|
function isLiteralTextCommand(command) {
|
|
230
293
|
ECHO_CMD_RE.lastIndex = 0;
|
|
231
|
-
|
|
294
|
+
if (!ECHO_CMD_RE.test(command)) return false;
|
|
295
|
+
if (command.includes(">")) {
|
|
296
|
+
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
297
|
+
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
298
|
+
d = d.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
299
|
+
d = d.replace(/'[^']*'|"[^"]*"/g, "");
|
|
300
|
+
if (/>{1,2}\s*(?!&)\S/.test(d)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return true;
|
|
232
305
|
}
|
|
233
306
|
function extractInlineCode(command) {
|
|
234
307
|
const parts = [];
|
|
@@ -305,7 +378,7 @@ function analyzeInlineScript(command) {
|
|
|
305
378
|
riskType: "file_access"
|
|
306
379
|
};
|
|
307
380
|
}
|
|
308
|
-
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
381
|
+
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SHELL_RC_FRAGMENTS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
309
382
|
var init_inline_analyzer = __esm({
|
|
310
383
|
"src/engine/inline-analyzer.ts"() {
|
|
311
384
|
"use strict";
|
|
@@ -375,6 +448,13 @@ var init_inline_analyzer = __esm({
|
|
|
375
448
|
// xargs bash / xargs sh
|
|
376
449
|
/\|\s*xargs\s+(?:bash|sh|zsh)/gi
|
|
377
450
|
];
|
|
451
|
+
SHELL_RC_FRAGMENTS = [
|
|
452
|
+
".bashrc",
|
|
453
|
+
".zshrc",
|
|
454
|
+
".profile",
|
|
455
|
+
".bash_profile",
|
|
456
|
+
".zprofile"
|
|
457
|
+
];
|
|
378
458
|
SENSITIVE_PATH_FRAGMENTS = [
|
|
379
459
|
"/etc/shadow",
|
|
380
460
|
"/etc/gshadow",
|
|
@@ -391,9 +471,10 @@ var init_inline_analyzer = __esm({
|
|
|
391
471
|
"/dev/kmsg",
|
|
392
472
|
".ssh/id_rsa",
|
|
393
473
|
".ssh/id_ed25519",
|
|
394
|
-
".ssh/authorized_keys"
|
|
474
|
+
".ssh/authorized_keys",
|
|
475
|
+
...SHELL_RC_FRAGMENTS
|
|
395
476
|
];
|
|
396
|
-
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "i");
|
|
477
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(?:\\S*/)?(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?:\\s|$)`, "i");
|
|
397
478
|
DIR_TRAVERSAL_KEYWORDS = [
|
|
398
479
|
// Python 目录遍历
|
|
399
480
|
"os.listdir",
|
|
@@ -416,11 +497,34 @@ function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWOR
|
|
|
416
497
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
417
498
|
if (WRITE_COMMAND_RES.some((rx) => rx.test(actionDetail))) return true;
|
|
418
499
|
if (/\bsed\s+-[a-zA-Z]*i/i.test(actionDetail)) return true;
|
|
500
|
+
for (const seg of actionDetail.split(/&&|\|\||;|\n/)) {
|
|
501
|
+
const trimmed = seg.trim();
|
|
502
|
+
if (!trimmed) continue;
|
|
503
|
+
const parts = trimmed.split(/\s+/);
|
|
504
|
+
const fw = parts[0]?.toLowerCase() || "";
|
|
505
|
+
if (fw === "wget" || fw === "curl") {
|
|
506
|
+
const isStdout = /\s(?:-O|-o)-(?:\s|$)/.test(trimmed) || /\s--output-document=-(?:\s|$)/i.test(trimmed) || /\s--output=-(?:\s|$)/i.test(trimmed);
|
|
507
|
+
if (isStdout) continue;
|
|
508
|
+
const isDevnull = /\s(?:-O|--output-document|--output)\s*\/dev\/null(?:\s|$)/i.test(trimmed) || /\s(?:-O|--output-document|--output)=[/]?dev\/null(?:\s|$)/i.test(trimmed);
|
|
509
|
+
if (isDevnull) continue;
|
|
510
|
+
if (/\s(?:-O|--output-document|--output)(?:=|\s|$)/i.test(trimmed) || /\s-O\S/.test(trimmed)) {
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (fw === "rsync" && !/--list-only|--dry-run/i.test(trimmed) && !/(^|[^a-zA-Z])-n([^a-zA-Z]|$)/.test(trimmed)) {
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
419
518
|
if (actionDetail.includes(">")) {
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
519
|
+
const detailWithoutCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
520
|
+
const detailWithoutDevnull = detailWithoutCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
521
|
+
const detailUnquotedRedirect = detailWithoutDevnull.replace(
|
|
522
|
+
/>{1,2}\s*(['"])([~./][^'"]+)\1/g,
|
|
523
|
+
(_m, _q, p) => ">" + p
|
|
524
|
+
);
|
|
525
|
+
const detailWithoutQuotes = detailUnquotedRedirect.replace(/'[^']*'|"[^"]*"/g, "");
|
|
526
|
+
if (/>{1,2}\s*(?!&)\S/.test(detailWithoutQuotes)) {
|
|
527
|
+
return true;
|
|
424
528
|
}
|
|
425
529
|
}
|
|
426
530
|
return false;
|
|
@@ -444,27 +548,31 @@ function runLogicCheck(input) {
|
|
|
444
548
|
if (rbwResult) return rbwResult;
|
|
445
549
|
if (input.promptSession && input.promptSession.trim()) {
|
|
446
550
|
if (!isExcludedType) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
551
|
+
if (INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
552
|
+
} else {
|
|
553
|
+
const promptLower = input.promptSession.toLowerCase();
|
|
554
|
+
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
555
|
+
const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
|
|
556
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
557
|
+
if (intentMatched && dangerousMatched) {
|
|
558
|
+
return {
|
|
559
|
+
allowed: false,
|
|
560
|
+
reason: pattern.reason,
|
|
561
|
+
riskLevel: "high",
|
|
562
|
+
riskType: "intent_deviation",
|
|
563
|
+
confidence: 85,
|
|
564
|
+
source: "logic_rule",
|
|
565
|
+
violatedLayers: ["2"]
|
|
566
|
+
};
|
|
567
|
+
}
|
|
461
568
|
}
|
|
462
569
|
}
|
|
463
570
|
}
|
|
464
571
|
}
|
|
465
572
|
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
|
|
466
573
|
if (indirectResult) return indirectResult;
|
|
467
|
-
if (
|
|
574
|
+
if (INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
575
|
+
} else if (containsPrivilegeEscalation(actionDetail)) {
|
|
468
576
|
const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
|
|
469
577
|
const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
|
|
470
578
|
if (!isAdminCmd) {
|
|
@@ -494,6 +602,9 @@ function extractFilePaths(text) {
|
|
|
494
602
|
const paths = [];
|
|
495
603
|
const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
|
|
496
604
|
if (unixPaths) paths.push(...unixPaths.map((p) => p.trim()));
|
|
605
|
+
const textNoQuotes = text.replace(/'[^']*'|"[^"]*"/g, "");
|
|
606
|
+
const kvPaths = textNoQuotes.match(/(?:=)(\/[^\s;|&><'"]+)/g);
|
|
607
|
+
if (kvPaths) paths.push(...kvPaths.map((p) => p.replace(/^=/, "")));
|
|
497
608
|
const quotedPaths = text.match(/["']\/([^"']+)["']/g);
|
|
498
609
|
if (quotedPaths) paths.push(...quotedPaths.map((p) => p.replace(/["']/g, "")));
|
|
499
610
|
return [...new Set(paths)];
|
|
@@ -504,8 +615,74 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
504
615
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
505
616
|
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail) : WRITE_KEYWORDS.some((kw) => actionType.includes(kw));
|
|
506
617
|
if (!isWrite) return null;
|
|
507
|
-
const
|
|
508
|
-
if (
|
|
618
|
+
const WRITE_CMDS = /* @__PURE__ */ new Set(["cp", "mv", "install", "tee", "dd", "truncate", "mkfs", "fdisk", "parted"]);
|
|
619
|
+
if (isBashLike) {
|
|
620
|
+
const detailNoCompare = actionDetail.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
621
|
+
const detailNoDevnull = detailNoCompare.replace(DEVNULL_REDIRECT_RE, "");
|
|
622
|
+
const detailUnquotedRedirect = detailNoDevnull.replace(
|
|
623
|
+
/>{1,2}\s*(['"])([~./][^'"]+)\1/g,
|
|
624
|
+
(_m, _q, p) => ">" + p
|
|
625
|
+
);
|
|
626
|
+
const detailNoQuotes = detailUnquotedRedirect.replace(/'[^']*'|"[^"]*"/g, "");
|
|
627
|
+
const hasRedirectWrite = />{1,2}\s*(?!&)\S/.test(detailNoQuotes);
|
|
628
|
+
let hasWriteSeg = hasRedirectWrite;
|
|
629
|
+
if (!hasWriteSeg) {
|
|
630
|
+
for (const seg of actionDetail.split(/&&|\|\||;|\n/)) {
|
|
631
|
+
const trimmed = seg.trim();
|
|
632
|
+
if (!trimmed) continue;
|
|
633
|
+
const fw = trimmed.split(/\s+/)[0]?.toLowerCase() || "";
|
|
634
|
+
if (WRITE_CMDS.has(fw) || /\bsed\s+-[a-zA-Z]*i/i.test(trimmed)) {
|
|
635
|
+
hasWriteSeg = true;
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
if ((fw === "wget" || fw === "curl") && (/\s(?:-O|--output-document|--output)(?:=|\s|$)/i.test(trimmed) || /\s-O\S/.test(trimmed))) {
|
|
639
|
+
hasWriteSeg = true;
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
if (fw === "rsync" && !/--list-only|--dry-run/i.test(trimmed) && !/(^|[^a-zA-Z])-n([^a-zA-Z]|$)/.test(trimmed)) {
|
|
643
|
+
hasWriteSeg = true;
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (!hasWriteSeg) return null;
|
|
649
|
+
}
|
|
650
|
+
const SOURCE_ARG_CMDS = /* @__PURE__ */ new Set(["cp", "mv", "install", "rsync"]);
|
|
651
|
+
const REDIRECT_TARGET_RE = new RegExp(`>{1,2}\\s*(?!&)(?:'(/[^']+)'|"(/[^\\"]+)"|(/[^\\s;|&><'\\"]+))`);
|
|
652
|
+
const targetPaths = [];
|
|
653
|
+
for (const seg of actionDetail.split(/&&|\|\||;|\n/)) {
|
|
654
|
+
const trimmed = seg.trim();
|
|
655
|
+
if (!trimmed) continue;
|
|
656
|
+
const firstWord = trimmed.split(/\s+/)[0]?.toLowerCase() || "";
|
|
657
|
+
if (firstWord === "cd") {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (READ_ONLY_COMMANDS.has(firstWord)) {
|
|
661
|
+
const segUnquoted = trimmed.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
662
|
+
const segNoDevnull = segUnquoted.replace(DEVNULL_REDIRECT_RE, "");
|
|
663
|
+
const segNoQuotes = segNoDevnull.replace(/'[^']*'|"[^"]*"/g, "");
|
|
664
|
+
const m = segNoQuotes.match(REDIRECT_TARGET_RE);
|
|
665
|
+
if (m) targetPaths.push(m[1] || m[2] || m[3]);
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const segPaths = extractFilePaths(trimmed);
|
|
669
|
+
const segPathsFiltered = firstWord === "wget" || firstWord === "curl" ? segPaths.filter((p) => p !== "/dev/null") : segPaths;
|
|
670
|
+
if (SOURCE_ARG_CMDS.has(firstWord)) {
|
|
671
|
+
if (segPathsFiltered.length > 1) targetPaths.push(...segPathsFiltered.slice(1));
|
|
672
|
+
} else if (firstWord === "dd") {
|
|
673
|
+
const ifSourceMatches = trimmed.match(/\bif=(\/[^\s;|&><'"]+)/g);
|
|
674
|
+
const ifSourcePaths = new Set(ifSourceMatches ? ifSourceMatches.map((m) => m.replace(/^if=/, "")) : []);
|
|
675
|
+
const devNullMatches = trimmed.match(/\bof=(\/dev\/\S+)/g);
|
|
676
|
+
const devNullTargets = new Set(devNullMatches ? devNullMatches.map((m) => m.replace(/^of=/, "")) : []);
|
|
677
|
+
for (const sp of segPaths) {
|
|
678
|
+
if (!ifSourcePaths.has(sp) && !devNullTargets.has(sp)) targetPaths.push(sp);
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
targetPaths.push(...segPathsFiltered);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const uniqueTargetPaths = [...new Set(targetPaths)];
|
|
685
|
+
if (uniqueTargetPaths.length === 0) return null;
|
|
509
686
|
const historyReadPaths = /* @__PURE__ */ new Set();
|
|
510
687
|
for (const action of actionHistory) {
|
|
511
688
|
const histDetail = action.actionDetail.toLowerCase();
|
|
@@ -518,7 +695,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
518
695
|
}
|
|
519
696
|
}
|
|
520
697
|
const unreadPaths = [];
|
|
521
|
-
for (const path3 of
|
|
698
|
+
for (const path3 of uniqueTargetPaths) {
|
|
522
699
|
const hasRead = [...historyReadPaths].some(
|
|
523
700
|
(rp) => path3 === rp || path3.startsWith(rp) || rp.startsWith(path3)
|
|
524
701
|
);
|
|
@@ -581,6 +758,18 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
|
581
758
|
violatedLayers: ["2"]
|
|
582
759
|
};
|
|
583
760
|
}
|
|
761
|
+
function containsPrivilegeEscalation(actionDetail) {
|
|
762
|
+
const ad = actionDetail.toLowerCase();
|
|
763
|
+
for (const seg of ad.split(/&&|\|\||;|\n/)) {
|
|
764
|
+
const trimmed = seg.trim();
|
|
765
|
+
if (!trimmed) continue;
|
|
766
|
+
const parts = trimmed.split(/\s+/);
|
|
767
|
+
const fw = parts[0]?.toLowerCase() || "";
|
|
768
|
+
if (fw === "sudo" || fw === "su" || fw === "setuid") return true;
|
|
769
|
+
if (fw === "chmod" && /\bu\s*\+\s*s\b/.test(trimmed)) return true;
|
|
770
|
+
}
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
584
773
|
function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
585
774
|
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
|
|
586
775
|
return null;
|
|
@@ -685,12 +874,13 @@ var init_logic_rules = __esm({
|
|
|
685
874
|
{ path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
|
|
686
875
|
// 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
|
|
687
876
|
{ path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
|
|
688
|
-
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
|
|
877
|
+
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907\uFF08\u8BFB\u5199\u5747\u5371\u9669\uFF09", credential: true },
|
|
689
878
|
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
690
879
|
// SSH 主机私钥
|
|
691
|
-
|
|
692
|
-
{ path: "/etc/ssh/
|
|
693
|
-
{ path: "/etc/ssh/
|
|
880
|
+
// SSH 主机私钥(排除 .pub 公钥变体,对齐 Python .ssh/id_rsa(?!\.pub) 范式)
|
|
881
|
+
{ path: "/etc/ssh/ssh_host_rsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
|
|
882
|
+
{ path: "/etc/ssh/ssh_host_ed25519_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
|
|
883
|
+
{ path: "/etc/ssh/ssh_host_ecdsa_key(?!\\.pub)", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
|
|
694
884
|
];
|
|
695
885
|
HOME_SENSITIVE_PATHS = [
|
|
696
886
|
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
|
|
@@ -731,7 +921,7 @@ var init_logic_rules = __esm({
|
|
|
731
921
|
"cat"
|
|
732
922
|
]);
|
|
733
923
|
NON_WRITE_TOOL_PREFIXES = ["memory-", "skill-manager-", "mailbox-", "app-"];
|
|
734
|
-
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"];
|
|
924
|
+
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"];
|
|
735
925
|
WRITE_COMMAND_RES = [
|
|
736
926
|
/\brm\b/,
|
|
737
927
|
/\bunlink\b/,
|
|
@@ -777,6 +967,12 @@ var init_logic_rules = __esm({
|
|
|
777
967
|
"nl",
|
|
778
968
|
"tac",
|
|
779
969
|
"rev",
|
|
970
|
+
// NEW-FP-I: echo/printf 是文本输出命令,参数是字面量非写目标。加入此集让
|
|
971
|
+
// read_before_write 的 echo 段走只读命令分支(只提取重定向目标,不提取参数路径),
|
|
972
|
+
// 避免 echo "cat /etc/shadow" > /tmp/x.sh 的引号内 /etc/shadow 被当写目标误报。
|
|
973
|
+
// echo "x" > /tmp/x.sh 的重定向目标 /tmp/x.sh 仍被提取(真实写不漏报)。
|
|
974
|
+
"echo",
|
|
975
|
+
"printf",
|
|
780
976
|
"lsblk",
|
|
781
977
|
"blockdev",
|
|
782
978
|
"smartctl",
|
|
@@ -829,7 +1025,7 @@ var init_logic_rules = __esm({
|
|
|
829
1025
|
"modinfo",
|
|
830
1026
|
"sysctl"
|
|
831
1027
|
]);
|
|
832
|
-
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b
|
|
1028
|
+
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
833
1029
|
READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
|
|
834
1030
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
835
1031
|
PASSWORD_MODIFY_PATTERNS = [
|
|
@@ -879,7 +1075,7 @@ var init_logic_rules = __esm({
|
|
|
879
1075
|
// src/engine/skill-loader.ts
|
|
880
1076
|
import { readFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
881
1077
|
import { resolve, dirname } from "path";
|
|
882
|
-
import { fileURLToPath as
|
|
1078
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
883
1079
|
function loadAllSkills() {
|
|
884
1080
|
if (cachedSkills) return cachedSkills;
|
|
885
1081
|
cachedSkills = {};
|
|
@@ -950,7 +1146,7 @@ var init_skill_loader = __esm({
|
|
|
950
1146
|
"src/engine/skill-loader.ts"() {
|
|
951
1147
|
"use strict";
|
|
952
1148
|
init_esm_shims();
|
|
953
|
-
__filename2 =
|
|
1149
|
+
__filename2 = fileURLToPath3(import.meta.url);
|
|
954
1150
|
__dirname2 = dirname(__filename2);
|
|
955
1151
|
SKILLS_DIR = resolve(__dirname2, "../skills");
|
|
956
1152
|
SKILLS_DIR_FALLBACK = resolve(__dirname2, "../../../agent_moss/skills");
|
|
@@ -1025,7 +1221,7 @@ __export(runtime_config_exports, {
|
|
|
1025
1221
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1026
1222
|
import { join } from "path";
|
|
1027
1223
|
import { homedir } from "os";
|
|
1028
|
-
import { fileURLToPath as
|
|
1224
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1029
1225
|
import { dirname as dirname2 } from "path";
|
|
1030
1226
|
function getRuntimeConfigPath() {
|
|
1031
1227
|
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
@@ -1881,7 +2077,7 @@ var init_runtime_config = __esm({
|
|
|
1881
2077
|
init_heuristic();
|
|
1882
2078
|
init_logic_rules();
|
|
1883
2079
|
init_skill_loader();
|
|
1884
|
-
__dirname3 = dirname2(
|
|
2080
|
+
__dirname3 = dirname2(fileURLToPath4(import.meta.url));
|
|
1885
2081
|
RUNTIME_CONFIG_DIR = join(homedir(), ".config", "agentmoss");
|
|
1886
2082
|
RUNTIME_CONFIG_PATH = join(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
1887
2083
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
@@ -1904,7 +2100,7 @@ var init_runtime_config = __esm({
|
|
|
1904
2100
|
// src/agents/index.ts
|
|
1905
2101
|
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
1906
2102
|
import { resolve as resolve2, dirname as dirname3 } from "path";
|
|
1907
|
-
import { fileURLToPath as
|
|
2103
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1908
2104
|
function resolveAgentsDir() {
|
|
1909
2105
|
if (existsSync4(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
1910
2106
|
if (existsSync4(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
@@ -1953,7 +2149,7 @@ var init_agents = __esm({
|
|
|
1953
2149
|
"src/agents/index.ts"() {
|
|
1954
2150
|
"use strict";
|
|
1955
2151
|
init_esm_shims();
|
|
1956
|
-
__filename3 =
|
|
2152
|
+
__filename3 = fileURLToPath5(import.meta.url);
|
|
1957
2153
|
__dirname4 = dirname3(__filename3);
|
|
1958
2154
|
AGENTS_DIR_PROD = resolve2(__dirname4, "agents");
|
|
1959
2155
|
AGENTS_DIR_DEV_FALLBACK = resolve2(__dirname4, "../../../agent_moss/agents");
|
|
@@ -1973,19 +2169,25 @@ function runHeuristicCheck(input) {
|
|
|
1973
2169
|
const re = new RegExp(rule.pattern);
|
|
1974
2170
|
if (re.test(command)) {
|
|
1975
2171
|
if (rule.action === "Deny") {
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2172
|
+
return {
|
|
2173
|
+
allowed: false,
|
|
2174
|
+
reason: `\u547D\u4E2D\u7528\u6237\u654F\u611F\u52A8\u4F5C\u89C4\u5219: ${rule.description || rule.pattern}`,
|
|
2175
|
+
riskLevel: rule.severity,
|
|
2176
|
+
riskType: "sensitive_action",
|
|
2177
|
+
confidence: 90,
|
|
2178
|
+
source: "heuristic",
|
|
2179
|
+
violatedLayers: ["1.1"]
|
|
2180
|
+
};
|
|
1981
2181
|
}
|
|
1982
2182
|
}
|
|
1983
2183
|
} catch {
|
|
1984
2184
|
}
|
|
1985
2185
|
}
|
|
2186
|
+
const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
|
|
2187
|
+
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
1986
2188
|
let topRiskType = "";
|
|
1987
2189
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
1988
|
-
if (rule.pattern.test(
|
|
2190
|
+
if (rule.pattern.test(scanCommand)) {
|
|
1989
2191
|
matched.push(rule.description);
|
|
1990
2192
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
1991
2193
|
highestSeverity = rule.severity;
|
|
@@ -1999,7 +2201,7 @@ function runHeuristicCheck(input) {
|
|
|
1999
2201
|
for (const rule of agentExt.extra_command_patterns) {
|
|
2000
2202
|
try {
|
|
2001
2203
|
const re = new RegExp(rule.pattern, "i");
|
|
2002
|
-
if (re.test(
|
|
2204
|
+
if (re.test(scanCommand)) {
|
|
2003
2205
|
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
2004
2206
|
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
2005
2207
|
highestSeverity = rule.risk_level;
|
|
@@ -2023,14 +2225,12 @@ function runHeuristicCheck(input) {
|
|
|
2023
2225
|
const actionTypeL = input.aNext.actionType.toLowerCase();
|
|
2024
2226
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2025
2227
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2228
|
+
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2229
|
+
if (dangerousResult) return dangerousResult;
|
|
2026
2230
|
if (!isExcludedType) {
|
|
2027
2231
|
const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
2028
2232
|
if (pathResult) return pathResult;
|
|
2029
2233
|
}
|
|
2030
|
-
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2031
|
-
if (dangerousResult) return dangerousResult;
|
|
2032
|
-
const lateralResult = checkLateralMovement(detailL);
|
|
2033
|
-
if (lateralResult) return lateralResult;
|
|
2034
2234
|
if (matched.length > 0) {
|
|
2035
2235
|
return {
|
|
2036
2236
|
allowed: false,
|
|
@@ -2043,6 +2243,8 @@ function runHeuristicCheck(input) {
|
|
|
2043
2243
|
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
2044
2244
|
};
|
|
2045
2245
|
}
|
|
2246
|
+
const lateralResult = checkLateralMovement(detailL);
|
|
2247
|
+
if (lateralResult) return lateralResult;
|
|
2046
2248
|
return null;
|
|
2047
2249
|
}
|
|
2048
2250
|
function checkInjectionKeywords(text, extraKeywords) {
|
|
@@ -2065,6 +2267,68 @@ function isInlineScriptCommand(actionDetail) {
|
|
|
2065
2267
|
const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
2066
2268
|
return re.test(actionDetail);
|
|
2067
2269
|
}
|
|
2270
|
+
function stripLiteralTextSegments(actionDetail) {
|
|
2271
|
+
const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
|
|
2272
|
+
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
|
|
2273
|
+
if (writeScriptExec || pipeToInterpreter) {
|
|
2274
|
+
return actionDetail;
|
|
2275
|
+
}
|
|
2276
|
+
const resultParts = [];
|
|
2277
|
+
for (const seg of splitCommandSegments(actionDetail)) {
|
|
2278
|
+
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
2279
|
+
if (fw === "echo" || fw === "printf") {
|
|
2280
|
+
const segUnquotedRedirect = seg.replace(
|
|
2281
|
+
/>{1,2}\s*(['"])([~./][^'"]+)\1/g,
|
|
2282
|
+
(_m, _q, p) => ">" + p
|
|
2283
|
+
);
|
|
2284
|
+
resultParts.push(segUnquotedRedirect.replace(/'[^']*'|"[^"]*"/g, ""));
|
|
2285
|
+
} else {
|
|
2286
|
+
resultParts.push(seg);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return resultParts.join("");
|
|
2290
|
+
}
|
|
2291
|
+
function splitCommandSegments(actionDetail) {
|
|
2292
|
+
const segments = [];
|
|
2293
|
+
let cur = "";
|
|
2294
|
+
let quote = null;
|
|
2295
|
+
for (let i = 0; i < actionDetail.length; i++) {
|
|
2296
|
+
const ch = actionDetail[i];
|
|
2297
|
+
if (quote) {
|
|
2298
|
+
cur += ch;
|
|
2299
|
+
if (ch === quote) quote = null;
|
|
2300
|
+
continue;
|
|
2301
|
+
}
|
|
2302
|
+
if (ch === "'" || ch === '"') {
|
|
2303
|
+
quote = ch;
|
|
2304
|
+
cur += ch;
|
|
2305
|
+
continue;
|
|
2306
|
+
}
|
|
2307
|
+
if (ch === ";" || ch === "\n") {
|
|
2308
|
+
segments.push(cur);
|
|
2309
|
+
segments.push(ch);
|
|
2310
|
+
cur = "";
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
if (ch === "&" && actionDetail[i + 1] === "&") {
|
|
2314
|
+
segments.push(cur);
|
|
2315
|
+
segments.push("&&");
|
|
2316
|
+
cur = "";
|
|
2317
|
+
i++;
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
if (ch === "|" && actionDetail[i + 1] === "|") {
|
|
2321
|
+
segments.push(cur);
|
|
2322
|
+
segments.push("||");
|
|
2323
|
+
cur = "";
|
|
2324
|
+
i++;
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
cur += ch;
|
|
2328
|
+
}
|
|
2329
|
+
if (cur) segments.push(cur);
|
|
2330
|
+
return segments;
|
|
2331
|
+
}
|
|
2068
2332
|
function severityRank(s) {
|
|
2069
2333
|
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
2070
2334
|
return ranks[s] || 0;
|
|
@@ -2072,13 +2336,18 @@ function severityRank(s) {
|
|
|
2072
2336
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2073
2337
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2074
2338
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2339
|
+
const pathScanText = stripLiteralTextSegments(command);
|
|
2075
2340
|
const pathMatches = (text, path3) => {
|
|
2076
|
-
const
|
|
2341
|
+
const parts = path3.split(/(\(\?!.*?\))/);
|
|
2342
|
+
const escaped = parts.map((part) => {
|
|
2343
|
+
if (part.startsWith("(?!")) return part;
|
|
2344
|
+
return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2345
|
+
}).join("");
|
|
2077
2346
|
const re = path3.startsWith(".") || path3.startsWith("/") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`, "i") : new RegExp(`\\b${escaped}\\b`, "i");
|
|
2078
2347
|
return re.test(text);
|
|
2079
2348
|
};
|
|
2080
2349
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
2081
|
-
if (pathMatches(
|
|
2350
|
+
if (pathMatches(pathScanText, sp.path.toLowerCase())) {
|
|
2082
2351
|
if (sp.path === "/dev/random") {
|
|
2083
2352
|
if (isWriteOpFlag) {
|
|
2084
2353
|
continue;
|
|
@@ -2112,7 +2381,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2112
2381
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
2113
2382
|
const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
2114
2383
|
const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
|
|
2115
|
-
if (pathRe.test(
|
|
2384
|
+
if (pathRe.test(pathScanText) || expandedRe.test(pathScanText)) {
|
|
2116
2385
|
if (!isWriteOpFlag && !sp.credential) {
|
|
2117
2386
|
continue;
|
|
2118
2387
|
}
|
|
@@ -2183,8 +2452,10 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2183
2452
|
}
|
|
2184
2453
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2185
2454
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2186
|
-
const TEMP_DIRS = ["/tmp
|
|
2187
|
-
const isTempPath = TEMP_DIRS.some(
|
|
2455
|
+
const TEMP_DIRS = ["/tmp", "/var/tmp", "/run", "/dev/shm"];
|
|
2456
|
+
const isTempPath = TEMP_DIRS.some(
|
|
2457
|
+
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
2458
|
+
);
|
|
2188
2459
|
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
2189
2460
|
return {
|
|
2190
2461
|
allowed: false,
|
|
@@ -2196,7 +2467,22 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2196
2467
|
violatedLayers: ["1.1"]
|
|
2197
2468
|
};
|
|
2198
2469
|
}
|
|
2199
|
-
|
|
2470
|
+
const findWriteMatch = actionDetail.match(
|
|
2471
|
+
/\bfind\b.*?(?:(?:^|\s)-exec(?:dir)?\s+(chmod|chown|cp|mv|rm|dd|tee|truncate|mkfs|shred|rmdir|unlink)\b|(?:^|\s)\|\s*xargs\s+(?:sed\s+-[a-zA-Z]*i|chmod|chown|cp|mv|rm|dd|tee|truncate|mkfs|shred|rmdir|unlink)\b)/i
|
|
2472
|
+
);
|
|
2473
|
+
if (findWriteMatch) {
|
|
2474
|
+
const cmdHit = findWriteMatch[1] || "xargs";
|
|
2475
|
+
return {
|
|
2476
|
+
allowed: false,
|
|
2477
|
+
reason: `\u68C0\u6D4B\u5230 find \u6279\u91CF\u6267\u884C\u5199\u64CD\u4F5C (${cmdHit})\uFF0C\u53EF\u80FD\u6279\u91CF\u4FEE\u6539/\u5220\u9664\u6587\u4EF6`,
|
|
2478
|
+
riskLevel: "high",
|
|
2479
|
+
riskType: "script_execution",
|
|
2480
|
+
confidence: 90,
|
|
2481
|
+
source: "heuristic",
|
|
2482
|
+
violatedLayers: ["1.1"]
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail)) {
|
|
2200
2486
|
return {
|
|
2201
2487
|
allowed: false,
|
|
2202
2488
|
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
@@ -2207,17 +2493,95 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2207
2493
|
violatedLayers: ["1.1"]
|
|
2208
2494
|
};
|
|
2209
2495
|
}
|
|
2496
|
+
if (/>\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i.test(actionDetail)) {
|
|
2497
|
+
return {
|
|
2498
|
+
allowed: false,
|
|
2499
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6",
|
|
2500
|
+
riskLevel: "high",
|
|
2501
|
+
riskType: "file_access",
|
|
2502
|
+
confidence: 90,
|
|
2503
|
+
source: "heuristic",
|
|
2504
|
+
violatedLayers: ["1.1"]
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2210
2507
|
return null;
|
|
2211
2508
|
}
|
|
2212
2509
|
function extractRemoteHost(command) {
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2510
|
+
const tokens = command.split(/\s+/).map((t) => t.replace(/^["']|["']$/g, ""));
|
|
2511
|
+
for (const tok of tokens) {
|
|
2512
|
+
const am = tok.match(/^([\w.-]+)@([\w.-]+)(:.*)?$/);
|
|
2513
|
+
if (am && am[2] && !isLocalHost(am[2])) {
|
|
2514
|
+
return `${am[1]}@${am[2]}`;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
for (const tok of tokens) {
|
|
2518
|
+
const hm = tok.match(/^([\w.-]+):\/(.*)?$/);
|
|
2519
|
+
if (hm && hm[1] && !isLocalHost(hm[1])) {
|
|
2520
|
+
return hm[1];
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
const skipValueOpts = /* @__PURE__ */ new Set([
|
|
2524
|
+
"-p",
|
|
2525
|
+
"-l",
|
|
2526
|
+
"-i",
|
|
2527
|
+
"-o",
|
|
2528
|
+
"-b",
|
|
2529
|
+
"-c",
|
|
2530
|
+
"-m",
|
|
2531
|
+
"-F",
|
|
2532
|
+
"-e",
|
|
2533
|
+
"-S",
|
|
2534
|
+
"-W",
|
|
2535
|
+
"-J",
|
|
2536
|
+
"-I",
|
|
2537
|
+
"-T",
|
|
2538
|
+
"-B",
|
|
2539
|
+
"-L",
|
|
2540
|
+
"-R",
|
|
2541
|
+
"-D",
|
|
2542
|
+
"-E",
|
|
2543
|
+
"-M",
|
|
2544
|
+
"-O",
|
|
2545
|
+
"-P",
|
|
2546
|
+
"-q"
|
|
2547
|
+
]);
|
|
2548
|
+
let foundCmd = false;
|
|
2549
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
2550
|
+
const t = tokens[i];
|
|
2551
|
+
if (t === "ssh" || t === "scp" || t === "rsync") {
|
|
2552
|
+
foundCmd = true;
|
|
2553
|
+
continue;
|
|
2554
|
+
}
|
|
2555
|
+
if (!foundCmd) continue;
|
|
2556
|
+
if (t.startsWith("-")) {
|
|
2557
|
+
if (skipValueOpts.has(t) && i + 1 < tokens.length) {
|
|
2558
|
+
i++;
|
|
2559
|
+
}
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
if (t.startsWith("/")) continue;
|
|
2563
|
+
return t;
|
|
2564
|
+
}
|
|
2565
|
+
return null;
|
|
2215
2566
|
}
|
|
2216
2567
|
function isLocalHost(host) {
|
|
2217
2568
|
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
2218
2569
|
return localNames.includes(host.replace(/.*@/, ""));
|
|
2219
2570
|
}
|
|
2220
2571
|
function checkLateralMovement(actionDetail) {
|
|
2572
|
+
if (/\b(?:python|python3|perl|ruby|node|php|lua)\b\s+.*-[cer][a-zA-Z]*\s/.test(actionDetail)) return null;
|
|
2573
|
+
const segments = actionDetail.split(/&&|\|\||;|\n/).map((s) => s.trim()).filter(Boolean);
|
|
2574
|
+
let hasRealCall = false;
|
|
2575
|
+
for (const seg of segments) {
|
|
2576
|
+
const parts = seg.split(/\s+/);
|
|
2577
|
+
const fw = (parts[0] || "").toLowerCase();
|
|
2578
|
+
if (fw === "echo" || fw === "printf") continue;
|
|
2579
|
+
if (/(?:ssh|scp|rsync)\s+/.test(seg)) {
|
|
2580
|
+
hasRealCall = true;
|
|
2581
|
+
break;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
if (!hasRealCall) return null;
|
|
2221
2585
|
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
2222
2586
|
const targetHost = extractRemoteHost(actionDetail);
|
|
2223
2587
|
if (targetHost && !isLocalHost(targetHost)) {
|
|
@@ -2317,10 +2681,14 @@ var init_heuristic = __esm({
|
|
|
2317
2681
|
{ pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
|
|
2318
2682
|
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
2319
2683
|
// --- Critical: remote code execution ---
|
|
2320
|
-
|
|
2684
|
+
// curl|bash 优先匹配 user_rules(high/sensitive_action),见下方 UserRuleMatcher 段;
|
|
2685
|
+
// 非 bash 的 curl|sh/zsh/dash 走 CommandPatternScanner critical。两条分流由
|
|
2686
|
+
// runHeuristicCheck 的规则顺序保证(bash 在前 high,其余 fall-through 到 critical)。
|
|
2687
|
+
{ pattern: /curl\s+.*\|\s*bash/i, severity: "high", description: "curl pipe bash \u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2688
|
+
{ pattern: /curl\s+.*\|\s*(sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2321
2689
|
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
2322
2690
|
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
2323
|
-
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "
|
|
2691
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "high", description: "base64 \u89E3\u7801\u6267\u884C (user_rules)", riskType: "sensitive_action" },
|
|
2324
2692
|
// --- Critical: reverse shell ---
|
|
2325
2693
|
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
2326
2694
|
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
@@ -2331,13 +2699,25 @@ var init_heuristic = __esm({
|
|
|
2331
2699
|
{ pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
2332
2700
|
// --- Critical: privilege escalation / container escape ---
|
|
2333
2701
|
{ pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
|
|
2334
|
-
// ---
|
|
2335
|
-
|
|
2702
|
+
// --- Critical: dangerous permissions / ownership (提权 / 权限敞口) ---
|
|
2703
|
+
// 修正「只拦 777、xx7 漏放」的缺口:
|
|
2704
|
+
// 1) 八进制末位 7:other 位含写+执行敞口(chmod 777/647/007/4777 等);644/755/1700 不命中
|
|
2705
|
+
// 2) 八进制 setuid/setgid 位:首位 4(setuid)/2(setgid) 的 4 位八进制(chmod 4755/2755)
|
|
2706
|
+
// 3) 符号 setuid/setgid:chmod u+s / g+s / a+s
|
|
2707
|
+
// 普通权限修改(chmod 644)不在此列——L2 read_before_write 已豁免 chmod/chown(不写内容),
|
|
2708
|
+
// 危险形态在此 L1 专门拦,以「提权」正确理由拦截,不再靠 RBW 误打误撞。
|
|
2709
|
+
{ pattern: /chmod\s+[0-7]{0,3}7\b/i, severity: "critical", description: "\u5371\u9669\u6743\u9650\u8BBE\u7F6E (chmod ...7, other \u4F4D\u542B\u5199+\u6267\u884C\u655E\u53E3)", riskType: "privilege_escalation" },
|
|
2710
|
+
{ pattern: /chmod\s+[24][0-7]{3}\b/i, severity: "critical", description: "setuid/setgid \u4F4D\u8BBE\u7F6E (chmod 4xxx/2xxx, \u63D0\u6743)", riskType: "privilege_escalation" },
|
|
2711
|
+
{ pattern: /chmod\s+[ugoa]\+s\b/i, severity: "critical", description: "\u7B26\u53F7 setuid/setgid \u8BBE\u7F6E (chmod u+s/g+s/a+s, \u63D0\u6743)", riskType: "privilege_escalation" },
|
|
2336
2712
|
{ pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
|
|
2337
2713
|
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
2338
2714
|
{ pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
|
|
2339
2715
|
// --- High: system config overwrite ---
|
|
2716
|
+
// > /etc/ 重定向覆盖:对齐 Python CommandPatternScanner `>\s*/etc/`(不兼容引号路径),
|
|
2717
|
+
// 引号包裹的重定向目标(cat > '/etc/ssh/sshd_config')走 checkSensitivePath 敏感路径 high。
|
|
2340
2718
|
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
2719
|
+
{ pattern: />\s*\/boot\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55", riskType: "file_access" },
|
|
2720
|
+
// 注:/proc/ 重定向覆盖走 checkSensitivePath 的 high(内核参数 /proc/sys/),此处不拦 /proc/ 避免遮蔽(对齐 Python)。
|
|
2341
2721
|
// --- High: download script + execute risk ---
|
|
2342
2722
|
{ pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
|
|
2343
2723
|
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
@@ -2353,7 +2733,11 @@ var init_heuristic = __esm({
|
|
|
2353
2733
|
// --- Medium: credential export ---
|
|
2354
2734
|
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
2355
2735
|
// --- High: shell rc file append (persistence) ---
|
|
2736
|
+
// 两条规则:第一条匹配 ~ 与 /home/<user>/ 形态;第二条按文件名兜底,
|
|
2737
|
+
// 覆盖非标准 home 路径(如鸿蒙 /storage/Users/currentUser/.bashrc、
|
|
2738
|
+
// /root/.bashrc),避免按 home 前缀枚举导致漏检持久化后门。
|
|
2356
2739
|
{ pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
|
|
2740
|
+
{ pattern: />>\s*\S*\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)\b/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
|
|
2357
2741
|
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
2358
2742
|
{ pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
|
|
2359
2743
|
// --- High: non-interactive password modification ---
|
|
@@ -2383,7 +2767,7 @@ var init_heuristic = __esm({
|
|
|
2383
2767
|
// src/engine/template-loader.ts
|
|
2384
2768
|
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
2385
2769
|
import { resolve as resolve3, dirname as dirname4 } from "path";
|
|
2386
|
-
import { fileURLToPath as
|
|
2770
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2387
2771
|
function loadTemplates() {
|
|
2388
2772
|
if (cachedTemplates) return cachedTemplates;
|
|
2389
2773
|
cachedTemplates = {};
|
|
@@ -2407,7 +2791,7 @@ var init_template_loader = __esm({
|
|
|
2407
2791
|
"src/engine/template-loader.ts"() {
|
|
2408
2792
|
"use strict";
|
|
2409
2793
|
init_esm_shims();
|
|
2410
|
-
__filename4 =
|
|
2794
|
+
__filename4 = fileURLToPath6(import.meta.url);
|
|
2411
2795
|
__dirname5 = dirname4(__filename4);
|
|
2412
2796
|
TEMPLATES_DIR = resolve3(__dirname5, "../templates");
|
|
2413
2797
|
TEMPLATES_DIR_FALLBACK = resolve3(__dirname5, "../../../agent_moss/templates");
|
|
@@ -3487,11 +3871,13 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
3487
3871
|
} catch {
|
|
3488
3872
|
}
|
|
3489
3873
|
clearTimeout(totalTimeoutTimer);
|
|
3874
|
+
const riskLevel = parsed.riskLevel ?? parsed.risk_level;
|
|
3875
|
+
const riskType = parsed.riskType ?? parsed.risk_type;
|
|
3490
3876
|
return {
|
|
3491
3877
|
allowed: parsed.allowed !== false,
|
|
3492
3878
|
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
3493
|
-
riskLevel: ["low", "medium", "high", "critical"].includes(
|
|
3494
|
-
riskType:
|
|
3879
|
+
riskLevel: ["low", "medium", "high", "critical"].includes(riskLevel) ? riskLevel : "low",
|
|
3880
|
+
riskType: riskType || "",
|
|
3495
3881
|
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
3496
3882
|
source: "llm",
|
|
3497
3883
|
violatedLayers: ["3"]
|
|
@@ -3560,7 +3946,10 @@ async function runAnalysis(input) {
|
|
|
3560
3946
|
if (heuristicResult && !heuristicResult.allowed) {
|
|
3561
3947
|
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && heuristicResult.riskType === "file_access";
|
|
3562
3948
|
if (!skipInlineFileAccess) {
|
|
3563
|
-
|
|
3949
|
+
const shouldDenyL1 = heuristicResult.riskLevel === "high" || heuristicResult.riskLevel === "critical" || heuristicResult.riskLevel === "medium" && heuristicResult.riskType === "file_access";
|
|
3950
|
+
if (shouldDenyL1) {
|
|
3951
|
+
return heuristicResult;
|
|
3952
|
+
}
|
|
3564
3953
|
}
|
|
3565
3954
|
}
|
|
3566
3955
|
let scriptChain = null;
|
|
@@ -3638,7 +4027,8 @@ async function runAnalysis(input) {
|
|
|
3638
4027
|
) && DANGEROUS_PATTERNS.some(
|
|
3639
4028
|
(p) => (p.severity === "critical" || p.severity === "high") && p.pattern.test(lowerDetail)
|
|
3640
4029
|
);
|
|
3641
|
-
|
|
4030
|
+
const hasRedirectWrite = isWriteOperation(firstCmd, lowerDetail);
|
|
4031
|
+
if (!fullCmdHit && !hasRedirectWrite) {
|
|
3642
4032
|
return {
|
|
3643
4033
|
allowed: true,
|
|
3644
4034
|
reason: `\u5B89\u5168\u7684\u53EA\u8BFB bash \u547D\u4EE4: ${firstCmd}`,
|
|
@@ -3937,7 +4327,7 @@ var require_bindings = __commonJS({
|
|
|
3937
4327
|
init_esm_shims();
|
|
3938
4328
|
var fs4 = __require("fs");
|
|
3939
4329
|
var path3 = __require("path");
|
|
3940
|
-
var
|
|
4330
|
+
var fileURLToPath9 = require_file_uri_to_path();
|
|
3941
4331
|
var join6 = path3.join;
|
|
3942
4332
|
var dirname7 = path3.dirname;
|
|
3943
4333
|
var exists = fs4.accessSync && function(path4) {
|
|
@@ -4049,7 +4439,7 @@ var require_bindings = __commonJS({
|
|
|
4049
4439
|
Error.stackTraceLimit = origSTL;
|
|
4050
4440
|
var fileSchema = "file://";
|
|
4051
4441
|
if (fileName.indexOf(fileSchema) === 0) {
|
|
4052
|
-
fileName =
|
|
4442
|
+
fileName = fileURLToPath9(fileName);
|
|
4053
4443
|
}
|
|
4054
4444
|
return fileName;
|
|
4055
4445
|
};
|
|
@@ -4897,8 +5287,8 @@ init_token_stats();
|
|
|
4897
5287
|
import { Hono } from "hono";
|
|
4898
5288
|
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4899
5289
|
import { join as join5, dirname as dirname6 } from "path";
|
|
4900
|
-
import { fileURLToPath as
|
|
4901
|
-
var __dirname6 = dirname6(
|
|
5290
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
5291
|
+
var __dirname6 = dirname6(fileURLToPath7(import.meta.url));
|
|
4902
5292
|
var STATIC_DIR = join5(__dirname6, "static");
|
|
4903
5293
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
4904
5294
|
function getConsoleToken() {
|
|
@@ -5136,7 +5526,7 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
5136
5526
|
}
|
|
5137
5527
|
|
|
5138
5528
|
// src/cli.ts
|
|
5139
|
-
import { fileURLToPath as
|
|
5529
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
5140
5530
|
import { realpathSync } from "fs";
|
|
5141
5531
|
function printHelp() {
|
|
5142
5532
|
console.log(`AgentMoss security analysis engine (TypeScript v${version})
|
|
@@ -5227,7 +5617,7 @@ async function main() {
|
|
|
5227
5617
|
const { execSync } = await import("child_process");
|
|
5228
5618
|
const fs4 = await import("fs");
|
|
5229
5619
|
const path3 = await import("path");
|
|
5230
|
-
const { fileURLToPath:
|
|
5620
|
+
const { fileURLToPath: fileURLToPath9 } = await import("url");
|
|
5231
5621
|
if (process.getuid?.() !== 0) {
|
|
5232
5622
|
try {
|
|
5233
5623
|
execSync("sudo -n true", { stdio: "ignore", timeout: 5e3 });
|
|
@@ -5242,7 +5632,7 @@ async function main() {
|
|
|
5242
5632
|
execSync(sudoArgs.join(" "), { stdio: "inherit" });
|
|
5243
5633
|
process.exit(0);
|
|
5244
5634
|
}
|
|
5245
|
-
const __filename5 =
|
|
5635
|
+
const __filename5 = fileURLToPath9(import.meta.url);
|
|
5246
5636
|
const __dirname7 = path3.dirname(__filename5);
|
|
5247
5637
|
let srcPath = path3.join(__dirname7, "data", "agent_moss.service");
|
|
5248
5638
|
if (!fs4.existsSync(srcPath)) {
|
|
@@ -5384,9 +5774,9 @@ async function main() {
|
|
|
5384
5774
|
var isMain = (() => {
|
|
5385
5775
|
try {
|
|
5386
5776
|
if (!process.argv[1]) return false;
|
|
5387
|
-
const thisFile = realpathSync(
|
|
5777
|
+
const thisFile = realpathSync(fileURLToPath8(import.meta.url));
|
|
5388
5778
|
const argv1 = realpathSync(
|
|
5389
|
-
process.argv[1].startsWith("file://") ?
|
|
5779
|
+
process.argv[1].startsWith("file://") ? fileURLToPath8(process.argv[1]) : process.argv[1]
|
|
5390
5780
|
);
|
|
5391
5781
|
return thisFile === argv1;
|
|
5392
5782
|
} catch {
|