@kenhkl/agent-moss 0.12.9 → 0.13.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 +682 -119
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.js +678 -174
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/mc_rules.json +137 -0
- package/dist/rules/tool_registry.json +25 -0
- package/dist/scenarios/__init__.py +29 -0
- package/dist/scenarios/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/server.js +752 -188
- package/dist/server.js.map +1 -1
- package/dist/static/index.html +85 -9
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -137,6 +137,7 @@ __export(config_exports, {
|
|
|
137
137
|
getRetryConfig: () => getRetryConfig,
|
|
138
138
|
getTimeoutConfig: () => getTimeoutConfig,
|
|
139
139
|
isBrainEnabled: () => isBrainEnabled,
|
|
140
|
+
isFormalObserveMode: () => isFormalObserveMode,
|
|
140
141
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
141
142
|
isLLMAvailable: () => isLLMAvailable,
|
|
142
143
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
@@ -252,6 +253,7 @@ function getCustomRules() {
|
|
|
252
253
|
}
|
|
253
254
|
function isLLMAvailable() {
|
|
254
255
|
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
256
|
+
if (!layerEnabledOrDefault("L3_llm_analysis")) return false;
|
|
255
257
|
return llmConfig.apiKey.length > 0;
|
|
256
258
|
}
|
|
257
259
|
async function probeLLMConnection(timeoutMs = 8e3) {
|
|
@@ -316,13 +318,26 @@ function getProviderHeaders(provider) {
|
|
|
316
318
|
return PROVIDER_HEADERS[provider] || {};
|
|
317
319
|
}
|
|
318
320
|
function isHeuristicEnabled() {
|
|
319
|
-
|
|
321
|
+
if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
|
|
322
|
+
return layerEnabledOrDefault("L1_heuristic");
|
|
323
|
+
}
|
|
324
|
+
function layerEnabledOrDefault(key) {
|
|
325
|
+
try {
|
|
326
|
+
const { isLayerEnabled: isLayerEnabled2 } = __require("../infra/runtime-config");
|
|
327
|
+
return isLayerEnabled2(key);
|
|
328
|
+
} catch {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
320
331
|
}
|
|
321
332
|
function isBrainEnabled() {
|
|
322
333
|
return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
|
|
323
334
|
}
|
|
324
335
|
function isLogicRulesEnabled() {
|
|
325
|
-
|
|
336
|
+
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
337
|
+
return layerEnabledOrDefault("L2_logic_rules");
|
|
338
|
+
}
|
|
339
|
+
function isFormalObserveMode() {
|
|
340
|
+
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
326
341
|
}
|
|
327
342
|
var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
328
343
|
var init_config = __esm({
|
|
@@ -580,8 +595,12 @@ var init_inline_analyzer = __esm({
|
|
|
580
595
|
|
|
581
596
|
// src/infra/rules-loader.ts
|
|
582
597
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
583
|
-
import { resolve, dirname } from "path";
|
|
598
|
+
import { resolve, dirname, join as join2 } from "path";
|
|
584
599
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
600
|
+
import { homedir as homedir2 } from "os";
|
|
601
|
+
function dataRootDir() {
|
|
602
|
+
return process.env.AGENT_MOSS_DATA_DIR || join2(homedir2(), ".config", "agentmoss");
|
|
603
|
+
}
|
|
585
604
|
function findRulesFile(name) {
|
|
586
605
|
for (const dir of RULES_DIR_CANDS) {
|
|
587
606
|
const p = resolve(dir, name);
|
|
@@ -607,6 +626,24 @@ function loadL2() {
|
|
|
607
626
|
function loadL3() {
|
|
608
627
|
return loadJson("l3_rules.json");
|
|
609
628
|
}
|
|
629
|
+
function loadMC() {
|
|
630
|
+
const mc = loadJson("mc_rules.json");
|
|
631
|
+
const rules = mc.rules || [];
|
|
632
|
+
try {
|
|
633
|
+
const rt = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
634
|
+
if (rules.length > 0 && typeof rt.hasRuntimeConfig === "function" && rt.hasRuntimeConfig()) {
|
|
635
|
+
const rtRules = rt.loadRuntimeConfig()?.L2_4?.ltl_rules?.rules || [];
|
|
636
|
+
const enabledById = {};
|
|
637
|
+
for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
|
|
638
|
+
for (const r of rules) if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
|
|
639
|
+
}
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
return mc;
|
|
643
|
+
}
|
|
644
|
+
function loadToolRegistry() {
|
|
645
|
+
return loadJson("tool_registry.json");
|
|
646
|
+
}
|
|
610
647
|
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
611
648
|
var init_rules_loader = __esm({
|
|
612
649
|
"src/infra/rules-loader.ts"() {
|
|
@@ -616,6 +653,10 @@ var init_rules_loader = __esm({
|
|
|
616
653
|
__dirname2 = dirname(__filename2);
|
|
617
654
|
RULES_DIR_CANDS = (() => {
|
|
618
655
|
const cands = [
|
|
656
|
+
// 用户数据目录覆盖优先(与 Python rules_loader._load_json 的
|
|
657
|
+
// <data_root>/rules/<name> 优先逻辑对齐)。用户安装 pip/npm 包后把自定义
|
|
658
|
+
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
659
|
+
resolve(dataRootDir(), "rules"),
|
|
619
660
|
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
620
661
|
resolve(__dirname2, "../rules"),
|
|
621
662
|
resolve(__dirname2, "rules"),
|
|
@@ -1353,6 +1394,7 @@ var init_skill_loader = __esm({
|
|
|
1353
1394
|
// src/infra/runtime-config.ts
|
|
1354
1395
|
var runtime_config_exports = {};
|
|
1355
1396
|
__export(runtime_config_exports, {
|
|
1397
|
+
DEFAULT_LAYERS: () => DEFAULT_LAYERS,
|
|
1356
1398
|
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
1357
1399
|
addAgentRule: () => addAgentRule,
|
|
1358
1400
|
addCustomRule: () => addCustomRule,
|
|
@@ -1404,7 +1446,7 @@ __export(runtime_config_exports, {
|
|
|
1404
1446
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1405
1447
|
});
|
|
1406
1448
|
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1407
|
-
import { join as
|
|
1449
|
+
import { join as join3 } from "path";
|
|
1408
1450
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1409
1451
|
import { dirname as dirname3 } from "path";
|
|
1410
1452
|
function runtimeDir() {
|
|
@@ -1428,6 +1470,7 @@ function emptyRuntimeConfig() {
|
|
|
1428
1470
|
L1_rules: {},
|
|
1429
1471
|
L2_rules: {},
|
|
1430
1472
|
L3_skills: {},
|
|
1473
|
+
L2_4: {},
|
|
1431
1474
|
agent_rules: {},
|
|
1432
1475
|
scenario_rules: {}
|
|
1433
1476
|
};
|
|
@@ -1455,6 +1498,7 @@ function loadRuntimeConfig() {
|
|
|
1455
1498
|
L1_rules: merged.L1_rules || {},
|
|
1456
1499
|
L2_rules: merged.L2_rules || {},
|
|
1457
1500
|
L3_skills: merged.L3_skills || {},
|
|
1501
|
+
L2_4: merged.L2_4 || {},
|
|
1458
1502
|
agent_rules: merged.agent_rules || {},
|
|
1459
1503
|
scenario_rules: merged.scenario_rules || {}
|
|
1460
1504
|
};
|
|
@@ -1702,7 +1746,7 @@ function _scenarioRulesToDefaults() {
|
|
|
1702
1746
|
return out;
|
|
1703
1747
|
}
|
|
1704
1748
|
for (const scenarioId of entries) {
|
|
1705
|
-
const rulesFile =
|
|
1749
|
+
const rulesFile = join3(scenDir, scenarioId, "rules.json");
|
|
1706
1750
|
if (!existsSync4(rulesFile)) continue;
|
|
1707
1751
|
let data;
|
|
1708
1752
|
try {
|
|
@@ -1751,7 +1795,7 @@ function _agentRulesToDefaults() {
|
|
|
1751
1795
|
return out;
|
|
1752
1796
|
}
|
|
1753
1797
|
for (const agentId of entries) {
|
|
1754
|
-
const rulesFile =
|
|
1798
|
+
const rulesFile = join3(agentsDir, agentId, "rules.json");
|
|
1755
1799
|
if (!existsSync4(rulesFile)) continue;
|
|
1756
1800
|
let data;
|
|
1757
1801
|
try {
|
|
@@ -1792,7 +1836,7 @@ function _agentRulesToDefaults() {
|
|
|
1792
1836
|
function generateSourceDefaults() {
|
|
1793
1837
|
return {
|
|
1794
1838
|
version: 1,
|
|
1795
|
-
layers: {
|
|
1839
|
+
layers: { ...DEFAULT_LAYERS },
|
|
1796
1840
|
L1_rules: {
|
|
1797
1841
|
dangerous_commands: {
|
|
1798
1842
|
category_enabled: true,
|
|
@@ -1831,10 +1875,45 @@ function generateSourceDefaults() {
|
|
|
1831
1875
|
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1832
1876
|
},
|
|
1833
1877
|
L3_skills: _l3SkillsToRules(),
|
|
1878
|
+
L2_4: _ltlRulesToRuntime(),
|
|
1834
1879
|
agent_rules: _agentRulesToDefaults(),
|
|
1835
1880
|
scenario_rules: _scenarioRulesToDefaults()
|
|
1836
1881
|
};
|
|
1837
1882
|
}
|
|
1883
|
+
function _ltlRulesToRuntime() {
|
|
1884
|
+
const cands = [
|
|
1885
|
+
join3(__dirname4, "..", "rules"),
|
|
1886
|
+
join3(__dirname4, "..", "..", "..", "agent_moss", "rules"),
|
|
1887
|
+
join3(process.cwd(), "agent_moss", "rules")
|
|
1888
|
+
];
|
|
1889
|
+
let mc = {};
|
|
1890
|
+
for (const dir of cands) {
|
|
1891
|
+
const p = join3(dir, "mc_rules.json");
|
|
1892
|
+
if (existsSync4(p)) {
|
|
1893
|
+
try {
|
|
1894
|
+
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
1895
|
+
} catch {
|
|
1896
|
+
mc = {};
|
|
1897
|
+
}
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
const rules = mc.rules || [];
|
|
1902
|
+
return {
|
|
1903
|
+
ltl_rules: {
|
|
1904
|
+
category_enabled: true,
|
|
1905
|
+
category_desc: "LTL \u653B\u51FB\u6A21\u5F0F\u89C4\u5219\uFF08\u65F6\u5E8F\u653B\u51FB\u68C0\u6D4B\uFF0C\u5148 A \u540E B\uFF09",
|
|
1906
|
+
rules: rules.map((r) => ({
|
|
1907
|
+
id: r.id,
|
|
1908
|
+
reason: r.reason || "",
|
|
1909
|
+
risk_level: r.risk_level || "high",
|
|
1910
|
+
action: r.action || "block",
|
|
1911
|
+
enabled: true,
|
|
1912
|
+
builtin: true
|
|
1913
|
+
}))
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1838
1917
|
function normalizePatternForFingerprint(pattern) {
|
|
1839
1918
|
return pattern.replace(/\\\//g, "/");
|
|
1840
1919
|
}
|
|
@@ -1984,6 +2063,7 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
1984
2063
|
);
|
|
1985
2064
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1986
2065
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2066
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_4");
|
|
1987
2067
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1988
2068
|
_mergeAgentRules(cfg, srcDefaults);
|
|
1989
2069
|
_mergeAgentRules(cfg, srcDefaults, "scenario_rules");
|
|
@@ -2176,12 +2256,12 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2176
2256
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2177
2257
|
_validateSkillId(skillId);
|
|
2178
2258
|
const runtime = loadRuntimeConfig();
|
|
2179
|
-
const skillsDir =
|
|
2259
|
+
const skillsDir = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2180
2260
|
try {
|
|
2181
2261
|
mkdirSync(skillsDir, { recursive: true });
|
|
2182
2262
|
} catch {
|
|
2183
2263
|
}
|
|
2184
|
-
writeFileSync(
|
|
2264
|
+
writeFileSync(join3(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2185
2265
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2186
2266
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2187
2267
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -2196,7 +2276,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2196
2276
|
if (idx >= 0) {
|
|
2197
2277
|
if (skills[idx].builtin) return null;
|
|
2198
2278
|
skills.splice(idx, 1);
|
|
2199
|
-
const skillPath =
|
|
2279
|
+
const skillPath = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2200
2280
|
try {
|
|
2201
2281
|
if (existsSync4(skillPath)) unlinkSync(skillPath);
|
|
2202
2282
|
} catch {
|
|
@@ -2390,7 +2470,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
2390
2470
|
saveRuntimeConfig(runtime);
|
|
2391
2471
|
return runtime;
|
|
2392
2472
|
}
|
|
2393
|
-
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
|
|
2473
|
+
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;
|
|
2394
2474
|
var init_runtime_config = __esm({
|
|
2395
2475
|
"src/infra/runtime-config.ts"() {
|
|
2396
2476
|
"use strict";
|
|
@@ -2402,21 +2482,28 @@ var init_runtime_config = __esm({
|
|
|
2402
2482
|
__dirname4 = dirname3(fileURLToPath5(import.meta.url));
|
|
2403
2483
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2404
2484
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
2405
|
-
RUNTIME_CONFIG_PATH =
|
|
2485
|
+
RUNTIME_CONFIG_PATH = join3(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2406
2486
|
LAYER_DISABLE_ENV = {
|
|
2407
2487
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2408
2488
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2409
2489
|
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2410
2490
|
};
|
|
2491
|
+
DEFAULT_LAYERS = {
|
|
2492
|
+
L1_heuristic: true,
|
|
2493
|
+
L2_logic_rules: true,
|
|
2494
|
+
L3_llm_analysis: true,
|
|
2495
|
+
agent_rules_enabled: true,
|
|
2496
|
+
scenario_rules_enabled: true
|
|
2497
|
+
};
|
|
2411
2498
|
DENY_MODE_LABELS = {
|
|
2412
2499
|
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2413
2500
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2414
2501
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2415
2502
|
};
|
|
2416
|
-
_AGENTS_DIR_PROD =
|
|
2417
|
-
_AGENTS_DIR_DEV =
|
|
2418
|
-
_SCENARIOS_DIR_PROD =
|
|
2419
|
-
_SCENARIOS_DIR_DEV =
|
|
2503
|
+
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2504
|
+
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2505
|
+
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
2506
|
+
_SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
2420
2507
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2421
2508
|
}
|
|
2422
2509
|
});
|
|
@@ -2554,11 +2641,21 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2554
2641
|
);
|
|
2555
2642
|
return ext;
|
|
2556
2643
|
}
|
|
2644
|
+
function getScenarioExtensionsMulti(scenarioSpec) {
|
|
2645
|
+
if (!scenarioSpec) return null;
|
|
2646
|
+
const ids = scenarioSpec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2647
|
+
if (ids.length === 0) return null;
|
|
2648
|
+
const exts = ids.map((id) => getScenarioExtensions(id));
|
|
2649
|
+
const nonNull = exts.filter((e) => e !== null);
|
|
2650
|
+
if (nonNull.length === 0) return null;
|
|
2651
|
+
return mergeAgentExtensions(...nonNull);
|
|
2652
|
+
}
|
|
2557
2653
|
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
|
|
2558
2654
|
var init_scenarios = __esm({
|
|
2559
2655
|
"src/scenarios/index.ts"() {
|
|
2560
2656
|
"use strict";
|
|
2561
2657
|
init_esm_shims();
|
|
2658
|
+
init_agents();
|
|
2562
2659
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
2563
2660
|
__dirname6 = dirname5(__filename5);
|
|
2564
2661
|
SCENARIOS_DIR_PROD = resolve4(__dirname6, "scenarios");
|
|
@@ -2607,8 +2704,8 @@ function runHeuristicCheck(input) {
|
|
|
2607
2704
|
}
|
|
2608
2705
|
highestRiskType = topRiskType || highestRiskType;
|
|
2609
2706
|
const agentExt = mergeAgentExtensions(
|
|
2610
|
-
getAgentExtensions(input.agentId),
|
|
2611
|
-
|
|
2707
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
2708
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
2612
2709
|
);
|
|
2613
2710
|
if (agentExt?.extra_command_patterns) {
|
|
2614
2711
|
for (const rule of agentExt.extra_command_patterns) {
|
|
@@ -3053,6 +3150,7 @@ var init_heuristic = __esm({
|
|
|
3053
3150
|
init_config();
|
|
3054
3151
|
init_agents();
|
|
3055
3152
|
init_scenarios();
|
|
3153
|
+
init_runtime_config();
|
|
3056
3154
|
init_rules_loader();
|
|
3057
3155
|
init_logic_rules();
|
|
3058
3156
|
INJECTION_KEYWORDS = (() => {
|
|
@@ -3252,6 +3350,112 @@ var init_token_stats = __esm({
|
|
|
3252
3350
|
}
|
|
3253
3351
|
});
|
|
3254
3352
|
|
|
3353
|
+
// src/engine/formal-engine/decode.ts
|
|
3354
|
+
var decode_exports = {};
|
|
3355
|
+
__export(decode_exports, {
|
|
3356
|
+
allCandidates: () => allCandidates,
|
|
3357
|
+
decodeBase64Literals: () => decodeBase64Literals,
|
|
3358
|
+
decodeUrlAndNumeric: () => decodeUrlAndNumeric,
|
|
3359
|
+
matchStageAny: () => matchStageAny
|
|
3360
|
+
});
|
|
3361
|
+
function isValidBase64Token(tok) {
|
|
3362
|
+
if (tok.length < 8) return false;
|
|
3363
|
+
if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
|
|
3364
|
+
return true;
|
|
3365
|
+
}
|
|
3366
|
+
function isPrintable(s) {
|
|
3367
|
+
for (let i = 0; i < s.length; i++) {
|
|
3368
|
+
const c = s.charCodeAt(i);
|
|
3369
|
+
if (c < 32 || c > 126) return false;
|
|
3370
|
+
}
|
|
3371
|
+
return true;
|
|
3372
|
+
}
|
|
3373
|
+
function decodeBase64Literals(text) {
|
|
3374
|
+
const out = [];
|
|
3375
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3376
|
+
for (const m of text.matchAll(B64_TOKEN_RE)) {
|
|
3377
|
+
const tok = m[0];
|
|
3378
|
+
if (!isValidBase64Token(tok)) continue;
|
|
3379
|
+
if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
|
|
3380
|
+
try {
|
|
3381
|
+
const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
|
|
3382
|
+
const bin = Buffer.from(padded, "base64");
|
|
3383
|
+
const s = bin.toString("utf8");
|
|
3384
|
+
if (s && isPrintable(s) && !seen.has(s)) {
|
|
3385
|
+
seen.add(s);
|
|
3386
|
+
out.push(s);
|
|
3387
|
+
}
|
|
3388
|
+
} catch {
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
return out;
|
|
3392
|
+
}
|
|
3393
|
+
function decodeUrlAndNumeric(text) {
|
|
3394
|
+
const out = [];
|
|
3395
|
+
let s = text;
|
|
3396
|
+
for (let i = 0; i < 3; i++) {
|
|
3397
|
+
let nxt = s;
|
|
3398
|
+
try {
|
|
3399
|
+
nxt = decodeURIComponent(s);
|
|
3400
|
+
} catch {
|
|
3401
|
+
break;
|
|
3402
|
+
}
|
|
3403
|
+
if (nxt === s) break;
|
|
3404
|
+
s = nxt;
|
|
3405
|
+
}
|
|
3406
|
+
if (s !== text) out.push(s);
|
|
3407
|
+
if (/\\[0-7]{3}/.test(text)) {
|
|
3408
|
+
let expanded = "";
|
|
3409
|
+
try {
|
|
3410
|
+
expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
3414
|
+
}
|
|
3415
|
+
if (/\\x[0-9a-fA-F]{2}/.test(text)) {
|
|
3416
|
+
let expanded = "";
|
|
3417
|
+
try {
|
|
3418
|
+
expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
|
|
3419
|
+
} catch {
|
|
3420
|
+
}
|
|
3421
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
3422
|
+
}
|
|
3423
|
+
return out;
|
|
3424
|
+
}
|
|
3425
|
+
function allCandidates(detail, max = 8) {
|
|
3426
|
+
const cands = [detail];
|
|
3427
|
+
cands.push(...decodeBase64Literals(detail));
|
|
3428
|
+
cands.push(...decodeUrlAndNumeric(detail));
|
|
3429
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3430
|
+
const out = [];
|
|
3431
|
+
for (const c of cands) {
|
|
3432
|
+
if (seen.has(c) || c.length > 4096) continue;
|
|
3433
|
+
seen.add(c);
|
|
3434
|
+
out.push(c);
|
|
3435
|
+
if (out.length >= max) break;
|
|
3436
|
+
}
|
|
3437
|
+
return out;
|
|
3438
|
+
}
|
|
3439
|
+
function matchStageAny(detail, stages) {
|
|
3440
|
+
for (const cand of allCandidates(detail)) {
|
|
3441
|
+
for (const [name, pats] of Object.entries(stages)) {
|
|
3442
|
+
for (const p of pats) {
|
|
3443
|
+
p.lastIndex = 0;
|
|
3444
|
+
if (p.test(cand)) return name;
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
return "";
|
|
3449
|
+
}
|
|
3450
|
+
var B64_TOKEN_RE;
|
|
3451
|
+
var init_decode = __esm({
|
|
3452
|
+
"src/engine/formal-engine/decode.ts"() {
|
|
3453
|
+
"use strict";
|
|
3454
|
+
init_esm_shims();
|
|
3455
|
+
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
3456
|
+
}
|
|
3457
|
+
});
|
|
3458
|
+
|
|
3255
3459
|
// node_modules/better-sqlite3/lib/util.js
|
|
3256
3460
|
var require_util = __commonJS({
|
|
3257
3461
|
"node_modules/better-sqlite3/lib/util.js"(exports) {
|
|
@@ -3335,7 +3539,7 @@ var require_bindings = __commonJS({
|
|
|
3335
3539
|
var fs7 = __require("fs");
|
|
3336
3540
|
var path6 = __require("path");
|
|
3337
3541
|
var fileURLToPath10 = require_file_uri_to_path();
|
|
3338
|
-
var
|
|
3542
|
+
var join11 = path6.join;
|
|
3339
3543
|
var dirname11 = path6.dirname;
|
|
3340
3544
|
var exists = fs7.accessSync && function(path7) {
|
|
3341
3545
|
try {
|
|
@@ -3395,7 +3599,7 @@ var require_bindings = __commonJS({
|
|
|
3395
3599
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
3396
3600
|
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
3397
3601
|
for (; i < l; i++) {
|
|
3398
|
-
n =
|
|
3602
|
+
n = join11.apply(
|
|
3399
3603
|
null,
|
|
3400
3604
|
opts.try[i].map(function(p) {
|
|
3401
3605
|
return opts[p] || p;
|
|
@@ -3456,7 +3660,7 @@ var require_bindings = __commonJS({
|
|
|
3456
3660
|
if (dir === ".") {
|
|
3457
3661
|
dir = process.cwd();
|
|
3458
3662
|
}
|
|
3459
|
-
if (exists(
|
|
3663
|
+
if (exists(join11(dir, "package.json")) || exists(join11(dir, "node_modules"))) {
|
|
3460
3664
|
return dir;
|
|
3461
3665
|
}
|
|
3462
3666
|
if (prev === dir) {
|
|
@@ -3465,7 +3669,7 @@ var require_bindings = __commonJS({
|
|
|
3465
3669
|
);
|
|
3466
3670
|
}
|
|
3467
3671
|
prev = dir;
|
|
3468
|
-
dir =
|
|
3672
|
+
dir = join11(dir, "..");
|
|
3469
3673
|
}
|
|
3470
3674
|
};
|
|
3471
3675
|
}
|
|
@@ -4171,7 +4375,7 @@ function loadTemplates() {
|
|
|
4171
4375
|
// src/engine/script-analyzer.ts
|
|
4172
4376
|
init_esm_shims();
|
|
4173
4377
|
import { readFileSync as readFileSync8 } from "fs";
|
|
4174
|
-
import { resolve as resolve6, join as
|
|
4378
|
+
import { resolve as resolve6, join as join4 } from "path";
|
|
4175
4379
|
var SUSPICIOUS_PATTERNS = [
|
|
4176
4380
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
4177
4381
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -4304,7 +4508,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4304
4508
|
}
|
|
4305
4509
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4306
4510
|
try {
|
|
4307
|
-
const expandedPath = scriptPath.startsWith("~") ?
|
|
4511
|
+
const expandedPath = scriptPath.startsWith("~") ? join4(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve6(scriptPath);
|
|
4308
4512
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
4309
4513
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4310
4514
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -4360,8 +4564,8 @@ init_esm_shims();
|
|
|
4360
4564
|
init_heuristic();
|
|
4361
4565
|
init_logic_rules();
|
|
4362
4566
|
import { statSync as statSync2, existsSync as existsSync9 } from "fs";
|
|
4363
|
-
import { resolve as resolve8, join as
|
|
4364
|
-
import { homedir as
|
|
4567
|
+
import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
4568
|
+
import { homedir as homedir4 } from "os";
|
|
4365
4569
|
|
|
4366
4570
|
// src/engine/script-engine/tracker.ts
|
|
4367
4571
|
init_esm_shims();
|
|
@@ -6006,7 +6210,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
6006
6210
|
function resolvePath2(p) {
|
|
6007
6211
|
let expanded = p;
|
|
6008
6212
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6009
|
-
expanded =
|
|
6213
|
+
expanded = join6(homedir4(), expanded.slice(1));
|
|
6010
6214
|
}
|
|
6011
6215
|
return resolve8(expanded);
|
|
6012
6216
|
}
|
|
@@ -6087,7 +6291,7 @@ function analyzeScriptContent2(input) {
|
|
|
6087
6291
|
isInline = true;
|
|
6088
6292
|
}
|
|
6089
6293
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
6090
|
-
resolvedPath =
|
|
6294
|
+
resolvedPath = join6(input.cwd || process.cwd(), resolvedPath);
|
|
6091
6295
|
}
|
|
6092
6296
|
const l1Adapter = (content) => {
|
|
6093
6297
|
const virtualInput = {
|
|
@@ -6398,6 +6602,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
6398
6602
|
|
|
6399
6603
|
// src/engine/coordinator.ts
|
|
6400
6604
|
init_config();
|
|
6605
|
+
init_runtime_config();
|
|
6401
6606
|
init_agents();
|
|
6402
6607
|
init_scenarios();
|
|
6403
6608
|
|
|
@@ -6407,9 +6612,9 @@ init_esm_shims();
|
|
|
6407
6612
|
// src/brain/rule-store.ts
|
|
6408
6613
|
init_esm_shims();
|
|
6409
6614
|
import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
6410
|
-
import { join as
|
|
6411
|
-
import { homedir as
|
|
6412
|
-
var DEFAULT_RULES_PATH =
|
|
6615
|
+
import { join as join7 } from "path";
|
|
6616
|
+
import { homedir as homedir5 } from "os";
|
|
6617
|
+
var DEFAULT_RULES_PATH = join7(homedir5(), ".agentmoss", "brain", "rules.json");
|
|
6413
6618
|
var ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
6414
6619
|
var STATUS_DRAFT = "draft";
|
|
6415
6620
|
var STATUS_APPROVED = "approved";
|
|
@@ -6935,98 +7140,132 @@ init_esm_shims();
|
|
|
6935
7140
|
// src/engine/formal-engine/builder.ts
|
|
6936
7141
|
init_esm_shims();
|
|
6937
7142
|
init_rules_loader();
|
|
7143
|
+
init_decode();
|
|
6938
7144
|
|
|
6939
|
-
// src/engine/formal-engine/
|
|
7145
|
+
// src/engine/formal-engine/ltl.ts
|
|
6940
7146
|
init_esm_shims();
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
const c = s.charCodeAt(i);
|
|
6950
|
-
if (c < 32 || c > 126) return false;
|
|
6951
|
-
}
|
|
6952
|
-
return true;
|
|
7147
|
+
init_rules_loader();
|
|
7148
|
+
|
|
7149
|
+
// src/engine/formal-engine/ir.ts
|
|
7150
|
+
init_esm_shims();
|
|
7151
|
+
function allNodes(mg) {
|
|
7152
|
+
const out = [];
|
|
7153
|
+
for (const s of mg.sessions) out.push(...s.nodes);
|
|
7154
|
+
return out;
|
|
6953
7155
|
}
|
|
6954
|
-
function
|
|
7156
|
+
function allEdges(mg) {
|
|
6955
7157
|
const out = [];
|
|
6956
|
-
const
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
7158
|
+
for (const s of mg.sessions) out.push(...s.edges);
|
|
7159
|
+
out.push(...mg.crossEdges);
|
|
7160
|
+
return out;
|
|
7161
|
+
}
|
|
7162
|
+
function emptyVerificationResult() {
|
|
7163
|
+
return { verified: true, violations: [] };
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
// src/engine/formal-engine/ltl.ts
|
|
7167
|
+
var AP_RE = null;
|
|
7168
|
+
function compileAps(apPatterns) {
|
|
7169
|
+
if (AP_RE) return;
|
|
7170
|
+
AP_RE = {};
|
|
7171
|
+
for (const [name, pats] of Object.entries(apPatterns || {})) {
|
|
7172
|
+
AP_RE[name] = pats.map((p) => new RegExp(p, "i"));
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
function labelEvent(actionDetail) {
|
|
7176
|
+
if (!AP_RE) compileAps(loadMC().ap_patterns || {});
|
|
7177
|
+
let candidates = [actionDetail];
|
|
7178
|
+
try {
|
|
7179
|
+
candidates = (init_decode(), __toCommonJS(decode_exports)).allCandidates(actionDetail);
|
|
7180
|
+
} catch {
|
|
7181
|
+
}
|
|
7182
|
+
const hits = /* @__PURE__ */ new Set();
|
|
7183
|
+
for (const [name, pats] of Object.entries(AP_RE || {})) {
|
|
7184
|
+
for (const c of candidates) {
|
|
7185
|
+
const m = pats.some((p) => p.test(c));
|
|
7186
|
+
if (m) {
|
|
7187
|
+
hits.add(name);
|
|
7188
|
+
break;
|
|
6968
7189
|
}
|
|
6969
|
-
} catch {
|
|
6970
7190
|
}
|
|
6971
7191
|
}
|
|
6972
|
-
return
|
|
7192
|
+
return hits;
|
|
6973
7193
|
}
|
|
6974
|
-
function
|
|
6975
|
-
const
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
7194
|
+
function evaluateLTL(formula, trace, pos) {
|
|
7195
|
+
const op = formula[0];
|
|
7196
|
+
switch (op) {
|
|
7197
|
+
case "ap":
|
|
7198
|
+
return trace[pos].has(formula[1]);
|
|
7199
|
+
case "!":
|
|
7200
|
+
return !evaluateLTL(formula[1], trace, pos);
|
|
7201
|
+
case "&":
|
|
7202
|
+
return evaluateLTL(formula[1], trace, pos) && evaluateLTL(formula[2], trace, pos);
|
|
7203
|
+
case "|":
|
|
7204
|
+
return evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
|
|
7205
|
+
case "->":
|
|
7206
|
+
return !evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
|
|
7207
|
+
case "X":
|
|
7208
|
+
return pos + 1 < trace.length && evaluateLTL(formula[1], trace, pos + 1);
|
|
7209
|
+
case "G": {
|
|
7210
|
+
for (let j = pos; j < trace.length; j++) {
|
|
7211
|
+
if (!evaluateLTL(formula[1], trace, j)) return false;
|
|
7212
|
+
}
|
|
7213
|
+
return true;
|
|
6983
7214
|
}
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
let expanded = "";
|
|
6990
|
-
try {
|
|
6991
|
-
expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
|
|
6992
|
-
} catch {
|
|
7215
|
+
case "F": {
|
|
7216
|
+
for (let j = pos; j < trace.length; j++) {
|
|
7217
|
+
if (evaluateLTL(formula[1], trace, j)) return true;
|
|
7218
|
+
}
|
|
7219
|
+
return false;
|
|
6993
7220
|
}
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7221
|
+
case "U": {
|
|
7222
|
+
for (let j = pos; j < trace.length; j++) {
|
|
7223
|
+
if (evaluateLTL(formula[2], trace, j)) {
|
|
7224
|
+
for (let k = pos; k < j; k++) {
|
|
7225
|
+
if (!evaluateLTL(formula[1], trace, k)) return false;
|
|
7226
|
+
}
|
|
7227
|
+
return true;
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
return false;
|
|
7001
7231
|
}
|
|
7002
|
-
|
|
7232
|
+
default:
|
|
7233
|
+
return false;
|
|
7003
7234
|
}
|
|
7004
|
-
return out;
|
|
7005
7235
|
}
|
|
7006
|
-
function
|
|
7007
|
-
const
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
if (
|
|
7014
|
-
seen.add(c);
|
|
7015
|
-
out.push(c);
|
|
7016
|
-
if (out.length >= max) break;
|
|
7236
|
+
function ruleViolates(rule, traceWith, witnessMode) {
|
|
7237
|
+
const formula = rule?.formula;
|
|
7238
|
+
if (!Array.isArray(formula) || formula.length === 0) return false;
|
|
7239
|
+
const satWith = evaluateLTL(formula, traceWith, 0);
|
|
7240
|
+
if (!satWith) return false;
|
|
7241
|
+
if (witnessMode && traceWith.length > 1) {
|
|
7242
|
+
const traceWithout = traceWith.slice(0, -1);
|
|
7243
|
+
if (evaluateLTL(formula, traceWithout, 0)) return false;
|
|
7017
7244
|
}
|
|
7018
|
-
return
|
|
7245
|
+
return true;
|
|
7019
7246
|
}
|
|
7020
|
-
function
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7247
|
+
function checkMCRules(graph) {
|
|
7248
|
+
const violations = [];
|
|
7249
|
+
const nodes = allNodes(graph);
|
|
7250
|
+
if (nodes.length === 0) return violations;
|
|
7251
|
+
const mc = loadMC();
|
|
7252
|
+
const rules = mc.rules || [];
|
|
7253
|
+
if (rules.length === 0) return violations;
|
|
7254
|
+
const trace = nodes.map((n) => labelEvent(n.actionDetail));
|
|
7255
|
+
const witnessMode = graph.witnessIdx !== void 0;
|
|
7256
|
+
for (const rule of rules) {
|
|
7257
|
+
if (rule?.type !== "ltl" || !(rule.enabled !== false)) continue;
|
|
7258
|
+
if (ruleViolates(rule, trace, witnessMode)) {
|
|
7259
|
+
violations.push({
|
|
7260
|
+
propertyId: String(rule.id || "mc-ltl"),
|
|
7261
|
+
reason: String(rule.reason || "L2.2 \u6A21\u578B\u68C0\u6D4B\u89C4\u5219\u547D\u4E2D"),
|
|
7262
|
+
riskLevel: String(rule.risk_level || "high"),
|
|
7263
|
+
path: nodes.filter((n) => labelEvent(n.actionDetail).size > 0).slice(-3).map((n) => ({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }))
|
|
7264
|
+
});
|
|
7265
|
+
if (witnessMode) return violations;
|
|
7027
7266
|
}
|
|
7028
7267
|
}
|
|
7029
|
-
return
|
|
7268
|
+
return violations;
|
|
7030
7269
|
}
|
|
7031
7270
|
|
|
7032
7271
|
// src/engine/formal-engine/builder.ts
|
|
@@ -7057,6 +7296,26 @@ function extractResources(actionDetail) {
|
|
|
7057
7296
|
function markStage(actionDetail) {
|
|
7058
7297
|
return matchStageAny(actionDetail, loadStages());
|
|
7059
7298
|
}
|
|
7299
|
+
function markIntegrity(actionDetail) {
|
|
7300
|
+
const aps = labelEvent(actionDetail);
|
|
7301
|
+
if (aps.has("download_file") || aps.has("prompt_injection")) {
|
|
7302
|
+
return { integrity: "low", isLowSource: true };
|
|
7303
|
+
}
|
|
7304
|
+
return { integrity: "medium", isLowSource: false };
|
|
7305
|
+
}
|
|
7306
|
+
function initNode(sessionId, seq, actionType, actionDetail) {
|
|
7307
|
+
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
7308
|
+
return {
|
|
7309
|
+
sessionId,
|
|
7310
|
+
seq,
|
|
7311
|
+
actionType,
|
|
7312
|
+
actionDetail,
|
|
7313
|
+
stage: markStage(actionDetail),
|
|
7314
|
+
resources: extractResources(actionDetail),
|
|
7315
|
+
integrity,
|
|
7316
|
+
isLowSource
|
|
7317
|
+
};
|
|
7318
|
+
}
|
|
7060
7319
|
function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
7061
7320
|
const nodes = [];
|
|
7062
7321
|
const edges = [];
|
|
@@ -7070,17 +7329,9 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
|
7070
7329
|
details.push({ actionType: String(aNext.actionType || ""), actionDetail: String(aNext.actionDetail || "") });
|
|
7071
7330
|
}
|
|
7072
7331
|
details.forEach((d, seq) => {
|
|
7073
|
-
const
|
|
7074
|
-
const node = {
|
|
7075
|
-
sessionId,
|
|
7076
|
-
seq,
|
|
7077
|
-
actionType: d.actionType,
|
|
7078
|
-
actionDetail: d.actionDetail,
|
|
7079
|
-
stage,
|
|
7080
|
-
resources: extractResources(d.actionDetail)
|
|
7081
|
-
};
|
|
7332
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
|
|
7082
7333
|
nodes.push(node);
|
|
7083
|
-
if (stage) stageFlags.add(stage);
|
|
7334
|
+
if (node.stage) stageFlags.add(node.stage);
|
|
7084
7335
|
});
|
|
7085
7336
|
for (let i = 0; i < nodes.length - 1; i++) {
|
|
7086
7337
|
edges.push({ src: i, dst: i + 1, kind: "next" });
|
|
@@ -7100,15 +7351,12 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
7100
7351
|
if (aNext) {
|
|
7101
7352
|
const last = mg.sessions[mg.sessions.length - 1];
|
|
7102
7353
|
if (last.sessionId === "current") {
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
stage,
|
|
7110
|
-
resources: extractResources(String(aNext.actionDetail || ""))
|
|
7111
|
-
});
|
|
7354
|
+
last.nodes.push(initNode(
|
|
7355
|
+
last.sessionId,
|
|
7356
|
+
last.nodes.length,
|
|
7357
|
+
String(aNext.actionType || ""),
|
|
7358
|
+
String(aNext.actionDetail || "")
|
|
7359
|
+
));
|
|
7112
7360
|
if (last.nodes.length > 1) {
|
|
7113
7361
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
7114
7362
|
}
|
|
@@ -7122,11 +7370,25 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
7122
7370
|
function addCrossDataEdges(mg) {
|
|
7123
7371
|
const nodes = allNodesFlat(mg);
|
|
7124
7372
|
if (nodes.length < 2) return;
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
const
|
|
7129
|
-
if (
|
|
7373
|
+
const resIndex = /* @__PURE__ */ new Map();
|
|
7374
|
+
nodes.forEach((n, i) => {
|
|
7375
|
+
for (const r of n.resources) {
|
|
7376
|
+
const list = resIndex.get(r);
|
|
7377
|
+
if (list) list.push(i);
|
|
7378
|
+
else resIndex.set(r, [i]);
|
|
7379
|
+
}
|
|
7380
|
+
});
|
|
7381
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7382
|
+
for (const idxs of resIndex.values()) {
|
|
7383
|
+
if (idxs.length < 2) continue;
|
|
7384
|
+
for (let a = 0; a < idxs.length; a++) {
|
|
7385
|
+
const i = idxs[a];
|
|
7386
|
+
for (let b = a + 1; b < idxs.length; b++) {
|
|
7387
|
+
const j = idxs[b];
|
|
7388
|
+
if (nodes[i].sessionId === nodes[j].sessionId) continue;
|
|
7389
|
+
const key = `${i}:${j}`;
|
|
7390
|
+
if (seen.has(key)) continue;
|
|
7391
|
+
seen.add(key);
|
|
7130
7392
|
mg.crossEdges.push({ src: i, dst: j, kind: "data" });
|
|
7131
7393
|
}
|
|
7132
7394
|
}
|
|
@@ -7137,28 +7399,6 @@ function allNodesFlat(mg) {
|
|
|
7137
7399
|
for (const s of mg.sessions) out.push(...s.nodes);
|
|
7138
7400
|
return out;
|
|
7139
7401
|
}
|
|
7140
|
-
function intersect(a, b) {
|
|
7141
|
-
const out = /* @__PURE__ */ new Set();
|
|
7142
|
-
for (const v of a) if (b.has(v)) out.add(v);
|
|
7143
|
-
return out;
|
|
7144
|
-
}
|
|
7145
|
-
|
|
7146
|
-
// src/engine/formal-engine/ir.ts
|
|
7147
|
-
init_esm_shims();
|
|
7148
|
-
function allNodes(mg) {
|
|
7149
|
-
const out = [];
|
|
7150
|
-
for (const s of mg.sessions) out.push(...s.nodes);
|
|
7151
|
-
return out;
|
|
7152
|
-
}
|
|
7153
|
-
function allEdges(mg) {
|
|
7154
|
-
const out = [];
|
|
7155
|
-
for (const s of mg.sessions) out.push(...s.edges);
|
|
7156
|
-
out.push(...mg.crossEdges);
|
|
7157
|
-
return out;
|
|
7158
|
-
}
|
|
7159
|
-
function emptyVerificationResult() {
|
|
7160
|
-
return { verified: true, violations: [] };
|
|
7161
|
-
}
|
|
7162
7402
|
|
|
7163
7403
|
// src/engine/formal-engine/properties.ts
|
|
7164
7404
|
init_esm_shims();
|
|
@@ -7181,27 +7421,50 @@ function checkCrossSessionChain(graph) {
|
|
|
7181
7421
|
const violations = [];
|
|
7182
7422
|
const nodes = allNodes(graph);
|
|
7183
7423
|
if (nodes.length === 0) return violations;
|
|
7424
|
+
const witness = graph.witnessIdx;
|
|
7184
7425
|
for (const chain of loadCrossSessionChains()) {
|
|
7185
7426
|
const stages = chain.stages;
|
|
7186
7427
|
if (!stages.length) continue;
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7428
|
+
if (witness !== void 0) {
|
|
7429
|
+
if (nodes[witness].stage !== stages[stages.length - 1]) continue;
|
|
7430
|
+
let matched = 0;
|
|
7431
|
+
const path6 = [];
|
|
7432
|
+
for (let k = 0; k < witness; k++) {
|
|
7433
|
+
const n = nodes[k];
|
|
7434
|
+
if (matched < stages.length - 1 && n.stage === stages[matched]) {
|
|
7435
|
+
matched += 1;
|
|
7436
|
+
path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
|
|
7437
|
+
}
|
|
7196
7438
|
}
|
|
7197
|
-
|
|
7198
|
-
|
|
7439
|
+
if (matched < stages.length - 1) continue;
|
|
7440
|
+
path6.push({ sessionId: nodes[witness].sessionId, seq: nodes[witness].seq, detail: nodes[witness].actionDetail.slice(0, 60) });
|
|
7199
7441
|
violations.push({
|
|
7200
7442
|
propertyId: "cross-" + chain.id,
|
|
7201
7443
|
reason: chain.reason,
|
|
7202
7444
|
riskLevel: chain.riskLevel,
|
|
7203
7445
|
path: path6
|
|
7204
7446
|
});
|
|
7447
|
+
return violations;
|
|
7448
|
+
} else {
|
|
7449
|
+
let matched = 0;
|
|
7450
|
+
let stageIdx = 0;
|
|
7451
|
+
const path6 = [];
|
|
7452
|
+
for (const n of nodes) {
|
|
7453
|
+
if (stageIdx < stages.length && n.stage === stages[stageIdx]) {
|
|
7454
|
+
matched += 1;
|
|
7455
|
+
path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
|
|
7456
|
+
stageIdx += 1;
|
|
7457
|
+
if (matched >= stages.length) break;
|
|
7458
|
+
}
|
|
7459
|
+
}
|
|
7460
|
+
if (matched >= stages.length) {
|
|
7461
|
+
violations.push({
|
|
7462
|
+
propertyId: "cross-" + chain.id,
|
|
7463
|
+
reason: chain.reason,
|
|
7464
|
+
riskLevel: chain.riskLevel,
|
|
7465
|
+
path: path6
|
|
7466
|
+
});
|
|
7467
|
+
}
|
|
7205
7468
|
}
|
|
7206
7469
|
}
|
|
7207
7470
|
return violations;
|
|
@@ -7236,7 +7499,10 @@ function checkSensitiveDataFlow(graph) {
|
|
|
7236
7499
|
}
|
|
7237
7500
|
}
|
|
7238
7501
|
}
|
|
7239
|
-
|
|
7502
|
+
const witness = graph.witnessIdx;
|
|
7503
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7504
|
+
for (const i of candidates) {
|
|
7505
|
+
const n = nodes[i];
|
|
7240
7506
|
if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
|
|
7241
7507
|
violations.push({
|
|
7242
7508
|
propertyId: "sensitive-data-exfil",
|
|
@@ -7244,6 +7510,7 @@ function checkSensitiveDataFlow(graph) {
|
|
|
7244
7510
|
riskLevel: "critical",
|
|
7245
7511
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
7246
7512
|
});
|
|
7513
|
+
if (witness !== void 0) return violations;
|
|
7247
7514
|
}
|
|
7248
7515
|
}
|
|
7249
7516
|
return violations;
|
|
@@ -7274,6 +7541,32 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
7274
7541
|
const violations = [];
|
|
7275
7542
|
const nodes = allNodes(graph);
|
|
7276
7543
|
if (nodes.length < 2) return violations;
|
|
7544
|
+
const witness = graph.witnessIdx;
|
|
7545
|
+
if (witness !== void 0) {
|
|
7546
|
+
const evIdx = [];
|
|
7547
|
+
nodes.forEach((n, i) => {
|
|
7548
|
+
if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
|
|
7549
|
+
});
|
|
7550
|
+
if (evIdx.length === 0) return violations;
|
|
7551
|
+
const firstEv = Math.min(...evIdx);
|
|
7552
|
+
let pre = null;
|
|
7553
|
+
for (let i = 0; i < firstEv; i++) {
|
|
7554
|
+
if (isPrivescNode(nodes[i])) pre = nodes[i];
|
|
7555
|
+
}
|
|
7556
|
+
if (pre) {
|
|
7557
|
+
const post = nodes[firstEv];
|
|
7558
|
+
return [{
|
|
7559
|
+
propertyId: "no-privesc-then-exfil",
|
|
7560
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
|
|
7561
|
+
riskLevel: "critical",
|
|
7562
|
+
path: [
|
|
7563
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
7564
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
7565
|
+
]
|
|
7566
|
+
}];
|
|
7567
|
+
}
|
|
7568
|
+
return violations;
|
|
7569
|
+
}
|
|
7277
7570
|
for (let i = 0; i < nodes.length; i++) {
|
|
7278
7571
|
if (!isPrivescNode(nodes[i])) continue;
|
|
7279
7572
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
@@ -7297,6 +7590,32 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
7297
7590
|
const violations = [];
|
|
7298
7591
|
const nodes = allNodes(graph);
|
|
7299
7592
|
if (nodes.length < 2) return violations;
|
|
7593
|
+
const witness = graph.witnessIdx;
|
|
7594
|
+
if (witness !== void 0) {
|
|
7595
|
+
const evIdx = [];
|
|
7596
|
+
nodes.forEach((n, i) => {
|
|
7597
|
+
if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
|
|
7598
|
+
});
|
|
7599
|
+
if (evIdx.length === 0) return violations;
|
|
7600
|
+
const firstEv = Math.min(...evIdx);
|
|
7601
|
+
let pre = null;
|
|
7602
|
+
for (let i = 0; i < firstEv; i++) {
|
|
7603
|
+
if (isCredNode(nodes[i])) pre = nodes[i];
|
|
7604
|
+
}
|
|
7605
|
+
if (pre) {
|
|
7606
|
+
const post = nodes[firstEv];
|
|
7607
|
+
return [{
|
|
7608
|
+
propertyId: "no-cred-then-exfil",
|
|
7609
|
+
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
7610
|
+
riskLevel: "critical",
|
|
7611
|
+
path: [
|
|
7612
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
7613
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
7614
|
+
]
|
|
7615
|
+
}];
|
|
7616
|
+
}
|
|
7617
|
+
return violations;
|
|
7618
|
+
}
|
|
7300
7619
|
for (let i = 0; i < nodes.length; i++) {
|
|
7301
7620
|
if (!isCredNode(nodes[i])) continue;
|
|
7302
7621
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
@@ -7316,11 +7635,172 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
7316
7635
|
}
|
|
7317
7636
|
return violations;
|
|
7318
7637
|
}
|
|
7638
|
+
var TOOL_REGISTRY = null;
|
|
7639
|
+
function getToolRegistry() {
|
|
7640
|
+
if (TOOL_REGISTRY) return TOOL_REGISTRY;
|
|
7641
|
+
TOOL_REGISTRY = loadToolRegistry().tools || {};
|
|
7642
|
+
return TOOL_REGISTRY;
|
|
7643
|
+
}
|
|
7644
|
+
function lookupToolClass(actionType) {
|
|
7645
|
+
const entry = getToolRegistry()[actionType] || {};
|
|
7646
|
+
return entry.actionClass;
|
|
7647
|
+
}
|
|
7648
|
+
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;
|
|
7649
|
+
var STRONG_COMPUTE_TYPES = /* @__PURE__ */ new Set(["python", "node", "exec", "terminal", "cmd"]);
|
|
7650
|
+
function isComputeNode(n) {
|
|
7651
|
+
if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
|
|
7652
|
+
const cls = lookupToolClass(n.actionType);
|
|
7653
|
+
if (cls === "read" || cls === "write" || cls === "network_egress" || cls === "message_egress") return false;
|
|
7654
|
+
return COMPUTE_RE.test(n.actionDetail);
|
|
7655
|
+
}
|
|
7656
|
+
function checkLowIntegrityCommandExec(graph) {
|
|
7657
|
+
const violations = [];
|
|
7658
|
+
const nodes = allNodes(graph);
|
|
7659
|
+
if (nodes.length === 0) return violations;
|
|
7660
|
+
const witness = graph.witnessIdx;
|
|
7661
|
+
const lowResources = /* @__PURE__ */ new Set();
|
|
7662
|
+
const lowAt = /* @__PURE__ */ new Map();
|
|
7663
|
+
nodes.forEach((n, i) => {
|
|
7664
|
+
if (n.isLowSource) {
|
|
7665
|
+
lowAt.set(i, n.actionDetail.slice(0, 60));
|
|
7666
|
+
for (const r of n.resources) lowResources.add(r);
|
|
7667
|
+
}
|
|
7668
|
+
});
|
|
7669
|
+
let changed = true;
|
|
7670
|
+
while (changed) {
|
|
7671
|
+
changed = false;
|
|
7672
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
7673
|
+
if (lowAt.has(i)) continue;
|
|
7674
|
+
let shared = "";
|
|
7675
|
+
for (const r of nodes[i].resources) {
|
|
7676
|
+
if (lowResources.has(r)) {
|
|
7677
|
+
shared = r;
|
|
7678
|
+
break;
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
if (shared) {
|
|
7682
|
+
lowAt.set(i, `\u7EE7\u627F\u81EA ${shared}`);
|
|
7683
|
+
for (const r of nodes[i].resources) lowResources.add(r);
|
|
7684
|
+
changed = true;
|
|
7685
|
+
}
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
if (lowAt.size === 0) return violations;
|
|
7689
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7690
|
+
for (const i of candidates) {
|
|
7691
|
+
const n = nodes[i];
|
|
7692
|
+
if (lowAt.has(i) && isComputeNode(n)) {
|
|
7693
|
+
violations.push({
|
|
7694
|
+
propertyId: "low-integrity-controls-command-exec",
|
|
7695
|
+
reason: `\u4F4E\u5B8C\u6574\u6027\u6570\u636E\u63A7\u5236\u547D\u4EE4\u6267\u884C\uFF1A${lowAt.get(i)} \u2192 ${n.sessionId}#${n.seq} \u6267\u884C\u4E0D\u53EF\u4FE1\u5185\u5BB9`,
|
|
7696
|
+
riskLevel: "critical",
|
|
7697
|
+
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
7698
|
+
});
|
|
7699
|
+
if (witness !== void 0) return violations;
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
return violations;
|
|
7703
|
+
}
|
|
7704
|
+
var DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
|
|
7705
|
+
function checkCrossSessionReachability(graph) {
|
|
7706
|
+
const violations = [];
|
|
7707
|
+
const nodes = allNodes(graph);
|
|
7708
|
+
if (nodes.length === 0) return violations;
|
|
7709
|
+
const witness = graph.witnessIdx;
|
|
7710
|
+
const sources = /* @__PURE__ */ new Set();
|
|
7711
|
+
nodes.forEach((n, i) => {
|
|
7712
|
+
if (n.isLowSource || DL_OR_LOW_RE.test(n.actionDetail)) sources.add(i);
|
|
7713
|
+
});
|
|
7714
|
+
if (sources.size === 0) return violations;
|
|
7715
|
+
const reached = new Set(sources);
|
|
7716
|
+
let changed = true;
|
|
7717
|
+
while (changed) {
|
|
7718
|
+
changed = false;
|
|
7719
|
+
for (const e of allEdges(graph)) {
|
|
7720
|
+
if (reached.has(e.src) && !reached.has(e.dst)) {
|
|
7721
|
+
const srcRes = nodes[e.src].resources;
|
|
7722
|
+
const dstRes = nodes[e.dst].resources;
|
|
7723
|
+
for (const r of srcRes) {
|
|
7724
|
+
if (dstRes.has(r)) {
|
|
7725
|
+
reached.add(e.dst);
|
|
7726
|
+
changed = true;
|
|
7727
|
+
break;
|
|
7728
|
+
}
|
|
7729
|
+
}
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7733
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7734
|
+
for (const i of candidates) {
|
|
7735
|
+
const n = nodes[i];
|
|
7736
|
+
if (sources.has(i)) continue;
|
|
7737
|
+
if (reached.has(i)) {
|
|
7738
|
+
const srcIdx = Math.min(...sources);
|
|
7739
|
+
const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
|
|
7740
|
+
violations.push({
|
|
7741
|
+
propertyId: "cross-session-reachability",
|
|
7742
|
+
reason: `\u4F4E\u5B8C\u6574\u6027\u6E90\u7ECF\u8DE8 session \u6570\u636E\u94FE\u591A\u8DF3\u5230\u8FBE\u5F53\u524D\u6267\u884C\uFF08\u6E90: ${srcDesc} \u2192 ${n.sessionId}#${n.seq}\uFF09`,
|
|
7743
|
+
riskLevel: "high",
|
|
7744
|
+
path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
|
|
7745
|
+
});
|
|
7746
|
+
if (witness !== void 0) return violations;
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
return violations;
|
|
7750
|
+
}
|
|
7751
|
+
function checkSensitiveDataAggregation(graph) {
|
|
7752
|
+
const violations = [];
|
|
7753
|
+
const nodes = allNodes(graph);
|
|
7754
|
+
if (nodes.length < 2) return violations;
|
|
7755
|
+
const witness = graph.witnessIdx;
|
|
7756
|
+
const resIndex = /* @__PURE__ */ new Map();
|
|
7757
|
+
nodes.forEach((n, i) => {
|
|
7758
|
+
for (const r of n.resources) {
|
|
7759
|
+
const list = resIndex.get(r);
|
|
7760
|
+
if (list) list.push(i);
|
|
7761
|
+
else resIndex.set(r, [i]);
|
|
7762
|
+
}
|
|
7763
|
+
});
|
|
7764
|
+
const sourcesFor = (exfilIdx) => {
|
|
7765
|
+
const srcs = /* @__PURE__ */ new Set();
|
|
7766
|
+
const n = nodes[exfilIdx];
|
|
7767
|
+
for (const r of n.resources) {
|
|
7768
|
+
for (const j of resIndex.get(r) || []) {
|
|
7769
|
+
if (j === exfilIdx) continue;
|
|
7770
|
+
srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
7773
|
+
return srcs;
|
|
7774
|
+
};
|
|
7775
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7776
|
+
for (const i of candidates) {
|
|
7777
|
+
const n = nodes[i];
|
|
7778
|
+
if (!isExfilNode(n)) continue;
|
|
7779
|
+
const srcs = sourcesFor(i);
|
|
7780
|
+
if (srcs.size >= 2) {
|
|
7781
|
+
violations.push({
|
|
7782
|
+
propertyId: "sensitive-data-aggregation",
|
|
7783
|
+
reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
|
|
7784
|
+
riskLevel: "critical",
|
|
7785
|
+
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
7786
|
+
});
|
|
7787
|
+
if (witness !== void 0) return violations;
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
return violations;
|
|
7791
|
+
}
|
|
7319
7792
|
var ALL_PROPERTIES = [
|
|
7320
7793
|
{ id: "cross-session-chain", check: checkCrossSessionChain },
|
|
7321
7794
|
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow },
|
|
7795
|
+
{ id: "cross-session-reachability", check: checkCrossSessionReachability },
|
|
7322
7796
|
{ id: "no-privesc-then-exfil", check: checkNeverExfilAfterPrivesc },
|
|
7323
|
-
{ id: "no-cred-then-exfil", check: checkNeverExfilAfterCred }
|
|
7797
|
+
{ id: "no-cred-then-exfil", check: checkNeverExfilAfterCred },
|
|
7798
|
+
// L2.2 模型检测(通用 LTL 求值器,mc_rules.json 单一事实源;witness 语义见 ltl.ts)
|
|
7799
|
+
{ id: "mc-ltl-rules", check: checkMCRules },
|
|
7800
|
+
// Rule 5 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
|
|
7801
|
+
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec },
|
|
7802
|
+
// Rule 12 敏感数据聚合外传(egress 聚合 ≥2 事件源)
|
|
7803
|
+
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation }
|
|
7324
7804
|
];
|
|
7325
7805
|
|
|
7326
7806
|
// src/engine/formal-engine/engine.ts
|
|
@@ -7352,6 +7832,9 @@ var FormalEngine = class {
|
|
|
7352
7832
|
});
|
|
7353
7833
|
}
|
|
7354
7834
|
const graph = buildMultiSessionGraph(all, aNext);
|
|
7835
|
+
if (aNext) {
|
|
7836
|
+
graph.witnessIdx = allNodes(graph).length - 1;
|
|
7837
|
+
}
|
|
7355
7838
|
const result = emptyVerificationResult();
|
|
7356
7839
|
for (const prop of this.properties) {
|
|
7357
7840
|
let violations;
|
|
@@ -7380,6 +7863,7 @@ var FormalEngine = class {
|
|
|
7380
7863
|
// src/engine/formal-engine/stateful.ts
|
|
7381
7864
|
init_esm_shims();
|
|
7382
7865
|
init_rules_loader();
|
|
7866
|
+
init_decode();
|
|
7383
7867
|
var DESTROY_RE = /\b(?:pkill|kill)\b|systemctl\s+stop|service\s+stop|crontab\s+-r|rm\s+-[rf]?\s+/i;
|
|
7384
7868
|
var NEVER_ACTIVATED_RE = /\b(?:not\s+found|no\s+such\s+file|command\s+not\s+found|refused|failed\s+to\s+(?:start|bind|listen)|address\s+already\s+in\s+use)\b/i;
|
|
7385
7869
|
var CRED_FILE_RE2 = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
|
|
@@ -7892,6 +8376,7 @@ function getBrainStore() {
|
|
|
7892
8376
|
}
|
|
7893
8377
|
var formalEngine = new FormalEngine();
|
|
7894
8378
|
async function runAnalysis(input) {
|
|
8379
|
+
const observedFormalLayers = [];
|
|
7895
8380
|
const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
|
|
7896
8381
|
const isInlineScriptCmd = isInlineScriptCommand(input.aNext.actionDetail.toLowerCase());
|
|
7897
8382
|
const isLiteralTextCmd = isLiteralTextCommand(input.aNext.actionDetail.toLowerCase());
|
|
@@ -7946,8 +8431,8 @@ async function runAnalysis(input) {
|
|
|
7946
8431
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
7947
8432
|
const lowerDetail = rawDetail.toLowerCase();
|
|
7948
8433
|
const agentExt = mergeAgentExtensions(
|
|
7949
|
-
getAgentExtensions(input.agentId),
|
|
7950
|
-
|
|
8434
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
8435
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
7951
8436
|
);
|
|
7952
8437
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
7953
8438
|
return {
|
|
@@ -8064,15 +8549,22 @@ async function runAnalysis(input) {
|
|
|
8064
8549
|
const formalResult = formalEngine.verify(formalSessions, input.aNext);
|
|
8065
8550
|
if (!formalResult.verified) {
|
|
8066
8551
|
const v = formalResult.violations[0];
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8552
|
+
if (isFormalObserveMode()) {
|
|
8553
|
+
console.warn(
|
|
8554
|
+
`[AgentMoss] \u5F62\u5F0F\u5316\u9A8C\u8BC1 observe\uFF08\u53EA\u8BB0\u5F55\u4E0D\u62E6\u622A\uFF09: property=${v?.propertyId ?? "?"} risk=${v?.riskLevel ?? "?"} path_len=${v?.path?.length ?? 0} reason=${(v?.reason ?? "?").slice(0, 200)}`
|
|
8555
|
+
);
|
|
8556
|
+
observedFormalLayers.push("2.4");
|
|
8557
|
+
} else {
|
|
8558
|
+
return {
|
|
8559
|
+
allowed: false,
|
|
8560
|
+
reason: v ? v.reason : "\u5F62\u5F0F\u5316\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF08\u884C\u4E3A\u6027\u8D28\u8FDD\u53CD\uFF09",
|
|
8561
|
+
riskLevel: v ? v.riskLevel : "high",
|
|
8562
|
+
riskType: "formal_verification",
|
|
8563
|
+
confidence: 90,
|
|
8564
|
+
source: "formal_engine",
|
|
8565
|
+
violatedLayers: ["2.4"]
|
|
8566
|
+
};
|
|
8567
|
+
}
|
|
8076
8568
|
}
|
|
8077
8569
|
}
|
|
8078
8570
|
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
@@ -8119,7 +8611,7 @@ async function runAnalysis(input) {
|
|
|
8119
8611
|
riskType: "",
|
|
8120
8612
|
confidence: 85,
|
|
8121
8613
|
source: "heuristic_and_logic_only",
|
|
8122
|
-
violatedLayers: []
|
|
8614
|
+
violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
|
|
8123
8615
|
};
|
|
8124
8616
|
}
|
|
8125
8617
|
let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
|
|
@@ -8164,7 +8656,7 @@ async function runAnalysis(input) {
|
|
|
8164
8656
|
riskType: "",
|
|
8165
8657
|
confidence: 50,
|
|
8166
8658
|
source: "llm_failure_fallback",
|
|
8167
|
-
violatedLayers: []
|
|
8659
|
+
violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
|
|
8168
8660
|
};
|
|
8169
8661
|
}
|
|
8170
8662
|
return {
|
|
@@ -8191,6 +8683,9 @@ async function runAnalysis(input) {
|
|
|
8191
8683
|
} catch {
|
|
8192
8684
|
}
|
|
8193
8685
|
}
|
|
8686
|
+
if (observedFormalLayers.length > 0 && llmResult.allowed) {
|
|
8687
|
+
llmResult.violatedLayers = Array.from(/* @__PURE__ */ new Set([...observedFormalLayers, ...llmResult.violatedLayers || []]));
|
|
8688
|
+
}
|
|
8194
8689
|
return llmResult;
|
|
8195
8690
|
}
|
|
8196
8691
|
|
|
@@ -8682,12 +9177,13 @@ init_esm_shims();
|
|
|
8682
9177
|
init_esm_shims();
|
|
8683
9178
|
init_runtime_config();
|
|
8684
9179
|
init_token_stats();
|
|
9180
|
+
init_rules_loader();
|
|
8685
9181
|
import { Hono } from "hono";
|
|
8686
9182
|
import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
8687
|
-
import { join as
|
|
9183
|
+
import { join as join10, dirname as dirname10 } from "path";
|
|
8688
9184
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8689
9185
|
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
8690
|
-
var STATIC_DIR =
|
|
9186
|
+
var STATIC_DIR = join10(__dirname8, "static");
|
|
8691
9187
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
8692
9188
|
function getConsoleToken() {
|
|
8693
9189
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -8711,7 +9207,7 @@ function createConsoleRouter() {
|
|
|
8711
9207
|
const console2 = new Hono();
|
|
8712
9208
|
console2.use("/api/*", authMiddleware());
|
|
8713
9209
|
const serveIndex = (c) => {
|
|
8714
|
-
const indexFile =
|
|
9210
|
+
const indexFile = join10(STATIC_DIR, "index.html");
|
|
8715
9211
|
if (existsSync14(indexFile)) {
|
|
8716
9212
|
return c.html(readFileSync15(indexFile, "utf-8"));
|
|
8717
9213
|
}
|
|
@@ -8728,24 +9224,89 @@ function createConsoleRouter() {
|
|
|
8728
9224
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
8729
9225
|
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
8730
9226
|
});
|
|
9227
|
+
function getL24View() {
|
|
9228
|
+
const ltlHuman = {
|
|
9229
|
+
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
9230
|
+
"mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
|
|
9231
|
+
"mc-ltl-003": "\u5148\u7981\u7528\u4E86\u5B89\u5168\u9632\u62A4\uFF0C\u4E4B\u540E\u51FA\u73B0\u7834\u574F\u6027\u64CD\u4F5C\uFF08\u6267\u884C/\u5220\u9664/\u63D0\u6743\uFF09",
|
|
9232
|
+
"mc-ltl-004": "\u5148\u51FA\u73B0\u63D0\u793A\u8BCD\u6CE8\u5165\uFF0C\u4E4B\u540E\u51FA\u73B0\u654F\u611F\u64CD\u4F5C\uFF08\u5916\u4F20/\u5199\u6587\u4EF6/\u6267\u884C\uFF09",
|
|
9233
|
+
"mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
|
|
9234
|
+
"mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
|
|
9235
|
+
"mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9236
|
+
"mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9237
|
+
"mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
|
|
9238
|
+
"mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
|
|
9239
|
+
"mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
|
|
9240
|
+
};
|
|
9241
|
+
const mc = loadMC();
|
|
9242
|
+
const rules = mc.rules || [];
|
|
9243
|
+
return {
|
|
9244
|
+
enabled_via: "L2_logic_rules",
|
|
9245
|
+
description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
|
|
9246
|
+
ltl_rules: rules.map((r) => ({
|
|
9247
|
+
id: r.id,
|
|
9248
|
+
type: r.type,
|
|
9249
|
+
formula: Array.isArray(r.formula) ? "<\u516C\u5F0F>" : r.formula,
|
|
9250
|
+
reason: r.reason,
|
|
9251
|
+
risk_level: r.risk_level,
|
|
9252
|
+
action: r.action,
|
|
9253
|
+
enabled: r.enabled !== false,
|
|
9254
|
+
human: ltlHuman[r.id] || r.reason || ""
|
|
9255
|
+
}))
|
|
9256
|
+
};
|
|
9257
|
+
}
|
|
9258
|
+
function withL24View(rc) {
|
|
9259
|
+
if (rc && rc.L2_4 && typeof rc.L2_4 === "object" && !Array.isArray(rc.L2_4)) {
|
|
9260
|
+
const cat = rc.L2_4.ltl_rules;
|
|
9261
|
+
const rls = cat && cat.rules || [];
|
|
9262
|
+
const ltlHumanLocal = {
|
|
9263
|
+
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
9264
|
+
"mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
|
|
9265
|
+
"mc-ltl-003": "\u5148\u7981\u7528\u4E86\u5B89\u5168\u9632\u62A4\uFF0C\u4E4B\u540E\u51FA\u73B0\u7834\u574F\u6027\u64CD\u4F5C\uFF08\u6267\u884C/\u5220\u9664/\u63D0\u6743\uFF09",
|
|
9266
|
+
"mc-ltl-004": "\u5148\u51FA\u73B0\u63D0\u793A\u8BCD\u6CE8\u5165\uFF0C\u4E4B\u540E\u51FA\u73B0\u654F\u611F\u64CD\u4F5C\uFF08\u5916\u4F20/\u5199\u6587\u4EF6/\u6267\u884C\uFF09",
|
|
9267
|
+
"mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
|
|
9268
|
+
"mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
|
|
9269
|
+
"mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9270
|
+
"mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9271
|
+
"mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
|
|
9272
|
+
"mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
|
|
9273
|
+
"mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
|
|
9274
|
+
};
|
|
9275
|
+
rc.L2_4 = {
|
|
9276
|
+
enabled_via: "L2_logic_rules",
|
|
9277
|
+
description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
|
|
9278
|
+
ltl_rules: rls.map((r) => ({
|
|
9279
|
+
id: r.id,
|
|
9280
|
+
type: "ltl",
|
|
9281
|
+
formula: "<\u516C\u5F0F>",
|
|
9282
|
+
reason: r.reason || "",
|
|
9283
|
+
risk_level: r.risk_level || "high",
|
|
9284
|
+
action: r.action || "block",
|
|
9285
|
+
enabled: r.enabled !== false,
|
|
9286
|
+
human: ltlHumanLocal[r.id] || r.reason || ""
|
|
9287
|
+
}))
|
|
9288
|
+
};
|
|
9289
|
+
}
|
|
9290
|
+
return rc;
|
|
9291
|
+
}
|
|
8731
9292
|
console2.get("/api/rules", (c) => {
|
|
8732
9293
|
const rc = loadRuntimeConfig();
|
|
8733
|
-
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
9294
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules, L2_4: getL24View() });
|
|
8734
9295
|
});
|
|
8735
9296
|
console2.put("/api/rules/enabled", async (c) => {
|
|
8736
9297
|
const b = await c.req.json();
|
|
8737
9298
|
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
8738
|
-
return c.json(rc);
|
|
9299
|
+
return c.json(withL24View(rc));
|
|
8739
9300
|
});
|
|
8740
9301
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
8741
9302
|
const b = await c.req.json();
|
|
8742
9303
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
8743
|
-
return c.json(rc);
|
|
9304
|
+
return c.json(withL24View(rc));
|
|
8744
9305
|
});
|
|
8745
9306
|
console2.put("/api/rules/skip_l3", async (c) => {
|
|
8746
9307
|
const b = await c.req.json();
|
|
8747
9308
|
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
8748
|
-
return c.json(rc);
|
|
9309
|
+
return c.json(withL24View(rc));
|
|
8749
9310
|
});
|
|
8750
9311
|
console2.put("/api/categories/enabled", async (c) => {
|
|
8751
9312
|
const b = await c.req.json();
|
|
@@ -8804,9 +9365,9 @@ function createConsoleRouter() {
|
|
|
8804
9365
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
8805
9366
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
8806
9367
|
}
|
|
8807
|
-
const skillPath =
|
|
9368
|
+
const skillPath = join10(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
8808
9369
|
if (!existsSync14(skillPath)) {
|
|
8809
|
-
const builtinPath =
|
|
9370
|
+
const builtinPath = join10(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
8810
9371
|
if (existsSync14(builtinPath)) return c.text(readFileSync15(builtinPath, "utf-8"));
|
|
8811
9372
|
return c.json({ error: "skill not found" }, 404);
|
|
8812
9373
|
}
|
|
@@ -8895,7 +9456,7 @@ function createConsoleRouter() {
|
|
|
8895
9456
|
});
|
|
8896
9457
|
console2.get("/api/config", (c) => {
|
|
8897
9458
|
const rc = loadRuntimeConfig();
|
|
8898
|
-
return c.json(rc);
|
|
9459
|
+
return c.json({ ...rc, L2_4: getL24View() });
|
|
8899
9460
|
});
|
|
8900
9461
|
console2.get("/api/env-overrides", (c) => {
|
|
8901
9462
|
return c.json({ overrides: getEnvOverrides() });
|
|
@@ -8972,4 +9533,7 @@ export {
|
|
|
8972
9533
|
findFreePort,
|
|
8973
9534
|
startServer
|
|
8974
9535
|
};
|
|
9536
|
+
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
9537
|
+
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
9538
|
+
//! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
|
|
8975
9539
|
//# sourceMappingURL=server.js.map
|