@kenhkl/agent-moss 0.13.0 → 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 +482 -122
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +1 -1
- package/dist/engine/coordinator.js +426 -180
- 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 +258 -26
- package/dist/server.js +571 -220
- package/dist/server.js.map +1 -1
- package/dist/static/index.html +6 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -633,7 +633,7 @@ function loadMC() {
|
|
|
633
633
|
try {
|
|
634
634
|
const rt = (init_runtime_config(), __toCommonJS(runtime_config_exports));
|
|
635
635
|
if (rules.length > 0 && typeof rt.hasRuntimeConfig === "function" && rt.hasRuntimeConfig()) {
|
|
636
|
-
const rtRules = rt.loadRuntimeConfig()?.
|
|
636
|
+
const rtRules = rt.loadRuntimeConfig()?.L2_2?.ltl_rules?.rules || [];
|
|
637
637
|
const enabledById = {};
|
|
638
638
|
for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
|
|
639
639
|
for (const r of rules) if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
|
|
@@ -681,7 +681,8 @@ function _loadSensitivePathScope(scope) {
|
|
|
681
681
|
path: sp.path,
|
|
682
682
|
riskLevel: sp.risk_level,
|
|
683
683
|
desc: sp.desc,
|
|
684
|
-
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
|
|
685
686
|
}));
|
|
686
687
|
}
|
|
687
688
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
@@ -779,7 +780,7 @@ function runLogicCheck(input) {
|
|
|
779
780
|
riskType: "intent_deviation",
|
|
780
781
|
confidence: 85,
|
|
781
782
|
source: "logic_rule",
|
|
782
|
-
violatedLayers: ["
|
|
783
|
+
violatedLayers: ["L2.1"]
|
|
783
784
|
};
|
|
784
785
|
}
|
|
785
786
|
}
|
|
@@ -803,7 +804,7 @@ function runLogicCheck(input) {
|
|
|
803
804
|
riskType: "privilege_escalation",
|
|
804
805
|
confidence: 85,
|
|
805
806
|
source: "logic_rule",
|
|
806
|
-
violatedLayers: ["
|
|
807
|
+
violatedLayers: ["L2.1"]
|
|
807
808
|
};
|
|
808
809
|
}
|
|
809
810
|
}
|
|
@@ -855,7 +856,7 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
855
856
|
riskType: "combo_chain",
|
|
856
857
|
confidence: 85,
|
|
857
858
|
source: "logic_rule",
|
|
858
|
-
violatedLayers: ["
|
|
859
|
+
violatedLayers: ["L2.1"]
|
|
859
860
|
};
|
|
860
861
|
}
|
|
861
862
|
}
|
|
@@ -980,7 +981,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
980
981
|
riskType: "read_before_write",
|
|
981
982
|
confidence: 85,
|
|
982
983
|
source: "logic_rule",
|
|
983
|
-
violatedLayers: ["
|
|
984
|
+
violatedLayers: ["L2.1"]
|
|
984
985
|
};
|
|
985
986
|
}
|
|
986
987
|
return null;
|
|
@@ -1000,7 +1001,7 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
1000
1001
|
riskType: "consent_missing",
|
|
1001
1002
|
confidence: 85,
|
|
1002
1003
|
source: "logic_rule",
|
|
1003
|
-
violatedLayers: ["
|
|
1004
|
+
violatedLayers: ["L2.1"]
|
|
1004
1005
|
};
|
|
1005
1006
|
}
|
|
1006
1007
|
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
@@ -1018,7 +1019,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
|
1018
1019
|
riskType: "consent_missing",
|
|
1019
1020
|
confidence: 85,
|
|
1020
1021
|
source: "logic_rule",
|
|
1021
|
-
violatedLayers: ["
|
|
1022
|
+
violatedLayers: ["L2.1"]
|
|
1022
1023
|
};
|
|
1023
1024
|
}
|
|
1024
1025
|
function containsPrivilegeEscalation(actionDetail) {
|
|
@@ -1078,7 +1079,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1078
1079
|
riskType: "file_access",
|
|
1079
1080
|
confidence: 95,
|
|
1080
1081
|
source: "logic_rule",
|
|
1081
|
-
violatedLayers: ["
|
|
1082
|
+
violatedLayers: ["L2.1"],
|
|
1082
1083
|
isCredential: true
|
|
1083
1084
|
};
|
|
1084
1085
|
}
|
|
@@ -1094,7 +1095,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1094
1095
|
riskType: "file_access",
|
|
1095
1096
|
confidence: 95,
|
|
1096
1097
|
source: "logic_rule",
|
|
1097
|
-
violatedLayers: ["
|
|
1098
|
+
violatedLayers: ["L2.1"]
|
|
1098
1099
|
};
|
|
1099
1100
|
}
|
|
1100
1101
|
}
|
|
@@ -1108,7 +1109,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1108
1109
|
riskType: "file_access",
|
|
1109
1110
|
confidence: 70,
|
|
1110
1111
|
source: "logic_rule",
|
|
1111
|
-
violatedLayers: ["
|
|
1112
|
+
violatedLayers: ["L2.1"]
|
|
1112
1113
|
};
|
|
1113
1114
|
}
|
|
1114
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;
|
|
@@ -1120,7 +1121,7 @@ var init_logic_rules = __esm({
|
|
|
1120
1121
|
init_rules_loader();
|
|
1121
1122
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1122
1123
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1123
|
-
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 }));
|
|
1124
1125
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1125
1126
|
"ask_user_question",
|
|
1126
1127
|
"glob",
|
|
@@ -1471,7 +1472,7 @@ function emptyRuntimeConfig() {
|
|
|
1471
1472
|
L1_rules: {},
|
|
1472
1473
|
L2_rules: {},
|
|
1473
1474
|
L3_skills: {},
|
|
1474
|
-
|
|
1475
|
+
L2_2: {},
|
|
1475
1476
|
agent_rules: {},
|
|
1476
1477
|
scenario_rules: {}
|
|
1477
1478
|
};
|
|
@@ -1499,7 +1500,7 @@ function loadRuntimeConfig() {
|
|
|
1499
1500
|
L1_rules: merged.L1_rules || {},
|
|
1500
1501
|
L2_rules: merged.L2_rules || {},
|
|
1501
1502
|
L3_skills: merged.L3_skills || {},
|
|
1502
|
-
|
|
1503
|
+
L2_2: merged.L2_2 || {},
|
|
1503
1504
|
agent_rules: merged.agent_rules || {},
|
|
1504
1505
|
scenario_rules: merged.scenario_rules || {}
|
|
1505
1506
|
};
|
|
@@ -1552,6 +1553,7 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1552
1553
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1553
1554
|
if (r.credential) out.credential = true;
|
|
1554
1555
|
if (r.read_only) out.read_only = true;
|
|
1556
|
+
if (r.dir_del_only) out.dir_del_only = true;
|
|
1555
1557
|
return out;
|
|
1556
1558
|
});
|
|
1557
1559
|
}
|
|
@@ -1660,6 +1662,7 @@ function _l2SensitivePathsToRules() {
|
|
|
1660
1662
|
builtin: true
|
|
1661
1663
|
};
|
|
1662
1664
|
_syncDenyModeFields(rule);
|
|
1665
|
+
if (sp.dirDelOnly) rule.dir_del_only = true;
|
|
1663
1666
|
return rule;
|
|
1664
1667
|
});
|
|
1665
1668
|
}
|
|
@@ -1876,7 +1879,7 @@ function generateSourceDefaults() {
|
|
|
1876
1879
|
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1877
1880
|
},
|
|
1878
1881
|
L3_skills: _l3SkillsToRules(),
|
|
1879
|
-
|
|
1882
|
+
L2_2: _ltlRulesToRuntime(),
|
|
1880
1883
|
agent_rules: _agentRulesToDefaults(),
|
|
1881
1884
|
scenario_rules: _scenarioRulesToDefaults()
|
|
1882
1885
|
};
|
|
@@ -2064,7 +2067,11 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
2064
2067
|
);
|
|
2065
2068
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
2066
2069
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2067
|
-
|
|
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");
|
|
2068
2075
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2069
2076
|
_mergeAgentRules(cfg, srcDefaults);
|
|
2070
2077
|
_mergeAgentRules(cfg, srcDefaults, "scenario_rules");
|
|
@@ -2684,7 +2691,7 @@ function runHeuristicCheck(input) {
|
|
|
2684
2691
|
riskType: "sensitive_action",
|
|
2685
2692
|
confidence: 90,
|
|
2686
2693
|
source: "heuristic",
|
|
2687
|
-
violatedLayers: ["
|
|
2694
|
+
violatedLayers: ["L1.1"]
|
|
2688
2695
|
};
|
|
2689
2696
|
}
|
|
2690
2697
|
}
|
|
@@ -2694,8 +2701,10 @@ function runHeuristicCheck(input) {
|
|
|
2694
2701
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2695
2702
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2696
2703
|
let topRiskType = "";
|
|
2704
|
+
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2697
2705
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
2698
2706
|
if (rule.pattern.test(scanCommand)) {
|
|
2707
|
+
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2699
2708
|
matched.push(rule.description);
|
|
2700
2709
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
2701
2710
|
highestSeverity = rule.severity;
|
|
@@ -2737,10 +2746,26 @@ function runHeuristicCheck(input) {
|
|
|
2737
2746
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2738
2747
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2739
2748
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
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;
|
|
2744
2769
|
}
|
|
2745
2770
|
if (matched.length > 0) {
|
|
2746
2771
|
return {
|
|
@@ -2750,8 +2775,7 @@ function runHeuristicCheck(input) {
|
|
|
2750
2775
|
riskType: highestRiskType,
|
|
2751
2776
|
confidence: 95,
|
|
2752
2777
|
source: "heuristic",
|
|
2753
|
-
violatedLayers: ["
|
|
2754
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
2778
|
+
violatedLayers: ["L1.1"]
|
|
2755
2779
|
};
|
|
2756
2780
|
}
|
|
2757
2781
|
const lateralResult = checkLateralMovement(detailL);
|
|
@@ -2879,6 +2903,7 @@ function isSearchActionType(actionType) {
|
|
|
2879
2903
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2880
2904
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2881
2905
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2906
|
+
const keygenGenerate = isSshKeygenGenerate(command);
|
|
2882
2907
|
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2883
2908
|
const pathMatches = (text, path6) => {
|
|
2884
2909
|
const parts = path6.split(/(\(\?!.*?\))/);
|
|
@@ -2891,6 +2916,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2891
2916
|
};
|
|
2892
2917
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
2893
2918
|
if (pathMatches(pathScanText, sp.path.toLowerCase())) {
|
|
2919
|
+
if (keygenGenerate) continue;
|
|
2894
2920
|
if (sp.path === "/dev/random") {
|
|
2895
2921
|
if (isWriteOpFlag) {
|
|
2896
2922
|
continue;
|
|
@@ -2902,7 +2928,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2902
2928
|
riskType: "file_access",
|
|
2903
2929
|
confidence: 90,
|
|
2904
2930
|
source: "heuristic",
|
|
2905
|
-
violatedLayers: ["
|
|
2931
|
+
violatedLayers: ["L1.1"]
|
|
2906
2932
|
};
|
|
2907
2933
|
}
|
|
2908
2934
|
if (!isWriteOpFlag && !sp.credential) {
|
|
@@ -2915,16 +2941,51 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2915
2941
|
riskType: "file_access",
|
|
2916
2942
|
confidence: 90,
|
|
2917
2943
|
source: "heuristic",
|
|
2918
|
-
violatedLayers: ["
|
|
2944
|
+
violatedLayers: ["L1.1"],
|
|
2919
2945
|
isCredential: !!sp.credential
|
|
2920
2946
|
};
|
|
2921
2947
|
}
|
|
2922
2948
|
}
|
|
2923
2949
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
2924
2950
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
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
|
+
}
|
|
2928
2989
|
if (!isWriteOpFlag && !sp.credential) {
|
|
2929
2990
|
continue;
|
|
2930
2991
|
}
|
|
@@ -2935,7 +2996,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2935
2996
|
riskType: "file_access",
|
|
2936
2997
|
confidence: 90,
|
|
2937
2998
|
source: "heuristic",
|
|
2938
|
-
violatedLayers: ["
|
|
2999
|
+
violatedLayers: ["L1.1"],
|
|
2939
3000
|
isCredential: !!sp.credential
|
|
2940
3001
|
};
|
|
2941
3002
|
}
|
|
@@ -2958,7 +3019,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2958
3019
|
riskType: "file_access",
|
|
2959
3020
|
confidence: 90,
|
|
2960
3021
|
source: "heuristic",
|
|
2961
|
-
violatedLayers: ["
|
|
3022
|
+
violatedLayers: ["L1.1"],
|
|
2962
3023
|
isCredential: !!sp.credential
|
|
2963
3024
|
};
|
|
2964
3025
|
}
|
|
@@ -2975,7 +3036,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2975
3036
|
riskType: "file_access",
|
|
2976
3037
|
confidence: 90,
|
|
2977
3038
|
source: "heuristic",
|
|
2978
|
-
violatedLayers: ["
|
|
3039
|
+
violatedLayers: ["L1.1"],
|
|
2979
3040
|
isCredential: true
|
|
2980
3041
|
};
|
|
2981
3042
|
}
|
|
@@ -2988,18 +3049,142 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2988
3049
|
riskType: "file_access",
|
|
2989
3050
|
confidence: 90,
|
|
2990
3051
|
source: "heuristic",
|
|
2991
|
-
violatedLayers: ["
|
|
3052
|
+
violatedLayers: ["L1.1"]
|
|
2992
3053
|
};
|
|
2993
3054
|
}
|
|
2994
3055
|
return null;
|
|
2995
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
|
+
}
|
|
2996
3181
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2997
3182
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2998
3183
|
const scan = stripHeredocContent(actionDetail);
|
|
2999
3184
|
const TEMP_DIRS = ["/tmp", "/var/tmp", "/run", "/dev/shm"];
|
|
3000
3185
|
const isTempPath = TEMP_DIRS.some(
|
|
3001
3186
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
3002
|
-
);
|
|
3187
|
+
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
3003
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;
|
|
3004
3189
|
if (!isTempPath && wildcardDeleteRe.test(scan)) {
|
|
3005
3190
|
return {
|
|
@@ -3009,7 +3194,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3009
3194
|
riskType: "script_execution",
|
|
3010
3195
|
confidence: 90,
|
|
3011
3196
|
source: "heuristic",
|
|
3012
|
-
violatedLayers: ["
|
|
3197
|
+
violatedLayers: ["L1.1"]
|
|
3013
3198
|
};
|
|
3014
3199
|
}
|
|
3015
3200
|
const findWriteMatch = actionDetail.match(
|
|
@@ -3024,10 +3209,10 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3024
3209
|
riskType: "script_execution",
|
|
3025
3210
|
confidence: 90,
|
|
3026
3211
|
source: "heuristic",
|
|
3027
|
-
violatedLayers: ["
|
|
3212
|
+
violatedLayers: ["L1.1"]
|
|
3028
3213
|
};
|
|
3029
3214
|
}
|
|
3030
|
-
if (/>\s
|
|
3215
|
+
if (/>\s*['"]?\/etc\//.test(actionDetail) || />\s*['"]?\/boot\//.test(actionDetail) || />\s*['"]?\/proc\//.test(actionDetail)) {
|
|
3031
3216
|
return {
|
|
3032
3217
|
allowed: false,
|
|
3033
3218
|
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
@@ -3035,7 +3220,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3035
3220
|
riskType: "file_access",
|
|
3036
3221
|
confidence: 95,
|
|
3037
3222
|
source: "heuristic",
|
|
3038
|
-
violatedLayers: ["
|
|
3223
|
+
violatedLayers: ["L1.1"]
|
|
3039
3224
|
};
|
|
3040
3225
|
}
|
|
3041
3226
|
if (/>\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i.test(actionDetail)) {
|
|
@@ -3046,7 +3231,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3046
3231
|
riskType: "file_access",
|
|
3047
3232
|
confidence: 90,
|
|
3048
3233
|
source: "heuristic",
|
|
3049
|
-
violatedLayers: ["
|
|
3234
|
+
violatedLayers: ["L1.1"]
|
|
3050
3235
|
};
|
|
3051
3236
|
}
|
|
3052
3237
|
return null;
|
|
@@ -3137,13 +3322,13 @@ function checkLateralMovement(actionDetail) {
|
|
|
3137
3322
|
riskType: "lateral_movement",
|
|
3138
3323
|
confidence: 80,
|
|
3139
3324
|
source: "heuristic",
|
|
3140
|
-
violatedLayers: ["
|
|
3325
|
+
violatedLayers: ["L1.1"]
|
|
3141
3326
|
};
|
|
3142
3327
|
}
|
|
3143
3328
|
}
|
|
3144
3329
|
return null;
|
|
3145
3330
|
}
|
|
3146
|
-
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;
|
|
3147
3332
|
var init_heuristic = __esm({
|
|
3148
3333
|
"src/engine/heuristic.ts"() {
|
|
3149
3334
|
"use strict";
|
|
@@ -3173,6 +3358,8 @@ var init_heuristic = __esm({
|
|
|
3173
3358
|
})();
|
|
3174
3359
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
3175
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;
|
|
3176
3363
|
}
|
|
3177
3364
|
});
|
|
3178
3365
|
|
|
@@ -4967,6 +5154,13 @@ function extractScriptPath2(command) {
|
|
|
4967
5154
|
return null;
|
|
4968
5155
|
}
|
|
4969
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
|
+
}
|
|
4970
5164
|
const scriptPath = extractScriptPath2(command);
|
|
4971
5165
|
if (!scriptPath) return { path: null, args: [] };
|
|
4972
5166
|
const idx = command.indexOf(scriptPath);
|
|
@@ -4980,11 +5174,59 @@ function extractScriptPathWithArgs(command) {
|
|
|
4980
5174
|
}
|
|
4981
5175
|
return { path: scriptPath, args };
|
|
4982
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
|
+
}
|
|
4983
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
|
+
}
|
|
4984
5226
|
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
4985
|
-
let
|
|
4986
|
-
while ((
|
|
4987
|
-
const p =
|
|
5227
|
+
let m2;
|
|
5228
|
+
while ((m2 = re.exec(actionDetail)) !== null) {
|
|
5229
|
+
const p = m2[1];
|
|
4988
5230
|
let resolved;
|
|
4989
5231
|
try {
|
|
4990
5232
|
resolved = resolvePath2(p);
|
|
@@ -5170,6 +5412,7 @@ var init_script_content_analyzer = __esm({
|
|
|
5170
5412
|
init_esm_shims();
|
|
5171
5413
|
init_heuristic();
|
|
5172
5414
|
init_logic_rules();
|
|
5415
|
+
init_inline_analyzer();
|
|
5173
5416
|
init_tracker();
|
|
5174
5417
|
SUSPICIOUS_PATTERNS2 = [
|
|
5175
5418
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -5634,7 +5877,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
5634
5877
|
riskType: riskType || "",
|
|
5635
5878
|
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
5636
5879
|
source: "llm",
|
|
5637
|
-
violatedLayers: ["
|
|
5880
|
+
violatedLayers: ["L3.1"]
|
|
5638
5881
|
};
|
|
5639
5882
|
} catch (err) {
|
|
5640
5883
|
if (abortController.signal.aborted) {
|
|
@@ -6220,13 +6463,6 @@ var init_brain = __esm({
|
|
|
6220
6463
|
});
|
|
6221
6464
|
|
|
6222
6465
|
// src/engine/formal-engine/decode.ts
|
|
6223
|
-
var decode_exports = {};
|
|
6224
|
-
__export(decode_exports, {
|
|
6225
|
-
allCandidates: () => allCandidates,
|
|
6226
|
-
decodeBase64Literals: () => decodeBase64Literals,
|
|
6227
|
-
decodeUrlAndNumeric: () => decodeUrlAndNumeric,
|
|
6228
|
-
matchStageAny: () => matchStageAny
|
|
6229
|
-
});
|
|
6230
6466
|
function isValidBase64Token(tok) {
|
|
6231
6467
|
if (tok.length < 8) return false;
|
|
6232
6468
|
if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
|
|
@@ -6259,6 +6495,13 @@ function decodeBase64Literals(text) {
|
|
|
6259
6495
|
}
|
|
6260
6496
|
return out;
|
|
6261
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
|
+
}
|
|
6262
6505
|
function decodeUrlAndNumeric(text) {
|
|
6263
6506
|
const out = [];
|
|
6264
6507
|
let s = text;
|
|
@@ -6292,7 +6535,10 @@ function decodeUrlAndNumeric(text) {
|
|
|
6292
6535
|
return out;
|
|
6293
6536
|
}
|
|
6294
6537
|
function allCandidates(detail, max = 8) {
|
|
6295
|
-
const
|
|
6538
|
+
const shell = stripStringLiterals(detail);
|
|
6539
|
+
const cands = [shell];
|
|
6540
|
+
cands.push(...decodeBase64Literals(shell));
|
|
6541
|
+
cands.push(...decodeUrlAndNumeric(shell));
|
|
6296
6542
|
cands.push(...decodeBase64Literals(detail));
|
|
6297
6543
|
cands.push(...decodeUrlAndNumeric(detail));
|
|
6298
6544
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6316,12 +6562,14 @@ function matchStageAny(detail, stages) {
|
|
|
6316
6562
|
}
|
|
6317
6563
|
return "";
|
|
6318
6564
|
}
|
|
6319
|
-
var B64_TOKEN_RE;
|
|
6565
|
+
var B64_TOKEN_RE, QUOTED_STR_RE, OUTPUT_CMDS;
|
|
6320
6566
|
var init_decode = __esm({
|
|
6321
6567
|
"src/engine/formal-engine/decode.ts"() {
|
|
6322
6568
|
"use strict";
|
|
6323
6569
|
init_esm_shims();
|
|
6324
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"]);
|
|
6325
6573
|
}
|
|
6326
6574
|
});
|
|
6327
6575
|
|
|
@@ -6357,11 +6605,7 @@ function compileAps(apPatterns) {
|
|
|
6357
6605
|
}
|
|
6358
6606
|
function labelEvent(actionDetail) {
|
|
6359
6607
|
if (!AP_RE) compileAps(loadMC().ap_patterns || {});
|
|
6360
|
-
|
|
6361
|
-
try {
|
|
6362
|
-
candidates = (init_decode(), __toCommonJS(decode_exports)).allCandidates(actionDetail);
|
|
6363
|
-
} catch {
|
|
6364
|
-
}
|
|
6608
|
+
const candidates = allCandidates(actionDetail);
|
|
6365
6609
|
const hits = /* @__PURE__ */ new Set();
|
|
6366
6610
|
for (const [name, pats] of Object.entries(AP_RE || {})) {
|
|
6367
6611
|
for (const c of candidates) {
|
|
@@ -6457,6 +6701,7 @@ var init_ltl = __esm({
|
|
|
6457
6701
|
init_esm_shims();
|
|
6458
6702
|
init_rules_loader();
|
|
6459
6703
|
init_ir();
|
|
6704
|
+
init_decode();
|
|
6460
6705
|
AP_RE = null;
|
|
6461
6706
|
}
|
|
6462
6707
|
});
|
|
@@ -6703,7 +6948,7 @@ function checkSensitiveDataFlow(graph) {
|
|
|
6703
6948
|
const n = nodes[i];
|
|
6704
6949
|
if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
|
|
6705
6950
|
violations.push({
|
|
6706
|
-
propertyId: "sensitive-data-
|
|
6951
|
+
propertyId: "sensitive-data-flow",
|
|
6707
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`,
|
|
6708
6953
|
riskLevel: "critical",
|
|
6709
6954
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6749,7 +6994,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6749
6994
|
if (pre) {
|
|
6750
6995
|
const post = nodes[firstEv];
|
|
6751
6996
|
return [{
|
|
6752
|
-
propertyId: "no-
|
|
6997
|
+
propertyId: "no-exfil-after-privesc",
|
|
6753
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)",
|
|
6754
6999
|
riskLevel: "critical",
|
|
6755
7000
|
path: [
|
|
@@ -6765,7 +7010,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6765
7010
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6766
7011
|
if (isExfilNode(nodes[j])) {
|
|
6767
7012
|
violations.push({
|
|
6768
|
-
propertyId: "no-
|
|
7013
|
+
propertyId: "no-exfil-after-privesc",
|
|
6769
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)",
|
|
6770
7015
|
riskLevel: "critical",
|
|
6771
7016
|
path: [
|
|
@@ -6798,7 +7043,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6798
7043
|
if (pre) {
|
|
6799
7044
|
const post = nodes[firstEv];
|
|
6800
7045
|
return [{
|
|
6801
|
-
propertyId: "no-
|
|
7046
|
+
propertyId: "no-exfil-after-credential",
|
|
6802
7047
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6803
7048
|
riskLevel: "critical",
|
|
6804
7049
|
path: [
|
|
@@ -6814,7 +7059,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6814
7059
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6815
7060
|
if (isExfilNode(nodes[j])) {
|
|
6816
7061
|
violations.push({
|
|
6817
|
-
propertyId: "no-
|
|
7062
|
+
propertyId: "no-exfil-after-credential",
|
|
6818
7063
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6819
7064
|
riskLevel: "critical",
|
|
6820
7065
|
path: [
|
|
@@ -6881,7 +7126,7 @@ function checkLowIntegrityCommandExec(graph) {
|
|
|
6881
7126
|
const n = nodes[i];
|
|
6882
7127
|
if (lowAt.has(i) && isComputeNode(n)) {
|
|
6883
7128
|
violations.push({
|
|
6884
|
-
propertyId: "low-integrity-
|
|
7129
|
+
propertyId: "low-integrity-command-exec",
|
|
6885
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`,
|
|
6886
7131
|
riskLevel: "critical",
|
|
6887
7132
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6927,7 +7172,7 @@ function checkCrossSessionReachability(graph) {
|
|
|
6927
7172
|
const srcIdx = Math.min(...sources);
|
|
6928
7173
|
const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
|
|
6929
7174
|
violations.push({
|
|
6930
|
-
propertyId: "
|
|
7175
|
+
propertyId: "low-integrity-reachability",
|
|
6931
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`,
|
|
6932
7177
|
riskLevel: "high",
|
|
6933
7178
|
path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
|
|
@@ -6999,17 +7244,17 @@ var init_properties = __esm({
|
|
|
6999
7244
|
STRONG_COMPUTE_TYPES = /* @__PURE__ */ new Set(["python", "node", "exec", "terminal", "cmd"]);
|
|
7000
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;
|
|
7001
7246
|
ALL_PROPERTIES = [
|
|
7002
|
-
{ id: "cross-session-chain", check: checkCrossSessionChain },
|
|
7003
|
-
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow },
|
|
7004
|
-
{ id: "
|
|
7005
|
-
{ id: "no-
|
|
7006
|
-
{ 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" },
|
|
7007
7252
|
// L2.2 模型检测(通用 LTL 求值器,mc_rules.json 单一事实源;witness 语义见 ltl.ts)
|
|
7008
|
-
{ id: "mc-ltl-rules", check: checkMCRules },
|
|
7009
|
-
//
|
|
7010
|
-
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec },
|
|
7011
|
-
//
|
|
7012
|
-
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation }
|
|
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" }
|
|
7013
7258
|
];
|
|
7014
7259
|
}
|
|
7015
7260
|
});
|
|
@@ -7082,6 +7327,32 @@ var init_engine = __esm({
|
|
|
7082
7327
|
});
|
|
7083
7328
|
|
|
7084
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
|
+
}
|
|
7085
7356
|
function loadRules() {
|
|
7086
7357
|
if (STAGES_PATTERNS !== null && CHAINS !== null) {
|
|
7087
7358
|
return { stages: STAGES_PATTERNS, chains: CHAINS };
|
|
@@ -7121,7 +7392,7 @@ function lifecycleFor(detail, stage, isError) {
|
|
|
7121
7392
|
if (detail.includes(">>") || detail.includes(">")) return "alive";
|
|
7122
7393
|
return "";
|
|
7123
7394
|
}
|
|
7124
|
-
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;
|
|
7125
7396
|
var init_stateful = __esm({
|
|
7126
7397
|
"src/engine/formal-engine/stateful.ts"() {
|
|
7127
7398
|
"use strict";
|
|
@@ -7131,11 +7402,20 @@ var init_stateful = __esm({
|
|
|
7131
7402
|
DESTROY_RE = /\b(?:pkill|kill)\b|systemctl\s+stop|service\s+stop|crontab\s+-r|rm\s+-[rf]?\s+/i;
|
|
7132
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;
|
|
7133
7404
|
CRED_FILE_RE2 = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
|
|
7134
|
-
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/;
|
|
7135
7411
|
STAGES_PATTERNS = null;
|
|
7136
7412
|
CHAINS = null;
|
|
7137
7413
|
MAX_NODES = 1e5;
|
|
7138
|
-
StatefulChainTracker = class {
|
|
7414
|
+
StatefulChainTracker = class _StatefulChainTracker {
|
|
7415
|
+
/** 本地接收时刻(实时路径无 called_at 时的兜底时间戳,ISO)。 */
|
|
7416
|
+
static now() {
|
|
7417
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7418
|
+
}
|
|
7139
7419
|
agentId;
|
|
7140
7420
|
nodes_ = [];
|
|
7141
7421
|
ptr = {};
|
|
@@ -7157,10 +7437,14 @@ var init_stateful = __esm({
|
|
|
7157
7437
|
for (const chain of chains) {
|
|
7158
7438
|
const stages = chain.stages || [];
|
|
7159
7439
|
if (stages.length === 0) continue;
|
|
7160
|
-
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
|
+
}
|
|
7161
7445
|
if (matched > 0 && matched < stages.length) {
|
|
7162
7446
|
const pf = {
|
|
7163
|
-
chainId:
|
|
7447
|
+
chainId: cid,
|
|
7164
7448
|
stages,
|
|
7165
7449
|
matched,
|
|
7166
7450
|
riskLevel: String(chain.riskLevel || chain.risk_level || "high"),
|
|
@@ -7236,8 +7520,10 @@ var init_stateful = __esm({
|
|
|
7236
7520
|
actionDetail: ad,
|
|
7237
7521
|
stage,
|
|
7238
7522
|
resources: extractResources2(ad),
|
|
7523
|
+
instances: extractInstances(ad, stage),
|
|
7239
7524
|
isError,
|
|
7240
|
-
lifecycle: lifecycleFor(ad, stage, isError)
|
|
7525
|
+
lifecycle: lifecycleFor(ad, stage, isError),
|
|
7526
|
+
timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
|
|
7241
7527
|
};
|
|
7242
7528
|
this.nodes_.push(node);
|
|
7243
7529
|
this.seqNext += 1;
|
|
@@ -7245,11 +7531,19 @@ var init_stateful = __esm({
|
|
|
7245
7531
|
}
|
|
7246
7532
|
recordLifecycle(node) {
|
|
7247
7533
|
if (node.lifecycle === "dead") {
|
|
7248
|
-
|
|
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;
|
|
7249
7543
|
for (const r of node.resources) {
|
|
7250
7544
|
if (r in this.aliveResources) {
|
|
7251
7545
|
delete this.aliveResources[r];
|
|
7252
|
-
|
|
7546
|
+
pathHit = true;
|
|
7253
7547
|
}
|
|
7254
7548
|
}
|
|
7255
7549
|
const detail = node.actionDetail;
|
|
@@ -7257,16 +7551,30 @@ var init_stateful = __esm({
|
|
|
7257
7551
|
const base = r.split("/").pop() || "";
|
|
7258
7552
|
if (base && detail.includes(base)) {
|
|
7259
7553
|
delete this.aliveResources[r];
|
|
7260
|
-
|
|
7554
|
+
pathHit = true;
|
|
7261
7555
|
}
|
|
7262
7556
|
}
|
|
7263
|
-
if (
|
|
7557
|
+
if (clearedInsts.length > 0 && !pathHit) {
|
|
7558
|
+
for (const inst of clearedInsts) this.resetChainsForInstance(inst);
|
|
7559
|
+
} else {
|
|
7560
|
+
this.resetChainsOnDestroy();
|
|
7561
|
+
}
|
|
7264
7562
|
return;
|
|
7265
7563
|
}
|
|
7266
7564
|
if (node.lifecycle === "alive") {
|
|
7267
7565
|
for (const r of node.resources) {
|
|
7268
7566
|
this.aliveResources[r] = node.seq;
|
|
7269
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;
|
|
7270
7578
|
}
|
|
7271
7579
|
}
|
|
7272
7580
|
resetChainsOnDestroy() {
|
|
@@ -7274,6 +7582,29 @@ var init_stateful = __esm({
|
|
|
7274
7582
|
this.ptr[cid] = 0;
|
|
7275
7583
|
}
|
|
7276
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
|
+
}
|
|
7277
7608
|
advanceChains(node) {
|
|
7278
7609
|
if (node.lifecycle === "dead") return null;
|
|
7279
7610
|
if (node.lifecycle === "never_activated") {
|
|
@@ -7287,23 +7618,32 @@ var init_stateful = __esm({
|
|
|
7287
7618
|
const stages = chain.stages || [];
|
|
7288
7619
|
if (stages.length === 0) continue;
|
|
7289
7620
|
const cid = String(chain.id || "");
|
|
7290
|
-
const
|
|
7291
|
-
|
|
7292
|
-
this.ptr[
|
|
7293
|
-
if (cur
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
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
|
+
}
|
|
7301
7635
|
}
|
|
7302
7636
|
}
|
|
7303
7637
|
}
|
|
7304
7638
|
return null;
|
|
7305
7639
|
}
|
|
7306
7640
|
invalidateChainOnFailure(node) {
|
|
7641
|
+
if (node.instances.length > 0) {
|
|
7642
|
+
for (const inst of node.instances) {
|
|
7643
|
+
this.resetChainsForInstance(inst);
|
|
7644
|
+
}
|
|
7645
|
+
return;
|
|
7646
|
+
}
|
|
7307
7647
|
const failed = new Set(node.resources);
|
|
7308
7648
|
if (failed.size === 0) return;
|
|
7309
7649
|
const { chains } = loadRules();
|
|
@@ -7319,7 +7659,7 @@ var init_stateful = __esm({
|
|
|
7319
7659
|
let want = stages[0];
|
|
7320
7660
|
for (const n of this.nodes_) {
|
|
7321
7661
|
if (n.stage === want) {
|
|
7322
|
-
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 });
|
|
7323
7663
|
const idx = stages.indexOf(n.stage);
|
|
7324
7664
|
if (idx + 1 < stages.length) {
|
|
7325
7665
|
want = stages[idx + 1];
|
|
@@ -7355,7 +7695,7 @@ var init_stateful = __esm({
|
|
|
7355
7695
|
if (!this.tainted.has(node.seq)) return [];
|
|
7356
7696
|
if (EXFIL_RE2.test(node.actionDetail)) {
|
|
7357
7697
|
return [{
|
|
7358
|
-
property_id: "sensitive-data-
|
|
7698
|
+
property_id: "sensitive-data-flow",
|
|
7359
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`,
|
|
7360
7700
|
risk_level: "critical",
|
|
7361
7701
|
path: [[node.sessionId, node.seq, node.actionDetail.slice(0, 60)]]
|
|
@@ -7397,8 +7737,12 @@ var init_stateful = __esm({
|
|
|
7397
7737
|
actionDetail: String(raw.actionDetail || ""),
|
|
7398
7738
|
stage: String(raw.stage || ""),
|
|
7399
7739
|
resources: [...raw.resources || []],
|
|
7740
|
+
instances: [...raw.instances || []],
|
|
7741
|
+
// 旧快照无此字段 → 空
|
|
7400
7742
|
isError: Boolean(raw.isError),
|
|
7401
|
-
lifecycle: String(raw.lifecycle || "")
|
|
7743
|
+
lifecycle: String(raw.lifecycle || ""),
|
|
7744
|
+
timestamp: typeof raw.timestamp === "string" ? raw.timestamp : null
|
|
7745
|
+
// 旧快照无此字段 → null
|
|
7402
7746
|
});
|
|
7403
7747
|
}
|
|
7404
7748
|
this.seqNext = Number(snapshot.seq_next || this.nodes_.length);
|
|
@@ -7591,7 +7935,7 @@ async function runAnalysis(input) {
|
|
|
7591
7935
|
riskType: node.l1RiskType,
|
|
7592
7936
|
confidence: 90,
|
|
7593
7937
|
source: "heuristic_script_scan",
|
|
7594
|
-
violatedLayers: ["
|
|
7938
|
+
violatedLayers: ["L1.2"]
|
|
7595
7939
|
};
|
|
7596
7940
|
}
|
|
7597
7941
|
}
|
|
@@ -7709,7 +8053,7 @@ async function runAnalysis(input) {
|
|
|
7709
8053
|
riskType: inlineJudgment.riskType,
|
|
7710
8054
|
confidence: 90,
|
|
7711
8055
|
source: "logic_rule",
|
|
7712
|
-
violatedLayers: ["1
|
|
8056
|
+
violatedLayers: ["L2.1"]
|
|
7713
8057
|
};
|
|
7714
8058
|
}
|
|
7715
8059
|
return {
|
|
@@ -7742,7 +8086,7 @@ async function runAnalysis(input) {
|
|
|
7742
8086
|
console.warn(
|
|
7743
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)}`
|
|
7744
8088
|
);
|
|
7745
|
-
observedFormalLayers.push("2
|
|
8089
|
+
observedFormalLayers.push("L2.2");
|
|
7746
8090
|
} else {
|
|
7747
8091
|
return {
|
|
7748
8092
|
allowed: false,
|
|
@@ -7751,7 +8095,7 @@ async function runAnalysis(input) {
|
|
|
7751
8095
|
riskType: "formal_verification",
|
|
7752
8096
|
confidence: 90,
|
|
7753
8097
|
source: "formal_engine",
|
|
7754
|
-
violatedLayers: ["2
|
|
8098
|
+
violatedLayers: ["L2.2"]
|
|
7755
8099
|
};
|
|
7756
8100
|
}
|
|
7757
8101
|
}
|
|
@@ -7768,7 +8112,7 @@ async function runAnalysis(input) {
|
|
|
7768
8112
|
riskType: node.l2RiskType,
|
|
7769
8113
|
confidence: 85,
|
|
7770
8114
|
source: "logic_rule_script_scan",
|
|
7771
|
-
violatedLayers: ["
|
|
8115
|
+
violatedLayers: ["L2.3"]
|
|
7772
8116
|
};
|
|
7773
8117
|
}
|
|
7774
8118
|
}
|
|
@@ -7855,7 +8199,7 @@ async function runAnalysis(input) {
|
|
|
7855
8199
|
riskType: "",
|
|
7856
8200
|
confidence: 60,
|
|
7857
8201
|
source: "llm_failure_fallback",
|
|
7858
|
-
violatedLayers: ["
|
|
8202
|
+
violatedLayers: ["L3.1"]
|
|
7859
8203
|
};
|
|
7860
8204
|
}
|
|
7861
8205
|
if (!llmResult.allowed && llmResult.confidence && llmResult.confidence >= 90 && _brainExtractor) {
|
|
@@ -8797,7 +9141,7 @@ var AnalyzeRequestSchema = z.object({
|
|
|
8797
9141
|
session_history: z.array(z.object({
|
|
8798
9142
|
session_id: z.string(),
|
|
8799
9143
|
action_history: z.array(ActionItemSchema).default([])
|
|
8800
|
-
})).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)."),
|
|
8801
9145
|
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
8802
9146
|
reason: z.string().default("").describe("Reason for executing this action"),
|
|
8803
9147
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
@@ -8814,7 +9158,9 @@ var AnalyzeResponseSchema = z.object({
|
|
|
8814
9158
|
policy: z.string().default(""),
|
|
8815
9159
|
violated_policy: z.string().default(""),
|
|
8816
9160
|
confidence: z.number().int().min(0).max(100).default(100),
|
|
8817
|
-
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([])
|
|
8818
9164
|
});
|
|
8819
9165
|
var HealthResponseSchema = z.object({
|
|
8820
9166
|
status: z.string().default("healthy"),
|
|
@@ -9198,7 +9544,7 @@ async function analyzeRoute(c) {
|
|
|
9198
9544
|
const judgment = incMatchedToDeny(inc);
|
|
9199
9545
|
if (judgment) {
|
|
9200
9546
|
const layers = [...result.violatedLayers];
|
|
9201
|
-
if (!layers.includes("2
|
|
9547
|
+
if (!layers.includes("L2.2-stateful")) layers.push("L2.2-stateful");
|
|
9202
9548
|
console.error(
|
|
9203
9549
|
`[AgentMoss] stateful \u589E\u91CF\u62E6\u622A: agent=${agentId || "-"} property=${judgment.property_id} risk=${judgment.risk_level}`
|
|
9204
9550
|
);
|
|
@@ -9212,7 +9558,8 @@ async function analyzeRoute(c) {
|
|
|
9212
9558
|
policy: "",
|
|
9213
9559
|
violated_policy: "",
|
|
9214
9560
|
confidence: 90,
|
|
9215
|
-
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
|
|
9216
9563
|
})
|
|
9217
9564
|
);
|
|
9218
9565
|
}
|
|
@@ -9251,17 +9598,20 @@ function getChainStateStoreFor(_agentId) {
|
|
|
9251
9598
|
}
|
|
9252
9599
|
function incMatchedToDeny(inc) {
|
|
9253
9600
|
if (inc.matched) {
|
|
9601
|
+
const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp]);
|
|
9254
9602
|
return {
|
|
9255
9603
|
property_id: "cross-" + inc.matched.chainId,
|
|
9256
9604
|
reason: inc.matched.reason,
|
|
9257
|
-
risk_level: inc.matched.riskLevel
|
|
9605
|
+
risk_level: inc.matched.riskLevel,
|
|
9606
|
+
violation_chain: chain
|
|
9258
9607
|
};
|
|
9259
9608
|
}
|
|
9260
9609
|
if (inc.exfil && inc.exfil.length > 0) {
|
|
9261
9610
|
return {
|
|
9262
|
-
property_id: "sensitive-data-
|
|
9611
|
+
property_id: "sensitive-data-flow",
|
|
9263
9612
|
reason: inc.exfil[0].reason,
|
|
9264
|
-
risk_level: inc.exfil[0].risk_level
|
|
9613
|
+
risk_level: inc.exfil[0].risk_level,
|
|
9614
|
+
violation_chain: []
|
|
9265
9615
|
};
|
|
9266
9616
|
}
|
|
9267
9617
|
return null;
|
|
@@ -9387,7 +9737,7 @@ function createConsoleRouter() {
|
|
|
9387
9737
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
9388
9738
|
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
9389
9739
|
});
|
|
9390
|
-
function
|
|
9740
|
+
function getL22View() {
|
|
9391
9741
|
const ltlHuman = {
|
|
9392
9742
|
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
9393
9743
|
"mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
|
|
@@ -9418,9 +9768,9 @@ function createConsoleRouter() {
|
|
|
9418
9768
|
}))
|
|
9419
9769
|
};
|
|
9420
9770
|
}
|
|
9421
|
-
function
|
|
9422
|
-
if (rc && rc.
|
|
9423
|
-
const cat = rc.
|
|
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;
|
|
9424
9774
|
const rls = cat && cat.rules || [];
|
|
9425
9775
|
const ltlHumanLocal = {
|
|
9426
9776
|
"mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
|
|
@@ -9435,7 +9785,7 @@ function createConsoleRouter() {
|
|
|
9435
9785
|
"mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
|
|
9436
9786
|
"mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
|
|
9437
9787
|
};
|
|
9438
|
-
rc.
|
|
9788
|
+
rc.L2_2 = {
|
|
9439
9789
|
enabled_via: "L2_logic_rules",
|
|
9440
9790
|
description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
|
|
9441
9791
|
ltl_rules: rls.map((r) => ({
|
|
@@ -9454,22 +9804,22 @@ function createConsoleRouter() {
|
|
|
9454
9804
|
}
|
|
9455
9805
|
console2.get("/api/rules", (c) => {
|
|
9456
9806
|
const rc = loadRuntimeConfig();
|
|
9457
|
-
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() });
|
|
9458
9808
|
});
|
|
9459
9809
|
console2.put("/api/rules/enabled", async (c) => {
|
|
9460
9810
|
const b = await c.req.json();
|
|
9461
9811
|
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
9462
|
-
return c.json(
|
|
9812
|
+
return c.json(withL22View(rc));
|
|
9463
9813
|
});
|
|
9464
9814
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
9465
9815
|
const b = await c.req.json();
|
|
9466
9816
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
9467
|
-
return c.json(
|
|
9817
|
+
return c.json(withL22View(rc));
|
|
9468
9818
|
});
|
|
9469
9819
|
console2.put("/api/rules/skip_l3", async (c) => {
|
|
9470
9820
|
const b = await c.req.json();
|
|
9471
9821
|
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
9472
|
-
return c.json(
|
|
9822
|
+
return c.json(withL22View(rc));
|
|
9473
9823
|
});
|
|
9474
9824
|
console2.put("/api/categories/enabled", async (c) => {
|
|
9475
9825
|
const b = await c.req.json();
|
|
@@ -9619,7 +9969,7 @@ function createConsoleRouter() {
|
|
|
9619
9969
|
});
|
|
9620
9970
|
console2.get("/api/config", (c) => {
|
|
9621
9971
|
const rc = loadRuntimeConfig();
|
|
9622
|
-
return c.json({ ...rc,
|
|
9972
|
+
return c.json({ ...rc, L2_2: getL22View() });
|
|
9623
9973
|
});
|
|
9624
9974
|
console2.get("/api/env-overrides", (c) => {
|
|
9625
9975
|
return c.json({ overrides: getEnvOverrides() });
|
|
@@ -9959,4 +10309,14 @@ if (isMain) {
|
|
|
9959
10309
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
9960
10310
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
9961
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 敏感数据聚合外传
|
|
9962
10322
|
//# sourceMappingURL=cli.js.map
|