@kenhkl/agent-moss 0.13.0 → 0.15.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 +630 -150
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +1 -1
- package/dist/engine/coordinator.js +554 -197
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/rules/l1_rules.json +28 -6
- package/dist/rules/l2_rules.json +242 -214
- package/dist/rules/mc_rules.json +258 -26
- package/dist/server.js +716 -246
- package/dist/server.js.map +1 -1
- package/dist/static/index.html +22 -8
- package/package.json +1 -1
|
@@ -628,10 +628,15 @@ function loadMC() {
|
|
|
628
628
|
try {
|
|
629
629
|
const rt = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
630
630
|
if (rules.length > 0 && typeof rt.hasRuntimeConfig === "function" && rt.hasRuntimeConfig()) {
|
|
631
|
-
const
|
|
631
|
+
const ltl = rt.loadRuntimeConfig()?.L2_2?.ltl_rules || {};
|
|
632
|
+
const rtRules = ltl.rules || [];
|
|
632
633
|
const enabledById = {};
|
|
633
634
|
for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
|
|
634
|
-
|
|
635
|
+
const catEnabled = ltl.category_enabled !== false;
|
|
636
|
+
for (const r of rules) {
|
|
637
|
+
if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
|
|
638
|
+
if (!catEnabled) r.enabled = false;
|
|
639
|
+
}
|
|
635
640
|
}
|
|
636
641
|
} catch {
|
|
637
642
|
}
|
|
@@ -676,7 +681,8 @@ function _loadSensitivePathScope(scope) {
|
|
|
676
681
|
path: sp.path,
|
|
677
682
|
riskLevel: sp.risk_level,
|
|
678
683
|
desc: sp.desc,
|
|
679
|
-
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
|
|
680
686
|
}));
|
|
681
687
|
}
|
|
682
688
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
@@ -774,7 +780,7 @@ function runLogicCheck(input) {
|
|
|
774
780
|
riskType: "intent_deviation",
|
|
775
781
|
confidence: 85,
|
|
776
782
|
source: "logic_rule",
|
|
777
|
-
violatedLayers: ["
|
|
783
|
+
violatedLayers: ["L2.1"]
|
|
778
784
|
};
|
|
779
785
|
}
|
|
780
786
|
}
|
|
@@ -798,7 +804,7 @@ function runLogicCheck(input) {
|
|
|
798
804
|
riskType: "privilege_escalation",
|
|
799
805
|
confidence: 85,
|
|
800
806
|
source: "logic_rule",
|
|
801
|
-
violatedLayers: ["
|
|
807
|
+
violatedLayers: ["L2.1"]
|
|
802
808
|
};
|
|
803
809
|
}
|
|
804
810
|
}
|
|
@@ -850,7 +856,7 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
850
856
|
riskType: "combo_chain",
|
|
851
857
|
confidence: 85,
|
|
852
858
|
source: "logic_rule",
|
|
853
|
-
violatedLayers: ["
|
|
859
|
+
violatedLayers: ["L2.1"]
|
|
854
860
|
};
|
|
855
861
|
}
|
|
856
862
|
}
|
|
@@ -975,7 +981,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
975
981
|
riskType: "read_before_write",
|
|
976
982
|
confidence: 85,
|
|
977
983
|
source: "logic_rule",
|
|
978
|
-
violatedLayers: ["
|
|
984
|
+
violatedLayers: ["L2.1"]
|
|
979
985
|
};
|
|
980
986
|
}
|
|
981
987
|
return null;
|
|
@@ -995,7 +1001,7 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
995
1001
|
riskType: "consent_missing",
|
|
996
1002
|
confidence: 85,
|
|
997
1003
|
source: "logic_rule",
|
|
998
|
-
violatedLayers: ["
|
|
1004
|
+
violatedLayers: ["L2.1"]
|
|
999
1005
|
};
|
|
1000
1006
|
}
|
|
1001
1007
|
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
@@ -1013,7 +1019,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
|
1013
1019
|
riskType: "consent_missing",
|
|
1014
1020
|
confidence: 85,
|
|
1015
1021
|
source: "logic_rule",
|
|
1016
|
-
violatedLayers: ["
|
|
1022
|
+
violatedLayers: ["L2.1"]
|
|
1017
1023
|
};
|
|
1018
1024
|
}
|
|
1019
1025
|
function containsPrivilegeEscalation(actionDetail) {
|
|
@@ -1073,7 +1079,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1073
1079
|
riskType: "file_access",
|
|
1074
1080
|
confidence: 95,
|
|
1075
1081
|
source: "logic_rule",
|
|
1076
|
-
violatedLayers: ["
|
|
1082
|
+
violatedLayers: ["L2.1"],
|
|
1077
1083
|
isCredential: true
|
|
1078
1084
|
};
|
|
1079
1085
|
}
|
|
@@ -1089,7 +1095,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1089
1095
|
riskType: "file_access",
|
|
1090
1096
|
confidence: 95,
|
|
1091
1097
|
source: "logic_rule",
|
|
1092
|
-
violatedLayers: ["
|
|
1098
|
+
violatedLayers: ["L2.1"]
|
|
1093
1099
|
};
|
|
1094
1100
|
}
|
|
1095
1101
|
}
|
|
@@ -1103,7 +1109,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1103
1109
|
riskType: "file_access",
|
|
1104
1110
|
confidence: 70,
|
|
1105
1111
|
source: "logic_rule",
|
|
1106
|
-
violatedLayers: ["
|
|
1112
|
+
violatedLayers: ["L2.1"]
|
|
1107
1113
|
};
|
|
1108
1114
|
}
|
|
1109
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;
|
|
@@ -1115,7 +1121,7 @@ var init_logic_rules = __esm({
|
|
|
1115
1121
|
init_rules_loader();
|
|
1116
1122
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1117
1123
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1118
|
-
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc }) => ({ path: path5, riskLevel, desc, credential: true }));
|
|
1124
|
+
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc, dirDelOnly }) => ({ path: path5, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1119
1125
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1120
1126
|
"ask_user_question",
|
|
1121
1127
|
"glob",
|
|
@@ -1428,6 +1434,7 @@ __export(runtime_config_exports, {
|
|
|
1428
1434
|
isLayerEnabled: () => isLayerEnabled,
|
|
1429
1435
|
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
1430
1436
|
mergeConfig: () => mergeConfig,
|
|
1437
|
+
pickDeletableRule: () => pickDeletableRule,
|
|
1431
1438
|
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
1432
1439
|
updateAgentCategoryEnabled: () => updateAgentCategoryEnabled,
|
|
1433
1440
|
updateAgentRuleEnabled: () => updateAgentRuleEnabled,
|
|
@@ -1466,7 +1473,7 @@ function emptyRuntimeConfig() {
|
|
|
1466
1473
|
L1_rules: {},
|
|
1467
1474
|
L2_rules: {},
|
|
1468
1475
|
L3_skills: {},
|
|
1469
|
-
|
|
1476
|
+
L2_2: {},
|
|
1470
1477
|
agent_rules: {},
|
|
1471
1478
|
scenario_rules: {}
|
|
1472
1479
|
};
|
|
@@ -1494,7 +1501,7 @@ function loadRuntimeConfig() {
|
|
|
1494
1501
|
L1_rules: merged.L1_rules || {},
|
|
1495
1502
|
L2_rules: merged.L2_rules || {},
|
|
1496
1503
|
L3_skills: merged.L3_skills || {},
|
|
1497
|
-
|
|
1504
|
+
L2_2: merged.L2_2 || {},
|
|
1498
1505
|
agent_rules: merged.agent_rules || {},
|
|
1499
1506
|
scenario_rules: merged.scenario_rules || {}
|
|
1500
1507
|
};
|
|
@@ -1547,6 +1554,7 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1547
1554
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1548
1555
|
if (r.credential) out.credential = true;
|
|
1549
1556
|
if (r.read_only) out.read_only = true;
|
|
1557
|
+
if (r.dir_del_only) out.dir_del_only = true;
|
|
1550
1558
|
return out;
|
|
1551
1559
|
});
|
|
1552
1560
|
}
|
|
@@ -1592,8 +1600,16 @@ function getDisabledL2RulesWithSkipL3() {
|
|
|
1592
1600
|
function _slugify(text) {
|
|
1593
1601
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
|
|
1594
1602
|
}
|
|
1603
|
+
function _fnv1a32(text) {
|
|
1604
|
+
let h = 2166136261;
|
|
1605
|
+
for (const b of Buffer.from(text, "utf-8")) {
|
|
1606
|
+
h ^= b;
|
|
1607
|
+
h = h * 16777619 >>> 0;
|
|
1608
|
+
}
|
|
1609
|
+
return h.toString(16).padStart(8, "0");
|
|
1610
|
+
}
|
|
1595
1611
|
function _generatePatternId(pattern) {
|
|
1596
|
-
return `pat_${_slugify(pattern)}`;
|
|
1612
|
+
return `pat_${_slugify(pattern)}_${_fnv1a32(pattern)}`;
|
|
1597
1613
|
}
|
|
1598
1614
|
function _deriveDenyMode(rule) {
|
|
1599
1615
|
if (rule.deny_mode) return rule.deny_mode;
|
|
@@ -1655,6 +1671,7 @@ function _l2SensitivePathsToRules() {
|
|
|
1655
1671
|
builtin: true
|
|
1656
1672
|
};
|
|
1657
1673
|
_syncDenyModeFields(rule);
|
|
1674
|
+
if (sp.dirDelOnly) rule.dir_del_only = true;
|
|
1658
1675
|
return rule;
|
|
1659
1676
|
});
|
|
1660
1677
|
}
|
|
@@ -1871,17 +1888,20 @@ function generateSourceDefaults() {
|
|
|
1871
1888
|
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1872
1889
|
},
|
|
1873
1890
|
L3_skills: _l3SkillsToRules(),
|
|
1874
|
-
|
|
1891
|
+
L2_2: _ltlRulesToRuntime(),
|
|
1875
1892
|
agent_rules: _agentRulesToDefaults(),
|
|
1876
1893
|
scenario_rules: _scenarioRulesToDefaults()
|
|
1877
1894
|
};
|
|
1878
1895
|
}
|
|
1879
1896
|
function _ltlRulesToRuntime() {
|
|
1880
1897
|
const cands = [
|
|
1881
|
-
join3(
|
|
1882
|
-
join3(__dirname4, "
|
|
1883
|
-
join3(
|
|
1884
|
-
|
|
1898
|
+
getRuntimeConfigDir() && join3(getRuntimeConfigDir(), "rules"),
|
|
1899
|
+
join3(__dirname4, "../rules"),
|
|
1900
|
+
join3(__dirname4, "rules"),
|
|
1901
|
+
join3(__dirname4, "../../../agent_moss/rules"),
|
|
1902
|
+
join3(__dirname4, "../../../../agent_moss/rules"),
|
|
1903
|
+
join3(process.cwd(), "agent_moss/rules")
|
|
1904
|
+
].filter(Boolean);
|
|
1885
1905
|
let mc = {};
|
|
1886
1906
|
for (const dir of cands) {
|
|
1887
1907
|
const p = join3(dir, "mc_rules.json");
|
|
@@ -2059,7 +2079,11 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
2059
2079
|
);
|
|
2060
2080
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
2061
2081
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2062
|
-
|
|
2082
|
+
if (!cfg.L2_2 && cfg.L2_4) {
|
|
2083
|
+
cfg.L2_2 = cfg.L2_4;
|
|
2084
|
+
delete cfg.L2_4;
|
|
2085
|
+
}
|
|
2086
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_2");
|
|
2063
2087
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2064
2088
|
_mergeAgentRules(cfg, srcDefaults);
|
|
2065
2089
|
_mergeAgentRules(cfg, srcDefaults, "scenario_rules");
|
|
@@ -2237,16 +2261,20 @@ function addCustomRule(layer, category, rule) {
|
|
|
2237
2261
|
saveRuntimeConfig(runtime);
|
|
2238
2262
|
return runtime;
|
|
2239
2263
|
}
|
|
2240
|
-
function
|
|
2241
|
-
const
|
|
2242
|
-
const rules = runtime[layer]?.[category]?.rules || [];
|
|
2243
|
-
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
2264
|
+
function pickDeletableRule(rules, ruleId) {
|
|
2265
|
+
const idx = rules.findIndex((r) => r.id === ruleId && (!r.builtin || r.source_removed));
|
|
2244
2266
|
if (idx >= 0) {
|
|
2245
|
-
const r = rules[idx];
|
|
2246
|
-
if (r.builtin && !r.source_removed) return null;
|
|
2247
2267
|
rules.splice(idx, 1);
|
|
2248
|
-
|
|
2268
|
+
return rules;
|
|
2249
2269
|
}
|
|
2270
|
+
if (rules.some((r) => r.id === ruleId)) return null;
|
|
2271
|
+
return rules;
|
|
2272
|
+
}
|
|
2273
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
2274
|
+
const runtime = loadRuntimeConfig();
|
|
2275
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
2276
|
+
if (pickDeletableRule(rules, ruleId) === null) return null;
|
|
2277
|
+
saveRuntimeConfig(runtime);
|
|
2250
2278
|
return runtime;
|
|
2251
2279
|
}
|
|
2252
2280
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
@@ -2679,7 +2707,7 @@ function runHeuristicCheck(input) {
|
|
|
2679
2707
|
riskType: "sensitive_action",
|
|
2680
2708
|
confidence: 90,
|
|
2681
2709
|
source: "heuristic",
|
|
2682
|
-
violatedLayers: ["
|
|
2710
|
+
violatedLayers: ["L1.1"]
|
|
2683
2711
|
};
|
|
2684
2712
|
}
|
|
2685
2713
|
}
|
|
@@ -2689,8 +2717,10 @@ function runHeuristicCheck(input) {
|
|
|
2689
2717
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2690
2718
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2691
2719
|
let topRiskType = "";
|
|
2720
|
+
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2692
2721
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
2693
2722
|
if (rule.pattern.test(scanCommand)) {
|
|
2723
|
+
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2694
2724
|
matched.push(rule.description);
|
|
2695
2725
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
2696
2726
|
highestSeverity = rule.severity;
|
|
@@ -2732,10 +2762,26 @@ function runHeuristicCheck(input) {
|
|
|
2732
2762
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2733
2763
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2734
2764
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2765
|
+
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
2766
|
+
const homeResult = checkHomeDeletion(actionTypeL, detailL);
|
|
2767
|
+
const mergeResults = [dangerousResult, pathResult, homeResult].filter(Boolean);
|
|
2768
|
+
if (mergeResults.length > 0) {
|
|
2769
|
+
let best = mergeResults[0];
|
|
2770
|
+
for (const r of mergeResults) {
|
|
2771
|
+
if (severityRank(r.riskLevel) > severityRank(best.riskLevel)) best = r;
|
|
2772
|
+
}
|
|
2773
|
+
if (severityRank(highestSeverity) > severityRank(best.riskLevel)) {
|
|
2774
|
+
return {
|
|
2775
|
+
allowed: false,
|
|
2776
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
2777
|
+
riskLevel: highestSeverity,
|
|
2778
|
+
riskType: highestRiskType,
|
|
2779
|
+
confidence: 95,
|
|
2780
|
+
source: "heuristic",
|
|
2781
|
+
violatedLayers: ["L1.1"]
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
return best;
|
|
2739
2785
|
}
|
|
2740
2786
|
if (matched.length > 0) {
|
|
2741
2787
|
return {
|
|
@@ -2745,8 +2791,7 @@ function runHeuristicCheck(input) {
|
|
|
2745
2791
|
riskType: highestRiskType,
|
|
2746
2792
|
confidence: 95,
|
|
2747
2793
|
source: "heuristic",
|
|
2748
|
-
violatedLayers: ["
|
|
2749
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
2794
|
+
violatedLayers: ["L1.1"]
|
|
2750
2795
|
};
|
|
2751
2796
|
}
|
|
2752
2797
|
const lateralResult = checkLateralMovement(detailL);
|
|
@@ -2874,6 +2919,7 @@ function isSearchActionType(actionType) {
|
|
|
2874
2919
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2875
2920
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2876
2921
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2922
|
+
const keygenGenerate = isSshKeygenGenerate(command);
|
|
2877
2923
|
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2878
2924
|
const pathMatches = (text, path5) => {
|
|
2879
2925
|
const parts = path5.split(/(\(\?!.*?\))/);
|
|
@@ -2886,6 +2932,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2886
2932
|
};
|
|
2887
2933
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
2888
2934
|
if (pathMatches(pathScanText, sp.path.toLowerCase())) {
|
|
2935
|
+
if (keygenGenerate) continue;
|
|
2889
2936
|
if (sp.path === "/dev/random") {
|
|
2890
2937
|
if (isWriteOpFlag) {
|
|
2891
2938
|
continue;
|
|
@@ -2897,7 +2944,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2897
2944
|
riskType: "file_access",
|
|
2898
2945
|
confidence: 90,
|
|
2899
2946
|
source: "heuristic",
|
|
2900
|
-
violatedLayers: ["
|
|
2947
|
+
violatedLayers: ["L1.1"]
|
|
2901
2948
|
};
|
|
2902
2949
|
}
|
|
2903
2950
|
if (!isWriteOpFlag && !sp.credential) {
|
|
@@ -2910,16 +2957,51 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2910
2957
|
riskType: "file_access",
|
|
2911
2958
|
confidence: 90,
|
|
2912
2959
|
source: "heuristic",
|
|
2913
|
-
violatedLayers: ["
|
|
2960
|
+
violatedLayers: ["L1.1"],
|
|
2914
2961
|
isCredential: !!sp.credential
|
|
2915
2962
|
};
|
|
2916
2963
|
}
|
|
2917
2964
|
}
|
|
2918
2965
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
2919
2966
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2967
|
+
let pPath = sp.path;
|
|
2968
|
+
if (!sp.path.endsWith(".pub")) pPath = `${sp.path}(?!\\.pub)`;
|
|
2969
|
+
let dirPrefixMatch = null;
|
|
2970
|
+
if (sp.dirDelOnly) {
|
|
2971
|
+
const dirRe = (t) => new RegExp(`(?:^|[\\s/\\\\'"])${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/|[\\s'"\\\\]|$)`, "i");
|
|
2972
|
+
dirPrefixMatch = dirRe(sp.path).test(pathScanText) || dirRe(expanded.toLowerCase()).test(pathScanText);
|
|
2973
|
+
}
|
|
2974
|
+
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch : pathMatches(pathScanText, pPath);
|
|
2975
|
+
const expandedReMatch = sp.dirDelOnly ? false : pathMatches(pathScanText, expanded + (sp.path.endsWith(".pub") ? "" : "(?!\\.pub)"));
|
|
2976
|
+
if (pathReMatch || expandedReMatch) {
|
|
2977
|
+
if (keygenGenerate) continue;
|
|
2978
|
+
if (sp.dirDelOnly) {
|
|
2979
|
+
if (isDeleteActionForDir(command, sp.path)) {
|
|
2980
|
+
return {
|
|
2981
|
+
allowed: false,
|
|
2982
|
+
reason: `\u5220\u9664\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2983
|
+
riskLevel: sp.riskLevel,
|
|
2984
|
+
riskType: "file_access",
|
|
2985
|
+
confidence: 90,
|
|
2986
|
+
source: "heuristic",
|
|
2987
|
+
violatedLayers: ["L1.1"],
|
|
2988
|
+
isCredential: !!sp.credential
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
if (isArchiveReadOfDir(command, sp.path)) {
|
|
2992
|
+
return {
|
|
2993
|
+
allowed: false,
|
|
2994
|
+
reason: `\u5F52\u6863/\u590D\u5236\u8BFB\u53D6\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2995
|
+
riskLevel: sp.riskLevel,
|
|
2996
|
+
riskType: "file_access",
|
|
2997
|
+
confidence: 90,
|
|
2998
|
+
source: "heuristic",
|
|
2999
|
+
violatedLayers: ["L1.1"],
|
|
3000
|
+
isCredential: !!sp.credential
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
continue;
|
|
3004
|
+
}
|
|
2923
3005
|
if (!isWriteOpFlag && !sp.credential) {
|
|
2924
3006
|
continue;
|
|
2925
3007
|
}
|
|
@@ -2930,7 +3012,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2930
3012
|
riskType: "file_access",
|
|
2931
3013
|
confidence: 90,
|
|
2932
3014
|
source: "heuristic",
|
|
2933
|
-
violatedLayers: ["
|
|
3015
|
+
violatedLayers: ["L1.1"],
|
|
2934
3016
|
isCredential: !!sp.credential
|
|
2935
3017
|
};
|
|
2936
3018
|
}
|
|
@@ -2953,7 +3035,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2953
3035
|
riskType: "file_access",
|
|
2954
3036
|
confidence: 90,
|
|
2955
3037
|
source: "heuristic",
|
|
2956
|
-
violatedLayers: ["
|
|
3038
|
+
violatedLayers: ["L1.1"],
|
|
2957
3039
|
isCredential: !!sp.credential
|
|
2958
3040
|
};
|
|
2959
3041
|
}
|
|
@@ -2970,7 +3052,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2970
3052
|
riskType: "file_access",
|
|
2971
3053
|
confidence: 90,
|
|
2972
3054
|
source: "heuristic",
|
|
2973
|
-
violatedLayers: ["
|
|
3055
|
+
violatedLayers: ["L1.1"],
|
|
2974
3056
|
isCredential: true
|
|
2975
3057
|
};
|
|
2976
3058
|
}
|
|
@@ -2983,18 +3065,142 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2983
3065
|
riskType: "file_access",
|
|
2984
3066
|
confidence: 90,
|
|
2985
3067
|
source: "heuristic",
|
|
2986
|
-
violatedLayers: ["
|
|
3068
|
+
violatedLayers: ["L1.1"]
|
|
2987
3069
|
};
|
|
2988
3070
|
}
|
|
2989
3071
|
return null;
|
|
2990
3072
|
}
|
|
3073
|
+
function isHomeValue(path5) {
|
|
3074
|
+
let p = path5.trim().toLowerCase();
|
|
3075
|
+
if (p.startsWith("$(") && p.endsWith(")")) {
|
|
3076
|
+
const inner = p.slice(2, -1).trim();
|
|
3077
|
+
return HOME_TARGET_RE.test(" " + inner);
|
|
3078
|
+
}
|
|
3079
|
+
if (/^\/home\/[^/\s]+\/?$/.test(p)) return true;
|
|
3080
|
+
return HOME_TARGET_RE.test(" " + p);
|
|
3081
|
+
}
|
|
3082
|
+
function isDeleteCommand(seg) {
|
|
3083
|
+
const s = seg.trimStart();
|
|
3084
|
+
if (/^(?:rm|rmdir|unlink)\b/i.test(s)) return true;
|
|
3085
|
+
return /删除|移除/.test(s);
|
|
3086
|
+
}
|
|
3087
|
+
function homeEscapeTarget(seg) {
|
|
3088
|
+
const low = seg.toLowerCase();
|
|
3089
|
+
const re = /(?:~|(?:\$home|\$\{home\})|\/home\/[a-z0-9_.-]+)/g;
|
|
3090
|
+
let m;
|
|
3091
|
+
while ((m = re.exec(low)) !== null) {
|
|
3092
|
+
const endIdx = m.index + m[0].length;
|
|
3093
|
+
const tail = low.slice(endIdx);
|
|
3094
|
+
if (!tail) continue;
|
|
3095
|
+
let token = tail.trimStart().split(/\s+/)[0] || "";
|
|
3096
|
+
token = token.split("&&")[0].split("||")[0].split(";")[0].replace(/\/+$/, "");
|
|
3097
|
+
token = token.replace(/^['"]|['"]$/g, "");
|
|
3098
|
+
const segments = token.split("/").filter((s) => s && s !== ".");
|
|
3099
|
+
if (!segments.some((s) => s === "..")) continue;
|
|
3100
|
+
let depth = 0;
|
|
3101
|
+
let minDepth = 0;
|
|
3102
|
+
for (const s of segments) {
|
|
3103
|
+
depth += s === ".." ? -1 : 1;
|
|
3104
|
+
minDepth = Math.min(minDepth, depth);
|
|
3105
|
+
}
|
|
3106
|
+
if (minDepth < 0) return true;
|
|
3107
|
+
if (depth <= 0) return true;
|
|
3108
|
+
}
|
|
3109
|
+
return false;
|
|
3110
|
+
}
|
|
3111
|
+
function checkHomeDeletion(actionType, actionDetail) {
|
|
3112
|
+
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") return null;
|
|
3113
|
+
const segs = actionDetail.split(/&&|\|\||;|\n/).map((s) => s.trim()).filter(Boolean);
|
|
3114
|
+
for (const seg of segs) {
|
|
3115
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3116
|
+
if (HOME_TARGET_RE.test(seg) || homeEscapeTarget(seg)) {
|
|
3117
|
+
return {
|
|
3118
|
+
allowed: false,
|
|
3119
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u4E3B\u76EE\u5F55 (~/$HOME) \u64CD\u4F5C",
|
|
3120
|
+
riskLevel: "high",
|
|
3121
|
+
riskType: "script_execution",
|
|
3122
|
+
confidence: 90,
|
|
3123
|
+
source: "heuristic",
|
|
3124
|
+
violatedLayers: ["L1.1"]
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
const assigned = {};
|
|
3129
|
+
for (const m of actionDetail.matchAll(HOME_ASSIGN_RE)) assigned[m[1]] = m[2];
|
|
3130
|
+
for (const [varName, path5] of Object.entries(assigned)) {
|
|
3131
|
+
if (!isHomeValue(path5)) continue;
|
|
3132
|
+
const varRefRe = new RegExp(`(?:${escapeRegExp(varName)}|\\$\\{${escapeRegExp(varName)}\\})`);
|
|
3133
|
+
for (const seg of segs) {
|
|
3134
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3135
|
+
if (varRefRe.test(seg)) {
|
|
3136
|
+
return {
|
|
3137
|
+
allowed: false,
|
|
3138
|
+
reason: `\u68C0\u6D4B\u5230\u901A\u8FC7\u53D8\u91CF ${varName}\uFF08=${path5}\uFF0C\u4E3B\u76EE\u5F55\uFF09\u6267\u884C\u5220\u9664`,
|
|
3139
|
+
riskLevel: "high",
|
|
3140
|
+
riskType: "script_execution",
|
|
3141
|
+
confidence: 90,
|
|
3142
|
+
source: "heuristic",
|
|
3143
|
+
violatedLayers: ["L1.1"]
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
return null;
|
|
3149
|
+
}
|
|
3150
|
+
function escapeRegExp(s) {
|
|
3151
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3152
|
+
}
|
|
3153
|
+
function isDeleteActionForDir(command, dirPath) {
|
|
3154
|
+
const low = command.toLowerCase();
|
|
3155
|
+
const dpath = dirPath.toLowerCase();
|
|
3156
|
+
if (new RegExp(`\\b(?:rm|unlink|rmdir)\\b[^&|;]*?${escapeRegExp(dpath)}`, "i").test(low)) {
|
|
3157
|
+
return true;
|
|
3158
|
+
}
|
|
3159
|
+
if (new RegExp(`(?:\u5220\u9664|\u79FB\u9664)[^\\n;|&]*?${escapeRegExp(dpath)}`).test(low)) {
|
|
3160
|
+
return true;
|
|
3161
|
+
}
|
|
3162
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3163
|
+
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3164
|
+
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") && low.includes(dpath)) {
|
|
3165
|
+
return true;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
return false;
|
|
3169
|
+
}
|
|
3170
|
+
function isArchiveReadOfDir(command, dirPath) {
|
|
3171
|
+
const low = command.toLowerCase();
|
|
3172
|
+
const dpath = dirPath.toLowerCase();
|
|
3173
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3174
|
+
const s = seg.trim();
|
|
3175
|
+
if (!s || !s.includes(dpath)) continue;
|
|
3176
|
+
const parts = s.split(/\s+/);
|
|
3177
|
+
const fw = parts[0] || "";
|
|
3178
|
+
if (fw === "zip") return true;
|
|
3179
|
+
if (fw === "tar") {
|
|
3180
|
+
if (/(?:^|\s)-[a-zA-Z]*x|--extract/.test(s)) continue;
|
|
3181
|
+
return true;
|
|
3182
|
+
}
|
|
3183
|
+
if (fw === "cp" || fw === "rsync") {
|
|
3184
|
+
const segPaths = s.match(/[/~][^\s;|&><'"]*/g) || [];
|
|
3185
|
+
if (segPaths.length && segPaths[0].includes(dpath)) return true;
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
return false;
|
|
3189
|
+
}
|
|
3190
|
+
function isSshKeygenGenerate(command) {
|
|
3191
|
+
const low = command.toLowerCase();
|
|
3192
|
+
if (!low.includes("ssh-keygen")) return false;
|
|
3193
|
+
if (!/(?:^|\s)-t\s+\S+/.test(low)) return false;
|
|
3194
|
+
if (/(?:^|\s)-\S*[ylpB]\S*/.test(low)) return false;
|
|
3195
|
+
return true;
|
|
3196
|
+
}
|
|
2991
3197
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2992
3198
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2993
3199
|
const scan = stripHeredocContent(actionDetail);
|
|
2994
3200
|
const TEMP_DIRS = ["/tmp", "/var/tmp", "/run", "/dev/shm"];
|
|
2995
3201
|
const isTempPath = TEMP_DIRS.some(
|
|
2996
3202
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
2997
|
-
);
|
|
3203
|
+
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
2998
3204
|
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;
|
|
2999
3205
|
if (!isTempPath && wildcardDeleteRe.test(scan)) {
|
|
3000
3206
|
return {
|
|
@@ -3004,7 +3210,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3004
3210
|
riskType: "script_execution",
|
|
3005
3211
|
confidence: 90,
|
|
3006
3212
|
source: "heuristic",
|
|
3007
|
-
violatedLayers: ["
|
|
3213
|
+
violatedLayers: ["L1.1"]
|
|
3008
3214
|
};
|
|
3009
3215
|
}
|
|
3010
3216
|
const findWriteMatch = actionDetail.match(
|
|
@@ -3019,10 +3225,10 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3019
3225
|
riskType: "script_execution",
|
|
3020
3226
|
confidence: 90,
|
|
3021
3227
|
source: "heuristic",
|
|
3022
|
-
violatedLayers: ["
|
|
3228
|
+
violatedLayers: ["L1.1"]
|
|
3023
3229
|
};
|
|
3024
3230
|
}
|
|
3025
|
-
if (/>\s
|
|
3231
|
+
if (/>\s*['"]?\/etc\//.test(actionDetail) || />\s*['"]?\/boot\//.test(actionDetail) || />\s*['"]?\/proc\//.test(actionDetail)) {
|
|
3026
3232
|
return {
|
|
3027
3233
|
allowed: false,
|
|
3028
3234
|
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
@@ -3030,7 +3236,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3030
3236
|
riskType: "file_access",
|
|
3031
3237
|
confidence: 95,
|
|
3032
3238
|
source: "heuristic",
|
|
3033
|
-
violatedLayers: ["
|
|
3239
|
+
violatedLayers: ["L1.1"]
|
|
3034
3240
|
};
|
|
3035
3241
|
}
|
|
3036
3242
|
if (/>\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i.test(actionDetail)) {
|
|
@@ -3041,7 +3247,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3041
3247
|
riskType: "file_access",
|
|
3042
3248
|
confidence: 90,
|
|
3043
3249
|
source: "heuristic",
|
|
3044
|
-
violatedLayers: ["
|
|
3250
|
+
violatedLayers: ["L1.1"]
|
|
3045
3251
|
};
|
|
3046
3252
|
}
|
|
3047
3253
|
return null;
|
|
@@ -3132,13 +3338,13 @@ function checkLateralMovement(actionDetail) {
|
|
|
3132
3338
|
riskType: "lateral_movement",
|
|
3133
3339
|
confidence: 80,
|
|
3134
3340
|
source: "heuristic",
|
|
3135
|
-
violatedLayers: ["
|
|
3341
|
+
violatedLayers: ["L1.1"]
|
|
3136
3342
|
};
|
|
3137
3343
|
}
|
|
3138
3344
|
}
|
|
3139
3345
|
return null;
|
|
3140
3346
|
}
|
|
3141
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES;
|
|
3347
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3142
3348
|
var init_heuristic = __esm({
|
|
3143
3349
|
"src/engine/heuristic.ts"() {
|
|
3144
3350
|
"use strict";
|
|
@@ -3168,6 +3374,8 @@ var init_heuristic = __esm({
|
|
|
3168
3374
|
})();
|
|
3169
3375
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
3170
3376
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
3377
|
+
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
3378
|
+
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;
|
|
3171
3379
|
}
|
|
3172
3380
|
});
|
|
3173
3381
|
|
|
@@ -3346,112 +3554,6 @@ var init_token_stats = __esm({
|
|
|
3346
3554
|
}
|
|
3347
3555
|
});
|
|
3348
3556
|
|
|
3349
|
-
// src/engine/formal-engine/decode.ts
|
|
3350
|
-
var decode_exports = {};
|
|
3351
|
-
__export(decode_exports, {
|
|
3352
|
-
allCandidates: () => allCandidates,
|
|
3353
|
-
decodeBase64Literals: () => decodeBase64Literals,
|
|
3354
|
-
decodeUrlAndNumeric: () => decodeUrlAndNumeric,
|
|
3355
|
-
matchStageAny: () => matchStageAny
|
|
3356
|
-
});
|
|
3357
|
-
function isValidBase64Token(tok) {
|
|
3358
|
-
if (tok.length < 8) return false;
|
|
3359
|
-
if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
|
|
3360
|
-
return true;
|
|
3361
|
-
}
|
|
3362
|
-
function isPrintable(s) {
|
|
3363
|
-
for (let i = 0; i < s.length; i++) {
|
|
3364
|
-
const c = s.charCodeAt(i);
|
|
3365
|
-
if (c < 32 || c > 126) return false;
|
|
3366
|
-
}
|
|
3367
|
-
return true;
|
|
3368
|
-
}
|
|
3369
|
-
function decodeBase64Literals(text) {
|
|
3370
|
-
const out = [];
|
|
3371
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3372
|
-
for (const m of text.matchAll(B64_TOKEN_RE)) {
|
|
3373
|
-
const tok = m[0];
|
|
3374
|
-
if (!isValidBase64Token(tok)) continue;
|
|
3375
|
-
if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
|
|
3376
|
-
try {
|
|
3377
|
-
const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
|
|
3378
|
-
const bin = Buffer.from(padded, "base64");
|
|
3379
|
-
const s = bin.toString("utf8");
|
|
3380
|
-
if (s && isPrintable(s) && !seen.has(s)) {
|
|
3381
|
-
seen.add(s);
|
|
3382
|
-
out.push(s);
|
|
3383
|
-
}
|
|
3384
|
-
} catch {
|
|
3385
|
-
}
|
|
3386
|
-
}
|
|
3387
|
-
return out;
|
|
3388
|
-
}
|
|
3389
|
-
function decodeUrlAndNumeric(text) {
|
|
3390
|
-
const out = [];
|
|
3391
|
-
let s = text;
|
|
3392
|
-
for (let i = 0; i < 3; i++) {
|
|
3393
|
-
let nxt = s;
|
|
3394
|
-
try {
|
|
3395
|
-
nxt = decodeURIComponent(s);
|
|
3396
|
-
} catch {
|
|
3397
|
-
break;
|
|
3398
|
-
}
|
|
3399
|
-
if (nxt === s) break;
|
|
3400
|
-
s = nxt;
|
|
3401
|
-
}
|
|
3402
|
-
if (s !== text) out.push(s);
|
|
3403
|
-
if (/\\[0-7]{3}/.test(text)) {
|
|
3404
|
-
let expanded = "";
|
|
3405
|
-
try {
|
|
3406
|
-
expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
|
|
3407
|
-
} catch {
|
|
3408
|
-
}
|
|
3409
|
-
if (expanded && expanded !== text) out.push(expanded);
|
|
3410
|
-
}
|
|
3411
|
-
if (/\\x[0-9a-fA-F]{2}/.test(text)) {
|
|
3412
|
-
let expanded = "";
|
|
3413
|
-
try {
|
|
3414
|
-
expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
|
|
3415
|
-
} catch {
|
|
3416
|
-
}
|
|
3417
|
-
if (expanded && expanded !== text) out.push(expanded);
|
|
3418
|
-
}
|
|
3419
|
-
return out;
|
|
3420
|
-
}
|
|
3421
|
-
function allCandidates(detail, max = 8) {
|
|
3422
|
-
const cands = [detail];
|
|
3423
|
-
cands.push(...decodeBase64Literals(detail));
|
|
3424
|
-
cands.push(...decodeUrlAndNumeric(detail));
|
|
3425
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3426
|
-
const out = [];
|
|
3427
|
-
for (const c of cands) {
|
|
3428
|
-
if (seen.has(c) || c.length > 4096) continue;
|
|
3429
|
-
seen.add(c);
|
|
3430
|
-
out.push(c);
|
|
3431
|
-
if (out.length >= max) break;
|
|
3432
|
-
}
|
|
3433
|
-
return out;
|
|
3434
|
-
}
|
|
3435
|
-
function matchStageAny(detail, stages) {
|
|
3436
|
-
for (const cand of allCandidates(detail)) {
|
|
3437
|
-
for (const [name, pats] of Object.entries(stages)) {
|
|
3438
|
-
for (const p of pats) {
|
|
3439
|
-
p.lastIndex = 0;
|
|
3440
|
-
if (p.test(cand)) return name;
|
|
3441
|
-
}
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
return "";
|
|
3445
|
-
}
|
|
3446
|
-
var B64_TOKEN_RE;
|
|
3447
|
-
var init_decode = __esm({
|
|
3448
|
-
"src/engine/formal-engine/decode.ts"() {
|
|
3449
|
-
"use strict";
|
|
3450
|
-
init_esm_shims();
|
|
3451
|
-
B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
3452
|
-
}
|
|
3453
|
-
});
|
|
3454
|
-
|
|
3455
3557
|
// src/engine/coordinator.ts
|
|
3456
3558
|
init_esm_shims();
|
|
3457
3559
|
init_heuristic();
|
|
@@ -3684,6 +3786,7 @@ ${result.scriptContent}
|
|
|
3684
3786
|
init_esm_shims();
|
|
3685
3787
|
init_heuristic();
|
|
3686
3788
|
init_logic_rules();
|
|
3789
|
+
init_inline_analyzer();
|
|
3687
3790
|
import { statSync as statSync2, existsSync as existsSync9 } from "fs";
|
|
3688
3791
|
import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
3689
3792
|
import { homedir as homedir4 } from "os";
|
|
@@ -5298,6 +5401,13 @@ function extractScriptPath2(command) {
|
|
|
5298
5401
|
return null;
|
|
5299
5402
|
}
|
|
5300
5403
|
function extractScriptPathWithArgs(command) {
|
|
5404
|
+
const inline = command.match(/(?:^|\s|&&|\|\|)\s*(?:bash|\/bin\/bash|sh|zsh)\s+-c\s*(['"])([\s\S]*?)\1/i);
|
|
5405
|
+
if (inline) {
|
|
5406
|
+
const inner = inline[2];
|
|
5407
|
+
const realPath = extractScriptPath2(inner);
|
|
5408
|
+
if (realPath) return { path: realPath, args: [] };
|
|
5409
|
+
return { path: null, args: [] };
|
|
5410
|
+
}
|
|
5301
5411
|
const scriptPath = extractScriptPath2(command);
|
|
5302
5412
|
if (!scriptPath) return { path: null, args: [] };
|
|
5303
5413
|
const idx = command.indexOf(scriptPath);
|
|
@@ -5311,11 +5421,59 @@ function extractScriptPathWithArgs(command) {
|
|
|
5311
5421
|
}
|
|
5312
5422
|
return { path: scriptPath, args };
|
|
5313
5423
|
}
|
|
5424
|
+
function looksLikeScriptPath(tok) {
|
|
5425
|
+
const t = tok.trim().replace(/^['"]|['"]$/g, "");
|
|
5426
|
+
if (!t) return false;
|
|
5427
|
+
const low = t.toLowerCase();
|
|
5428
|
+
if (["bash", "sh", "zsh", "dash", "python", "python3", "perl", "ruby", "node", "php", "lua"].includes(low)) return false;
|
|
5429
|
+
if (/\.(sh|bash|py|pl|rb|js|php|lua)$/.test(low)) return true;
|
|
5430
|
+
if (t.includes("/") && !t.startsWith("-")) return true;
|
|
5431
|
+
return false;
|
|
5432
|
+
}
|
|
5314
5433
|
function extractInlineScriptPath(actionDetail) {
|
|
5434
|
+
const code = extractInlineCode(actionDetail);
|
|
5435
|
+
if (code) {
|
|
5436
|
+
const candidates = [];
|
|
5437
|
+
const osRe = /os\.(?:system|popen)\s*\(\s*(['"])(.*?)\1\s*\)/gi;
|
|
5438
|
+
let m;
|
|
5439
|
+
while ((m = osRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5440
|
+
const subListRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*\[(.*?)\]/gi;
|
|
5441
|
+
while ((m = subListRe.exec(code)) !== null) candidates.push(m[1]);
|
|
5442
|
+
const subShellRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*(['"])(.*?)\1[^)]*?shell\s*=\s*true/gi;
|
|
5443
|
+
while ((m = subShellRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5444
|
+
for (const cand of candidates) {
|
|
5445
|
+
const tokens = cand.includes(",") ? cand.split(/\s*,\s*/).map((t) => t.trim().replace(/^['"]|['"]$/g, "")) : cand.split(/\s+/);
|
|
5446
|
+
for (const tok of tokens) {
|
|
5447
|
+
if (looksLikeScriptPath(tok)) {
|
|
5448
|
+
try {
|
|
5449
|
+
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5450
|
+
if (existsSync9(resolved)) {
|
|
5451
|
+
const stat = statSync2(resolved);
|
|
5452
|
+
if (stat.isFile()) return resolved;
|
|
5453
|
+
}
|
|
5454
|
+
} catch {
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
const openRe = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5460
|
+
while ((m = openRe.exec(code)) !== null) {
|
|
5461
|
+
const p = m[1];
|
|
5462
|
+
try {
|
|
5463
|
+
const resolved = resolvePath2(p);
|
|
5464
|
+
if (existsSync9(resolved)) {
|
|
5465
|
+
const stat = statSync2(resolved);
|
|
5466
|
+
if (stat.isFile()) return resolved;
|
|
5467
|
+
}
|
|
5468
|
+
} catch {
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
return null;
|
|
5472
|
+
}
|
|
5315
5473
|
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5316
|
-
let
|
|
5317
|
-
while ((
|
|
5318
|
-
const p =
|
|
5474
|
+
let m2;
|
|
5475
|
+
while ((m2 = re.exec(actionDetail)) !== null) {
|
|
5476
|
+
const p = m2[1];
|
|
5319
5477
|
let resolved;
|
|
5320
5478
|
try {
|
|
5321
5479
|
resolved = resolvePath2(p);
|
|
@@ -5700,7 +5858,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
5700
5858
|
riskType: riskType || "",
|
|
5701
5859
|
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
5702
5860
|
source: "llm",
|
|
5703
|
-
violatedLayers: ["
|
|
5861
|
+
violatedLayers: ["L3.1"]
|
|
5704
5862
|
};
|
|
5705
5863
|
} catch (err) {
|
|
5706
5864
|
if (abortController.signal.aborted) {
|
|
@@ -6261,7 +6419,139 @@ init_esm_shims();
|
|
|
6261
6419
|
// src/engine/formal-engine/builder.ts
|
|
6262
6420
|
init_esm_shims();
|
|
6263
6421
|
init_rules_loader();
|
|
6264
|
-
|
|
6422
|
+
|
|
6423
|
+
// src/engine/formal-engine/decode.ts
|
|
6424
|
+
init_esm_shims();
|
|
6425
|
+
var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
6426
|
+
function isValidBase64Token(tok) {
|
|
6427
|
+
if (tok.length < 8) return false;
|
|
6428
|
+
if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
|
|
6429
|
+
return true;
|
|
6430
|
+
}
|
|
6431
|
+
function isPrintable(s) {
|
|
6432
|
+
for (let i = 0; i < s.length; i++) {
|
|
6433
|
+
const c = s.charCodeAt(i);
|
|
6434
|
+
if (c < 32 || c > 126) return false;
|
|
6435
|
+
}
|
|
6436
|
+
return true;
|
|
6437
|
+
}
|
|
6438
|
+
function decodeBase64Literals(text) {
|
|
6439
|
+
const out = [];
|
|
6440
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6441
|
+
for (const m of text.matchAll(B64_TOKEN_RE)) {
|
|
6442
|
+
const tok = m[0];
|
|
6443
|
+
if (!isValidBase64Token(tok)) continue;
|
|
6444
|
+
if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
|
|
6445
|
+
try {
|
|
6446
|
+
const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
|
|
6447
|
+
const bin = Buffer.from(padded, "base64");
|
|
6448
|
+
const s = bin.toString("utf8");
|
|
6449
|
+
if (s && isPrintable(s) && !seen.has(s)) {
|
|
6450
|
+
seen.add(s);
|
|
6451
|
+
out.push(s);
|
|
6452
|
+
}
|
|
6453
|
+
} catch {
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
return out;
|
|
6457
|
+
}
|
|
6458
|
+
var QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
6459
|
+
var OUTPUT_CMDS = /* @__PURE__ */ new Set(["echo", "printf", "print"]);
|
|
6460
|
+
function stripStringLiterals(text) {
|
|
6461
|
+
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
6462
|
+
if (!m) return text;
|
|
6463
|
+
const cmd = m[1].split("/").pop().toLowerCase();
|
|
6464
|
+
if (!OUTPUT_CMDS.has(cmd)) return text;
|
|
6465
|
+
return text.replace(QUOTED_STR_RE, "");
|
|
6466
|
+
}
|
|
6467
|
+
function decodeUrlAndNumeric(text) {
|
|
6468
|
+
const out = [];
|
|
6469
|
+
let s = text;
|
|
6470
|
+
for (let i = 0; i < 3; i++) {
|
|
6471
|
+
let nxt = s;
|
|
6472
|
+
try {
|
|
6473
|
+
nxt = decodeURIComponent(s);
|
|
6474
|
+
} catch {
|
|
6475
|
+
break;
|
|
6476
|
+
}
|
|
6477
|
+
if (nxt === s) break;
|
|
6478
|
+
s = nxt;
|
|
6479
|
+
}
|
|
6480
|
+
if (s !== text) out.push(s);
|
|
6481
|
+
if (/\\[0-7]{3}/.test(text)) {
|
|
6482
|
+
let expanded = "";
|
|
6483
|
+
try {
|
|
6484
|
+
expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
|
|
6485
|
+
} catch {
|
|
6486
|
+
}
|
|
6487
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
6488
|
+
}
|
|
6489
|
+
if (/\\x[0-9a-fA-F]{2}/.test(text)) {
|
|
6490
|
+
let expanded = "";
|
|
6491
|
+
try {
|
|
6492
|
+
expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
|
|
6493
|
+
} catch {
|
|
6494
|
+
}
|
|
6495
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
6496
|
+
}
|
|
6497
|
+
return out;
|
|
6498
|
+
}
|
|
6499
|
+
function allCandidates(detail, max = 8) {
|
|
6500
|
+
const shell = stripStringLiterals(detail);
|
|
6501
|
+
const cands = [shell];
|
|
6502
|
+
cands.push(...decodeBase64Literals(shell));
|
|
6503
|
+
cands.push(...decodeUrlAndNumeric(shell));
|
|
6504
|
+
cands.push(...decodeBase64Literals(detail));
|
|
6505
|
+
cands.push(...decodeUrlAndNumeric(detail));
|
|
6506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6507
|
+
const out = [];
|
|
6508
|
+
for (const c of cands) {
|
|
6509
|
+
if (seen.has(c) || c.length > 4096) continue;
|
|
6510
|
+
seen.add(c);
|
|
6511
|
+
out.push(c);
|
|
6512
|
+
if (out.length >= max) break;
|
|
6513
|
+
}
|
|
6514
|
+
return out;
|
|
6515
|
+
}
|
|
6516
|
+
var STAGE_PRIORITY = ["EXFIL", "PRIVESC", "PERSIST", "SCAN", "LISTEN"];
|
|
6517
|
+
var PURPOSE_EXEC_RE = /subprocess\.\w+\s*\(|os\.system\s*\(|shell\s*=\s*True|\beval\s*\(|\bexec\s*\(|os\.popen\s*\(|\/exec\b/i;
|
|
6518
|
+
var LISTEN_LEAD_RE = /http\.server|flask\s+run|nohup\b|nc\s+-l|socat\s+TCP-LISTEN/i;
|
|
6519
|
+
var LISTEN_NOISE_RE = /shell\s*=\s*True|os\.system|subprocess\.(?:run|call|Popen)|\beval\s*\(|\bexec\s*\(/i;
|
|
6520
|
+
function matchStageAny(detail, stages) {
|
|
6521
|
+
const cands = allCandidates(detail);
|
|
6522
|
+
if (cands.length === 0) return "";
|
|
6523
|
+
const shell = cands[0];
|
|
6524
|
+
const hits = [];
|
|
6525
|
+
for (const [name, pats] of Object.entries(stages)) {
|
|
6526
|
+
for (const p of pats) {
|
|
6527
|
+
p.lastIndex = 0;
|
|
6528
|
+
if (p.test(shell)) {
|
|
6529
|
+
hits.push(name);
|
|
6530
|
+
break;
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
if (hits.length > 0) {
|
|
6535
|
+
if (hits.length === 1) return hits[0];
|
|
6536
|
+
if (hits.includes("EXEC") && PURPOSE_EXEC_RE.test(shell)) return "EXEC";
|
|
6537
|
+
if (hits.includes("LISTEN") && LISTEN_LEAD_RE.test(shell) && !LISTEN_NOISE_RE.test(shell)) {
|
|
6538
|
+
return "LISTEN";
|
|
6539
|
+
}
|
|
6540
|
+
for (const s of STAGE_PRIORITY) {
|
|
6541
|
+
if (hits.includes(s)) return s;
|
|
6542
|
+
}
|
|
6543
|
+
if (hits.includes("EXEC")) return "EXEC";
|
|
6544
|
+
}
|
|
6545
|
+
for (const cand of cands.slice(1)) {
|
|
6546
|
+
for (const [name, pats] of Object.entries(stages)) {
|
|
6547
|
+
for (const p of pats) {
|
|
6548
|
+
p.lastIndex = 0;
|
|
6549
|
+
if (p.test(cand)) return name;
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
return "";
|
|
6554
|
+
}
|
|
6265
6555
|
|
|
6266
6556
|
// src/engine/formal-engine/ltl.ts
|
|
6267
6557
|
init_esm_shims();
|
|
@@ -6295,11 +6585,7 @@ function compileAps(apPatterns) {
|
|
|
6295
6585
|
}
|
|
6296
6586
|
function labelEvent(actionDetail) {
|
|
6297
6587
|
if (!AP_RE) compileAps(loadMC().ap_patterns || {});
|
|
6298
|
-
|
|
6299
|
-
try {
|
|
6300
|
-
candidates = (init_decode(), __toCommonJS(decode_exports)).allCandidates(actionDetail);
|
|
6301
|
-
} catch {
|
|
6302
|
-
}
|
|
6588
|
+
const candidates = allCandidates(actionDetail);
|
|
6303
6589
|
const hits = /* @__PURE__ */ new Set();
|
|
6304
6590
|
for (const [name, pats] of Object.entries(AP_RE || {})) {
|
|
6305
6591
|
for (const c of candidates) {
|
|
@@ -6537,7 +6823,7 @@ function loadCrossSessionChains() {
|
|
|
6537
6823
|
return CROSS_SESSION_CHAINS;
|
|
6538
6824
|
}
|
|
6539
6825
|
var CRED_FILE_RE = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
|
|
6540
|
-
var EXFIL_RE = /https
|
|
6826
|
+
var EXFIL_RE = /https?:\/\/(?!127\.\d+\.\d+\.\d+|0\.0\.0\.0|localhost|\[?::1\]?)[^\s]+|\bscp\s+.*@|\brsync\s+.*@/i;
|
|
6541
6827
|
function checkCrossSessionChain(graph) {
|
|
6542
6828
|
const violations = [];
|
|
6543
6829
|
const nodes = allNodes(graph);
|
|
@@ -6626,7 +6912,7 @@ function checkSensitiveDataFlow(graph) {
|
|
|
6626
6912
|
const n = nodes[i];
|
|
6627
6913
|
if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
|
|
6628
6914
|
violations.push({
|
|
6629
|
-
propertyId: "sensitive-data-
|
|
6915
|
+
propertyId: "sensitive-data-flow",
|
|
6630
6916
|
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`,
|
|
6631
6917
|
riskLevel: "critical",
|
|
6632
6918
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6638,9 +6924,67 @@ function checkSensitiveDataFlow(graph) {
|
|
|
6638
6924
|
}
|
|
6639
6925
|
var EBPF_NET_RE = /connect|sendto|network_connect/i;
|
|
6640
6926
|
var REMOTE_RE = /to\s+\d+\.\d+\.\d+\.\d+|remote_addr/i;
|
|
6641
|
-
var PRIVESC_RE = /\bsudo\b|\bsu\b|chmod\s+u\s*\+\s*s|pkexec|setuid/i;
|
|
6642
6927
|
var 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;
|
|
6643
6928
|
var 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;
|
|
6929
|
+
var INLINE_SHELL_RE = /(?:os\.system|os\.popen)\s*\(\s*['"]([^'"]+)['"]|subprocess\.[a-z_]+\(\s*\[([^\]]+)\]|subprocess\.[a-z_]+\(\s*['"]([^'"]+)['"]|(?:bash|sh|zsh)\s+-c\s*['"]([^'"]+)['"]/gi;
|
|
6930
|
+
function extractInlineShell(s) {
|
|
6931
|
+
const out = [];
|
|
6932
|
+
let m;
|
|
6933
|
+
INLINE_SHELL_RE.lastIndex = 0;
|
|
6934
|
+
while ((m = INLINE_SHELL_RE.exec(s)) !== null) {
|
|
6935
|
+
let g = m[1] ?? m[2] ?? m[3] ?? m[4] ?? "";
|
|
6936
|
+
if (/^["']/.test(g) && (/"\s*,/.test(g) || /'\s*,/.test(g))) {
|
|
6937
|
+
g = g.replace(/['"\[\],]+/g, " ");
|
|
6938
|
+
}
|
|
6939
|
+
g = g.trim();
|
|
6940
|
+
if (g) out.push(g);
|
|
6941
|
+
}
|
|
6942
|
+
return out;
|
|
6943
|
+
}
|
|
6944
|
+
var PKG_MGR = /* @__PURE__ */ new Set(["apt", "apt-get", "aptitude", "yum", "dnf", "pacman", "zypper", "apk", "brew"]);
|
|
6945
|
+
var SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish", "ksh", "csh", "tcsh", "nologin"]);
|
|
6946
|
+
function containsPrivesc(shell) {
|
|
6947
|
+
if (!shell) return false;
|
|
6948
|
+
for (const seg of shell.split(/\&\&|\|\||;|\n/)) {
|
|
6949
|
+
const s = seg.trim();
|
|
6950
|
+
if (!s) continue;
|
|
6951
|
+
const toks = s.split(/\s+/);
|
|
6952
|
+
const head = (toks[0] || "").toLowerCase();
|
|
6953
|
+
const rest = toks.slice(1).join(" ");
|
|
6954
|
+
if (head === "chmod" && /(?:u\+s|g\+s|[2-7][0-7]{3})/.test(rest)) return true;
|
|
6955
|
+
if (head === "setcap" && /cap_/.test(rest)) return true;
|
|
6956
|
+
if (head === "pkexec" || head === "doas") return true;
|
|
6957
|
+
if (head === "sudo") {
|
|
6958
|
+
if (/^-u\s+/.test(rest)) {
|
|
6959
|
+
if (/^-u\s+root\b/.test(rest)) return true;
|
|
6960
|
+
continue;
|
|
6961
|
+
}
|
|
6962
|
+
const m = /^(\S+)(.*)$/.exec(rest);
|
|
6963
|
+
if (m) {
|
|
6964
|
+
const nxt = m[1].toLowerCase();
|
|
6965
|
+
const tail = m[2];
|
|
6966
|
+
if (PKG_MGR.has(nxt)) continue;
|
|
6967
|
+
if (SHELLS.has(nxt)) return true;
|
|
6968
|
+
if (nxt === "-i" || nxt === "-s") return true;
|
|
6969
|
+
if (nxt.startsWith("su")) return true;
|
|
6970
|
+
if (nxt.startsWith("/") && /bin\/(?:bash|sh|zsh|dash|fish)$/.test(nxt)) return true;
|
|
6971
|
+
}
|
|
6972
|
+
if (/\/(?:usr\/(?:local\/)?bin|bin)\/(?:bash|sh|zsh|dash|fish)\b/.test(rest)) return true;
|
|
6973
|
+
if (/\bchmod\s+(?:u\+s|g\+s|[2-7][0-7]{3})\b/.test(rest)) return true;
|
|
6974
|
+
if (/\bsetcap\s+cap_\w+/.test(rest)) return true;
|
|
6975
|
+
if (/\bvisudo\b/.test(rest)) return true;
|
|
6976
|
+
if (/\bpasswd\s+root\b/.test(rest)) return true;
|
|
6977
|
+
if (/\buseradd\b/.test(rest)) return true;
|
|
6978
|
+
}
|
|
6979
|
+
if (head === "su") {
|
|
6980
|
+
if (!rest) return true;
|
|
6981
|
+
if (/^-l?\s*$/.test(rest)) return true;
|
|
6982
|
+
if (/^root\b/.test(rest) || /^-l?\s+root\b/.test(rest)) return true;
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
if (/\bset(?:uid|euid|gid|egid|reuid|regid|resuid|resgid)\s*\(/i.test(shell)) return true;
|
|
6986
|
+
return false;
|
|
6987
|
+
}
|
|
6644
6988
|
function isExfilNode(n) {
|
|
6645
6989
|
if (n.stage === "EXFIL") return true;
|
|
6646
6990
|
if (EXFIL_RE.test(n.actionDetail) && EXFIL_ACTION_RE.test(n.actionDetail)) return true;
|
|
@@ -6655,8 +6999,12 @@ function isCredNode(n) {
|
|
|
6655
6999
|
return CRED_STRICT_RE.test(n.actionDetail);
|
|
6656
7000
|
}
|
|
6657
7001
|
function isPrivescNode(n) {
|
|
6658
|
-
|
|
6659
|
-
|
|
7002
|
+
const shell = stripStringLiterals(n.actionDetail);
|
|
7003
|
+
if (containsPrivesc(shell)) return true;
|
|
7004
|
+
for (const cmd of extractInlineShell(shell)) {
|
|
7005
|
+
if (containsPrivesc(cmd)) return true;
|
|
7006
|
+
}
|
|
7007
|
+
return false;
|
|
6660
7008
|
}
|
|
6661
7009
|
function checkNeverExfilAfterPrivesc(graph) {
|
|
6662
7010
|
const violations = [];
|
|
@@ -6677,7 +7025,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6677
7025
|
if (pre) {
|
|
6678
7026
|
const post = nodes[firstEv];
|
|
6679
7027
|
return [{
|
|
6680
|
-
propertyId: "no-
|
|
7028
|
+
propertyId: "no-exfil-after-privesc",
|
|
6681
7029
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
|
|
6682
7030
|
riskLevel: "critical",
|
|
6683
7031
|
path: [
|
|
@@ -6693,7 +7041,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6693
7041
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6694
7042
|
if (isExfilNode(nodes[j])) {
|
|
6695
7043
|
violations.push({
|
|
6696
|
-
propertyId: "no-
|
|
7044
|
+
propertyId: "no-exfil-after-privesc",
|
|
6697
7045
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
|
|
6698
7046
|
riskLevel: "critical",
|
|
6699
7047
|
path: [
|
|
@@ -6726,7 +7074,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6726
7074
|
if (pre) {
|
|
6727
7075
|
const post = nodes[firstEv];
|
|
6728
7076
|
return [{
|
|
6729
|
-
propertyId: "no-
|
|
7077
|
+
propertyId: "no-exfil-after-credential",
|
|
6730
7078
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6731
7079
|
riskLevel: "critical",
|
|
6732
7080
|
path: [
|
|
@@ -6742,7 +7090,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6742
7090
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6743
7091
|
if (isExfilNode(nodes[j])) {
|
|
6744
7092
|
violations.push({
|
|
6745
|
-
propertyId: "no-
|
|
7093
|
+
propertyId: "no-exfil-after-credential",
|
|
6746
7094
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6747
7095
|
riskLevel: "critical",
|
|
6748
7096
|
path: [
|
|
@@ -6812,7 +7160,7 @@ function checkLowIntegrityCommandExec(graph) {
|
|
|
6812
7160
|
const n = nodes[i];
|
|
6813
7161
|
if (lowAt.has(i) && isComputeNode(n)) {
|
|
6814
7162
|
violations.push({
|
|
6815
|
-
propertyId: "low-integrity-
|
|
7163
|
+
propertyId: "low-integrity-command-exec",
|
|
6816
7164
|
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`,
|
|
6817
7165
|
riskLevel: "critical",
|
|
6818
7166
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6859,7 +7207,7 @@ function checkCrossSessionReachability(graph) {
|
|
|
6859
7207
|
const srcIdx = Math.min(...sources);
|
|
6860
7208
|
const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
|
|
6861
7209
|
violations.push({
|
|
6862
|
-
propertyId: "
|
|
7210
|
+
propertyId: "low-integrity-reachability",
|
|
6863
7211
|
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`,
|
|
6864
7212
|
riskLevel: "high",
|
|
6865
7213
|
path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
|
|
@@ -6911,17 +7259,17 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
6911
7259
|
return violations;
|
|
6912
7260
|
}
|
|
6913
7261
|
var ALL_PROPERTIES = [
|
|
6914
|
-
{ id: "cross-session-chain", check: checkCrossSessionChain },
|
|
6915
|
-
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow },
|
|
6916
|
-
{ id: "
|
|
6917
|
-
{ id: "no-
|
|
6918
|
-
{ id: "no-
|
|
7262
|
+
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
7263
|
+
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow, shortName: "P2" },
|
|
7264
|
+
{ id: "low-integrity-reachability", check: checkCrossSessionReachability, shortName: "P3" },
|
|
7265
|
+
{ id: "no-exfil-after-privesc", check: checkNeverExfilAfterPrivesc, shortName: "P4" },
|
|
7266
|
+
{ id: "no-exfil-after-credential", check: checkNeverExfilAfterCred, shortName: "P5" },
|
|
6919
7267
|
// L2.2 模型检测(通用 LTL 求值器,mc_rules.json 单一事实源;witness 语义见 ltl.ts)
|
|
6920
|
-
{ id: "mc-ltl-rules", check: checkMCRules },
|
|
6921
|
-
//
|
|
6922
|
-
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec },
|
|
6923
|
-
//
|
|
6924
|
-
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation }
|
|
7268
|
+
{ id: "mc-ltl-rules", check: checkMCRules, shortName: "P6" },
|
|
7269
|
+
// P7 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
|
|
7270
|
+
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec, shortName: "P7" },
|
|
7271
|
+
// P8 敏感数据聚合外传(egress 聚合 ≥2 事件源)
|
|
7272
|
+
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation, shortName: "P8" }
|
|
6925
7273
|
];
|
|
6926
7274
|
|
|
6927
7275
|
// src/engine/formal-engine/engine.ts
|
|
@@ -6984,7 +7332,6 @@ var FormalEngine = class {
|
|
|
6984
7332
|
// src/engine/formal-engine/stateful.ts
|
|
6985
7333
|
init_esm_shims();
|
|
6986
7334
|
init_rules_loader();
|
|
6987
|
-
init_decode();
|
|
6988
7335
|
|
|
6989
7336
|
// src/engine/formal-engine/chain_state.ts
|
|
6990
7337
|
init_esm_shims();
|
|
@@ -7123,7 +7470,7 @@ async function runAnalysis(input) {
|
|
|
7123
7470
|
riskType: node.l1RiskType,
|
|
7124
7471
|
confidence: 90,
|
|
7125
7472
|
source: "heuristic_script_scan",
|
|
7126
|
-
violatedLayers: ["
|
|
7473
|
+
violatedLayers: ["L1.2"]
|
|
7127
7474
|
};
|
|
7128
7475
|
}
|
|
7129
7476
|
}
|
|
@@ -7241,7 +7588,7 @@ async function runAnalysis(input) {
|
|
|
7241
7588
|
riskType: inlineJudgment.riskType,
|
|
7242
7589
|
confidence: 90,
|
|
7243
7590
|
source: "logic_rule",
|
|
7244
|
-
violatedLayers: ["1
|
|
7591
|
+
violatedLayers: ["L2.1"]
|
|
7245
7592
|
};
|
|
7246
7593
|
}
|
|
7247
7594
|
return {
|
|
@@ -7274,7 +7621,7 @@ async function runAnalysis(input) {
|
|
|
7274
7621
|
console.warn(
|
|
7275
7622
|
`[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)}`
|
|
7276
7623
|
);
|
|
7277
|
-
observedFormalLayers.push("2
|
|
7624
|
+
observedFormalLayers.push("L2.2");
|
|
7278
7625
|
} else {
|
|
7279
7626
|
return {
|
|
7280
7627
|
allowed: false,
|
|
@@ -7283,7 +7630,7 @@ async function runAnalysis(input) {
|
|
|
7283
7630
|
riskType: "formal_verification",
|
|
7284
7631
|
confidence: 90,
|
|
7285
7632
|
source: "formal_engine",
|
|
7286
|
-
violatedLayers: ["2
|
|
7633
|
+
violatedLayers: ["L2.2"]
|
|
7287
7634
|
};
|
|
7288
7635
|
}
|
|
7289
7636
|
}
|
|
@@ -7300,7 +7647,7 @@ async function runAnalysis(input) {
|
|
|
7300
7647
|
riskType: node.l2RiskType,
|
|
7301
7648
|
confidence: 85,
|
|
7302
7649
|
source: "logic_rule_script_scan",
|
|
7303
|
-
violatedLayers: ["
|
|
7650
|
+
violatedLayers: ["L2.3"]
|
|
7304
7651
|
};
|
|
7305
7652
|
}
|
|
7306
7653
|
}
|
|
@@ -7387,7 +7734,7 @@ async function runAnalysis(input) {
|
|
|
7387
7734
|
riskType: "",
|
|
7388
7735
|
confidence: 60,
|
|
7389
7736
|
source: "llm_failure_fallback",
|
|
7390
|
-
violatedLayers: ["
|
|
7737
|
+
violatedLayers: ["L3.1"]
|
|
7391
7738
|
};
|
|
7392
7739
|
}
|
|
7393
7740
|
if (!llmResult.allowed && llmResult.confidence && llmResult.confidence >= 90 && _brainExtractor) {
|
|
@@ -7417,4 +7764,14 @@ export {
|
|
|
7417
7764
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
7418
7765
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
7419
7766
|
//! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
|
|
7767
|
+
//!
|
|
7768
|
+
//! shortName(P1~P8 连续短名)与 id(语义化英文)对照(与 Python 一致):
|
|
7769
|
+
//! P1 cross-session-chain 跨会话组合链
|
|
7770
|
+
//! P2 sensitive-data-flow 敏感数据传播
|
|
7771
|
+
//! P3 low-integrity-reachability 低完整性数据流可达
|
|
7772
|
+
//! P4 no-exfil-after-privesc 提权后禁外传
|
|
7773
|
+
//! P5 no-exfil-after-credential 凭据后禁外传
|
|
7774
|
+
//! P6 mc-ltl-rules LTL 模型检测
|
|
7775
|
+
//! P7 low-integrity-command-exec 低完整性命令执行
|
|
7776
|
+
//! P8 sensitive-data-aggregation 敏感数据聚合外传
|
|
7420
7777
|
//# sourceMappingURL=coordinator.js.map
|