@kenhkl/agent-moss 0.15.1 → 0.15.4
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.d.ts +10 -0
- package/dist/cli.js +147 -19
- package/dist/cli.js.map +1 -1
- package/dist/config.json.example +31 -0
- package/dist/engine/coordinator.js +101 -15
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/l1_rules.json +7 -1
- package/dist/scenarios/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/server.js +101 -15
- package/dist/server.js.map +1 -1
- package/dist/skills/script_execution_guard.md +14 -9
- package/dist/templates/security_judge_template.txt +7 -1
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentMoss CLI — TypeScript version.
|
|
4
|
+
* Supports: init | analyze | server
|
|
5
|
+
*/
|
|
6
|
+
/** 命令模式核心:把一条 shell 命令当 a_next 分析,输出 Deny/Allow(--json 时完整
|
|
7
|
+
* JSON),返回退出码(Allow=0 / Deny=1)。导出供测试直接调用(CLI 分支再 process.exit)。
|
|
8
|
+
* 零文件读写——镜像 Python cli.py 的 _analyze_command。 */
|
|
9
|
+
declare function analyzeRawCommand(command: string, useJson?: boolean): Promise<number>;
|
|
10
|
+
|
|
11
|
+
export { analyzeRawCommand };
|
package/dist/cli.js
CHANGED
|
@@ -161,8 +161,12 @@ function resolveConfigPath() {
|
|
|
161
161
|
if (envPath) return envPath;
|
|
162
162
|
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
163
163
|
if (fs.existsSync(userConfig)) return userConfig;
|
|
164
|
-
const
|
|
165
|
-
|
|
164
|
+
const defaultCandidates = [
|
|
165
|
+
path2.join(__dirname, "config.json.example"),
|
|
166
|
+
path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example")
|
|
167
|
+
];
|
|
168
|
+
const defaultConfig = defaultCandidates.find((p) => fs.existsSync(p));
|
|
169
|
+
if (defaultConfig) {
|
|
166
170
|
const configDir = path2.dirname(userConfig);
|
|
167
171
|
fs.mkdirSync(configDir, { recursive: true });
|
|
168
172
|
const pkgConfig = loadConfigFile(defaultConfig);
|
|
@@ -1435,6 +1439,7 @@ __export(runtime_config_exports, {
|
|
|
1435
1439
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
1436
1440
|
getScenarioRules: () => getScenarioRules,
|
|
1437
1441
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
1442
|
+
invalidateRuntimeConfigCache: () => invalidateRuntimeConfigCache,
|
|
1438
1443
|
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
1439
1444
|
isLayerEnabled: () => isLayerEnabled,
|
|
1440
1445
|
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
@@ -1453,7 +1458,7 @@ __export(runtime_config_exports, {
|
|
|
1453
1458
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1454
1459
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1455
1460
|
});
|
|
1456
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1461
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
|
|
1457
1462
|
import { join as join3 } from "path";
|
|
1458
1463
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1459
1464
|
import { dirname as dirname3 } from "path";
|
|
@@ -1470,6 +1475,7 @@ function saveRuntimeConfig(config) {
|
|
|
1470
1475
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1471
1476
|
if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
|
|
1472
1477
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1478
|
+
invalidateRuntimeConfigCache();
|
|
1473
1479
|
}
|
|
1474
1480
|
function emptyRuntimeConfig() {
|
|
1475
1481
|
return {
|
|
@@ -1483,6 +1489,9 @@ function emptyRuntimeConfig() {
|
|
|
1483
1489
|
scenario_rules: {}
|
|
1484
1490
|
};
|
|
1485
1491
|
}
|
|
1492
|
+
function invalidateRuntimeConfigCache() {
|
|
1493
|
+
runtimeConfigCache = null;
|
|
1494
|
+
}
|
|
1486
1495
|
function loadRuntimeConfig() {
|
|
1487
1496
|
const p = getRuntimeConfigPath();
|
|
1488
1497
|
if (!existsSync4(p)) {
|
|
@@ -1494,9 +1503,14 @@ function loadRuntimeConfig() {
|
|
|
1494
1503
|
return seeded;
|
|
1495
1504
|
}
|
|
1496
1505
|
try {
|
|
1506
|
+
const st = statSync(p);
|
|
1507
|
+
const cacheKey = `${p}|${st.mtimeMs}|${st.size}`;
|
|
1508
|
+
if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
|
|
1509
|
+
return runtimeConfigCache.value;
|
|
1510
|
+
}
|
|
1497
1511
|
const data = JSON.parse(readFileSync4(p, "utf-8"));
|
|
1498
1512
|
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1499
|
-
|
|
1513
|
+
const result = {
|
|
1500
1514
|
version: data.version || 1,
|
|
1501
1515
|
layers: {
|
|
1502
1516
|
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
@@ -1510,6 +1524,8 @@ function loadRuntimeConfig() {
|
|
|
1510
1524
|
agent_rules: merged.agent_rules || {},
|
|
1511
1525
|
scenario_rules: merged.scenario_rules || {}
|
|
1512
1526
|
};
|
|
1527
|
+
runtimeConfigCache = { key: cacheKey, value: result };
|
|
1528
|
+
return result;
|
|
1513
1529
|
} catch {
|
|
1514
1530
|
return emptyRuntimeConfig();
|
|
1515
1531
|
}
|
|
@@ -2499,7 +2515,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
2499
2515
|
saveRuntimeConfig(runtime);
|
|
2500
2516
|
return runtime;
|
|
2501
2517
|
}
|
|
2502
|
-
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
|
|
2518
|
+
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
|
|
2503
2519
|
var init_runtime_config = __esm({
|
|
2504
2520
|
"src/infra/runtime-config.ts"() {
|
|
2505
2521
|
"use strict";
|
|
@@ -2529,6 +2545,7 @@ var init_runtime_config = __esm({
|
|
|
2529
2545
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2530
2546
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2531
2547
|
};
|
|
2548
|
+
runtimeConfigCache = null;
|
|
2532
2549
|
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2533
2550
|
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2534
2551
|
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
@@ -2823,6 +2840,56 @@ function isInlineScriptCommand(actionDetail) {
|
|
|
2823
2840
|
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;
|
|
2824
2841
|
return re.test(actionDetail);
|
|
2825
2842
|
}
|
|
2843
|
+
function resolvePathTokens(segment) {
|
|
2844
|
+
const out = [];
|
|
2845
|
+
const low = segment.toLowerCase();
|
|
2846
|
+
for (const m of low.matchAll(HOME_PREFIX_RE)) {
|
|
2847
|
+
const prefix = m[0];
|
|
2848
|
+
const endIdx = (m.index ?? 0) + m[0].length;
|
|
2849
|
+
const tail = low.slice(endIdx);
|
|
2850
|
+
if (!tail || !(tail[0] === "/" || tail[0] === " ")) {
|
|
2851
|
+
out.push([prefix, prefix]);
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
const token = tail.trim().split(/\s+/)[0] || "";
|
|
2855
|
+
if (!token.startsWith("/")) continue;
|
|
2856
|
+
const cleaned = token.replace(/\/+$/, "").replace(/^['"]|['"]$/g, "");
|
|
2857
|
+
const segments = cleaned.split("/").filter((s) => s && s !== ".");
|
|
2858
|
+
if (!segments.some((s) => s === "..")) continue;
|
|
2859
|
+
let depth = 0;
|
|
2860
|
+
const kept = [];
|
|
2861
|
+
let escaped = false;
|
|
2862
|
+
for (const s of segments) {
|
|
2863
|
+
if (s === "..") {
|
|
2864
|
+
depth -= 1;
|
|
2865
|
+
if (kept.length) kept.pop();
|
|
2866
|
+
if (depth < 0) {
|
|
2867
|
+
escaped = true;
|
|
2868
|
+
depth = 0;
|
|
2869
|
+
}
|
|
2870
|
+
} else {
|
|
2871
|
+
depth += 1;
|
|
2872
|
+
kept.push(s);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
let resolved = kept.length ? "~/" + kept.join("/") : "~";
|
|
2876
|
+
if (escaped) resolved += "\uFF08\u26A0\uFE0F .. \u5F39\u6808\u8D85\u51FA\u4E3B\u76EE\u5F55\u6839\uFF0C\u5B9E\u9645\u76EE\u6807\u5728\u4E3B\u76EE\u5F55\u4E4B\u5916\uFF09";
|
|
2877
|
+
out.push([prefix + token, resolved]);
|
|
2878
|
+
}
|
|
2879
|
+
return out;
|
|
2880
|
+
}
|
|
2881
|
+
function buildPathContext(actionDetail) {
|
|
2882
|
+
if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
|
|
2883
|
+
const stripped = stripLiteralTextSegments(actionDetail);
|
|
2884
|
+
const lines = [];
|
|
2885
|
+
for (const seg of splitCommandSegments(stripped)) {
|
|
2886
|
+
for (const [raw, lex] of resolvePathTokens(seg)) {
|
|
2887
|
+
lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
if (!lines.length) return "";
|
|
2891
|
+
return "## \u8DEF\u5F84\u89E3\u6790\u4E8B\u5B9E\uFF08\u8BCD\u6CD5\u5206\u6790\uFF0C\u786E\u5B9A\u6027\u7ED3\u8BBA\uFF09\n" + lines.join("\n") + "\n\n## \u8FD0\u884C\u65F6\u672A\u9A8C\u8BC1\uFF08\u4F9B\u53C2\u8003\uFF0C\u975E\u4E8B\u5B9E\uFF09\n- \u5206\u6790\u5668\u4E0D\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A\u65E0\u6CD5\u786E\u8BA4\u8DEF\u5F84\u4E2D\u7684\u4E2D\u95F4\u76EE\u5F55\u662F\u5426\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF1B\u82E5\u4E2D\u95F4\u6BB5\u5728\u6267\u884C\u65F6\u662F\uFF08\u6216\u88AB\u66FF\u6362\u4E3A\uFF09\u6307\u5411\u4E3B\u76EE\u5F55\u5916\u7684\u8F6F\u94FE\uFF0C\u5B9E\u9645\u64CD\u4F5C\u76EE\u6807\u53EF\u80FD\u968F\u4E4B\u6539\u53D8\u3002\u8BCD\u6CD5\u89E3\u6790\u7ED3\u679C\u57FA\u4E8E\u6807\u51C6\u8DEF\u5F84\u8BED\u4E49\uFF0C\u4E0D\u5E94\u56E0\u731C\u6D4B\u300C\u89E3\u6790\u4F9D\u8D56\u67D0\u6BB5\u662F\u5426\u5B58\u5728\u300D\u800C\u5426\u5B9A\u5B83\u3002";
|
|
2892
|
+
}
|
|
2826
2893
|
function stripLiteralTextSegments(actionDetail) {
|
|
2827
2894
|
const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
|
|
2828
2895
|
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
|
|
@@ -3187,7 +3254,7 @@ function isArchiveReadOfDir(command, dirPath) {
|
|
|
3187
3254
|
}
|
|
3188
3255
|
if (fw === "cp" || fw === "rsync") {
|
|
3189
3256
|
const segPaths = s.match(/[/~][^\s;|&><'"]*/g) || [];
|
|
3190
|
-
if (segPaths
|
|
3257
|
+
if (segPaths[0]?.includes(dpath)) return true;
|
|
3191
3258
|
}
|
|
3192
3259
|
}
|
|
3193
3260
|
return false;
|
|
@@ -3349,7 +3416,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
3349
3416
|
}
|
|
3350
3417
|
return null;
|
|
3351
3418
|
}
|
|
3352
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3419
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3353
3420
|
var init_heuristic = __esm({
|
|
3354
3421
|
"src/engine/heuristic.ts"() {
|
|
3355
3422
|
"use strict";
|
|
@@ -3377,6 +3444,8 @@ var init_heuristic = __esm({
|
|
|
3377
3444
|
description: c.reason
|
|
3378
3445
|
}));
|
|
3379
3446
|
})();
|
|
3447
|
+
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
3448
|
+
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
3380
3449
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
3381
3450
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
3382
3451
|
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
@@ -5108,7 +5177,7 @@ var init_tracker = __esm({
|
|
|
5108
5177
|
});
|
|
5109
5178
|
|
|
5110
5179
|
// src/engine/script-content-analyzer.ts
|
|
5111
|
-
import { statSync as
|
|
5180
|
+
import { statSync as statSync3, existsSync as existsSync9 } from "fs";
|
|
5112
5181
|
import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
5113
5182
|
import { homedir as homedir4 } from "os";
|
|
5114
5183
|
function emptyChain() {
|
|
@@ -5222,7 +5291,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5222
5291
|
try {
|
|
5223
5292
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5224
5293
|
if (existsSync9(resolved)) {
|
|
5225
|
-
const stat =
|
|
5294
|
+
const stat = statSync3(resolved);
|
|
5226
5295
|
if (stat.isFile()) return resolved;
|
|
5227
5296
|
}
|
|
5228
5297
|
} catch {
|
|
@@ -5236,7 +5305,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5236
5305
|
try {
|
|
5237
5306
|
const resolved = resolvePath2(p);
|
|
5238
5307
|
if (existsSync9(resolved)) {
|
|
5239
|
-
const stat =
|
|
5308
|
+
const stat = statSync3(resolved);
|
|
5240
5309
|
if (stat.isFile()) return resolved;
|
|
5241
5310
|
}
|
|
5242
5311
|
} catch {
|
|
@@ -5252,7 +5321,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5252
5321
|
try {
|
|
5253
5322
|
resolved = resolvePath2(p);
|
|
5254
5323
|
if (existsSync9(resolved)) {
|
|
5255
|
-
const stat =
|
|
5324
|
+
const stat = statSync3(resolved);
|
|
5256
5325
|
if (stat.isFile()) return resolved;
|
|
5257
5326
|
}
|
|
5258
5327
|
} catch {
|
|
@@ -5729,7 +5798,7 @@ async function runLLMAnalysis(input, previous) {
|
|
|
5729
5798
|
} else {
|
|
5730
5799
|
scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
5731
5800
|
}
|
|
5732
|
-
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
|
|
5801
|
+
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText, previous.pathContext ?? "");
|
|
5733
5802
|
logPrompt(prompt, input.aNext, previous.llmLogPath);
|
|
5734
5803
|
const result = await analyzeWithLLM(prompt, config);
|
|
5735
5804
|
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
@@ -5753,7 +5822,7 @@ ${prompt}
|
|
|
5753
5822
|
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
5754
5823
|
}
|
|
5755
5824
|
}
|
|
5756
|
-
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
5825
|
+
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "", pathContext = "") {
|
|
5757
5826
|
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
5758
5827
|
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
5759
5828
|
const promptHistoryText = formatPromptHistory(input.promptHistory);
|
|
@@ -5761,7 +5830,7 @@ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis,
|
|
|
5761
5830
|
const hintsText = formatHints(previous);
|
|
5762
5831
|
const securityJudgeTemplate = templates["security_judge"];
|
|
5763
5832
|
if (securityJudgeTemplate) {
|
|
5764
|
-
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
|
|
5833
|
+
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{path_context}", pathContext || "\uFF08\u672C\u547D\u4EE4\u65E0\u4E3B\u76EE\u5F55\u76F8\u5173\u8DEF\u5F84\u6BB5\uFF0C\u4E0D\u9002\u7528\uFF09").replaceAll("{{", "{").replaceAll("}}", "}");
|
|
5765
5834
|
}
|
|
5766
5835
|
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
5767
5836
|
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
@@ -8182,6 +8251,21 @@ async function runAnalysis(input) {
|
|
|
8182
8251
|
if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
|
|
8183
8252
|
return effectiveResult;
|
|
8184
8253
|
}
|
|
8254
|
+
} else {
|
|
8255
|
+
if (isInlineScriptCmd || isLiteralTextCmd) {
|
|
8256
|
+
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
8257
|
+
if (inlineJudgment.actionable && !inlineJudgment.allowed) {
|
|
8258
|
+
return {
|
|
8259
|
+
allowed: false,
|
|
8260
|
+
reason: inlineJudgment.reason,
|
|
8261
|
+
riskLevel: inlineJudgment.riskLevel,
|
|
8262
|
+
riskType: inlineJudgment.riskType,
|
|
8263
|
+
confidence: 90,
|
|
8264
|
+
source: "logic_rule",
|
|
8265
|
+
violatedLayers: ["L2.1"]
|
|
8266
|
+
};
|
|
8267
|
+
}
|
|
8268
|
+
}
|
|
8185
8269
|
}
|
|
8186
8270
|
if (isLogicRulesEnabled()) {
|
|
8187
8271
|
const formalSessions = [];
|
|
@@ -8283,12 +8367,14 @@ async function runAnalysis(input) {
|
|
|
8283
8367
|
}
|
|
8284
8368
|
let llmResult;
|
|
8285
8369
|
try {
|
|
8370
|
+
const pathContext = buildPathContext(input.aNext.actionDetail);
|
|
8286
8371
|
llmResult = await runLLMAnalysis(input, {
|
|
8287
8372
|
heuristic: heuristicResult,
|
|
8288
8373
|
logic: logicResult,
|
|
8289
8374
|
skipL3Hints,
|
|
8290
8375
|
scriptChain: scriptChain ?? void 0,
|
|
8291
|
-
llmLogPath: input.llmLogPath
|
|
8376
|
+
llmLogPath: input.llmLogPath,
|
|
8377
|
+
pathContext
|
|
8292
8378
|
});
|
|
8293
8379
|
} catch (e) {
|
|
8294
8380
|
const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -10170,10 +10256,14 @@ function printHelp() {
|
|
|
10170
10256
|
|
|
10171
10257
|
Commands:
|
|
10172
10258
|
init Generate input.json template
|
|
10173
|
-
analyze <input>
|
|
10259
|
+
analyze <input> Analyze an input JSON file, or a raw shell command string
|
|
10260
|
+
e.g. agent-moss analyze "cat ~/.ssh/id_rsa" \u2192 prints Deny/Allow
|
|
10174
10261
|
server Start HTTP API server
|
|
10175
10262
|
install Install systemd service for AgentMoss
|
|
10176
10263
|
|
|
10264
|
+
Analyze options:
|
|
10265
|
+
--json Command mode: print the full JSON result instead of plain Deny/Allow
|
|
10266
|
+
|
|
10177
10267
|
Server options:
|
|
10178
10268
|
--host <host> Listen host (default: 127.0.0.1)
|
|
10179
10269
|
--port <port> Listen port (default: 9090, 0=auto assign)
|
|
@@ -10182,6 +10272,22 @@ Install options:
|
|
|
10182
10272
|
--enable Enable service on boot (systemctl enable)
|
|
10183
10273
|
`);
|
|
10184
10274
|
}
|
|
10275
|
+
async function analyzeRawCommand(command, useJson = false) {
|
|
10276
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
10277
|
+
const result = await runAnalysis2({
|
|
10278
|
+
sessionId: "cli-command",
|
|
10279
|
+
promptSession: "",
|
|
10280
|
+
actionHistory: [],
|
|
10281
|
+
aNext: { actionType: "bash", actionDetail: command },
|
|
10282
|
+
reason: "",
|
|
10283
|
+
osType: process.platform === "win32" ? "windows" : "linux",
|
|
10284
|
+
cwd: process.cwd(),
|
|
10285
|
+
agentId: ""
|
|
10286
|
+
});
|
|
10287
|
+
if (useJson) console.log(JSON.stringify(result, null, 2));
|
|
10288
|
+
else console.log(result.allowed ? "Allow" : "Deny");
|
|
10289
|
+
return result.allowed ? 0 : 1;
|
|
10290
|
+
}
|
|
10185
10291
|
async function main() {
|
|
10186
10292
|
const args = process.argv.slice(2);
|
|
10187
10293
|
const command = args[0];
|
|
@@ -10207,14 +10313,33 @@ async function main() {
|
|
|
10207
10313
|
break;
|
|
10208
10314
|
}
|
|
10209
10315
|
case "analyze": {
|
|
10210
|
-
|
|
10211
|
-
|
|
10316
|
+
const target = args[1];
|
|
10317
|
+
if (!target) {
|
|
10318
|
+
console.error('Usage: agent-moss analyze <input.json | "shell command">');
|
|
10212
10319
|
process.exit(1);
|
|
10213
10320
|
}
|
|
10214
10321
|
const fs7 = await import("fs");
|
|
10215
10322
|
const path6 = await import("path");
|
|
10216
10323
|
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
10217
|
-
const
|
|
10324
|
+
const useJson = args.includes("--json");
|
|
10325
|
+
const osType = process.platform === "win32" ? "windows" : "linux";
|
|
10326
|
+
const isFile = fs7.existsSync(target) && fs7.statSync(target).isFile();
|
|
10327
|
+
if (!isFile) {
|
|
10328
|
+
if (fs7.existsSync(target)) {
|
|
10329
|
+
console.error(`\u9519\u8BEF: \u4E0D\u662F\u6587\u4EF6: ${target}`);
|
|
10330
|
+
process.exit(2);
|
|
10331
|
+
}
|
|
10332
|
+
if (/\.(json|ya?ml|toml)$/i.test(target)) {
|
|
10333
|
+
console.error(`\u9519\u8BEF: \u6587\u4EF6\u4E0D\u5B58\u5728: ${target}`);
|
|
10334
|
+
process.exit(2);
|
|
10335
|
+
}
|
|
10336
|
+
if (!target.trim()) {
|
|
10337
|
+
console.error("\u9519\u8BEF: \u8F93\u5165\u4E3A\u7A7A\uFF08\u8BF7\u7ED9 JSON \u6587\u4EF6\u8DEF\u5F84\u6216 shell \u547D\u4EE4\uFF09");
|
|
10338
|
+
process.exit(2);
|
|
10339
|
+
}
|
|
10340
|
+
process.exit(await analyzeRawCommand(target, useJson));
|
|
10341
|
+
}
|
|
10342
|
+
const filePath = path6.resolve(target);
|
|
10218
10343
|
const data = JSON.parse(fs7.readFileSync(filePath, "utf-8"));
|
|
10219
10344
|
const result = await runAnalysis2({
|
|
10220
10345
|
sessionId: data.session_id || "unknown",
|
|
@@ -10426,6 +10551,9 @@ if (isMain) {
|
|
|
10426
10551
|
process.exit(1);
|
|
10427
10552
|
});
|
|
10428
10553
|
}
|
|
10554
|
+
export {
|
|
10555
|
+
analyzeRawCommand
|
|
10556
|
+
};
|
|
10429
10557
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
10430
10558
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
10431
10559
|
//! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
|