@kenhkl/agent-moss 0.12.9 → 0.14.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 +1124 -201
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +1 -1
- package/dist/engine/coordinator.js +899 -149
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/l1_rules.json +28 -6
- package/dist/rules/l2_rules.json +88 -60
- package/dist/rules/mc_rules.json +369 -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 +1108 -193
- package/dist/server.js.map +1 -1
- package/dist/static/index.html +85 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -138,6 +138,7 @@ __export(config_exports, {
|
|
|
138
138
|
getRetryConfig: () => getRetryConfig,
|
|
139
139
|
getTimeoutConfig: () => getTimeoutConfig,
|
|
140
140
|
isBrainEnabled: () => isBrainEnabled,
|
|
141
|
+
isFormalObserveMode: () => isFormalObserveMode,
|
|
141
142
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
142
143
|
isLLMAvailable: () => isLLMAvailable,
|
|
143
144
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
@@ -253,6 +254,7 @@ function getCustomRules() {
|
|
|
253
254
|
}
|
|
254
255
|
function isLLMAvailable() {
|
|
255
256
|
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
257
|
+
if (!layerEnabledOrDefault("L3_llm_analysis")) return false;
|
|
256
258
|
return llmConfig.apiKey.length > 0;
|
|
257
259
|
}
|
|
258
260
|
async function probeLLMConnection(timeoutMs = 8e3) {
|
|
@@ -317,13 +319,26 @@ function getProviderHeaders(provider) {
|
|
|
317
319
|
return PROVIDER_HEADERS[provider] || {};
|
|
318
320
|
}
|
|
319
321
|
function isHeuristicEnabled() {
|
|
320
|
-
|
|
322
|
+
if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
|
|
323
|
+
return layerEnabledOrDefault("L1_heuristic");
|
|
324
|
+
}
|
|
325
|
+
function layerEnabledOrDefault(key) {
|
|
326
|
+
try {
|
|
327
|
+
const { isLayerEnabled: isLayerEnabled2 } = __require("../infra/runtime-config");
|
|
328
|
+
return isLayerEnabled2(key);
|
|
329
|
+
} catch {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
321
332
|
}
|
|
322
333
|
function isBrainEnabled() {
|
|
323
334
|
return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
|
|
324
335
|
}
|
|
325
336
|
function isLogicRulesEnabled() {
|
|
326
|
-
|
|
337
|
+
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
338
|
+
return layerEnabledOrDefault("L2_logic_rules");
|
|
339
|
+
}
|
|
340
|
+
function isFormalObserveMode() {
|
|
341
|
+
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
327
342
|
}
|
|
328
343
|
var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
329
344
|
var init_config = __esm({
|
|
@@ -581,8 +596,12 @@ var init_inline_analyzer = __esm({
|
|
|
581
596
|
|
|
582
597
|
// src/infra/rules-loader.ts
|
|
583
598
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
584
|
-
import { resolve, dirname } from "path";
|
|
599
|
+
import { resolve, dirname, join as join2 } from "path";
|
|
585
600
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
601
|
+
import { homedir as homedir2 } from "os";
|
|
602
|
+
function dataRootDir() {
|
|
603
|
+
return process.env.AGENT_MOSS_DATA_DIR || join2(homedir2(), ".config", "agentmoss");
|
|
604
|
+
}
|
|
586
605
|
function findRulesFile(name) {
|
|
587
606
|
for (const dir of RULES_DIR_CANDS) {
|
|
588
607
|
const p = resolve(dir, name);
|
|
@@ -608,6 +627,24 @@ function loadL2() {
|
|
|
608
627
|
function loadL3() {
|
|
609
628
|
return loadJson("l3_rules.json");
|
|
610
629
|
}
|
|
630
|
+
function loadMC() {
|
|
631
|
+
const mc = loadJson("mc_rules.json");
|
|
632
|
+
const rules = mc.rules || [];
|
|
633
|
+
try {
|
|
634
|
+
const rt = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
635
|
+
if (rules.length > 0 && typeof rt.hasRuntimeConfig === "function" && rt.hasRuntimeConfig()) {
|
|
636
|
+
const rtRules = rt.loadRuntimeConfig()?.L2_2?.ltl_rules?.rules || [];
|
|
637
|
+
const enabledById = {};
|
|
638
|
+
for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
|
|
639
|
+
for (const r of rules) if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
|
|
640
|
+
}
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
return mc;
|
|
644
|
+
}
|
|
645
|
+
function loadToolRegistry() {
|
|
646
|
+
return loadJson("tool_registry.json");
|
|
647
|
+
}
|
|
611
648
|
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
612
649
|
var init_rules_loader = __esm({
|
|
613
650
|
"src/infra/rules-loader.ts"() {
|
|
@@ -617,6 +654,10 @@ var init_rules_loader = __esm({
|
|
|
617
654
|
__dirname2 = dirname(__filename2);
|
|
618
655
|
RULES_DIR_CANDS = (() => {
|
|
619
656
|
const cands = [
|
|
657
|
+
// 用户数据目录覆盖优先(与 Python rules_loader._load_json 的
|
|
658
|
+
// <data_root>/rules/<name> 优先逻辑对齐)。用户安装 pip/npm 包后把自定义
|
|
659
|
+
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
660
|
+
resolve(dataRootDir(), "rules"),
|
|
620
661
|
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
621
662
|
resolve(__dirname2, "../rules"),
|
|
622
663
|
resolve(__dirname2, "rules"),
|
|
@@ -640,7 +681,8 @@ function _loadSensitivePathScope(scope) {
|
|
|
640
681
|
path: sp.path,
|
|
641
682
|
riskLevel: sp.risk_level,
|
|
642
683
|
desc: sp.desc,
|
|
643
|
-
credential: sp.deny_mode === "deny_both" ? true : void 0
|
|
684
|
+
credential: sp.deny_mode === "deny_both" ? true : void 0,
|
|
685
|
+
dirDelOnly: sp.dir_del_only === true ? true : void 0
|
|
644
686
|
}));
|
|
645
687
|
}
|
|
646
688
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
@@ -738,7 +780,7 @@ function runLogicCheck(input) {
|
|
|
738
780
|
riskType: "intent_deviation",
|
|
739
781
|
confidence: 85,
|
|
740
782
|
source: "logic_rule",
|
|
741
|
-
violatedLayers: ["
|
|
783
|
+
violatedLayers: ["L2.1"]
|
|
742
784
|
};
|
|
743
785
|
}
|
|
744
786
|
}
|
|
@@ -762,7 +804,7 @@ function runLogicCheck(input) {
|
|
|
762
804
|
riskType: "privilege_escalation",
|
|
763
805
|
confidence: 85,
|
|
764
806
|
source: "logic_rule",
|
|
765
|
-
violatedLayers: ["
|
|
807
|
+
violatedLayers: ["L2.1"]
|
|
766
808
|
};
|
|
767
809
|
}
|
|
768
810
|
}
|
|
@@ -814,7 +856,7 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
814
856
|
riskType: "combo_chain",
|
|
815
857
|
confidence: 85,
|
|
816
858
|
source: "logic_rule",
|
|
817
|
-
violatedLayers: ["
|
|
859
|
+
violatedLayers: ["L2.1"]
|
|
818
860
|
};
|
|
819
861
|
}
|
|
820
862
|
}
|
|
@@ -939,7 +981,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
939
981
|
riskType: "read_before_write",
|
|
940
982
|
confidence: 85,
|
|
941
983
|
source: "logic_rule",
|
|
942
|
-
violatedLayers: ["
|
|
984
|
+
violatedLayers: ["L2.1"]
|
|
943
985
|
};
|
|
944
986
|
}
|
|
945
987
|
return null;
|
|
@@ -959,7 +1001,7 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
959
1001
|
riskType: "consent_missing",
|
|
960
1002
|
confidence: 85,
|
|
961
1003
|
source: "logic_rule",
|
|
962
|
-
violatedLayers: ["
|
|
1004
|
+
violatedLayers: ["L2.1"]
|
|
963
1005
|
};
|
|
964
1006
|
}
|
|
965
1007
|
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
@@ -977,7 +1019,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
|
977
1019
|
riskType: "consent_missing",
|
|
978
1020
|
confidence: 85,
|
|
979
1021
|
source: "logic_rule",
|
|
980
|
-
violatedLayers: ["
|
|
1022
|
+
violatedLayers: ["L2.1"]
|
|
981
1023
|
};
|
|
982
1024
|
}
|
|
983
1025
|
function containsPrivilegeEscalation(actionDetail) {
|
|
@@ -1037,7 +1079,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1037
1079
|
riskType: "file_access",
|
|
1038
1080
|
confidence: 95,
|
|
1039
1081
|
source: "logic_rule",
|
|
1040
|
-
violatedLayers: ["
|
|
1082
|
+
violatedLayers: ["L2.1"],
|
|
1041
1083
|
isCredential: true
|
|
1042
1084
|
};
|
|
1043
1085
|
}
|
|
@@ -1053,7 +1095,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1053
1095
|
riskType: "file_access",
|
|
1054
1096
|
confidence: 95,
|
|
1055
1097
|
source: "logic_rule",
|
|
1056
|
-
violatedLayers: ["
|
|
1098
|
+
violatedLayers: ["L2.1"]
|
|
1057
1099
|
};
|
|
1058
1100
|
}
|
|
1059
1101
|
}
|
|
@@ -1067,7 +1109,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1067
1109
|
riskType: "file_access",
|
|
1068
1110
|
confidence: 70,
|
|
1069
1111
|
source: "logic_rule",
|
|
1070
|
-
violatedLayers: ["
|
|
1112
|
+
violatedLayers: ["L2.1"]
|
|
1071
1113
|
};
|
|
1072
1114
|
}
|
|
1073
1115
|
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, WRITE_COMMAND_RES, READ_ONLY_COMMANDS, DEVNULL_REDIRECT_RE, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
@@ -1079,7 +1121,7 @@ var init_logic_rules = __esm({
|
|
|
1079
1121
|
init_rules_loader();
|
|
1080
1122
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1081
1123
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1082
|
-
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc }) => ({ path: path6, riskLevel, desc, credential: true }));
|
|
1124
|
+
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1083
1125
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1084
1126
|
"ask_user_question",
|
|
1085
1127
|
"glob",
|
|
@@ -1354,6 +1396,7 @@ var init_skill_loader = __esm({
|
|
|
1354
1396
|
// src/infra/runtime-config.ts
|
|
1355
1397
|
var runtime_config_exports = {};
|
|
1356
1398
|
__export(runtime_config_exports, {
|
|
1399
|
+
DEFAULT_LAYERS: () => DEFAULT_LAYERS,
|
|
1357
1400
|
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
1358
1401
|
addAgentRule: () => addAgentRule,
|
|
1359
1402
|
addCustomRule: () => addCustomRule,
|
|
@@ -1405,7 +1448,7 @@ __export(runtime_config_exports, {
|
|
|
1405
1448
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1406
1449
|
});
|
|
1407
1450
|
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
|
|
1408
|
-
import { join as
|
|
1451
|
+
import { join as join3 } from "path";
|
|
1409
1452
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1410
1453
|
import { dirname as dirname3 } from "path";
|
|
1411
1454
|
function runtimeDir() {
|
|
@@ -1429,6 +1472,7 @@ function emptyRuntimeConfig() {
|
|
|
1429
1472
|
L1_rules: {},
|
|
1430
1473
|
L2_rules: {},
|
|
1431
1474
|
L3_skills: {},
|
|
1475
|
+
L2_2: {},
|
|
1432
1476
|
agent_rules: {},
|
|
1433
1477
|
scenario_rules: {}
|
|
1434
1478
|
};
|
|
@@ -1456,6 +1500,7 @@ function loadRuntimeConfig() {
|
|
|
1456
1500
|
L1_rules: merged.L1_rules || {},
|
|
1457
1501
|
L2_rules: merged.L2_rules || {},
|
|
1458
1502
|
L3_skills: merged.L3_skills || {},
|
|
1503
|
+
L2_2: merged.L2_2 || {},
|
|
1459
1504
|
agent_rules: merged.agent_rules || {},
|
|
1460
1505
|
scenario_rules: merged.scenario_rules || {}
|
|
1461
1506
|
};
|
|
@@ -1508,6 +1553,7 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1508
1553
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1509
1554
|
if (r.credential) out.credential = true;
|
|
1510
1555
|
if (r.read_only) out.read_only = true;
|
|
1556
|
+
if (r.dir_del_only) out.dir_del_only = true;
|
|
1511
1557
|
return out;
|
|
1512
1558
|
});
|
|
1513
1559
|
}
|
|
@@ -1616,6 +1662,7 @@ function _l2SensitivePathsToRules() {
|
|
|
1616
1662
|
builtin: true
|
|
1617
1663
|
};
|
|
1618
1664
|
_syncDenyModeFields(rule);
|
|
1665
|
+
if (sp.dirDelOnly) rule.dir_del_only = true;
|
|
1619
1666
|
return rule;
|
|
1620
1667
|
});
|
|
1621
1668
|
}
|
|
@@ -1703,7 +1750,7 @@ function _scenarioRulesToDefaults() {
|
|
|
1703
1750
|
return out;
|
|
1704
1751
|
}
|
|
1705
1752
|
for (const scenarioId of entries) {
|
|
1706
|
-
const rulesFile =
|
|
1753
|
+
const rulesFile = join3(scenDir, scenarioId, "rules.json");
|
|
1707
1754
|
if (!existsSync4(rulesFile)) continue;
|
|
1708
1755
|
let data;
|
|
1709
1756
|
try {
|
|
@@ -1752,7 +1799,7 @@ function _agentRulesToDefaults() {
|
|
|
1752
1799
|
return out;
|
|
1753
1800
|
}
|
|
1754
1801
|
for (const agentId of entries) {
|
|
1755
|
-
const rulesFile =
|
|
1802
|
+
const rulesFile = join3(agentsDir, agentId, "rules.json");
|
|
1756
1803
|
if (!existsSync4(rulesFile)) continue;
|
|
1757
1804
|
let data;
|
|
1758
1805
|
try {
|
|
@@ -1793,7 +1840,7 @@ function _agentRulesToDefaults() {
|
|
|
1793
1840
|
function generateSourceDefaults() {
|
|
1794
1841
|
return {
|
|
1795
1842
|
version: 1,
|
|
1796
|
-
layers: {
|
|
1843
|
+
layers: { ...DEFAULT_LAYERS },
|
|
1797
1844
|
L1_rules: {
|
|
1798
1845
|
dangerous_commands: {
|
|
1799
1846
|
category_enabled: true,
|
|
@@ -1832,10 +1879,45 @@ function generateSourceDefaults() {
|
|
|
1832
1879
|
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1833
1880
|
},
|
|
1834
1881
|
L3_skills: _l3SkillsToRules(),
|
|
1882
|
+
L2_2: _ltlRulesToRuntime(),
|
|
1835
1883
|
agent_rules: _agentRulesToDefaults(),
|
|
1836
1884
|
scenario_rules: _scenarioRulesToDefaults()
|
|
1837
1885
|
};
|
|
1838
1886
|
}
|
|
1887
|
+
function _ltlRulesToRuntime() {
|
|
1888
|
+
const cands = [
|
|
1889
|
+
join3(__dirname4, "..", "rules"),
|
|
1890
|
+
join3(__dirname4, "..", "..", "..", "agent_moss", "rules"),
|
|
1891
|
+
join3(process.cwd(), "agent_moss", "rules")
|
|
1892
|
+
];
|
|
1893
|
+
let mc = {};
|
|
1894
|
+
for (const dir of cands) {
|
|
1895
|
+
const p = join3(dir, "mc_rules.json");
|
|
1896
|
+
if (existsSync4(p)) {
|
|
1897
|
+
try {
|
|
1898
|
+
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
1899
|
+
} catch {
|
|
1900
|
+
mc = {};
|
|
1901
|
+
}
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
const rules = mc.rules || [];
|
|
1906
|
+
return {
|
|
1907
|
+
ltl_rules: {
|
|
1908
|
+
category_enabled: true,
|
|
1909
|
+
category_desc: "LTL \u653B\u51FB\u6A21\u5F0F\u89C4\u5219\uFF08\u65F6\u5E8F\u653B\u51FB\u68C0\u6D4B\uFF0C\u5148 A \u540E B\uFF09",
|
|
1910
|
+
rules: rules.map((r) => ({
|
|
1911
|
+
id: r.id,
|
|
1912
|
+
reason: r.reason || "",
|
|
1913
|
+
risk_level: r.risk_level || "high",
|
|
1914
|
+
action: r.action || "block",
|
|
1915
|
+
enabled: true,
|
|
1916
|
+
builtin: true
|
|
1917
|
+
}))
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1839
1921
|
function normalizePatternForFingerprint(pattern) {
|
|
1840
1922
|
return pattern.replace(/\\\//g, "/");
|
|
1841
1923
|
}
|
|
@@ -1985,6 +2067,11 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
1985
2067
|
);
|
|
1986
2068
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1987
2069
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2070
|
+
if (!cfg.L2_2 && cfg.L2_4) {
|
|
2071
|
+
cfg.L2_2 = cfg.L2_4;
|
|
2072
|
+
delete cfg.L2_4;
|
|
2073
|
+
}
|
|
2074
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_2");
|
|
1988
2075
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
1989
2076
|
_mergeAgentRules(cfg, srcDefaults);
|
|
1990
2077
|
_mergeAgentRules(cfg, srcDefaults, "scenario_rules");
|
|
@@ -2177,12 +2264,12 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2177
2264
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2178
2265
|
_validateSkillId(skillId);
|
|
2179
2266
|
const runtime = loadRuntimeConfig();
|
|
2180
|
-
const skillsDir =
|
|
2267
|
+
const skillsDir = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2181
2268
|
try {
|
|
2182
2269
|
mkdirSync(skillsDir, { recursive: true });
|
|
2183
2270
|
} catch {
|
|
2184
2271
|
}
|
|
2185
|
-
writeFileSync(
|
|
2272
|
+
writeFileSync(join3(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2186
2273
|
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2187
2274
|
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2188
2275
|
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
@@ -2197,7 +2284,7 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2197
2284
|
if (idx >= 0) {
|
|
2198
2285
|
if (skills[idx].builtin) return null;
|
|
2199
2286
|
skills.splice(idx, 1);
|
|
2200
|
-
const skillPath =
|
|
2287
|
+
const skillPath = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2201
2288
|
try {
|
|
2202
2289
|
if (existsSync4(skillPath)) unlinkSync(skillPath);
|
|
2203
2290
|
} catch {
|
|
@@ -2391,7 +2478,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
2391
2478
|
saveRuntimeConfig(runtime);
|
|
2392
2479
|
return runtime;
|
|
2393
2480
|
}
|
|
2394
|
-
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;
|
|
2481
|
+
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;
|
|
2395
2482
|
var init_runtime_config = __esm({
|
|
2396
2483
|
"src/infra/runtime-config.ts"() {
|
|
2397
2484
|
"use strict";
|
|
@@ -2403,21 +2490,28 @@ var init_runtime_config = __esm({
|
|
|
2403
2490
|
__dirname4 = dirname3(fileURLToPath5(import.meta.url));
|
|
2404
2491
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2405
2492
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
2406
|
-
RUNTIME_CONFIG_PATH =
|
|
2493
|
+
RUNTIME_CONFIG_PATH = join3(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2407
2494
|
LAYER_DISABLE_ENV = {
|
|
2408
2495
|
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2409
2496
|
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2410
2497
|
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2411
2498
|
};
|
|
2499
|
+
DEFAULT_LAYERS = {
|
|
2500
|
+
L1_heuristic: true,
|
|
2501
|
+
L2_logic_rules: true,
|
|
2502
|
+
L3_llm_analysis: true,
|
|
2503
|
+
agent_rules_enabled: true,
|
|
2504
|
+
scenario_rules_enabled: true
|
|
2505
|
+
};
|
|
2412
2506
|
DENY_MODE_LABELS = {
|
|
2413
2507
|
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2414
2508
|
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2415
2509
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2416
2510
|
};
|
|
2417
|
-
_AGENTS_DIR_PROD =
|
|
2418
|
-
_AGENTS_DIR_DEV =
|
|
2419
|
-
_SCENARIOS_DIR_PROD =
|
|
2420
|
-
_SCENARIOS_DIR_DEV =
|
|
2511
|
+
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2512
|
+
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2513
|
+
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
2514
|
+
_SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
2421
2515
|
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2422
2516
|
}
|
|
2423
2517
|
});
|
|
@@ -2555,11 +2649,21 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2555
2649
|
);
|
|
2556
2650
|
return ext;
|
|
2557
2651
|
}
|
|
2652
|
+
function getScenarioExtensionsMulti(scenarioSpec) {
|
|
2653
|
+
if (!scenarioSpec) return null;
|
|
2654
|
+
const ids = scenarioSpec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2655
|
+
if (ids.length === 0) return null;
|
|
2656
|
+
const exts = ids.map((id) => getScenarioExtensions(id));
|
|
2657
|
+
const nonNull = exts.filter((e) => e !== null);
|
|
2658
|
+
if (nonNull.length === 0) return null;
|
|
2659
|
+
return mergeAgentExtensions(...nonNull);
|
|
2660
|
+
}
|
|
2558
2661
|
var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
|
|
2559
2662
|
var init_scenarios = __esm({
|
|
2560
2663
|
"src/scenarios/index.ts"() {
|
|
2561
2664
|
"use strict";
|
|
2562
2665
|
init_esm_shims();
|
|
2666
|
+
init_agents();
|
|
2563
2667
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
2564
2668
|
__dirname6 = dirname5(__filename5);
|
|
2565
2669
|
SCENARIOS_DIR_PROD = resolve4(__dirname6, "scenarios");
|
|
@@ -2587,7 +2691,7 @@ function runHeuristicCheck(input) {
|
|
|
2587
2691
|
riskType: "sensitive_action",
|
|
2588
2692
|
confidence: 90,
|
|
2589
2693
|
source: "heuristic",
|
|
2590
|
-
violatedLayers: ["
|
|
2694
|
+
violatedLayers: ["L1.1"]
|
|
2591
2695
|
};
|
|
2592
2696
|
}
|
|
2593
2697
|
}
|
|
@@ -2597,8 +2701,10 @@ function runHeuristicCheck(input) {
|
|
|
2597
2701
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2598
2702
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2599
2703
|
let topRiskType = "";
|
|
2704
|
+
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2600
2705
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
2601
2706
|
if (rule.pattern.test(scanCommand)) {
|
|
2707
|
+
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2602
2708
|
matched.push(rule.description);
|
|
2603
2709
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
2604
2710
|
highestSeverity = rule.severity;
|
|
@@ -2608,8 +2714,8 @@ function runHeuristicCheck(input) {
|
|
|
2608
2714
|
}
|
|
2609
2715
|
highestRiskType = topRiskType || highestRiskType;
|
|
2610
2716
|
const agentExt = mergeAgentExtensions(
|
|
2611
|
-
getAgentExtensions(input.agentId),
|
|
2612
|
-
|
|
2717
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
2718
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
2613
2719
|
);
|
|
2614
2720
|
if (agentExt?.extra_command_patterns) {
|
|
2615
2721
|
for (const rule of agentExt.extra_command_patterns) {
|
|
@@ -2640,10 +2746,26 @@ function runHeuristicCheck(input) {
|
|
|
2640
2746
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2641
2747
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2642
2748
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2749
|
+
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
2750
|
+
const homeResult = checkHomeDeletion(actionTypeL, detailL);
|
|
2751
|
+
const mergeResults = [dangerousResult, pathResult, homeResult].filter(Boolean);
|
|
2752
|
+
if (mergeResults.length > 0) {
|
|
2753
|
+
let best = mergeResults[0];
|
|
2754
|
+
for (const r of mergeResults) {
|
|
2755
|
+
if (severityRank(r.riskLevel) > severityRank(best.riskLevel)) best = r;
|
|
2756
|
+
}
|
|
2757
|
+
if (severityRank(highestSeverity) > severityRank(best.riskLevel)) {
|
|
2758
|
+
return {
|
|
2759
|
+
allowed: false,
|
|
2760
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
2761
|
+
riskLevel: highestSeverity,
|
|
2762
|
+
riskType: highestRiskType,
|
|
2763
|
+
confidence: 95,
|
|
2764
|
+
source: "heuristic",
|
|
2765
|
+
violatedLayers: ["L1.1"]
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
return best;
|
|
2647
2769
|
}
|
|
2648
2770
|
if (matched.length > 0) {
|
|
2649
2771
|
return {
|
|
@@ -2653,8 +2775,7 @@ function runHeuristicCheck(input) {
|
|
|
2653
2775
|
riskType: highestRiskType,
|
|
2654
2776
|
confidence: 95,
|
|
2655
2777
|
source: "heuristic",
|
|
2656
|
-
violatedLayers: ["
|
|
2657
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
2778
|
+
violatedLayers: ["L1.1"]
|
|
2658
2779
|
};
|
|
2659
2780
|
}
|
|
2660
2781
|
const lateralResult = checkLateralMovement(detailL);
|
|
@@ -2782,6 +2903,7 @@ function isSearchActionType(actionType) {
|
|
|
2782
2903
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2783
2904
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2784
2905
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2906
|
+
const keygenGenerate = isSshKeygenGenerate(command);
|
|
2785
2907
|
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2786
2908
|
const pathMatches = (text, path6) => {
|
|
2787
2909
|
const parts = path6.split(/(\(\?!.*?\))/);
|
|
@@ -2794,6 +2916,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2794
2916
|
};
|
|
2795
2917
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
2796
2918
|
if (pathMatches(pathScanText, sp.path.toLowerCase())) {
|
|
2919
|
+
if (keygenGenerate) continue;
|
|
2797
2920
|
if (sp.path === "/dev/random") {
|
|
2798
2921
|
if (isWriteOpFlag) {
|
|
2799
2922
|
continue;
|
|
@@ -2805,7 +2928,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2805
2928
|
riskType: "file_access",
|
|
2806
2929
|
confidence: 90,
|
|
2807
2930
|
source: "heuristic",
|
|
2808
|
-
violatedLayers: ["
|
|
2931
|
+
violatedLayers: ["L1.1"]
|
|
2809
2932
|
};
|
|
2810
2933
|
}
|
|
2811
2934
|
if (!isWriteOpFlag && !sp.credential) {
|
|
@@ -2818,16 +2941,51 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2818
2941
|
riskType: "file_access",
|
|
2819
2942
|
confidence: 90,
|
|
2820
2943
|
source: "heuristic",
|
|
2821
|
-
violatedLayers: ["
|
|
2944
|
+
violatedLayers: ["L1.1"],
|
|
2822
2945
|
isCredential: !!sp.credential
|
|
2823
2946
|
};
|
|
2824
2947
|
}
|
|
2825
2948
|
}
|
|
2826
2949
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
2827
2950
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2951
|
+
let pPath = sp.path;
|
|
2952
|
+
if (!sp.path.endsWith(".pub")) pPath = `${sp.path}(?!\\.pub)`;
|
|
2953
|
+
let dirPrefixMatch = null;
|
|
2954
|
+
if (sp.dirDelOnly) {
|
|
2955
|
+
const dirRe = (t) => new RegExp(`(?:^|[\\s/\\\\'"])${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/|[\\s'"\\\\]|$)`, "i");
|
|
2956
|
+
dirPrefixMatch = dirRe(sp.path).test(pathScanText) || dirRe(expanded.toLowerCase()).test(pathScanText);
|
|
2957
|
+
}
|
|
2958
|
+
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch : pathMatches(pathScanText, pPath);
|
|
2959
|
+
const expandedReMatch = sp.dirDelOnly ? false : pathMatches(pathScanText, expanded + (sp.path.endsWith(".pub") ? "" : "(?!\\.pub)"));
|
|
2960
|
+
if (pathReMatch || expandedReMatch) {
|
|
2961
|
+
if (keygenGenerate) continue;
|
|
2962
|
+
if (sp.dirDelOnly) {
|
|
2963
|
+
if (isDeleteActionForDir(command, sp.path)) {
|
|
2964
|
+
return {
|
|
2965
|
+
allowed: false,
|
|
2966
|
+
reason: `\u5220\u9664\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2967
|
+
riskLevel: sp.riskLevel,
|
|
2968
|
+
riskType: "file_access",
|
|
2969
|
+
confidence: 90,
|
|
2970
|
+
source: "heuristic",
|
|
2971
|
+
violatedLayers: ["L1.1"],
|
|
2972
|
+
isCredential: !!sp.credential
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2975
|
+
if (isArchiveReadOfDir(command, sp.path)) {
|
|
2976
|
+
return {
|
|
2977
|
+
allowed: false,
|
|
2978
|
+
reason: `\u5F52\u6863/\u590D\u5236\u8BFB\u53D6\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2979
|
+
riskLevel: sp.riskLevel,
|
|
2980
|
+
riskType: "file_access",
|
|
2981
|
+
confidence: 90,
|
|
2982
|
+
source: "heuristic",
|
|
2983
|
+
violatedLayers: ["L1.1"],
|
|
2984
|
+
isCredential: !!sp.credential
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
continue;
|
|
2988
|
+
}
|
|
2831
2989
|
if (!isWriteOpFlag && !sp.credential) {
|
|
2832
2990
|
continue;
|
|
2833
2991
|
}
|
|
@@ -2838,7 +2996,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2838
2996
|
riskType: "file_access",
|
|
2839
2997
|
confidence: 90,
|
|
2840
2998
|
source: "heuristic",
|
|
2841
|
-
violatedLayers: ["
|
|
2999
|
+
violatedLayers: ["L1.1"],
|
|
2842
3000
|
isCredential: !!sp.credential
|
|
2843
3001
|
};
|
|
2844
3002
|
}
|
|
@@ -2861,7 +3019,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2861
3019
|
riskType: "file_access",
|
|
2862
3020
|
confidence: 90,
|
|
2863
3021
|
source: "heuristic",
|
|
2864
|
-
violatedLayers: ["
|
|
3022
|
+
violatedLayers: ["L1.1"],
|
|
2865
3023
|
isCredential: !!sp.credential
|
|
2866
3024
|
};
|
|
2867
3025
|
}
|
|
@@ -2878,7 +3036,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2878
3036
|
riskType: "file_access",
|
|
2879
3037
|
confidence: 90,
|
|
2880
3038
|
source: "heuristic",
|
|
2881
|
-
violatedLayers: ["
|
|
3039
|
+
violatedLayers: ["L1.1"],
|
|
2882
3040
|
isCredential: true
|
|
2883
3041
|
};
|
|
2884
3042
|
}
|
|
@@ -2891,18 +3049,142 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2891
3049
|
riskType: "file_access",
|
|
2892
3050
|
confidence: 90,
|
|
2893
3051
|
source: "heuristic",
|
|
2894
|
-
violatedLayers: ["
|
|
3052
|
+
violatedLayers: ["L1.1"]
|
|
2895
3053
|
};
|
|
2896
3054
|
}
|
|
2897
3055
|
return null;
|
|
2898
3056
|
}
|
|
3057
|
+
function isHomeValue(path6) {
|
|
3058
|
+
let p = path6.trim().toLowerCase();
|
|
3059
|
+
if (p.startsWith("$(") && p.endsWith(")")) {
|
|
3060
|
+
const inner = p.slice(2, -1).trim();
|
|
3061
|
+
return HOME_TARGET_RE.test(" " + inner);
|
|
3062
|
+
}
|
|
3063
|
+
if (/^\/home\/[^/\s]+\/?$/.test(p)) return true;
|
|
3064
|
+
return HOME_TARGET_RE.test(" " + p);
|
|
3065
|
+
}
|
|
3066
|
+
function isDeleteCommand(seg) {
|
|
3067
|
+
const s = seg.trimStart();
|
|
3068
|
+
if (/^(?:rm|rmdir|unlink)\b/i.test(s)) return true;
|
|
3069
|
+
return /删除|移除/.test(s);
|
|
3070
|
+
}
|
|
3071
|
+
function homeEscapeTarget(seg) {
|
|
3072
|
+
const low = seg.toLowerCase();
|
|
3073
|
+
const re = /(?:~|(?:\$home|\$\{home\})|\/home\/[a-z0-9_.-]+)/g;
|
|
3074
|
+
let m;
|
|
3075
|
+
while ((m = re.exec(low)) !== null) {
|
|
3076
|
+
const endIdx = m.index + m[0].length;
|
|
3077
|
+
const tail = low.slice(endIdx);
|
|
3078
|
+
if (!tail) continue;
|
|
3079
|
+
let token = tail.trimStart().split(/\s+/)[0] || "";
|
|
3080
|
+
token = token.split("&&")[0].split("||")[0].split(";")[0].replace(/\/+$/, "");
|
|
3081
|
+
token = token.replace(/^['"]|['"]$/g, "");
|
|
3082
|
+
const segments = token.split("/").filter((s) => s && s !== ".");
|
|
3083
|
+
if (!segments.some((s) => s === "..")) continue;
|
|
3084
|
+
let depth = 0;
|
|
3085
|
+
let minDepth = 0;
|
|
3086
|
+
for (const s of segments) {
|
|
3087
|
+
depth += s === ".." ? -1 : 1;
|
|
3088
|
+
minDepth = Math.min(minDepth, depth);
|
|
3089
|
+
}
|
|
3090
|
+
if (minDepth < 0) return true;
|
|
3091
|
+
if (depth <= 0) return true;
|
|
3092
|
+
}
|
|
3093
|
+
return false;
|
|
3094
|
+
}
|
|
3095
|
+
function checkHomeDeletion(actionType, actionDetail) {
|
|
3096
|
+
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") return null;
|
|
3097
|
+
const segs = actionDetail.split(/&&|\|\||;|\n/).map((s) => s.trim()).filter(Boolean);
|
|
3098
|
+
for (const seg of segs) {
|
|
3099
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3100
|
+
if (HOME_TARGET_RE.test(seg) || homeEscapeTarget(seg)) {
|
|
3101
|
+
return {
|
|
3102
|
+
allowed: false,
|
|
3103
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u4E3B\u76EE\u5F55 (~/$HOME) \u64CD\u4F5C",
|
|
3104
|
+
riskLevel: "high",
|
|
3105
|
+
riskType: "script_execution",
|
|
3106
|
+
confidence: 90,
|
|
3107
|
+
source: "heuristic",
|
|
3108
|
+
violatedLayers: ["L1.1"]
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
const assigned = {};
|
|
3113
|
+
for (const m of actionDetail.matchAll(HOME_ASSIGN_RE)) assigned[m[1]] = m[2];
|
|
3114
|
+
for (const [varName, path6] of Object.entries(assigned)) {
|
|
3115
|
+
if (!isHomeValue(path6)) continue;
|
|
3116
|
+
const varRefRe = new RegExp(`(?:${escapeRegExp(varName)}|\\$\\{${escapeRegExp(varName)}\\})`);
|
|
3117
|
+
for (const seg of segs) {
|
|
3118
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3119
|
+
if (varRefRe.test(seg)) {
|
|
3120
|
+
return {
|
|
3121
|
+
allowed: false,
|
|
3122
|
+
reason: `\u68C0\u6D4B\u5230\u901A\u8FC7\u53D8\u91CF ${varName}\uFF08=${path6}\uFF0C\u4E3B\u76EE\u5F55\uFF09\u6267\u884C\u5220\u9664`,
|
|
3123
|
+
riskLevel: "high",
|
|
3124
|
+
riskType: "script_execution",
|
|
3125
|
+
confidence: 90,
|
|
3126
|
+
source: "heuristic",
|
|
3127
|
+
violatedLayers: ["L1.1"]
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
return null;
|
|
3133
|
+
}
|
|
3134
|
+
function escapeRegExp(s) {
|
|
3135
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3136
|
+
}
|
|
3137
|
+
function isDeleteActionForDir(command, dirPath) {
|
|
3138
|
+
const low = command.toLowerCase();
|
|
3139
|
+
const dpath = dirPath.toLowerCase();
|
|
3140
|
+
if (new RegExp(`\\b(?:rm|unlink|rmdir)\\b[^&|;]*?${escapeRegExp(dpath)}`, "i").test(low)) {
|
|
3141
|
+
return true;
|
|
3142
|
+
}
|
|
3143
|
+
if (new RegExp(`(?:\u5220\u9664|\u79FB\u9664)[^\\n;|&]*?${escapeRegExp(dpath)}`).test(low)) {
|
|
3144
|
+
return true;
|
|
3145
|
+
}
|
|
3146
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3147
|
+
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3148
|
+
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") && low.includes(dpath)) {
|
|
3149
|
+
return true;
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
return false;
|
|
3153
|
+
}
|
|
3154
|
+
function isArchiveReadOfDir(command, dirPath) {
|
|
3155
|
+
const low = command.toLowerCase();
|
|
3156
|
+
const dpath = dirPath.toLowerCase();
|
|
3157
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3158
|
+
const s = seg.trim();
|
|
3159
|
+
if (!s || !s.includes(dpath)) continue;
|
|
3160
|
+
const parts = s.split(/\s+/);
|
|
3161
|
+
const fw = parts[0] || "";
|
|
3162
|
+
if (fw === "zip") return true;
|
|
3163
|
+
if (fw === "tar") {
|
|
3164
|
+
if (/(?:^|\s)-[a-zA-Z]*x|--extract/.test(s)) continue;
|
|
3165
|
+
return true;
|
|
3166
|
+
}
|
|
3167
|
+
if (fw === "cp" || fw === "rsync") {
|
|
3168
|
+
const segPaths = s.match(/[/~][^\s;|&><'"]*/g) || [];
|
|
3169
|
+
if (segPaths.length && segPaths[0].includes(dpath)) return true;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
return false;
|
|
3173
|
+
}
|
|
3174
|
+
function isSshKeygenGenerate(command) {
|
|
3175
|
+
const low = command.toLowerCase();
|
|
3176
|
+
if (!low.includes("ssh-keygen")) return false;
|
|
3177
|
+
if (!/(?:^|\s)-t\s+\S+/.test(low)) return false;
|
|
3178
|
+
if (/(?:^|\s)-\S*[ylpB]\S*/.test(low)) return false;
|
|
3179
|
+
return true;
|
|
3180
|
+
}
|
|
2899
3181
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2900
3182
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2901
3183
|
const scan = stripHeredocContent(actionDetail);
|
|
2902
3184
|
const TEMP_DIRS = ["/tmp", "/var/tmp", "/run", "/dev/shm"];
|
|
2903
3185
|
const isTempPath = TEMP_DIRS.some(
|
|
2904
3186
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
2905
|
-
);
|
|
3187
|
+
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
2906
3188
|
const wildcardDeleteRe = /(?:\brm\b|\b(r?mdir|del|delete|remove|rmdir|unlink)\b|\b删除\b|\b移除\b)[\s\S]{0,40}?[*?]|[*?][\s\S]{0,40}?\b(?:rm|del|delete|remove|rmdir|unlink)\b|rm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\S*[*?]/i;
|
|
2907
3189
|
if (!isTempPath && wildcardDeleteRe.test(scan)) {
|
|
2908
3190
|
return {
|
|
@@ -2912,7 +3194,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2912
3194
|
riskType: "script_execution",
|
|
2913
3195
|
confidence: 90,
|
|
2914
3196
|
source: "heuristic",
|
|
2915
|
-
violatedLayers: ["
|
|
3197
|
+
violatedLayers: ["L1.1"]
|
|
2916
3198
|
};
|
|
2917
3199
|
}
|
|
2918
3200
|
const findWriteMatch = actionDetail.match(
|
|
@@ -2927,10 +3209,10 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2927
3209
|
riskType: "script_execution",
|
|
2928
3210
|
confidence: 90,
|
|
2929
3211
|
source: "heuristic",
|
|
2930
|
-
violatedLayers: ["
|
|
3212
|
+
violatedLayers: ["L1.1"]
|
|
2931
3213
|
};
|
|
2932
3214
|
}
|
|
2933
|
-
if (/>\s
|
|
3215
|
+
if (/>\s*['"]?\/etc\//.test(actionDetail) || />\s*['"]?\/boot\//.test(actionDetail) || />\s*['"]?\/proc\//.test(actionDetail)) {
|
|
2934
3216
|
return {
|
|
2935
3217
|
allowed: false,
|
|
2936
3218
|
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
@@ -2938,7 +3220,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2938
3220
|
riskType: "file_access",
|
|
2939
3221
|
confidence: 95,
|
|
2940
3222
|
source: "heuristic",
|
|
2941
|
-
violatedLayers: ["
|
|
3223
|
+
violatedLayers: ["L1.1"]
|
|
2942
3224
|
};
|
|
2943
3225
|
}
|
|
2944
3226
|
if (/>\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i.test(actionDetail)) {
|
|
@@ -2949,7 +3231,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
2949
3231
|
riskType: "file_access",
|
|
2950
3232
|
confidence: 90,
|
|
2951
3233
|
source: "heuristic",
|
|
2952
|
-
violatedLayers: ["
|
|
3234
|
+
violatedLayers: ["L1.1"]
|
|
2953
3235
|
};
|
|
2954
3236
|
}
|
|
2955
3237
|
return null;
|
|
@@ -3040,13 +3322,13 @@ function checkLateralMovement(actionDetail) {
|
|
|
3040
3322
|
riskType: "lateral_movement",
|
|
3041
3323
|
confidence: 80,
|
|
3042
3324
|
source: "heuristic",
|
|
3043
|
-
violatedLayers: ["
|
|
3325
|
+
violatedLayers: ["L1.1"]
|
|
3044
3326
|
};
|
|
3045
3327
|
}
|
|
3046
3328
|
}
|
|
3047
3329
|
return null;
|
|
3048
3330
|
}
|
|
3049
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES;
|
|
3331
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3050
3332
|
var init_heuristic = __esm({
|
|
3051
3333
|
"src/engine/heuristic.ts"() {
|
|
3052
3334
|
"use strict";
|
|
@@ -3054,6 +3336,7 @@ var init_heuristic = __esm({
|
|
|
3054
3336
|
init_config();
|
|
3055
3337
|
init_agents();
|
|
3056
3338
|
init_scenarios();
|
|
3339
|
+
init_runtime_config();
|
|
3057
3340
|
init_rules_loader();
|
|
3058
3341
|
init_logic_rules();
|
|
3059
3342
|
INJECTION_KEYWORDS = (() => {
|
|
@@ -3075,6 +3358,8 @@ var init_heuristic = __esm({
|
|
|
3075
3358
|
})();
|
|
3076
3359
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
3077
3360
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
3361
|
+
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
3362
|
+
HOME_ASSIGN_RE = /([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(~[A-Za-z0-9_./-]*|\$HOME[A-Za-z0-9_./-]*|\$\{HOME\}[A-Za-z0-9_./-]*|\/home\/[^\s;|&]+|\$\((?:[^()]|\([^()]*\))*\))(?=\s|;|$|\||&)/gi;
|
|
3078
3363
|
}
|
|
3079
3364
|
});
|
|
3080
3365
|
|
|
@@ -3116,7 +3401,7 @@ var init_template_loader = __esm({
|
|
|
3116
3401
|
|
|
3117
3402
|
// src/engine/script-analyzer.ts
|
|
3118
3403
|
import { readFileSync as readFileSync8 } from "fs";
|
|
3119
|
-
import { resolve as resolve6, join as
|
|
3404
|
+
import { resolve as resolve6, join as join4 } from "path";
|
|
3120
3405
|
function emptyResult() {
|
|
3121
3406
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
3122
3407
|
}
|
|
@@ -3169,7 +3454,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3169
3454
|
}
|
|
3170
3455
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3171
3456
|
try {
|
|
3172
|
-
const expandedPath = scriptPath.startsWith("~") ?
|
|
3457
|
+
const expandedPath = scriptPath.startsWith("~") ? join4(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve6(scriptPath);
|
|
3173
3458
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
3174
3459
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3175
3460
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -4803,8 +5088,8 @@ var init_tracker = __esm({
|
|
|
4803
5088
|
|
|
4804
5089
|
// src/engine/script-content-analyzer.ts
|
|
4805
5090
|
import { statSync as statSync2, existsSync as existsSync9 } from "fs";
|
|
4806
|
-
import { resolve as resolve8, join as
|
|
4807
|
-
import { homedir as
|
|
5091
|
+
import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
5092
|
+
import { homedir as homedir4 } from "os";
|
|
4808
5093
|
function emptyChain() {
|
|
4809
5094
|
return {
|
|
4810
5095
|
nodes: [],
|
|
@@ -4869,6 +5154,13 @@ function extractScriptPath2(command) {
|
|
|
4869
5154
|
return null;
|
|
4870
5155
|
}
|
|
4871
5156
|
function extractScriptPathWithArgs(command) {
|
|
5157
|
+
const inline = command.match(/(?:^|\s|&&|\|\|)\s*(?:bash|\/bin\/bash|sh|zsh)\s+-c\s*(['"])([\s\S]*?)\1/i);
|
|
5158
|
+
if (inline) {
|
|
5159
|
+
const inner = inline[2];
|
|
5160
|
+
const realPath = extractScriptPath2(inner);
|
|
5161
|
+
if (realPath) return { path: realPath, args: [] };
|
|
5162
|
+
return { path: null, args: [] };
|
|
5163
|
+
}
|
|
4872
5164
|
const scriptPath = extractScriptPath2(command);
|
|
4873
5165
|
if (!scriptPath) return { path: null, args: [] };
|
|
4874
5166
|
const idx = command.indexOf(scriptPath);
|
|
@@ -4882,11 +5174,59 @@ function extractScriptPathWithArgs(command) {
|
|
|
4882
5174
|
}
|
|
4883
5175
|
return { path: scriptPath, args };
|
|
4884
5176
|
}
|
|
5177
|
+
function looksLikeScriptPath(tok) {
|
|
5178
|
+
const t = tok.trim().replace(/^['"]|['"]$/g, "");
|
|
5179
|
+
if (!t) return false;
|
|
5180
|
+
const low = t.toLowerCase();
|
|
5181
|
+
if (["bash", "sh", "zsh", "dash", "python", "python3", "perl", "ruby", "node", "php", "lua"].includes(low)) return false;
|
|
5182
|
+
if (/\.(sh|bash|py|pl|rb|js|php|lua)$/.test(low)) return true;
|
|
5183
|
+
if (t.includes("/") && !t.startsWith("-")) return true;
|
|
5184
|
+
return false;
|
|
5185
|
+
}
|
|
4885
5186
|
function extractInlineScriptPath(actionDetail) {
|
|
5187
|
+
const code = extractInlineCode(actionDetail);
|
|
5188
|
+
if (code) {
|
|
5189
|
+
const candidates = [];
|
|
5190
|
+
const osRe = /os\.(?:system|popen)\s*\(\s*(['"])(.*?)\1\s*\)/gi;
|
|
5191
|
+
let m;
|
|
5192
|
+
while ((m = osRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5193
|
+
const subListRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*\[(.*?)\]/gi;
|
|
5194
|
+
while ((m = subListRe.exec(code)) !== null) candidates.push(m[1]);
|
|
5195
|
+
const subShellRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*(['"])(.*?)\1[^)]*?shell\s*=\s*true/gi;
|
|
5196
|
+
while ((m = subShellRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5197
|
+
for (const cand of candidates) {
|
|
5198
|
+
const tokens = cand.includes(",") ? cand.split(/\s*,\s*/).map((t) => t.trim().replace(/^['"]|['"]$/g, "")) : cand.split(/\s+/);
|
|
5199
|
+
for (const tok of tokens) {
|
|
5200
|
+
if (looksLikeScriptPath(tok)) {
|
|
5201
|
+
try {
|
|
5202
|
+
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5203
|
+
if (existsSync9(resolved)) {
|
|
5204
|
+
const stat = statSync2(resolved);
|
|
5205
|
+
if (stat.isFile()) return resolved;
|
|
5206
|
+
}
|
|
5207
|
+
} catch {
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
const openRe = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5213
|
+
while ((m = openRe.exec(code)) !== null) {
|
|
5214
|
+
const p = m[1];
|
|
5215
|
+
try {
|
|
5216
|
+
const resolved = resolvePath2(p);
|
|
5217
|
+
if (existsSync9(resolved)) {
|
|
5218
|
+
const stat = statSync2(resolved);
|
|
5219
|
+
if (stat.isFile()) return resolved;
|
|
5220
|
+
}
|
|
5221
|
+
} catch {
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
return null;
|
|
5225
|
+
}
|
|
4886
5226
|
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
4887
|
-
let
|
|
4888
|
-
while ((
|
|
4889
|
-
const p =
|
|
5227
|
+
let m2;
|
|
5228
|
+
while ((m2 = re.exec(actionDetail)) !== null) {
|
|
5229
|
+
const p = m2[1];
|
|
4890
5230
|
let resolved;
|
|
4891
5231
|
try {
|
|
4892
5232
|
resolved = resolvePath2(p);
|
|
@@ -4902,7 +5242,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
4902
5242
|
function resolvePath2(p) {
|
|
4903
5243
|
let expanded = p;
|
|
4904
5244
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
4905
|
-
expanded =
|
|
5245
|
+
expanded = join6(homedir4(), expanded.slice(1));
|
|
4906
5246
|
}
|
|
4907
5247
|
return resolve8(expanded);
|
|
4908
5248
|
}
|
|
@@ -4983,7 +5323,7 @@ function analyzeScriptContent2(input) {
|
|
|
4983
5323
|
isInline = true;
|
|
4984
5324
|
}
|
|
4985
5325
|
if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
|
|
4986
|
-
resolvedPath =
|
|
5326
|
+
resolvedPath = join6(input.cwd || process.cwd(), resolvedPath);
|
|
4987
5327
|
}
|
|
4988
5328
|
const l1Adapter = (content) => {
|
|
4989
5329
|
const virtualInput = {
|
|
@@ -5072,6 +5412,7 @@ var init_script_content_analyzer = __esm({
|
|
|
5072
5412
|
init_esm_shims();
|
|
5073
5413
|
init_heuristic();
|
|
5074
5414
|
init_logic_rules();
|
|
5415
|
+
init_inline_analyzer();
|
|
5075
5416
|
init_tracker();
|
|
5076
5417
|
SUSPICIOUS_PATTERNS2 = [
|
|
5077
5418
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -5536,7 +5877,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
5536
5877
|
riskType: riskType || "",
|
|
5537
5878
|
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
5538
5879
|
source: "llm",
|
|
5539
|
-
violatedLayers: ["
|
|
5880
|
+
violatedLayers: ["L3.1"]
|
|
5540
5881
|
};
|
|
5541
5882
|
} catch (err) {
|
|
5542
5883
|
if (abortController.signal.aborted) {
|
|
@@ -5573,8 +5914,8 @@ var init_llm_analyzer = __esm({
|
|
|
5573
5914
|
|
|
5574
5915
|
// src/brain/rule-store.ts
|
|
5575
5916
|
import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
5576
|
-
import { join as
|
|
5577
|
-
import { homedir as
|
|
5917
|
+
import { join as join7 } from "path";
|
|
5918
|
+
import { homedir as homedir5 } from "os";
|
|
5578
5919
|
function getRulesPath() {
|
|
5579
5920
|
const envPath = process.env[ENV_RULES_PATH];
|
|
5580
5921
|
if (envPath) return envPath;
|
|
@@ -5592,7 +5933,7 @@ var init_rule_store = __esm({
|
|
|
5592
5933
|
"src/brain/rule-store.ts"() {
|
|
5593
5934
|
"use strict";
|
|
5594
5935
|
init_esm_shims();
|
|
5595
|
-
DEFAULT_RULES_PATH =
|
|
5936
|
+
DEFAULT_RULES_PATH = join7(homedir5(), ".agentmoss", "brain", "rules.json");
|
|
5596
5937
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
5597
5938
|
STATUS_DRAFT = "draft";
|
|
5598
5939
|
STATUS_APPROVED = "approved";
|
|
@@ -6154,6 +6495,13 @@ function decodeBase64Literals(text) {
|
|
|
6154
6495
|
}
|
|
6155
6496
|
return out;
|
|
6156
6497
|
}
|
|
6498
|
+
function stripStringLiterals(text) {
|
|
6499
|
+
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
6500
|
+
if (!m) return text;
|
|
6501
|
+
const cmd = m[1].split("/").pop().toLowerCase();
|
|
6502
|
+
if (!OUTPUT_CMDS.has(cmd)) return text;
|
|
6503
|
+
return text.replace(QUOTED_STR_RE, "");
|
|
6504
|
+
}
|
|
6157
6505
|
function decodeUrlAndNumeric(text) {
|
|
6158
6506
|
const out = [];
|
|
6159
6507
|
let s = text;
|
|
@@ -6187,7 +6535,10 @@ function decodeUrlAndNumeric(text) {
|
|
|
6187
6535
|
return out;
|
|
6188
6536
|
}
|
|
6189
6537
|
function allCandidates(detail, max = 8) {
|
|
6190
|
-
const
|
|
6538
|
+
const shell = stripStringLiterals(detail);
|
|
6539
|
+
const cands = [shell];
|
|
6540
|
+
cands.push(...decodeBase64Literals(shell));
|
|
6541
|
+
cands.push(...decodeUrlAndNumeric(shell));
|
|
6191
6542
|
cands.push(...decodeBase64Literals(detail));
|
|
6192
6543
|
cands.push(...decodeUrlAndNumeric(detail));
|
|
6193
6544
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6211,12 +6562,147 @@ function matchStageAny(detail, stages) {
|
|
|
6211
6562
|
}
|
|
6212
6563
|
return "";
|
|
6213
6564
|
}
|
|
6214
|
-
var B64_TOKEN_RE;
|
|
6565
|
+
var B64_TOKEN_RE, QUOTED_STR_RE, OUTPUT_CMDS;
|
|
6215
6566
|
var init_decode = __esm({
|
|
6216
6567
|
"src/engine/formal-engine/decode.ts"() {
|
|
6217
6568
|
"use strict";
|
|
6218
6569
|
init_esm_shims();
|
|
6219
6570
|
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
6571
|
+
QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
6572
|
+
OUTPUT_CMDS = /* @__PURE__ */ new Set(["echo", "printf", "print"]);
|
|
6573
|
+
}
|
|
6574
|
+
});
|
|
6575
|
+
|
|
6576
|
+
// src/engine/formal-engine/ir.ts
|
|
6577
|
+
function allNodes(mg) {
|
|
6578
|
+
const out = [];
|
|
6579
|
+
for (const s of mg.sessions) out.push(...s.nodes);
|
|
6580
|
+
return out;
|
|
6581
|
+
}
|
|
6582
|
+
function allEdges(mg) {
|
|
6583
|
+
const out = [];
|
|
6584
|
+
for (const s of mg.sessions) out.push(...s.edges);
|
|
6585
|
+
out.push(...mg.crossEdges);
|
|
6586
|
+
return out;
|
|
6587
|
+
}
|
|
6588
|
+
function emptyVerificationResult() {
|
|
6589
|
+
return { verified: true, violations: [] };
|
|
6590
|
+
}
|
|
6591
|
+
var init_ir = __esm({
|
|
6592
|
+
"src/engine/formal-engine/ir.ts"() {
|
|
6593
|
+
"use strict";
|
|
6594
|
+
init_esm_shims();
|
|
6595
|
+
}
|
|
6596
|
+
});
|
|
6597
|
+
|
|
6598
|
+
// src/engine/formal-engine/ltl.ts
|
|
6599
|
+
function compileAps(apPatterns) {
|
|
6600
|
+
if (AP_RE) return;
|
|
6601
|
+
AP_RE = {};
|
|
6602
|
+
for (const [name, pats] of Object.entries(apPatterns || {})) {
|
|
6603
|
+
AP_RE[name] = pats.map((p) => new RegExp(p, "i"));
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
function labelEvent(actionDetail) {
|
|
6607
|
+
if (!AP_RE) compileAps(loadMC().ap_patterns || {});
|
|
6608
|
+
const candidates = allCandidates(actionDetail);
|
|
6609
|
+
const hits = /* @__PURE__ */ new Set();
|
|
6610
|
+
for (const [name, pats] of Object.entries(AP_RE || {})) {
|
|
6611
|
+
for (const c of candidates) {
|
|
6612
|
+
const m = pats.some((p) => p.test(c));
|
|
6613
|
+
if (m) {
|
|
6614
|
+
hits.add(name);
|
|
6615
|
+
break;
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
return hits;
|
|
6620
|
+
}
|
|
6621
|
+
function evaluateLTL(formula, trace, pos) {
|
|
6622
|
+
const op = formula[0];
|
|
6623
|
+
switch (op) {
|
|
6624
|
+
case "ap":
|
|
6625
|
+
return trace[pos].has(formula[1]);
|
|
6626
|
+
case "!":
|
|
6627
|
+
return !evaluateLTL(formula[1], trace, pos);
|
|
6628
|
+
case "&":
|
|
6629
|
+
return evaluateLTL(formula[1], trace, pos) && evaluateLTL(formula[2], trace, pos);
|
|
6630
|
+
case "|":
|
|
6631
|
+
return evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
|
|
6632
|
+
case "->":
|
|
6633
|
+
return !evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
|
|
6634
|
+
case "X":
|
|
6635
|
+
return pos + 1 < trace.length && evaluateLTL(formula[1], trace, pos + 1);
|
|
6636
|
+
case "G": {
|
|
6637
|
+
for (let j = pos; j < trace.length; j++) {
|
|
6638
|
+
if (!evaluateLTL(formula[1], trace, j)) return false;
|
|
6639
|
+
}
|
|
6640
|
+
return true;
|
|
6641
|
+
}
|
|
6642
|
+
case "F": {
|
|
6643
|
+
for (let j = pos; j < trace.length; j++) {
|
|
6644
|
+
if (evaluateLTL(formula[1], trace, j)) return true;
|
|
6645
|
+
}
|
|
6646
|
+
return false;
|
|
6647
|
+
}
|
|
6648
|
+
case "U": {
|
|
6649
|
+
for (let j = pos; j < trace.length; j++) {
|
|
6650
|
+
if (evaluateLTL(formula[2], trace, j)) {
|
|
6651
|
+
for (let k = pos; k < j; k++) {
|
|
6652
|
+
if (!evaluateLTL(formula[1], trace, k)) return false;
|
|
6653
|
+
}
|
|
6654
|
+
return true;
|
|
6655
|
+
}
|
|
6656
|
+
}
|
|
6657
|
+
return false;
|
|
6658
|
+
}
|
|
6659
|
+
default:
|
|
6660
|
+
return false;
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
function ruleViolates(rule, traceWith, witnessMode) {
|
|
6664
|
+
const formula = rule?.formula;
|
|
6665
|
+
if (!Array.isArray(formula) || formula.length === 0) return false;
|
|
6666
|
+
const satWith = evaluateLTL(formula, traceWith, 0);
|
|
6667
|
+
if (!satWith) return false;
|
|
6668
|
+
if (witnessMode && traceWith.length > 1) {
|
|
6669
|
+
const traceWithout = traceWith.slice(0, -1);
|
|
6670
|
+
if (evaluateLTL(formula, traceWithout, 0)) return false;
|
|
6671
|
+
}
|
|
6672
|
+
return true;
|
|
6673
|
+
}
|
|
6674
|
+
function checkMCRules(graph) {
|
|
6675
|
+
const violations = [];
|
|
6676
|
+
const nodes = allNodes(graph);
|
|
6677
|
+
if (nodes.length === 0) return violations;
|
|
6678
|
+
const mc = loadMC();
|
|
6679
|
+
const rules = mc.rules || [];
|
|
6680
|
+
if (rules.length === 0) return violations;
|
|
6681
|
+
const trace = nodes.map((n) => labelEvent(n.actionDetail));
|
|
6682
|
+
const witnessMode = graph.witnessIdx !== void 0;
|
|
6683
|
+
for (const rule of rules) {
|
|
6684
|
+
if (rule?.type !== "ltl" || !(rule.enabled !== false)) continue;
|
|
6685
|
+
if (ruleViolates(rule, trace, witnessMode)) {
|
|
6686
|
+
violations.push({
|
|
6687
|
+
propertyId: String(rule.id || "mc-ltl"),
|
|
6688
|
+
reason: String(rule.reason || "L2.2 \u6A21\u578B\u68C0\u6D4B\u89C4\u5219\u547D\u4E2D"),
|
|
6689
|
+
riskLevel: String(rule.risk_level || "high"),
|
|
6690
|
+
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) }))
|
|
6691
|
+
});
|
|
6692
|
+
if (witnessMode) return violations;
|
|
6693
|
+
}
|
|
6694
|
+
}
|
|
6695
|
+
return violations;
|
|
6696
|
+
}
|
|
6697
|
+
var AP_RE;
|
|
6698
|
+
var init_ltl = __esm({
|
|
6699
|
+
"src/engine/formal-engine/ltl.ts"() {
|
|
6700
|
+
"use strict";
|
|
6701
|
+
init_esm_shims();
|
|
6702
|
+
init_rules_loader();
|
|
6703
|
+
init_ir();
|
|
6704
|
+
init_decode();
|
|
6705
|
+
AP_RE = null;
|
|
6220
6706
|
}
|
|
6221
6707
|
});
|
|
6222
6708
|
|
|
@@ -6246,6 +6732,26 @@ function extractResources(actionDetail) {
|
|
|
6246
6732
|
function markStage(actionDetail) {
|
|
6247
6733
|
return matchStageAny(actionDetail, loadStages());
|
|
6248
6734
|
}
|
|
6735
|
+
function markIntegrity(actionDetail) {
|
|
6736
|
+
const aps = labelEvent(actionDetail);
|
|
6737
|
+
if (aps.has("download_file") || aps.has("prompt_injection")) {
|
|
6738
|
+
return { integrity: "low", isLowSource: true };
|
|
6739
|
+
}
|
|
6740
|
+
return { integrity: "medium", isLowSource: false };
|
|
6741
|
+
}
|
|
6742
|
+
function initNode(sessionId, seq, actionType, actionDetail) {
|
|
6743
|
+
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
6744
|
+
return {
|
|
6745
|
+
sessionId,
|
|
6746
|
+
seq,
|
|
6747
|
+
actionType,
|
|
6748
|
+
actionDetail,
|
|
6749
|
+
stage: markStage(actionDetail),
|
|
6750
|
+
resources: extractResources(actionDetail),
|
|
6751
|
+
integrity,
|
|
6752
|
+
isLowSource
|
|
6753
|
+
};
|
|
6754
|
+
}
|
|
6249
6755
|
function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
6250
6756
|
const nodes = [];
|
|
6251
6757
|
const edges = [];
|
|
@@ -6259,17 +6765,9 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
|
|
|
6259
6765
|
details.push({ actionType: String(aNext.actionType || ""), actionDetail: String(aNext.actionDetail || "") });
|
|
6260
6766
|
}
|
|
6261
6767
|
details.forEach((d, seq) => {
|
|
6262
|
-
const
|
|
6263
|
-
const node = {
|
|
6264
|
-
sessionId,
|
|
6265
|
-
seq,
|
|
6266
|
-
actionType: d.actionType,
|
|
6267
|
-
actionDetail: d.actionDetail,
|
|
6268
|
-
stage,
|
|
6269
|
-
resources: extractResources(d.actionDetail)
|
|
6270
|
-
};
|
|
6768
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
|
|
6271
6769
|
nodes.push(node);
|
|
6272
|
-
if (stage) stageFlags.add(stage);
|
|
6770
|
+
if (node.stage) stageFlags.add(node.stage);
|
|
6273
6771
|
});
|
|
6274
6772
|
for (let i = 0; i < nodes.length - 1; i++) {
|
|
6275
6773
|
edges.push({ src: i, dst: i + 1, kind: "next" });
|
|
@@ -6289,15 +6787,12 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
6289
6787
|
if (aNext) {
|
|
6290
6788
|
const last = mg.sessions[mg.sessions.length - 1];
|
|
6291
6789
|
if (last.sessionId === "current") {
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
stage,
|
|
6299
|
-
resources: extractResources(String(aNext.actionDetail || ""))
|
|
6300
|
-
});
|
|
6790
|
+
last.nodes.push(initNode(
|
|
6791
|
+
last.sessionId,
|
|
6792
|
+
last.nodes.length,
|
|
6793
|
+
String(aNext.actionType || ""),
|
|
6794
|
+
String(aNext.actionDetail || "")
|
|
6795
|
+
));
|
|
6301
6796
|
if (last.nodes.length > 1) {
|
|
6302
6797
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
6303
6798
|
}
|
|
@@ -6311,11 +6806,25 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
6311
6806
|
function addCrossDataEdges(mg) {
|
|
6312
6807
|
const nodes = allNodesFlat(mg);
|
|
6313
6808
|
if (nodes.length < 2) return;
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
const
|
|
6318
|
-
if (
|
|
6809
|
+
const resIndex = /* @__PURE__ */ new Map();
|
|
6810
|
+
nodes.forEach((n, i) => {
|
|
6811
|
+
for (const r of n.resources) {
|
|
6812
|
+
const list = resIndex.get(r);
|
|
6813
|
+
if (list) list.push(i);
|
|
6814
|
+
else resIndex.set(r, [i]);
|
|
6815
|
+
}
|
|
6816
|
+
});
|
|
6817
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6818
|
+
for (const idxs of resIndex.values()) {
|
|
6819
|
+
if (idxs.length < 2) continue;
|
|
6820
|
+
for (let a = 0; a < idxs.length; a++) {
|
|
6821
|
+
const i = idxs[a];
|
|
6822
|
+
for (let b = a + 1; b < idxs.length; b++) {
|
|
6823
|
+
const j = idxs[b];
|
|
6824
|
+
if (nodes[i].sessionId === nodes[j].sessionId) continue;
|
|
6825
|
+
const key = `${i}:${j}`;
|
|
6826
|
+
if (seen.has(key)) continue;
|
|
6827
|
+
seen.add(key);
|
|
6319
6828
|
mg.crossEdges.push({ src: i, dst: j, kind: "data" });
|
|
6320
6829
|
}
|
|
6321
6830
|
}
|
|
@@ -6326,11 +6835,6 @@ function allNodesFlat(mg) {
|
|
|
6326
6835
|
for (const s of mg.sessions) out.push(...s.nodes);
|
|
6327
6836
|
return out;
|
|
6328
6837
|
}
|
|
6329
|
-
function intersect(a, b) {
|
|
6330
|
-
const out = /* @__PURE__ */ new Set();
|
|
6331
|
-
for (const v of a) if (b.has(v)) out.add(v);
|
|
6332
|
-
return out;
|
|
6333
|
-
}
|
|
6334
6838
|
var COMBO_STAGES2, PATH_RE;
|
|
6335
6839
|
var init_builder = __esm({
|
|
6336
6840
|
"src/engine/formal-engine/builder.ts"() {
|
|
@@ -6338,33 +6842,12 @@ var init_builder = __esm({
|
|
|
6338
6842
|
init_esm_shims();
|
|
6339
6843
|
init_rules_loader();
|
|
6340
6844
|
init_decode();
|
|
6845
|
+
init_ltl();
|
|
6341
6846
|
COMBO_STAGES2 = null;
|
|
6342
6847
|
PATH_RE = /(?:^|\s|[@=])((?:\/|~\/)[\w./~-]+)/g;
|
|
6343
6848
|
}
|
|
6344
6849
|
});
|
|
6345
6850
|
|
|
6346
|
-
// src/engine/formal-engine/ir.ts
|
|
6347
|
-
function allNodes(mg) {
|
|
6348
|
-
const out = [];
|
|
6349
|
-
for (const s of mg.sessions) out.push(...s.nodes);
|
|
6350
|
-
return out;
|
|
6351
|
-
}
|
|
6352
|
-
function allEdges(mg) {
|
|
6353
|
-
const out = [];
|
|
6354
|
-
for (const s of mg.sessions) out.push(...s.edges);
|
|
6355
|
-
out.push(...mg.crossEdges);
|
|
6356
|
-
return out;
|
|
6357
|
-
}
|
|
6358
|
-
function emptyVerificationResult() {
|
|
6359
|
-
return { verified: true, violations: [] };
|
|
6360
|
-
}
|
|
6361
|
-
var init_ir = __esm({
|
|
6362
|
-
"src/engine/formal-engine/ir.ts"() {
|
|
6363
|
-
"use strict";
|
|
6364
|
-
init_esm_shims();
|
|
6365
|
-
}
|
|
6366
|
-
});
|
|
6367
|
-
|
|
6368
6851
|
// src/engine/formal-engine/properties.ts
|
|
6369
6852
|
function loadCrossSessionChains() {
|
|
6370
6853
|
if (CROSS_SESSION_CHAINS) return CROSS_SESSION_CHAINS;
|
|
@@ -6381,27 +6864,50 @@ function checkCrossSessionChain(graph) {
|
|
|
6381
6864
|
const violations = [];
|
|
6382
6865
|
const nodes = allNodes(graph);
|
|
6383
6866
|
if (nodes.length === 0) return violations;
|
|
6867
|
+
const witness = graph.witnessIdx;
|
|
6384
6868
|
for (const chain of loadCrossSessionChains()) {
|
|
6385
6869
|
const stages = chain.stages;
|
|
6386
6870
|
if (!stages.length) continue;
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6871
|
+
if (witness !== void 0) {
|
|
6872
|
+
if (nodes[witness].stage !== stages[stages.length - 1]) continue;
|
|
6873
|
+
let matched = 0;
|
|
6874
|
+
const path6 = [];
|
|
6875
|
+
for (let k = 0; k < witness; k++) {
|
|
6876
|
+
const n = nodes[k];
|
|
6877
|
+
if (matched < stages.length - 1 && n.stage === stages[matched]) {
|
|
6878
|
+
matched += 1;
|
|
6879
|
+
path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
|
|
6880
|
+
}
|
|
6396
6881
|
}
|
|
6397
|
-
|
|
6398
|
-
|
|
6882
|
+
if (matched < stages.length - 1) continue;
|
|
6883
|
+
path6.push({ sessionId: nodes[witness].sessionId, seq: nodes[witness].seq, detail: nodes[witness].actionDetail.slice(0, 60) });
|
|
6399
6884
|
violations.push({
|
|
6400
6885
|
propertyId: "cross-" + chain.id,
|
|
6401
6886
|
reason: chain.reason,
|
|
6402
6887
|
riskLevel: chain.riskLevel,
|
|
6403
6888
|
path: path6
|
|
6404
6889
|
});
|
|
6890
|
+
return violations;
|
|
6891
|
+
} else {
|
|
6892
|
+
let matched = 0;
|
|
6893
|
+
let stageIdx = 0;
|
|
6894
|
+
const path6 = [];
|
|
6895
|
+
for (const n of nodes) {
|
|
6896
|
+
if (stageIdx < stages.length && n.stage === stages[stageIdx]) {
|
|
6897
|
+
matched += 1;
|
|
6898
|
+
path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
|
|
6899
|
+
stageIdx += 1;
|
|
6900
|
+
if (matched >= stages.length) break;
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
if (matched >= stages.length) {
|
|
6904
|
+
violations.push({
|
|
6905
|
+
propertyId: "cross-" + chain.id,
|
|
6906
|
+
reason: chain.reason,
|
|
6907
|
+
riskLevel: chain.riskLevel,
|
|
6908
|
+
path: path6
|
|
6909
|
+
});
|
|
6910
|
+
}
|
|
6405
6911
|
}
|
|
6406
6912
|
}
|
|
6407
6913
|
return violations;
|
|
@@ -6436,14 +6942,18 @@ function checkSensitiveDataFlow(graph) {
|
|
|
6436
6942
|
}
|
|
6437
6943
|
}
|
|
6438
6944
|
}
|
|
6439
|
-
|
|
6945
|
+
const witness = graph.witnessIdx;
|
|
6946
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
6947
|
+
for (const i of candidates) {
|
|
6948
|
+
const n = nodes[i];
|
|
6440
6949
|
if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
|
|
6441
6950
|
violations.push({
|
|
6442
|
-
propertyId: "sensitive-data-
|
|
6951
|
+
propertyId: "sensitive-data-flow",
|
|
6443
6952
|
reason: `\u654F\u611F\u6570\u636E\u4F20\u64AD\u5916\u4F20\uFF1A${tainted.get(i)} \u2192 ${n.sessionId}#${n.seq} \u7ECF\u5171\u4EAB\u8F7D\u4F53\u5916\u53D1`,
|
|
6444
6953
|
riskLevel: "critical",
|
|
6445
6954
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
6446
6955
|
});
|
|
6956
|
+
if (witness !== void 0) return violations;
|
|
6447
6957
|
}
|
|
6448
6958
|
}
|
|
6449
6959
|
return violations;
|
|
@@ -6469,12 +6979,38 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6469
6979
|
const violations = [];
|
|
6470
6980
|
const nodes = allNodes(graph);
|
|
6471
6981
|
if (nodes.length < 2) return violations;
|
|
6982
|
+
const witness = graph.witnessIdx;
|
|
6983
|
+
if (witness !== void 0) {
|
|
6984
|
+
const evIdx = [];
|
|
6985
|
+
nodes.forEach((n, i) => {
|
|
6986
|
+
if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
|
|
6987
|
+
});
|
|
6988
|
+
if (evIdx.length === 0) return violations;
|
|
6989
|
+
const firstEv = Math.min(...evIdx);
|
|
6990
|
+
let pre = null;
|
|
6991
|
+
for (let i = 0; i < firstEv; i++) {
|
|
6992
|
+
if (isPrivescNode(nodes[i])) pre = nodes[i];
|
|
6993
|
+
}
|
|
6994
|
+
if (pre) {
|
|
6995
|
+
const post = nodes[firstEv];
|
|
6996
|
+
return [{
|
|
6997
|
+
propertyId: "no-exfil-after-privesc",
|
|
6998
|
+
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
|
|
6999
|
+
riskLevel: "critical",
|
|
7000
|
+
path: [
|
|
7001
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
7002
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
7003
|
+
]
|
|
7004
|
+
}];
|
|
7005
|
+
}
|
|
7006
|
+
return violations;
|
|
7007
|
+
}
|
|
6472
7008
|
for (let i = 0; i < nodes.length; i++) {
|
|
6473
7009
|
if (!isPrivescNode(nodes[i])) continue;
|
|
6474
7010
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6475
7011
|
if (isExfilNode(nodes[j])) {
|
|
6476
7012
|
violations.push({
|
|
6477
|
-
propertyId: "no-
|
|
7013
|
+
propertyId: "no-exfil-after-privesc",
|
|
6478
7014
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
|
|
6479
7015
|
riskLevel: "critical",
|
|
6480
7016
|
path: [
|
|
@@ -6492,12 +7028,38 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6492
7028
|
const violations = [];
|
|
6493
7029
|
const nodes = allNodes(graph);
|
|
6494
7030
|
if (nodes.length < 2) return violations;
|
|
7031
|
+
const witness = graph.witnessIdx;
|
|
7032
|
+
if (witness !== void 0) {
|
|
7033
|
+
const evIdx = [];
|
|
7034
|
+
nodes.forEach((n, i) => {
|
|
7035
|
+
if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
|
|
7036
|
+
});
|
|
7037
|
+
if (evIdx.length === 0) return violations;
|
|
7038
|
+
const firstEv = Math.min(...evIdx);
|
|
7039
|
+
let pre = null;
|
|
7040
|
+
for (let i = 0; i < firstEv; i++) {
|
|
7041
|
+
if (isCredNode(nodes[i])) pre = nodes[i];
|
|
7042
|
+
}
|
|
7043
|
+
if (pre) {
|
|
7044
|
+
const post = nodes[firstEv];
|
|
7045
|
+
return [{
|
|
7046
|
+
propertyId: "no-exfil-after-credential",
|
|
7047
|
+
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
7048
|
+
riskLevel: "critical",
|
|
7049
|
+
path: [
|
|
7050
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
7051
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
7052
|
+
]
|
|
7053
|
+
}];
|
|
7054
|
+
}
|
|
7055
|
+
return violations;
|
|
7056
|
+
}
|
|
6495
7057
|
for (let i = 0; i < nodes.length; i++) {
|
|
6496
7058
|
if (!isCredNode(nodes[i])) continue;
|
|
6497
7059
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6498
7060
|
if (isExfilNode(nodes[j])) {
|
|
6499
7061
|
violations.push({
|
|
6500
|
-
propertyId: "no-
|
|
7062
|
+
propertyId: "no-exfil-after-credential",
|
|
6501
7063
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6502
7064
|
riskLevel: "critical",
|
|
6503
7065
|
path: [
|
|
@@ -6511,13 +7073,164 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6511
7073
|
}
|
|
6512
7074
|
return violations;
|
|
6513
7075
|
}
|
|
6514
|
-
|
|
7076
|
+
function getToolRegistry() {
|
|
7077
|
+
if (TOOL_REGISTRY) return TOOL_REGISTRY;
|
|
7078
|
+
TOOL_REGISTRY = loadToolRegistry().tools || {};
|
|
7079
|
+
return TOOL_REGISTRY;
|
|
7080
|
+
}
|
|
7081
|
+
function lookupToolClass(actionType) {
|
|
7082
|
+
const entry = getToolRegistry()[actionType] || {};
|
|
7083
|
+
return entry.actionClass;
|
|
7084
|
+
}
|
|
7085
|
+
function isComputeNode(n) {
|
|
7086
|
+
if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
|
|
7087
|
+
const cls = lookupToolClass(n.actionType);
|
|
7088
|
+
if (cls === "read" || cls === "write" || cls === "network_egress" || cls === "message_egress") return false;
|
|
7089
|
+
return COMPUTE_RE.test(n.actionDetail);
|
|
7090
|
+
}
|
|
7091
|
+
function checkLowIntegrityCommandExec(graph) {
|
|
7092
|
+
const violations = [];
|
|
7093
|
+
const nodes = allNodes(graph);
|
|
7094
|
+
if (nodes.length === 0) return violations;
|
|
7095
|
+
const witness = graph.witnessIdx;
|
|
7096
|
+
const lowResources = /* @__PURE__ */ new Set();
|
|
7097
|
+
const lowAt = /* @__PURE__ */ new Map();
|
|
7098
|
+
nodes.forEach((n, i) => {
|
|
7099
|
+
if (n.isLowSource) {
|
|
7100
|
+
lowAt.set(i, n.actionDetail.slice(0, 60));
|
|
7101
|
+
for (const r of n.resources) lowResources.add(r);
|
|
7102
|
+
}
|
|
7103
|
+
});
|
|
7104
|
+
let changed = true;
|
|
7105
|
+
while (changed) {
|
|
7106
|
+
changed = false;
|
|
7107
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
7108
|
+
if (lowAt.has(i)) continue;
|
|
7109
|
+
let shared = "";
|
|
7110
|
+
for (const r of nodes[i].resources) {
|
|
7111
|
+
if (lowResources.has(r)) {
|
|
7112
|
+
shared = r;
|
|
7113
|
+
break;
|
|
7114
|
+
}
|
|
7115
|
+
}
|
|
7116
|
+
if (shared) {
|
|
7117
|
+
lowAt.set(i, `\u7EE7\u627F\u81EA ${shared}`);
|
|
7118
|
+
for (const r of nodes[i].resources) lowResources.add(r);
|
|
7119
|
+
changed = true;
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
if (lowAt.size === 0) return violations;
|
|
7124
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7125
|
+
for (const i of candidates) {
|
|
7126
|
+
const n = nodes[i];
|
|
7127
|
+
if (lowAt.has(i) && isComputeNode(n)) {
|
|
7128
|
+
violations.push({
|
|
7129
|
+
propertyId: "low-integrity-command-exec",
|
|
7130
|
+
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`,
|
|
7131
|
+
riskLevel: "critical",
|
|
7132
|
+
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
7133
|
+
});
|
|
7134
|
+
if (witness !== void 0) return violations;
|
|
7135
|
+
}
|
|
7136
|
+
}
|
|
7137
|
+
return violations;
|
|
7138
|
+
}
|
|
7139
|
+
function checkCrossSessionReachability(graph) {
|
|
7140
|
+
const violations = [];
|
|
7141
|
+
const nodes = allNodes(graph);
|
|
7142
|
+
if (nodes.length === 0) return violations;
|
|
7143
|
+
const witness = graph.witnessIdx;
|
|
7144
|
+
const sources = /* @__PURE__ */ new Set();
|
|
7145
|
+
nodes.forEach((n, i) => {
|
|
7146
|
+
if (n.isLowSource || DL_OR_LOW_RE.test(n.actionDetail)) sources.add(i);
|
|
7147
|
+
});
|
|
7148
|
+
if (sources.size === 0) return violations;
|
|
7149
|
+
const reached = new Set(sources);
|
|
7150
|
+
let changed = true;
|
|
7151
|
+
while (changed) {
|
|
7152
|
+
changed = false;
|
|
7153
|
+
for (const e of allEdges(graph)) {
|
|
7154
|
+
if (reached.has(e.src) && !reached.has(e.dst)) {
|
|
7155
|
+
const srcRes = nodes[e.src].resources;
|
|
7156
|
+
const dstRes = nodes[e.dst].resources;
|
|
7157
|
+
for (const r of srcRes) {
|
|
7158
|
+
if (dstRes.has(r)) {
|
|
7159
|
+
reached.add(e.dst);
|
|
7160
|
+
changed = true;
|
|
7161
|
+
break;
|
|
7162
|
+
}
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7168
|
+
for (const i of candidates) {
|
|
7169
|
+
const n = nodes[i];
|
|
7170
|
+
if (sources.has(i)) continue;
|
|
7171
|
+
if (reached.has(i)) {
|
|
7172
|
+
const srcIdx = Math.min(...sources);
|
|
7173
|
+
const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
|
|
7174
|
+
violations.push({
|
|
7175
|
+
propertyId: "low-integrity-reachability",
|
|
7176
|
+
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`,
|
|
7177
|
+
riskLevel: "high",
|
|
7178
|
+
path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
|
|
7179
|
+
});
|
|
7180
|
+
if (witness !== void 0) return violations;
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
return violations;
|
|
7184
|
+
}
|
|
7185
|
+
function checkSensitiveDataAggregation(graph) {
|
|
7186
|
+
const violations = [];
|
|
7187
|
+
const nodes = allNodes(graph);
|
|
7188
|
+
if (nodes.length < 2) return violations;
|
|
7189
|
+
const witness = graph.witnessIdx;
|
|
7190
|
+
const resIndex = /* @__PURE__ */ new Map();
|
|
7191
|
+
nodes.forEach((n, i) => {
|
|
7192
|
+
for (const r of n.resources) {
|
|
7193
|
+
const list = resIndex.get(r);
|
|
7194
|
+
if (list) list.push(i);
|
|
7195
|
+
else resIndex.set(r, [i]);
|
|
7196
|
+
}
|
|
7197
|
+
});
|
|
7198
|
+
const sourcesFor = (exfilIdx) => {
|
|
7199
|
+
const srcs = /* @__PURE__ */ new Set();
|
|
7200
|
+
const n = nodes[exfilIdx];
|
|
7201
|
+
for (const r of n.resources) {
|
|
7202
|
+
for (const j of resIndex.get(r) || []) {
|
|
7203
|
+
if (j === exfilIdx) continue;
|
|
7204
|
+
srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7207
|
+
return srcs;
|
|
7208
|
+
};
|
|
7209
|
+
const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
|
|
7210
|
+
for (const i of candidates) {
|
|
7211
|
+
const n = nodes[i];
|
|
7212
|
+
if (!isExfilNode(n)) continue;
|
|
7213
|
+
const srcs = sourcesFor(i);
|
|
7214
|
+
if (srcs.size >= 2) {
|
|
7215
|
+
violations.push({
|
|
7216
|
+
propertyId: "sensitive-data-aggregation",
|
|
7217
|
+
reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
|
|
7218
|
+
riskLevel: "critical",
|
|
7219
|
+
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
7220
|
+
});
|
|
7221
|
+
if (witness !== void 0) return violations;
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7224
|
+
return violations;
|
|
7225
|
+
}
|
|
7226
|
+
var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, PRIVESC_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
|
|
6515
7227
|
var init_properties = __esm({
|
|
6516
7228
|
"src/engine/formal-engine/properties.ts"() {
|
|
6517
7229
|
"use strict";
|
|
6518
7230
|
init_esm_shims();
|
|
6519
7231
|
init_rules_loader();
|
|
6520
7232
|
init_ir();
|
|
7233
|
+
init_ltl();
|
|
6521
7234
|
CROSS_SESSION_CHAINS = null;
|
|
6522
7235
|
CRED_FILE_RE = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
|
|
6523
7236
|
EXFIL_RE = /https?:\/\/|scp\s+.*@|rsync\s+.*@/i;
|
|
@@ -6526,11 +7239,22 @@ var init_properties = __esm({
|
|
|
6526
7239
|
PRIVESC_RE = /\bsudo\b|\bsu\b|chmod\s+u\s*\+\s*s|pkexec|setuid/i;
|
|
6527
7240
|
EXFIL_ACTION_RE = /\bcurl\b|\bwget\b|\bscp\b|\brsync\b|\brclone\b|\bftp\b|connect\s+.*\d+\.\d+\.\d+\.\d+|open\s+.*tcp|base64\s+.*\-o/i;
|
|
6528
7241
|
CRED_STRICT_RE = /(known_hosts|\.ssh|id_rsa|\.pem|\b\.key\b|\/etc\/shadow|\/etc\/passwd|\.aws\/credentials|credentials\.json|netrc|password|secrets?\.ya?ml|credentials?\s*(file|path)?\s*(=|\:)|\bconfig.*(password|token))/i;
|
|
7242
|
+
TOOL_REGISTRY = null;
|
|
7243
|
+
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;
|
|
7244
|
+
STRONG_COMPUTE_TYPES = /* @__PURE__ */ new Set(["python", "node", "exec", "terminal", "cmd"]);
|
|
7245
|
+
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;
|
|
6529
7246
|
ALL_PROPERTIES = [
|
|
6530
|
-
{ id: "cross-session-chain", check: checkCrossSessionChain },
|
|
6531
|
-
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow },
|
|
6532
|
-
{ id: "
|
|
6533
|
-
{ id: "no-
|
|
7247
|
+
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
7248
|
+
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow, shortName: "P2" },
|
|
7249
|
+
{ id: "low-integrity-reachability", check: checkCrossSessionReachability, shortName: "P3" },
|
|
7250
|
+
{ id: "no-exfil-after-privesc", check: checkNeverExfilAfterPrivesc, shortName: "P4" },
|
|
7251
|
+
{ id: "no-exfil-after-credential", check: checkNeverExfilAfterCred, shortName: "P5" },
|
|
7252
|
+
// L2.2 模型检测(通用 LTL 求值器,mc_rules.json 单一事实源;witness 语义见 ltl.ts)
|
|
7253
|
+
{ id: "mc-ltl-rules", check: checkMCRules, shortName: "P6" },
|
|
7254
|
+
// P7 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
|
|
7255
|
+
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec, shortName: "P7" },
|
|
7256
|
+
// P8 敏感数据聚合外传(egress 聚合 ≥2 事件源)
|
|
7257
|
+
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation, shortName: "P8" }
|
|
6534
7258
|
];
|
|
6535
7259
|
}
|
|
6536
7260
|
});
|
|
@@ -6572,6 +7296,9 @@ var init_engine = __esm({
|
|
|
6572
7296
|
});
|
|
6573
7297
|
}
|
|
6574
7298
|
const graph = buildMultiSessionGraph(all, aNext);
|
|
7299
|
+
if (aNext) {
|
|
7300
|
+
graph.witnessIdx = allNodes(graph).length - 1;
|
|
7301
|
+
}
|
|
6575
7302
|
const result = emptyVerificationResult();
|
|
6576
7303
|
for (const prop of this.properties) {
|
|
6577
7304
|
let violations;
|
|
@@ -6600,6 +7327,32 @@ var init_engine = __esm({
|
|
|
6600
7327
|
});
|
|
6601
7328
|
|
|
6602
7329
|
// src/engine/formal-engine/stateful.ts
|
|
7330
|
+
function extractInstances(actionDetail, stage) {
|
|
7331
|
+
const out = [];
|
|
7332
|
+
if (PORT_STAGE_RE.test(stage)) {
|
|
7333
|
+
const d = actionDetail;
|
|
7334
|
+
let m;
|
|
7335
|
+
const re = new RegExp(PORT_RE.source, "gi");
|
|
7336
|
+
while ((m = re.exec(d)) !== null) {
|
|
7337
|
+
const p = m[1] || m[2] || m[3];
|
|
7338
|
+
if (p && Number(p) > 0 && Number(p) <= 65535) out.push(`port:${p}`);
|
|
7339
|
+
}
|
|
7340
|
+
if (/SCAN/.test(stage)) {
|
|
7341
|
+
const bareRe = new RegExp(BARE_PORT_RE.source, "g");
|
|
7342
|
+
while ((m = bareRe.exec(d)) !== null) {
|
|
7343
|
+
if (m[1] && Number(m[1]) > 0 && Number(m[1]) <= 65535) out.push(`port:${m[1]}`);
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
7346
|
+
m = SOCK_RE.exec(d);
|
|
7347
|
+
if (m && m[1]) out.push(`sock:${m[1]}`);
|
|
7348
|
+
m = UNIT_RE.exec(d);
|
|
7349
|
+
if (m && m[1]) out.push(`unit:${m[1]}`);
|
|
7350
|
+
}
|
|
7351
|
+
return out;
|
|
7352
|
+
}
|
|
7353
|
+
function buckKey(chainId, inst) {
|
|
7354
|
+
return inst ? `${chainId}@@${inst}` : chainId;
|
|
7355
|
+
}
|
|
6603
7356
|
function loadRules() {
|
|
6604
7357
|
if (STAGES_PATTERNS !== null && CHAINS !== null) {
|
|
6605
7358
|
return { stages: STAGES_PATTERNS, chains: CHAINS };
|
|
@@ -6639,7 +7392,7 @@ function lifecycleFor(detail, stage, isError) {
|
|
|
6639
7392
|
if (detail.includes(">>") || detail.includes(">")) return "alive";
|
|
6640
7393
|
return "";
|
|
6641
7394
|
}
|
|
6642
|
-
var DESTROY_RE, NEVER_ACTIVATED_RE, CRED_FILE_RE2, EXFIL_RE2, STAGES_PATTERNS, CHAINS, MAX_NODES, StatefulChainTracker;
|
|
7395
|
+
var DESTROY_RE, NEVER_ACTIVATED_RE, CRED_FILE_RE2, EXFIL_RE2, PORT_RE, BARE_PORT_RE, SOCK_RE, UNIT_RE, PORT_STAGE_RE, STAGES_PATTERNS, CHAINS, MAX_NODES, StatefulChainTracker;
|
|
6643
7396
|
var init_stateful = __esm({
|
|
6644
7397
|
"src/engine/formal-engine/stateful.ts"() {
|
|
6645
7398
|
"use strict";
|
|
@@ -6649,11 +7402,20 @@ var init_stateful = __esm({
|
|
|
6649
7402
|
DESTROY_RE = /\b(?:pkill|kill)\b|systemctl\s+stop|service\s+stop|crontab\s+-r|rm\s+-[rf]?\s+/i;
|
|
6650
7403
|
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;
|
|
6651
7404
|
CRED_FILE_RE2 = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
|
|
6652
|
-
EXFIL_RE2 = /https
|
|
7405
|
+
EXFIL_RE2 = /https?:\/\/(?!127\.\d+\.\d+\.\d+|0\.0\.0\.0|localhost|\[?::1\]?)|scp\s+.*@|rsync\s+.*@/i;
|
|
7406
|
+
PORT_RE = /(?:-p\s+(\d{1,5})|-p(\d{1,5})|:(\d{1,5})\b)/i;
|
|
7407
|
+
BARE_PORT_RE = /(?<=^|\s)(\d{1,5})(?=\s|$)/g;
|
|
7408
|
+
SOCK_RE = /([\w./-]*(?:\/tmp\/|\/var\/run\/|\.sock\b)[\w./-]*)/;
|
|
7409
|
+
UNIT_RE = /systemctl\s+(?:start|enable|restart)\s+([\w.-]+)/i;
|
|
7410
|
+
PORT_STAGE_RE = /LISTEN|PERSIST|SCAN/;
|
|
6653
7411
|
STAGES_PATTERNS = null;
|
|
6654
7412
|
CHAINS = null;
|
|
6655
7413
|
MAX_NODES = 1e5;
|
|
6656
|
-
StatefulChainTracker = class {
|
|
7414
|
+
StatefulChainTracker = class _StatefulChainTracker {
|
|
7415
|
+
/** 本地接收时刻(实时路径无 called_at 时的兜底时间戳,ISO)。 */
|
|
7416
|
+
static now() {
|
|
7417
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7418
|
+
}
|
|
6657
7419
|
agentId;
|
|
6658
7420
|
nodes_ = [];
|
|
6659
7421
|
ptr = {};
|
|
@@ -6675,10 +7437,14 @@ var init_stateful = __esm({
|
|
|
6675
7437
|
for (const chain of chains) {
|
|
6676
7438
|
const stages = chain.stages || [];
|
|
6677
7439
|
if (stages.length === 0) continue;
|
|
6678
|
-
const
|
|
7440
|
+
const cid = String(chain.id || "");
|
|
7441
|
+
let matched = this.ptr[cid] || 0;
|
|
7442
|
+
for (const [k, v] of Object.entries(this.ptr)) {
|
|
7443
|
+
if (k.startsWith(`${cid}@@`) && v > matched && v < stages.length) matched = v;
|
|
7444
|
+
}
|
|
6679
7445
|
if (matched > 0 && matched < stages.length) {
|
|
6680
7446
|
const pf = {
|
|
6681
|
-
chainId:
|
|
7447
|
+
chainId: cid,
|
|
6682
7448
|
stages,
|
|
6683
7449
|
matched,
|
|
6684
7450
|
riskLevel: String(chain.riskLevel || chain.risk_level || "high"),
|
|
@@ -6754,8 +7520,10 @@ var init_stateful = __esm({
|
|
|
6754
7520
|
actionDetail: ad,
|
|
6755
7521
|
stage,
|
|
6756
7522
|
resources: extractResources2(ad),
|
|
7523
|
+
instances: extractInstances(ad, stage),
|
|
6757
7524
|
isError,
|
|
6758
|
-
lifecycle: lifecycleFor(ad, stage, isError)
|
|
7525
|
+
lifecycle: lifecycleFor(ad, stage, isError),
|
|
7526
|
+
timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
|
|
6759
7527
|
};
|
|
6760
7528
|
this.nodes_.push(node);
|
|
6761
7529
|
this.seqNext += 1;
|
|
@@ -6763,11 +7531,19 @@ var init_stateful = __esm({
|
|
|
6763
7531
|
}
|
|
6764
7532
|
recordLifecycle(node) {
|
|
6765
7533
|
if (node.lifecycle === "dead") {
|
|
6766
|
-
|
|
7534
|
+
const clearedInsts = [];
|
|
7535
|
+
for (const inst of node.instances) {
|
|
7536
|
+
const key = `inst:${inst}`;
|
|
7537
|
+
if (key in this.aliveResources) {
|
|
7538
|
+
delete this.aliveResources[key];
|
|
7539
|
+
clearedInsts.push(inst);
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
let pathHit = false;
|
|
6767
7543
|
for (const r of node.resources) {
|
|
6768
7544
|
if (r in this.aliveResources) {
|
|
6769
7545
|
delete this.aliveResources[r];
|
|
6770
|
-
|
|
7546
|
+
pathHit = true;
|
|
6771
7547
|
}
|
|
6772
7548
|
}
|
|
6773
7549
|
const detail = node.actionDetail;
|
|
@@ -6775,16 +7551,30 @@ var init_stateful = __esm({
|
|
|
6775
7551
|
const base = r.split("/").pop() || "";
|
|
6776
7552
|
if (base && detail.includes(base)) {
|
|
6777
7553
|
delete this.aliveResources[r];
|
|
6778
|
-
|
|
7554
|
+
pathHit = true;
|
|
6779
7555
|
}
|
|
6780
7556
|
}
|
|
6781
|
-
if (
|
|
7557
|
+
if (clearedInsts.length > 0 && !pathHit) {
|
|
7558
|
+
for (const inst of clearedInsts) this.resetChainsForInstance(inst);
|
|
7559
|
+
} else {
|
|
7560
|
+
this.resetChainsOnDestroy();
|
|
7561
|
+
}
|
|
6782
7562
|
return;
|
|
6783
7563
|
}
|
|
6784
7564
|
if (node.lifecycle === "alive") {
|
|
6785
7565
|
for (const r of node.resources) {
|
|
6786
7566
|
this.aliveResources[r] = node.seq;
|
|
6787
7567
|
}
|
|
7568
|
+
for (const inst of node.instances) {
|
|
7569
|
+
this.aliveResources[`inst:${inst}`] = node.seq;
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7573
|
+
/** 只重置指定实例在全部链上的进度(不影响其他端口/资源)。 */
|
|
7574
|
+
resetChainsForInstance(inst) {
|
|
7575
|
+
const key = buckKey("", inst).slice(2);
|
|
7576
|
+
for (const k of Object.keys(this.ptr)) {
|
|
7577
|
+
if (k.endsWith(`@@${inst}`)) this.ptr[k] = 0;
|
|
6788
7578
|
}
|
|
6789
7579
|
}
|
|
6790
7580
|
resetChainsOnDestroy() {
|
|
@@ -6792,6 +7582,29 @@ var init_stateful = __esm({
|
|
|
6792
7582
|
this.ptr[cid] = 0;
|
|
6793
7583
|
}
|
|
6794
7584
|
}
|
|
7585
|
+
/**
|
|
7586
|
+
* 当前节点应推进的链桶集合。
|
|
7587
|
+
* - 有实例节点(LISTEN/PERSIST 带端口/socket/unit)→ 只推自己的实例桶;
|
|
7588
|
+
* 同时为所有链的该实例建立桶(若首次出现)。
|
|
7589
|
+
* - 无实例节点(EXEC/SCAN 等中间段)→ 推全局桶 + 所有当前活跃实例桶
|
|
7590
|
+
* (信息不足时保守推进一切可能,避免漏判)。
|
|
7591
|
+
*/
|
|
7592
|
+
targetsForNode(node, chainId) {
|
|
7593
|
+
const insts = node.instances;
|
|
7594
|
+
if (insts.length > 0) {
|
|
7595
|
+
const active = insts.filter((i) => this.aliveResources[`inst:${i}`] !== void 0);
|
|
7596
|
+
if (active.length > 0) return active.map((i) => buckKey(chainId, i));
|
|
7597
|
+
return [chainId];
|
|
7598
|
+
}
|
|
7599
|
+
const targets = [chainId];
|
|
7600
|
+
for (const k of Object.keys(this.aliveResources)) {
|
|
7601
|
+
if (k.startsWith("inst:")) {
|
|
7602
|
+
const inst = k.slice("inst:".length);
|
|
7603
|
+
targets.push(`${chainId}@@${inst}`);
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
return targets;
|
|
7607
|
+
}
|
|
6795
7608
|
advanceChains(node) {
|
|
6796
7609
|
if (node.lifecycle === "dead") return null;
|
|
6797
7610
|
if (node.lifecycle === "never_activated") {
|
|
@@ -6805,23 +7618,32 @@ var init_stateful = __esm({
|
|
|
6805
7618
|
const stages = chain.stages || [];
|
|
6806
7619
|
if (stages.length === 0) continue;
|
|
6807
7620
|
const cid = String(chain.id || "");
|
|
6808
|
-
const
|
|
6809
|
-
|
|
6810
|
-
this.ptr[
|
|
6811
|
-
if (cur
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
7621
|
+
const targets = this.targetsForNode(node, cid);
|
|
7622
|
+
for (const key of targets) {
|
|
7623
|
+
const cur = this.ptr[key] || 0;
|
|
7624
|
+
if (cur < stages.length && stages[cur] === stage) {
|
|
7625
|
+
this.ptr[key] = cur + 1;
|
|
7626
|
+
if (cur + 1 >= stages.length) {
|
|
7627
|
+
return {
|
|
7628
|
+
chainId: cid,
|
|
7629
|
+
stages,
|
|
7630
|
+
riskLevel: String(chain.riskLevel || chain.risk_level || "high"),
|
|
7631
|
+
reason: String(chain.reason || "\u68C0\u6D4B\u5230\u7EC4\u5408\u653B\u51FB\u94FE"),
|
|
7632
|
+
path: this.chainPath(stages)
|
|
7633
|
+
};
|
|
7634
|
+
}
|
|
6819
7635
|
}
|
|
6820
7636
|
}
|
|
6821
7637
|
}
|
|
6822
7638
|
return null;
|
|
6823
7639
|
}
|
|
6824
7640
|
invalidateChainOnFailure(node) {
|
|
7641
|
+
if (node.instances.length > 0) {
|
|
7642
|
+
for (const inst of node.instances) {
|
|
7643
|
+
this.resetChainsForInstance(inst);
|
|
7644
|
+
}
|
|
7645
|
+
return;
|
|
7646
|
+
}
|
|
6825
7647
|
const failed = new Set(node.resources);
|
|
6826
7648
|
if (failed.size === 0) return;
|
|
6827
7649
|
const { chains } = loadRules();
|
|
@@ -6837,7 +7659,7 @@ var init_stateful = __esm({
|
|
|
6837
7659
|
let want = stages[0];
|
|
6838
7660
|
for (const n of this.nodes_) {
|
|
6839
7661
|
if (n.stage === want) {
|
|
6840
|
-
path6.push({ sessionId: n.sessionId, seq: n.seq, stage: n.stage });
|
|
7662
|
+
path6.push({ sessionId: n.sessionId, seq: n.seq, stage: n.stage, timestamp: n.timestamp });
|
|
6841
7663
|
const idx = stages.indexOf(n.stage);
|
|
6842
7664
|
if (idx + 1 < stages.length) {
|
|
6843
7665
|
want = stages[idx + 1];
|
|
@@ -6873,7 +7695,7 @@ var init_stateful = __esm({
|
|
|
6873
7695
|
if (!this.tainted.has(node.seq)) return [];
|
|
6874
7696
|
if (EXFIL_RE2.test(node.actionDetail)) {
|
|
6875
7697
|
return [{
|
|
6876
|
-
property_id: "sensitive-data-
|
|
7698
|
+
property_id: "sensitive-data-flow",
|
|
6877
7699
|
reason: `\u654F\u611F\u6570\u636E\u4F20\u64AD\u5916\u4F20\uFF1A${this.taintSource(node)} \u2192 ${node.sessionId}#${node.seq} \u7ECF\u5171\u4EAB\u8F7D\u4F53\u5916\u53D1`,
|
|
6878
7700
|
risk_level: "critical",
|
|
6879
7701
|
path: [[node.sessionId, node.seq, node.actionDetail.slice(0, 60)]]
|
|
@@ -6915,8 +7737,12 @@ var init_stateful = __esm({
|
|
|
6915
7737
|
actionDetail: String(raw.actionDetail || ""),
|
|
6916
7738
|
stage: String(raw.stage || ""),
|
|
6917
7739
|
resources: [...raw.resources || []],
|
|
7740
|
+
instances: [...raw.instances || []],
|
|
7741
|
+
// 旧快照无此字段 → 空
|
|
6918
7742
|
isError: Boolean(raw.isError),
|
|
6919
|
-
lifecycle: String(raw.lifecycle || "")
|
|
7743
|
+
lifecycle: String(raw.lifecycle || ""),
|
|
7744
|
+
timestamp: typeof raw.timestamp === "string" ? raw.timestamp : null
|
|
7745
|
+
// 旧快照无此字段 → null
|
|
6920
7746
|
});
|
|
6921
7747
|
}
|
|
6922
7748
|
this.seqNext = Number(snapshot.seq_next || this.nodes_.length);
|
|
@@ -7083,6 +7909,7 @@ function _resetBrainForTest() {
|
|
|
7083
7909
|
_brainInitialized = false;
|
|
7084
7910
|
}
|
|
7085
7911
|
async function runAnalysis(input) {
|
|
7912
|
+
const observedFormalLayers = [];
|
|
7086
7913
|
const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
|
|
7087
7914
|
const isInlineScriptCmd = isInlineScriptCommand(input.aNext.actionDetail.toLowerCase());
|
|
7088
7915
|
const isLiteralTextCmd = isLiteralTextCommand(input.aNext.actionDetail.toLowerCase());
|
|
@@ -7108,7 +7935,7 @@ async function runAnalysis(input) {
|
|
|
7108
7935
|
riskType: node.l1RiskType,
|
|
7109
7936
|
confidence: 90,
|
|
7110
7937
|
source: "heuristic_script_scan",
|
|
7111
|
-
violatedLayers: ["
|
|
7938
|
+
violatedLayers: ["L1.2"]
|
|
7112
7939
|
};
|
|
7113
7940
|
}
|
|
7114
7941
|
}
|
|
@@ -7137,8 +7964,8 @@ async function runAnalysis(input) {
|
|
|
7137
7964
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
7138
7965
|
const lowerDetail = rawDetail.toLowerCase();
|
|
7139
7966
|
const agentExt = mergeAgentExtensions(
|
|
7140
|
-
getAgentExtensions(input.agentId),
|
|
7141
|
-
|
|
7967
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
7968
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
7142
7969
|
);
|
|
7143
7970
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
7144
7971
|
return {
|
|
@@ -7226,7 +8053,7 @@ async function runAnalysis(input) {
|
|
|
7226
8053
|
riskType: inlineJudgment.riskType,
|
|
7227
8054
|
confidence: 90,
|
|
7228
8055
|
source: "logic_rule",
|
|
7229
|
-
violatedLayers: ["1
|
|
8056
|
+
violatedLayers: ["L2.1"]
|
|
7230
8057
|
};
|
|
7231
8058
|
}
|
|
7232
8059
|
return {
|
|
@@ -7255,15 +8082,22 @@ async function runAnalysis(input) {
|
|
|
7255
8082
|
const formalResult = formalEngine.verify(formalSessions, input.aNext);
|
|
7256
8083
|
if (!formalResult.verified) {
|
|
7257
8084
|
const v = formalResult.violations[0];
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
8085
|
+
if (isFormalObserveMode()) {
|
|
8086
|
+
console.warn(
|
|
8087
|
+
`[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)}`
|
|
8088
|
+
);
|
|
8089
|
+
observedFormalLayers.push("L2.2");
|
|
8090
|
+
} else {
|
|
8091
|
+
return {
|
|
8092
|
+
allowed: false,
|
|
8093
|
+
reason: v ? v.reason : "\u5F62\u5F0F\u5316\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF08\u884C\u4E3A\u6027\u8D28\u8FDD\u53CD\uFF09",
|
|
8094
|
+
riskLevel: v ? v.riskLevel : "high",
|
|
8095
|
+
riskType: "formal_verification",
|
|
8096
|
+
confidence: 90,
|
|
8097
|
+
source: "formal_engine",
|
|
8098
|
+
violatedLayers: ["L2.2"]
|
|
8099
|
+
};
|
|
8100
|
+
}
|
|
7267
8101
|
}
|
|
7268
8102
|
}
|
|
7269
8103
|
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
@@ -7278,7 +8112,7 @@ async function runAnalysis(input) {
|
|
|
7278
8112
|
riskType: node.l2RiskType,
|
|
7279
8113
|
confidence: 85,
|
|
7280
8114
|
source: "logic_rule_script_scan",
|
|
7281
|
-
violatedLayers: ["
|
|
8115
|
+
violatedLayers: ["L2.3"]
|
|
7282
8116
|
};
|
|
7283
8117
|
}
|
|
7284
8118
|
}
|
|
@@ -7310,7 +8144,7 @@ async function runAnalysis(input) {
|
|
|
7310
8144
|
riskType: "",
|
|
7311
8145
|
confidence: 85,
|
|
7312
8146
|
source: "heuristic_and_logic_only",
|
|
7313
|
-
violatedLayers: []
|
|
8147
|
+
violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
|
|
7314
8148
|
};
|
|
7315
8149
|
}
|
|
7316
8150
|
let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
|
|
@@ -7355,7 +8189,7 @@ async function runAnalysis(input) {
|
|
|
7355
8189
|
riskType: "",
|
|
7356
8190
|
confidence: 50,
|
|
7357
8191
|
source: "llm_failure_fallback",
|
|
7358
|
-
violatedLayers: []
|
|
8192
|
+
violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
|
|
7359
8193
|
};
|
|
7360
8194
|
}
|
|
7361
8195
|
return {
|
|
@@ -7365,7 +8199,7 @@ async function runAnalysis(input) {
|
|
|
7365
8199
|
riskType: "",
|
|
7366
8200
|
confidence: 60,
|
|
7367
8201
|
source: "llm_failure_fallback",
|
|
7368
|
-
violatedLayers: ["
|
|
8202
|
+
violatedLayers: ["L3.1"]
|
|
7369
8203
|
};
|
|
7370
8204
|
}
|
|
7371
8205
|
if (!llmResult.allowed && llmResult.confidence && llmResult.confidence >= 90 && _brainExtractor) {
|
|
@@ -7382,6 +8216,9 @@ async function runAnalysis(input) {
|
|
|
7382
8216
|
} catch {
|
|
7383
8217
|
}
|
|
7384
8218
|
}
|
|
8219
|
+
if (observedFormalLayers.length > 0 && llmResult.allowed) {
|
|
8220
|
+
llmResult.violatedLayers = Array.from(/* @__PURE__ */ new Set([...observedFormalLayers, ...llmResult.violatedLayers || []]));
|
|
8221
|
+
}
|
|
7385
8222
|
return llmResult;
|
|
7386
8223
|
}
|
|
7387
8224
|
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, FULLY_SAFE_BASH_PATTERNS, READONLY_SENSITIVE_BASH_PATTERNS, _brainStore, _brainExtractor, _brainInitialized, formalEngine;
|
|
@@ -7395,6 +8232,7 @@ var init_coordinator = __esm({
|
|
|
7395
8232
|
init_llm_analyzer();
|
|
7396
8233
|
init_script_content_analyzer();
|
|
7397
8234
|
init_config();
|
|
8235
|
+
init_runtime_config();
|
|
7398
8236
|
init_agents();
|
|
7399
8237
|
init_scenarios();
|
|
7400
8238
|
init_brain();
|
|
@@ -7556,7 +8394,7 @@ var require_bindings = __commonJS({
|
|
|
7556
8394
|
var fs7 = __require("fs");
|
|
7557
8395
|
var path6 = __require("path");
|
|
7558
8396
|
var fileURLToPath11 = require_file_uri_to_path();
|
|
7559
|
-
var
|
|
8397
|
+
var join11 = path6.join;
|
|
7560
8398
|
var dirname11 = path6.dirname;
|
|
7561
8399
|
var exists = fs7.accessSync && function(path7) {
|
|
7562
8400
|
try {
|
|
@@ -7616,7 +8454,7 @@ var require_bindings = __commonJS({
|
|
|
7616
8454
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
7617
8455
|
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
7618
8456
|
for (; i < l; i++) {
|
|
7619
|
-
n =
|
|
8457
|
+
n = join11.apply(
|
|
7620
8458
|
null,
|
|
7621
8459
|
opts.try[i].map(function(p) {
|
|
7622
8460
|
return opts[p] || p;
|
|
@@ -7677,7 +8515,7 @@ var require_bindings = __commonJS({
|
|
|
7677
8515
|
if (dir === ".") {
|
|
7678
8516
|
dir = process.cwd();
|
|
7679
8517
|
}
|
|
7680
|
-
if (exists(
|
|
8518
|
+
if (exists(join11(dir, "package.json")) || exists(join11(dir, "node_modules"))) {
|
|
7681
8519
|
return dir;
|
|
7682
8520
|
}
|
|
7683
8521
|
if (prev === dir) {
|
|
@@ -7686,7 +8524,7 @@ var require_bindings = __commonJS({
|
|
|
7686
8524
|
);
|
|
7687
8525
|
}
|
|
7688
8526
|
prev = dir;
|
|
7689
|
-
dir =
|
|
8527
|
+
dir = join11(dir, "..");
|
|
7690
8528
|
}
|
|
7691
8529
|
};
|
|
7692
8530
|
}
|
|
@@ -8303,7 +9141,7 @@ var AnalyzeRequestSchema = z.object({
|
|
|
8303
9141
|
session_history: z.array(z.object({
|
|
8304
9142
|
session_id: z.string(),
|
|
8305
9143
|
action_history: z.array(ActionItemSchema).default([])
|
|
8306
|
-
})).default([]).describe("Multi-session historical actions (optional): for L2.
|
|
9144
|
+
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
8307
9145
|
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
8308
9146
|
reason: z.string().default("").describe("Reason for executing this action"),
|
|
8309
9147
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
@@ -8320,7 +9158,9 @@ var AnalyzeResponseSchema = z.object({
|
|
|
8320
9158
|
policy: z.string().default(""),
|
|
8321
9159
|
violated_policy: z.string().default(""),
|
|
8322
9160
|
confidence: z.number().int().min(0).max(100).default(100),
|
|
8323
|
-
analysis_duration_ms: z.number().default(0)
|
|
9161
|
+
analysis_duration_ms: z.number().default(0),
|
|
9162
|
+
// 组合攻击链反例路径:每步 [session_id, seq, stage, timestamp],含真实发生时间
|
|
9163
|
+
violation_chain: z.array(z.array(z.union([z.string(), z.number()]).nullable())).default([])
|
|
8324
9164
|
});
|
|
8325
9165
|
var HealthResponseSchema = z.object({
|
|
8326
9166
|
status: z.string().default("healthy"),
|
|
@@ -8704,7 +9544,7 @@ async function analyzeRoute(c) {
|
|
|
8704
9544
|
const judgment = incMatchedToDeny(inc);
|
|
8705
9545
|
if (judgment) {
|
|
8706
9546
|
const layers = [...result.violatedLayers];
|
|
8707
|
-
if (!layers.includes("2
|
|
9547
|
+
if (!layers.includes("L2.2-stateful")) layers.push("L2.2-stateful");
|
|
8708
9548
|
console.error(
|
|
8709
9549
|
`[AgentMoss] stateful \u589E\u91CF\u62E6\u622A: agent=${agentId || "-"} property=${judgment.property_id} risk=${judgment.risk_level}`
|
|
8710
9550
|
);
|
|
@@ -8718,7 +9558,8 @@ async function analyzeRoute(c) {
|
|
|
8718
9558
|
policy: "",
|
|
8719
9559
|
violated_policy: "",
|
|
8720
9560
|
confidence: 90,
|
|
8721
|
-
analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10
|
|
9561
|
+
analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
|
|
9562
|
+
violation_chain: judgment.violation_chain
|
|
8722
9563
|
})
|
|
8723
9564
|
);
|
|
8724
9565
|
}
|
|
@@ -8757,17 +9598,20 @@ function getChainStateStoreFor(_agentId) {
|
|
|
8757
9598
|
}
|
|
8758
9599
|
function incMatchedToDeny(inc) {
|
|
8759
9600
|
if (inc.matched) {
|
|
9601
|
+
const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp]);
|
|
8760
9602
|
return {
|
|
8761
9603
|
property_id: "cross-" + inc.matched.chainId,
|
|
8762
9604
|
reason: inc.matched.reason,
|
|
8763
|
-
risk_level: inc.matched.riskLevel
|
|
9605
|
+
risk_level: inc.matched.riskLevel,
|
|
9606
|
+
violation_chain: chain
|
|
8764
9607
|
};
|
|
8765
9608
|
}
|
|
8766
9609
|
if (inc.exfil && inc.exfil.length > 0) {
|
|
8767
9610
|
return {
|
|
8768
|
-
property_id: "sensitive-data-
|
|
9611
|
+
property_id: "sensitive-data-flow",
|
|
8769
9612
|
reason: inc.exfil[0].reason,
|
|
8770
|
-
risk_level: inc.exfil[0].risk_level
|
|
9613
|
+
risk_level: inc.exfil[0].risk_level,
|
|
9614
|
+
violation_chain: []
|
|
8771
9615
|
};
|
|
8772
9616
|
}
|
|
8773
9617
|
return null;
|
|
@@ -8846,12 +9690,13 @@ init_esm_shims();
|
|
|
8846
9690
|
init_esm_shims();
|
|
8847
9691
|
init_runtime_config();
|
|
8848
9692
|
init_token_stats();
|
|
9693
|
+
init_rules_loader();
|
|
8849
9694
|
import { Hono } from "hono";
|
|
8850
9695
|
import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
8851
|
-
import { join as
|
|
9696
|
+
import { join as join10, dirname as dirname10 } from "path";
|
|
8852
9697
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8853
9698
|
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
8854
|
-
var STATIC_DIR =
|
|
9699
|
+
var STATIC_DIR = join10(__dirname8, "static");
|
|
8855
9700
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
8856
9701
|
function getConsoleToken() {
|
|
8857
9702
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -8875,7 +9720,7 @@ function createConsoleRouter() {
|
|
|
8875
9720
|
const console2 = new Hono();
|
|
8876
9721
|
console2.use("/api/*", authMiddleware());
|
|
8877
9722
|
const serveIndex = (c) => {
|
|
8878
|
-
const indexFile =
|
|
9723
|
+
const indexFile = join10(STATIC_DIR, "index.html");
|
|
8879
9724
|
if (existsSync14(indexFile)) {
|
|
8880
9725
|
return c.html(readFileSync15(indexFile, "utf-8"));
|
|
8881
9726
|
}
|
|
@@ -8892,24 +9737,89 @@ function createConsoleRouter() {
|
|
|
8892
9737
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
8893
9738
|
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
8894
9739
|
});
|
|
9740
|
+
function getL22View() {
|
|
9741
|
+
const ltlHuman = {
|
|
9742
|
+
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
9743
|
+
"mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
|
|
9744
|
+
"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",
|
|
9745
|
+
"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",
|
|
9746
|
+
"mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
|
|
9747
|
+
"mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
|
|
9748
|
+
"mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9749
|
+
"mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9750
|
+
"mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
|
|
9751
|
+
"mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
|
|
9752
|
+
"mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
|
|
9753
|
+
};
|
|
9754
|
+
const mc = loadMC();
|
|
9755
|
+
const rules = mc.rules || [];
|
|
9756
|
+
return {
|
|
9757
|
+
enabled_via: "L2_logic_rules",
|
|
9758
|
+
description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
|
|
9759
|
+
ltl_rules: rules.map((r) => ({
|
|
9760
|
+
id: r.id,
|
|
9761
|
+
type: r.type,
|
|
9762
|
+
formula: Array.isArray(r.formula) ? "<\u516C\u5F0F>" : r.formula,
|
|
9763
|
+
reason: r.reason,
|
|
9764
|
+
risk_level: r.risk_level,
|
|
9765
|
+
action: r.action,
|
|
9766
|
+
enabled: r.enabled !== false,
|
|
9767
|
+
human: ltlHuman[r.id] || r.reason || ""
|
|
9768
|
+
}))
|
|
9769
|
+
};
|
|
9770
|
+
}
|
|
9771
|
+
function withL22View(rc) {
|
|
9772
|
+
if (rc && rc.L2_2 && typeof rc.L2_2 === "object" && !Array.isArray(rc.L2_2)) {
|
|
9773
|
+
const cat = rc.L2_2.ltl_rules;
|
|
9774
|
+
const rls = cat && cat.rules || [];
|
|
9775
|
+
const ltlHumanLocal = {
|
|
9776
|
+
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
9777
|
+
"mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
|
|
9778
|
+
"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",
|
|
9779
|
+
"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",
|
|
9780
|
+
"mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
|
|
9781
|
+
"mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
|
|
9782
|
+
"mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9783
|
+
"mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
|
|
9784
|
+
"mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
|
|
9785
|
+
"mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
|
|
9786
|
+
"mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
|
|
9787
|
+
};
|
|
9788
|
+
rc.L2_2 = {
|
|
9789
|
+
enabled_via: "L2_logic_rules",
|
|
9790
|
+
description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
|
|
9791
|
+
ltl_rules: rls.map((r) => ({
|
|
9792
|
+
id: r.id,
|
|
9793
|
+
type: "ltl",
|
|
9794
|
+
formula: "<\u516C\u5F0F>",
|
|
9795
|
+
reason: r.reason || "",
|
|
9796
|
+
risk_level: r.risk_level || "high",
|
|
9797
|
+
action: r.action || "block",
|
|
9798
|
+
enabled: r.enabled !== false,
|
|
9799
|
+
human: ltlHumanLocal[r.id] || r.reason || ""
|
|
9800
|
+
}))
|
|
9801
|
+
};
|
|
9802
|
+
}
|
|
9803
|
+
return rc;
|
|
9804
|
+
}
|
|
8895
9805
|
console2.get("/api/rules", (c) => {
|
|
8896
9806
|
const rc = loadRuntimeConfig();
|
|
8897
|
-
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
9807
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules, L2_2: getL22View() });
|
|
8898
9808
|
});
|
|
8899
9809
|
console2.put("/api/rules/enabled", async (c) => {
|
|
8900
9810
|
const b = await c.req.json();
|
|
8901
9811
|
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
8902
|
-
return c.json(rc);
|
|
9812
|
+
return c.json(withL22View(rc));
|
|
8903
9813
|
});
|
|
8904
9814
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
8905
9815
|
const b = await c.req.json();
|
|
8906
9816
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
8907
|
-
return c.json(rc);
|
|
9817
|
+
return c.json(withL22View(rc));
|
|
8908
9818
|
});
|
|
8909
9819
|
console2.put("/api/rules/skip_l3", async (c) => {
|
|
8910
9820
|
const b = await c.req.json();
|
|
8911
9821
|
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
8912
|
-
return c.json(rc);
|
|
9822
|
+
return c.json(withL22View(rc));
|
|
8913
9823
|
});
|
|
8914
9824
|
console2.put("/api/categories/enabled", async (c) => {
|
|
8915
9825
|
const b = await c.req.json();
|
|
@@ -8968,9 +9878,9 @@ function createConsoleRouter() {
|
|
|
8968
9878
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
8969
9879
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
8970
9880
|
}
|
|
8971
|
-
const skillPath =
|
|
9881
|
+
const skillPath = join10(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
8972
9882
|
if (!existsSync14(skillPath)) {
|
|
8973
|
-
const builtinPath =
|
|
9883
|
+
const builtinPath = join10(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
8974
9884
|
if (existsSync14(builtinPath)) return c.text(readFileSync15(builtinPath, "utf-8"));
|
|
8975
9885
|
return c.json({ error: "skill not found" }, 404);
|
|
8976
9886
|
}
|
|
@@ -9059,7 +9969,7 @@ function createConsoleRouter() {
|
|
|
9059
9969
|
});
|
|
9060
9970
|
console2.get("/api/config", (c) => {
|
|
9061
9971
|
const rc = loadRuntimeConfig();
|
|
9062
|
-
return c.json(rc);
|
|
9972
|
+
return c.json({ ...rc, L2_2: getL22View() });
|
|
9063
9973
|
});
|
|
9064
9974
|
console2.get("/api/env-overrides", (c) => {
|
|
9065
9975
|
return c.json({ overrides: getEnvOverrides() });
|
|
@@ -9396,4 +10306,17 @@ if (isMain) {
|
|
|
9396
10306
|
process.exit(1);
|
|
9397
10307
|
});
|
|
9398
10308
|
}
|
|
10309
|
+
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
10310
|
+
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
10311
|
+
//! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
|
|
10312
|
+
//!
|
|
10313
|
+
//! shortName(P1~P8 连续短名)与 id(语义化英文)对照(与 Python 一致):
|
|
10314
|
+
//! P1 cross-session-chain 跨会话组合链
|
|
10315
|
+
//! P2 sensitive-data-flow 敏感数据传播
|
|
10316
|
+
//! P3 low-integrity-reachability 低完整性数据流可达
|
|
10317
|
+
//! P4 no-exfil-after-privesc 提权后禁外传
|
|
10318
|
+
//! P5 no-exfil-after-credential 凭据后禁外传
|
|
10319
|
+
//! P6 mc-ltl-rules LTL 模型检测
|
|
10320
|
+
//! P7 low-integrity-command-exec 低完整性命令执行
|
|
10321
|
+
//! P8 sensitive-data-aggregation 敏感数据聚合外传
|
|
9399
10322
|
//# sourceMappingURL=cli.js.map
|