@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
|
@@ -628,7 +628,7 @@ 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 rtRules = rt.loadRuntimeConfig()?.
|
|
631
|
+
const rtRules = rt.loadRuntimeConfig()?.L2_2?.ltl_rules?.rules || [];
|
|
632
632
|
const enabledById = {};
|
|
633
633
|
for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
|
|
634
634
|
for (const r of rules) if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
|
|
@@ -676,7 +676,8 @@ function _loadSensitivePathScope(scope) {
|
|
|
676
676
|
path: sp.path,
|
|
677
677
|
riskLevel: sp.risk_level,
|
|
678
678
|
desc: sp.desc,
|
|
679
|
-
credential: sp.deny_mode === "deny_both" ? true : void 0
|
|
679
|
+
credential: sp.deny_mode === "deny_both" ? true : void 0,
|
|
680
|
+
dirDelOnly: sp.dir_del_only === true ? true : void 0
|
|
680
681
|
}));
|
|
681
682
|
}
|
|
682
683
|
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS) {
|
|
@@ -774,7 +775,7 @@ function runLogicCheck(input) {
|
|
|
774
775
|
riskType: "intent_deviation",
|
|
775
776
|
confidence: 85,
|
|
776
777
|
source: "logic_rule",
|
|
777
|
-
violatedLayers: ["
|
|
778
|
+
violatedLayers: ["L2.1"]
|
|
778
779
|
};
|
|
779
780
|
}
|
|
780
781
|
}
|
|
@@ -798,7 +799,7 @@ function runLogicCheck(input) {
|
|
|
798
799
|
riskType: "privilege_escalation",
|
|
799
800
|
confidence: 85,
|
|
800
801
|
source: "logic_rule",
|
|
801
|
-
violatedLayers: ["
|
|
802
|
+
violatedLayers: ["L2.1"]
|
|
802
803
|
};
|
|
803
804
|
}
|
|
804
805
|
}
|
|
@@ -850,7 +851,7 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
850
851
|
riskType: "combo_chain",
|
|
851
852
|
confidence: 85,
|
|
852
853
|
source: "logic_rule",
|
|
853
|
-
violatedLayers: ["
|
|
854
|
+
violatedLayers: ["L2.1"]
|
|
854
855
|
};
|
|
855
856
|
}
|
|
856
857
|
}
|
|
@@ -975,7 +976,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
975
976
|
riskType: "read_before_write",
|
|
976
977
|
confidence: 85,
|
|
977
978
|
source: "logic_rule",
|
|
978
|
-
violatedLayers: ["
|
|
979
|
+
violatedLayers: ["L2.1"]
|
|
979
980
|
};
|
|
980
981
|
}
|
|
981
982
|
return null;
|
|
@@ -995,7 +996,7 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
995
996
|
riskType: "consent_missing",
|
|
996
997
|
confidence: 85,
|
|
997
998
|
source: "logic_rule",
|
|
998
|
-
violatedLayers: ["
|
|
999
|
+
violatedLayers: ["L2.1"]
|
|
999
1000
|
};
|
|
1000
1001
|
}
|
|
1001
1002
|
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
@@ -1013,7 +1014,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
|
1013
1014
|
riskType: "consent_missing",
|
|
1014
1015
|
confidence: 85,
|
|
1015
1016
|
source: "logic_rule",
|
|
1016
|
-
violatedLayers: ["
|
|
1017
|
+
violatedLayers: ["L2.1"]
|
|
1017
1018
|
};
|
|
1018
1019
|
}
|
|
1019
1020
|
function containsPrivilegeEscalation(actionDetail) {
|
|
@@ -1073,7 +1074,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1073
1074
|
riskType: "file_access",
|
|
1074
1075
|
confidence: 95,
|
|
1075
1076
|
source: "logic_rule",
|
|
1076
|
-
violatedLayers: ["
|
|
1077
|
+
violatedLayers: ["L2.1"],
|
|
1077
1078
|
isCredential: true
|
|
1078
1079
|
};
|
|
1079
1080
|
}
|
|
@@ -1089,7 +1090,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1089
1090
|
riskType: "file_access",
|
|
1090
1091
|
confidence: 95,
|
|
1091
1092
|
source: "logic_rule",
|
|
1092
|
-
violatedLayers: ["
|
|
1093
|
+
violatedLayers: ["L2.1"]
|
|
1093
1094
|
};
|
|
1094
1095
|
}
|
|
1095
1096
|
}
|
|
@@ -1103,7 +1104,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1103
1104
|
riskType: "file_access",
|
|
1104
1105
|
confidence: 70,
|
|
1105
1106
|
source: "logic_rule",
|
|
1106
|
-
violatedLayers: ["
|
|
1107
|
+
violatedLayers: ["L2.1"]
|
|
1107
1108
|
};
|
|
1108
1109
|
}
|
|
1109
1110
|
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 +1116,7 @@ var init_logic_rules = __esm({
|
|
|
1115
1116
|
init_rules_loader();
|
|
1116
1117
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1117
1118
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1118
|
-
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc }) => ({ path: path5, riskLevel, desc, credential: true }));
|
|
1119
|
+
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path5, riskLevel, desc, dirDelOnly }) => ({ path: path5, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1119
1120
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1120
1121
|
"ask_user_question",
|
|
1121
1122
|
"glob",
|
|
@@ -1466,7 +1467,7 @@ function emptyRuntimeConfig() {
|
|
|
1466
1467
|
L1_rules: {},
|
|
1467
1468
|
L2_rules: {},
|
|
1468
1469
|
L3_skills: {},
|
|
1469
|
-
|
|
1470
|
+
L2_2: {},
|
|
1470
1471
|
agent_rules: {},
|
|
1471
1472
|
scenario_rules: {}
|
|
1472
1473
|
};
|
|
@@ -1494,7 +1495,7 @@ function loadRuntimeConfig() {
|
|
|
1494
1495
|
L1_rules: merged.L1_rules || {},
|
|
1495
1496
|
L2_rules: merged.L2_rules || {},
|
|
1496
1497
|
L3_skills: merged.L3_skills || {},
|
|
1497
|
-
|
|
1498
|
+
L2_2: merged.L2_2 || {},
|
|
1498
1499
|
agent_rules: merged.agent_rules || {},
|
|
1499
1500
|
scenario_rules: merged.scenario_rules || {}
|
|
1500
1501
|
};
|
|
@@ -1547,6 +1548,7 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1547
1548
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1548
1549
|
if (r.credential) out.credential = true;
|
|
1549
1550
|
if (r.read_only) out.read_only = true;
|
|
1551
|
+
if (r.dir_del_only) out.dir_del_only = true;
|
|
1550
1552
|
return out;
|
|
1551
1553
|
});
|
|
1552
1554
|
}
|
|
@@ -1655,6 +1657,7 @@ function _l2SensitivePathsToRules() {
|
|
|
1655
1657
|
builtin: true
|
|
1656
1658
|
};
|
|
1657
1659
|
_syncDenyModeFields(rule);
|
|
1660
|
+
if (sp.dirDelOnly) rule.dir_del_only = true;
|
|
1658
1661
|
return rule;
|
|
1659
1662
|
});
|
|
1660
1663
|
}
|
|
@@ -1871,7 +1874,7 @@ function generateSourceDefaults() {
|
|
|
1871
1874
|
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
|
|
1872
1875
|
},
|
|
1873
1876
|
L3_skills: _l3SkillsToRules(),
|
|
1874
|
-
|
|
1877
|
+
L2_2: _ltlRulesToRuntime(),
|
|
1875
1878
|
agent_rules: _agentRulesToDefaults(),
|
|
1876
1879
|
scenario_rules: _scenarioRulesToDefaults()
|
|
1877
1880
|
};
|
|
@@ -2059,7 +2062,11 @@ function mergeConfig(userCfg, srcDefaults) {
|
|
|
2059
2062
|
);
|
|
2060
2063
|
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
2061
2064
|
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2062
|
-
|
|
2065
|
+
if (!cfg.L2_2 && cfg.L2_4) {
|
|
2066
|
+
cfg.L2_2 = cfg.L2_4;
|
|
2067
|
+
delete cfg.L2_4;
|
|
2068
|
+
}
|
|
2069
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_2");
|
|
2063
2070
|
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2064
2071
|
_mergeAgentRules(cfg, srcDefaults);
|
|
2065
2072
|
_mergeAgentRules(cfg, srcDefaults, "scenario_rules");
|
|
@@ -2679,7 +2686,7 @@ function runHeuristicCheck(input) {
|
|
|
2679
2686
|
riskType: "sensitive_action",
|
|
2680
2687
|
confidence: 90,
|
|
2681
2688
|
source: "heuristic",
|
|
2682
|
-
violatedLayers: ["
|
|
2689
|
+
violatedLayers: ["L1.1"]
|
|
2683
2690
|
};
|
|
2684
2691
|
}
|
|
2685
2692
|
}
|
|
@@ -2689,8 +2696,10 @@ function runHeuristicCheck(input) {
|
|
|
2689
2696
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2690
2697
|
const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
|
|
2691
2698
|
let topRiskType = "";
|
|
2699
|
+
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2692
2700
|
for (const rule of DANGEROUS_PATTERNS) {
|
|
2693
2701
|
if (rule.pattern.test(scanCommand)) {
|
|
2702
|
+
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2694
2703
|
matched.push(rule.description);
|
|
2695
2704
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
2696
2705
|
highestSeverity = rule.severity;
|
|
@@ -2732,10 +2741,26 @@ function runHeuristicCheck(input) {
|
|
|
2732
2741
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2733
2742
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2734
2743
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2744
|
+
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
2745
|
+
const homeResult = checkHomeDeletion(actionTypeL, detailL);
|
|
2746
|
+
const mergeResults = [dangerousResult, pathResult, homeResult].filter(Boolean);
|
|
2747
|
+
if (mergeResults.length > 0) {
|
|
2748
|
+
let best = mergeResults[0];
|
|
2749
|
+
for (const r of mergeResults) {
|
|
2750
|
+
if (severityRank(r.riskLevel) > severityRank(best.riskLevel)) best = r;
|
|
2751
|
+
}
|
|
2752
|
+
if (severityRank(highestSeverity) > severityRank(best.riskLevel)) {
|
|
2753
|
+
return {
|
|
2754
|
+
allowed: false,
|
|
2755
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
2756
|
+
riskLevel: highestSeverity,
|
|
2757
|
+
riskType: highestRiskType,
|
|
2758
|
+
confidence: 95,
|
|
2759
|
+
source: "heuristic",
|
|
2760
|
+
violatedLayers: ["L1.1"]
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
return best;
|
|
2739
2764
|
}
|
|
2740
2765
|
if (matched.length > 0) {
|
|
2741
2766
|
return {
|
|
@@ -2745,8 +2770,7 @@ function runHeuristicCheck(input) {
|
|
|
2745
2770
|
riskType: highestRiskType,
|
|
2746
2771
|
confidence: 95,
|
|
2747
2772
|
source: "heuristic",
|
|
2748
|
-
violatedLayers: ["
|
|
2749
|
-
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
2773
|
+
violatedLayers: ["L1.1"]
|
|
2750
2774
|
};
|
|
2751
2775
|
}
|
|
2752
2776
|
const lateralResult = checkLateralMovement(detailL);
|
|
@@ -2874,6 +2898,7 @@ function isSearchActionType(actionType) {
|
|
|
2874
2898
|
function checkSensitivePath(command, cwd, agentExt) {
|
|
2875
2899
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2876
2900
|
const isWriteOpFlag = isWriteOperation("", command);
|
|
2901
|
+
const keygenGenerate = isSshKeygenGenerate(command);
|
|
2877
2902
|
const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
|
|
2878
2903
|
const pathMatches = (text, path5) => {
|
|
2879
2904
|
const parts = path5.split(/(\(\?!.*?\))/);
|
|
@@ -2886,6 +2911,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2886
2911
|
};
|
|
2887
2912
|
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
2888
2913
|
if (pathMatches(pathScanText, sp.path.toLowerCase())) {
|
|
2914
|
+
if (keygenGenerate) continue;
|
|
2889
2915
|
if (sp.path === "/dev/random") {
|
|
2890
2916
|
if (isWriteOpFlag) {
|
|
2891
2917
|
continue;
|
|
@@ -2897,7 +2923,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2897
2923
|
riskType: "file_access",
|
|
2898
2924
|
confidence: 90,
|
|
2899
2925
|
source: "heuristic",
|
|
2900
|
-
violatedLayers: ["
|
|
2926
|
+
violatedLayers: ["L1.1"]
|
|
2901
2927
|
};
|
|
2902
2928
|
}
|
|
2903
2929
|
if (!isWriteOpFlag && !sp.credential) {
|
|
@@ -2910,16 +2936,51 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2910
2936
|
riskType: "file_access",
|
|
2911
2937
|
confidence: 90,
|
|
2912
2938
|
source: "heuristic",
|
|
2913
|
-
violatedLayers: ["
|
|
2939
|
+
violatedLayers: ["L1.1"],
|
|
2914
2940
|
isCredential: !!sp.credential
|
|
2915
2941
|
};
|
|
2916
2942
|
}
|
|
2917
2943
|
}
|
|
2918
2944
|
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
2919
2945
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2946
|
+
let pPath = sp.path;
|
|
2947
|
+
if (!sp.path.endsWith(".pub")) pPath = `${sp.path}(?!\\.pub)`;
|
|
2948
|
+
let dirPrefixMatch = null;
|
|
2949
|
+
if (sp.dirDelOnly) {
|
|
2950
|
+
const dirRe = (t) => new RegExp(`(?:^|[\\s/\\\\'"])${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/|[\\s'"\\\\]|$)`, "i");
|
|
2951
|
+
dirPrefixMatch = dirRe(sp.path).test(pathScanText) || dirRe(expanded.toLowerCase()).test(pathScanText);
|
|
2952
|
+
}
|
|
2953
|
+
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch : pathMatches(pathScanText, pPath);
|
|
2954
|
+
const expandedReMatch = sp.dirDelOnly ? false : pathMatches(pathScanText, expanded + (sp.path.endsWith(".pub") ? "" : "(?!\\.pub)"));
|
|
2955
|
+
if (pathReMatch || expandedReMatch) {
|
|
2956
|
+
if (keygenGenerate) continue;
|
|
2957
|
+
if (sp.dirDelOnly) {
|
|
2958
|
+
if (isDeleteActionForDir(command, sp.path)) {
|
|
2959
|
+
return {
|
|
2960
|
+
allowed: false,
|
|
2961
|
+
reason: `\u5220\u9664\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2962
|
+
riskLevel: sp.riskLevel,
|
|
2963
|
+
riskType: "file_access",
|
|
2964
|
+
confidence: 90,
|
|
2965
|
+
source: "heuristic",
|
|
2966
|
+
violatedLayers: ["L1.1"],
|
|
2967
|
+
isCredential: !!sp.credential
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2970
|
+
if (isArchiveReadOfDir(command, sp.path)) {
|
|
2971
|
+
return {
|
|
2972
|
+
allowed: false,
|
|
2973
|
+
reason: `\u5F52\u6863/\u590D\u5236\u8BFB\u53D6\u654F\u611F\u76EE\u5F55: ${sp.desc} (${sp.path})`,
|
|
2974
|
+
riskLevel: sp.riskLevel,
|
|
2975
|
+
riskType: "file_access",
|
|
2976
|
+
confidence: 90,
|
|
2977
|
+
source: "heuristic",
|
|
2978
|
+
violatedLayers: ["L1.1"],
|
|
2979
|
+
isCredential: !!sp.credential
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
continue;
|
|
2983
|
+
}
|
|
2923
2984
|
if (!isWriteOpFlag && !sp.credential) {
|
|
2924
2985
|
continue;
|
|
2925
2986
|
}
|
|
@@ -2930,7 +2991,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2930
2991
|
riskType: "file_access",
|
|
2931
2992
|
confidence: 90,
|
|
2932
2993
|
source: "heuristic",
|
|
2933
|
-
violatedLayers: ["
|
|
2994
|
+
violatedLayers: ["L1.1"],
|
|
2934
2995
|
isCredential: !!sp.credential
|
|
2935
2996
|
};
|
|
2936
2997
|
}
|
|
@@ -2953,7 +3014,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2953
3014
|
riskType: "file_access",
|
|
2954
3015
|
confidence: 90,
|
|
2955
3016
|
source: "heuristic",
|
|
2956
|
-
violatedLayers: ["
|
|
3017
|
+
violatedLayers: ["L1.1"],
|
|
2957
3018
|
isCredential: !!sp.credential
|
|
2958
3019
|
};
|
|
2959
3020
|
}
|
|
@@ -2970,7 +3031,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2970
3031
|
riskType: "file_access",
|
|
2971
3032
|
confidence: 90,
|
|
2972
3033
|
source: "heuristic",
|
|
2973
|
-
violatedLayers: ["
|
|
3034
|
+
violatedLayers: ["L1.1"],
|
|
2974
3035
|
isCredential: true
|
|
2975
3036
|
};
|
|
2976
3037
|
}
|
|
@@ -2983,18 +3044,142 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
2983
3044
|
riskType: "file_access",
|
|
2984
3045
|
confidence: 90,
|
|
2985
3046
|
source: "heuristic",
|
|
2986
|
-
violatedLayers: ["
|
|
3047
|
+
violatedLayers: ["L1.1"]
|
|
2987
3048
|
};
|
|
2988
3049
|
}
|
|
2989
3050
|
return null;
|
|
2990
3051
|
}
|
|
3052
|
+
function isHomeValue(path5) {
|
|
3053
|
+
let p = path5.trim().toLowerCase();
|
|
3054
|
+
if (p.startsWith("$(") && p.endsWith(")")) {
|
|
3055
|
+
const inner = p.slice(2, -1).trim();
|
|
3056
|
+
return HOME_TARGET_RE.test(" " + inner);
|
|
3057
|
+
}
|
|
3058
|
+
if (/^\/home\/[^/\s]+\/?$/.test(p)) return true;
|
|
3059
|
+
return HOME_TARGET_RE.test(" " + p);
|
|
3060
|
+
}
|
|
3061
|
+
function isDeleteCommand(seg) {
|
|
3062
|
+
const s = seg.trimStart();
|
|
3063
|
+
if (/^(?:rm|rmdir|unlink)\b/i.test(s)) return true;
|
|
3064
|
+
return /删除|移除/.test(s);
|
|
3065
|
+
}
|
|
3066
|
+
function homeEscapeTarget(seg) {
|
|
3067
|
+
const low = seg.toLowerCase();
|
|
3068
|
+
const re = /(?:~|(?:\$home|\$\{home\})|\/home\/[a-z0-9_.-]+)/g;
|
|
3069
|
+
let m;
|
|
3070
|
+
while ((m = re.exec(low)) !== null) {
|
|
3071
|
+
const endIdx = m.index + m[0].length;
|
|
3072
|
+
const tail = low.slice(endIdx);
|
|
3073
|
+
if (!tail) continue;
|
|
3074
|
+
let token = tail.trimStart().split(/\s+/)[0] || "";
|
|
3075
|
+
token = token.split("&&")[0].split("||")[0].split(";")[0].replace(/\/+$/, "");
|
|
3076
|
+
token = token.replace(/^['"]|['"]$/g, "");
|
|
3077
|
+
const segments = token.split("/").filter((s) => s && s !== ".");
|
|
3078
|
+
if (!segments.some((s) => s === "..")) continue;
|
|
3079
|
+
let depth = 0;
|
|
3080
|
+
let minDepth = 0;
|
|
3081
|
+
for (const s of segments) {
|
|
3082
|
+
depth += s === ".." ? -1 : 1;
|
|
3083
|
+
minDepth = Math.min(minDepth, depth);
|
|
3084
|
+
}
|
|
3085
|
+
if (minDepth < 0) return true;
|
|
3086
|
+
if (depth <= 0) return true;
|
|
3087
|
+
}
|
|
3088
|
+
return false;
|
|
3089
|
+
}
|
|
3090
|
+
function checkHomeDeletion(actionType, actionDetail) {
|
|
3091
|
+
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") return null;
|
|
3092
|
+
const segs = actionDetail.split(/&&|\|\||;|\n/).map((s) => s.trim()).filter(Boolean);
|
|
3093
|
+
for (const seg of segs) {
|
|
3094
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3095
|
+
if (HOME_TARGET_RE.test(seg) || homeEscapeTarget(seg)) {
|
|
3096
|
+
return {
|
|
3097
|
+
allowed: false,
|
|
3098
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u4E3B\u76EE\u5F55 (~/$HOME) \u64CD\u4F5C",
|
|
3099
|
+
riskLevel: "high",
|
|
3100
|
+
riskType: "script_execution",
|
|
3101
|
+
confidence: 90,
|
|
3102
|
+
source: "heuristic",
|
|
3103
|
+
violatedLayers: ["L1.1"]
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
const assigned = {};
|
|
3108
|
+
for (const m of actionDetail.matchAll(HOME_ASSIGN_RE)) assigned[m[1]] = m[2];
|
|
3109
|
+
for (const [varName, path5] of Object.entries(assigned)) {
|
|
3110
|
+
if (!isHomeValue(path5)) continue;
|
|
3111
|
+
const varRefRe = new RegExp(`(?:${escapeRegExp(varName)}|\\$\\{${escapeRegExp(varName)}\\})`);
|
|
3112
|
+
for (const seg of segs) {
|
|
3113
|
+
if (!isDeleteCommand(seg)) continue;
|
|
3114
|
+
if (varRefRe.test(seg)) {
|
|
3115
|
+
return {
|
|
3116
|
+
allowed: false,
|
|
3117
|
+
reason: `\u68C0\u6D4B\u5230\u901A\u8FC7\u53D8\u91CF ${varName}\uFF08=${path5}\uFF0C\u4E3B\u76EE\u5F55\uFF09\u6267\u884C\u5220\u9664`,
|
|
3118
|
+
riskLevel: "high",
|
|
3119
|
+
riskType: "script_execution",
|
|
3120
|
+
confidence: 90,
|
|
3121
|
+
source: "heuristic",
|
|
3122
|
+
violatedLayers: ["L1.1"]
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
return null;
|
|
3128
|
+
}
|
|
3129
|
+
function escapeRegExp(s) {
|
|
3130
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3131
|
+
}
|
|
3132
|
+
function isDeleteActionForDir(command, dirPath) {
|
|
3133
|
+
const low = command.toLowerCase();
|
|
3134
|
+
const dpath = dirPath.toLowerCase();
|
|
3135
|
+
if (new RegExp(`\\b(?:rm|unlink|rmdir)\\b[^&|;]*?${escapeRegExp(dpath)}`, "i").test(low)) {
|
|
3136
|
+
return true;
|
|
3137
|
+
}
|
|
3138
|
+
if (new RegExp(`(?:\u5220\u9664|\u79FB\u9664)[^\\n;|&]*?${escapeRegExp(dpath)}`).test(low)) {
|
|
3139
|
+
return true;
|
|
3140
|
+
}
|
|
3141
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3142
|
+
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3143
|
+
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") && low.includes(dpath)) {
|
|
3144
|
+
return true;
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
return false;
|
|
3148
|
+
}
|
|
3149
|
+
function isArchiveReadOfDir(command, dirPath) {
|
|
3150
|
+
const low = command.toLowerCase();
|
|
3151
|
+
const dpath = dirPath.toLowerCase();
|
|
3152
|
+
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3153
|
+
const s = seg.trim();
|
|
3154
|
+
if (!s || !s.includes(dpath)) continue;
|
|
3155
|
+
const parts = s.split(/\s+/);
|
|
3156
|
+
const fw = parts[0] || "";
|
|
3157
|
+
if (fw === "zip") return true;
|
|
3158
|
+
if (fw === "tar") {
|
|
3159
|
+
if (/(?:^|\s)-[a-zA-Z]*x|--extract/.test(s)) continue;
|
|
3160
|
+
return true;
|
|
3161
|
+
}
|
|
3162
|
+
if (fw === "cp" || fw === "rsync") {
|
|
3163
|
+
const segPaths = s.match(/[/~][^\s;|&><'"]*/g) || [];
|
|
3164
|
+
if (segPaths.length && segPaths[0].includes(dpath)) return true;
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
return false;
|
|
3168
|
+
}
|
|
3169
|
+
function isSshKeygenGenerate(command) {
|
|
3170
|
+
const low = command.toLowerCase();
|
|
3171
|
+
if (!low.includes("ssh-keygen")) return false;
|
|
3172
|
+
if (!/(?:^|\s)-t\s+\S+/.test(low)) return false;
|
|
3173
|
+
if (/(?:^|\s)-\S*[ylpB]\S*/.test(low)) return false;
|
|
3174
|
+
return true;
|
|
3175
|
+
}
|
|
2991
3176
|
function checkDangerousPatterns(actionType, actionDetail) {
|
|
2992
3177
|
if (actionType === "file_write" || actionType === "file_edit") return null;
|
|
2993
3178
|
const scan = stripHeredocContent(actionDetail);
|
|
2994
3179
|
const TEMP_DIRS = ["/tmp", "/var/tmp", "/run", "/dev/shm"];
|
|
2995
3180
|
const isTempPath = TEMP_DIRS.some(
|
|
2996
3181
|
(td) => new RegExp(`(?:^|[\\s/])${td.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[\\s/]|$)`).test(actionDetail)
|
|
2997
|
-
);
|
|
3182
|
+
) || /(?:^|[\s;]|&&\s*|\|\|\s*)var\/tmp(?:[/\s;]|$)/.test(actionDetail);
|
|
2998
3183
|
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
3184
|
if (!isTempPath && wildcardDeleteRe.test(scan)) {
|
|
3000
3185
|
return {
|
|
@@ -3004,7 +3189,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3004
3189
|
riskType: "script_execution",
|
|
3005
3190
|
confidence: 90,
|
|
3006
3191
|
source: "heuristic",
|
|
3007
|
-
violatedLayers: ["
|
|
3192
|
+
violatedLayers: ["L1.1"]
|
|
3008
3193
|
};
|
|
3009
3194
|
}
|
|
3010
3195
|
const findWriteMatch = actionDetail.match(
|
|
@@ -3019,10 +3204,10 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3019
3204
|
riskType: "script_execution",
|
|
3020
3205
|
confidence: 90,
|
|
3021
3206
|
source: "heuristic",
|
|
3022
|
-
violatedLayers: ["
|
|
3207
|
+
violatedLayers: ["L1.1"]
|
|
3023
3208
|
};
|
|
3024
3209
|
}
|
|
3025
|
-
if (/>\s
|
|
3210
|
+
if (/>\s*['"]?\/etc\//.test(actionDetail) || />\s*['"]?\/boot\//.test(actionDetail) || />\s*['"]?\/proc\//.test(actionDetail)) {
|
|
3026
3211
|
return {
|
|
3027
3212
|
allowed: false,
|
|
3028
3213
|
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
@@ -3030,7 +3215,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3030
3215
|
riskType: "file_access",
|
|
3031
3216
|
confidence: 95,
|
|
3032
3217
|
source: "heuristic",
|
|
3033
|
-
violatedLayers: ["
|
|
3218
|
+
violatedLayers: ["L1.1"]
|
|
3034
3219
|
};
|
|
3035
3220
|
}
|
|
3036
3221
|
if (/>\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i.test(actionDetail)) {
|
|
@@ -3041,7 +3226,7 @@ function checkDangerousPatterns(actionType, actionDetail) {
|
|
|
3041
3226
|
riskType: "file_access",
|
|
3042
3227
|
confidence: 90,
|
|
3043
3228
|
source: "heuristic",
|
|
3044
|
-
violatedLayers: ["
|
|
3229
|
+
violatedLayers: ["L1.1"]
|
|
3045
3230
|
};
|
|
3046
3231
|
}
|
|
3047
3232
|
return null;
|
|
@@ -3132,13 +3317,13 @@ function checkLateralMovement(actionDetail) {
|
|
|
3132
3317
|
riskType: "lateral_movement",
|
|
3133
3318
|
confidence: 80,
|
|
3134
3319
|
source: "heuristic",
|
|
3135
|
-
violatedLayers: ["
|
|
3320
|
+
violatedLayers: ["L1.1"]
|
|
3136
3321
|
};
|
|
3137
3322
|
}
|
|
3138
3323
|
}
|
|
3139
3324
|
return null;
|
|
3140
3325
|
}
|
|
3141
|
-
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES;
|
|
3326
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3142
3327
|
var init_heuristic = __esm({
|
|
3143
3328
|
"src/engine/heuristic.ts"() {
|
|
3144
3329
|
"use strict";
|
|
@@ -3168,6 +3353,8 @@ var init_heuristic = __esm({
|
|
|
3168
3353
|
})();
|
|
3169
3354
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
3170
3355
|
SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
|
|
3356
|
+
HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
|
|
3357
|
+
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
3358
|
}
|
|
3172
3359
|
});
|
|
3173
3360
|
|
|
@@ -3346,112 +3533,6 @@ var init_token_stats = __esm({
|
|
|
3346
3533
|
}
|
|
3347
3534
|
});
|
|
3348
3535
|
|
|
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
3536
|
// src/engine/coordinator.ts
|
|
3456
3537
|
init_esm_shims();
|
|
3457
3538
|
init_heuristic();
|
|
@@ -3684,6 +3765,7 @@ ${result.scriptContent}
|
|
|
3684
3765
|
init_esm_shims();
|
|
3685
3766
|
init_heuristic();
|
|
3686
3767
|
init_logic_rules();
|
|
3768
|
+
init_inline_analyzer();
|
|
3687
3769
|
import { statSync as statSync2, existsSync as existsSync9 } from "fs";
|
|
3688
3770
|
import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
3689
3771
|
import { homedir as homedir4 } from "os";
|
|
@@ -5298,6 +5380,13 @@ function extractScriptPath2(command) {
|
|
|
5298
5380
|
return null;
|
|
5299
5381
|
}
|
|
5300
5382
|
function extractScriptPathWithArgs(command) {
|
|
5383
|
+
const inline = command.match(/(?:^|\s|&&|\|\|)\s*(?:bash|\/bin\/bash|sh|zsh)\s+-c\s*(['"])([\s\S]*?)\1/i);
|
|
5384
|
+
if (inline) {
|
|
5385
|
+
const inner = inline[2];
|
|
5386
|
+
const realPath = extractScriptPath2(inner);
|
|
5387
|
+
if (realPath) return { path: realPath, args: [] };
|
|
5388
|
+
return { path: null, args: [] };
|
|
5389
|
+
}
|
|
5301
5390
|
const scriptPath = extractScriptPath2(command);
|
|
5302
5391
|
if (!scriptPath) return { path: null, args: [] };
|
|
5303
5392
|
const idx = command.indexOf(scriptPath);
|
|
@@ -5311,11 +5400,59 @@ function extractScriptPathWithArgs(command) {
|
|
|
5311
5400
|
}
|
|
5312
5401
|
return { path: scriptPath, args };
|
|
5313
5402
|
}
|
|
5403
|
+
function looksLikeScriptPath(tok) {
|
|
5404
|
+
const t = tok.trim().replace(/^['"]|['"]$/g, "");
|
|
5405
|
+
if (!t) return false;
|
|
5406
|
+
const low = t.toLowerCase();
|
|
5407
|
+
if (["bash", "sh", "zsh", "dash", "python", "python3", "perl", "ruby", "node", "php", "lua"].includes(low)) return false;
|
|
5408
|
+
if (/\.(sh|bash|py|pl|rb|js|php|lua)$/.test(low)) return true;
|
|
5409
|
+
if (t.includes("/") && !t.startsWith("-")) return true;
|
|
5410
|
+
return false;
|
|
5411
|
+
}
|
|
5314
5412
|
function extractInlineScriptPath(actionDetail) {
|
|
5413
|
+
const code = extractInlineCode(actionDetail);
|
|
5414
|
+
if (code) {
|
|
5415
|
+
const candidates = [];
|
|
5416
|
+
const osRe = /os\.(?:system|popen)\s*\(\s*(['"])(.*?)\1\s*\)/gi;
|
|
5417
|
+
let m;
|
|
5418
|
+
while ((m = osRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5419
|
+
const subListRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*\[(.*?)\]/gi;
|
|
5420
|
+
while ((m = subListRe.exec(code)) !== null) candidates.push(m[1]);
|
|
5421
|
+
const subShellRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*(['"])(.*?)\1[^)]*?shell\s*=\s*true/gi;
|
|
5422
|
+
while ((m = subShellRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5423
|
+
for (const cand of candidates) {
|
|
5424
|
+
const tokens = cand.includes(",") ? cand.split(/\s*,\s*/).map((t) => t.trim().replace(/^['"]|['"]$/g, "")) : cand.split(/\s+/);
|
|
5425
|
+
for (const tok of tokens) {
|
|
5426
|
+
if (looksLikeScriptPath(tok)) {
|
|
5427
|
+
try {
|
|
5428
|
+
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5429
|
+
if (existsSync9(resolved)) {
|
|
5430
|
+
const stat = statSync2(resolved);
|
|
5431
|
+
if (stat.isFile()) return resolved;
|
|
5432
|
+
}
|
|
5433
|
+
} catch {
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
const openRe = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5439
|
+
while ((m = openRe.exec(code)) !== null) {
|
|
5440
|
+
const p = m[1];
|
|
5441
|
+
try {
|
|
5442
|
+
const resolved = resolvePath2(p);
|
|
5443
|
+
if (existsSync9(resolved)) {
|
|
5444
|
+
const stat = statSync2(resolved);
|
|
5445
|
+
if (stat.isFile()) return resolved;
|
|
5446
|
+
}
|
|
5447
|
+
} catch {
|
|
5448
|
+
}
|
|
5449
|
+
}
|
|
5450
|
+
return null;
|
|
5451
|
+
}
|
|
5315
5452
|
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
5316
|
-
let
|
|
5317
|
-
while ((
|
|
5318
|
-
const p =
|
|
5453
|
+
let m2;
|
|
5454
|
+
while ((m2 = re.exec(actionDetail)) !== null) {
|
|
5455
|
+
const p = m2[1];
|
|
5319
5456
|
let resolved;
|
|
5320
5457
|
try {
|
|
5321
5458
|
resolved = resolvePath2(p);
|
|
@@ -5700,7 +5837,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
5700
5837
|
riskType: riskType || "",
|
|
5701
5838
|
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
5702
5839
|
source: "llm",
|
|
5703
|
-
violatedLayers: ["
|
|
5840
|
+
violatedLayers: ["L3.1"]
|
|
5704
5841
|
};
|
|
5705
5842
|
} catch (err) {
|
|
5706
5843
|
if (abortController.signal.aborted) {
|
|
@@ -6261,7 +6398,111 @@ init_esm_shims();
|
|
|
6261
6398
|
// src/engine/formal-engine/builder.ts
|
|
6262
6399
|
init_esm_shims();
|
|
6263
6400
|
init_rules_loader();
|
|
6264
|
-
|
|
6401
|
+
|
|
6402
|
+
// src/engine/formal-engine/decode.ts
|
|
6403
|
+
init_esm_shims();
|
|
6404
|
+
var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
|
|
6405
|
+
function isValidBase64Token(tok) {
|
|
6406
|
+
if (tok.length < 8) return false;
|
|
6407
|
+
if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
|
|
6408
|
+
return true;
|
|
6409
|
+
}
|
|
6410
|
+
function isPrintable(s) {
|
|
6411
|
+
for (let i = 0; i < s.length; i++) {
|
|
6412
|
+
const c = s.charCodeAt(i);
|
|
6413
|
+
if (c < 32 || c > 126) return false;
|
|
6414
|
+
}
|
|
6415
|
+
return true;
|
|
6416
|
+
}
|
|
6417
|
+
function decodeBase64Literals(text) {
|
|
6418
|
+
const out = [];
|
|
6419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6420
|
+
for (const m of text.matchAll(B64_TOKEN_RE)) {
|
|
6421
|
+
const tok = m[0];
|
|
6422
|
+
if (!isValidBase64Token(tok)) continue;
|
|
6423
|
+
if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
|
|
6424
|
+
try {
|
|
6425
|
+
const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
|
|
6426
|
+
const bin = Buffer.from(padded, "base64");
|
|
6427
|
+
const s = bin.toString("utf8");
|
|
6428
|
+
if (s && isPrintable(s) && !seen.has(s)) {
|
|
6429
|
+
seen.add(s);
|
|
6430
|
+
out.push(s);
|
|
6431
|
+
}
|
|
6432
|
+
} catch {
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
return out;
|
|
6436
|
+
}
|
|
6437
|
+
var QUOTED_STR_RE = /('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`)/g;
|
|
6438
|
+
var OUTPUT_CMDS = /* @__PURE__ */ new Set(["echo", "printf", "print"]);
|
|
6439
|
+
function stripStringLiterals(text) {
|
|
6440
|
+
const m = text.match(/\s*([A-Za-z_][\w-]*|\/?[\w./-]+)\b/);
|
|
6441
|
+
if (!m) return text;
|
|
6442
|
+
const cmd = m[1].split("/").pop().toLowerCase();
|
|
6443
|
+
if (!OUTPUT_CMDS.has(cmd)) return text;
|
|
6444
|
+
return text.replace(QUOTED_STR_RE, "");
|
|
6445
|
+
}
|
|
6446
|
+
function decodeUrlAndNumeric(text) {
|
|
6447
|
+
const out = [];
|
|
6448
|
+
let s = text;
|
|
6449
|
+
for (let i = 0; i < 3; i++) {
|
|
6450
|
+
let nxt = s;
|
|
6451
|
+
try {
|
|
6452
|
+
nxt = decodeURIComponent(s);
|
|
6453
|
+
} catch {
|
|
6454
|
+
break;
|
|
6455
|
+
}
|
|
6456
|
+
if (nxt === s) break;
|
|
6457
|
+
s = nxt;
|
|
6458
|
+
}
|
|
6459
|
+
if (s !== text) out.push(s);
|
|
6460
|
+
if (/\\[0-7]{3}/.test(text)) {
|
|
6461
|
+
let expanded = "";
|
|
6462
|
+
try {
|
|
6463
|
+
expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
|
|
6464
|
+
} catch {
|
|
6465
|
+
}
|
|
6466
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
6467
|
+
}
|
|
6468
|
+
if (/\\x[0-9a-fA-F]{2}/.test(text)) {
|
|
6469
|
+
let expanded = "";
|
|
6470
|
+
try {
|
|
6471
|
+
expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
|
|
6472
|
+
} catch {
|
|
6473
|
+
}
|
|
6474
|
+
if (expanded && expanded !== text) out.push(expanded);
|
|
6475
|
+
}
|
|
6476
|
+
return out;
|
|
6477
|
+
}
|
|
6478
|
+
function allCandidates(detail, max = 8) {
|
|
6479
|
+
const shell = stripStringLiterals(detail);
|
|
6480
|
+
const cands = [shell];
|
|
6481
|
+
cands.push(...decodeBase64Literals(shell));
|
|
6482
|
+
cands.push(...decodeUrlAndNumeric(shell));
|
|
6483
|
+
cands.push(...decodeBase64Literals(detail));
|
|
6484
|
+
cands.push(...decodeUrlAndNumeric(detail));
|
|
6485
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6486
|
+
const out = [];
|
|
6487
|
+
for (const c of cands) {
|
|
6488
|
+
if (seen.has(c) || c.length > 4096) continue;
|
|
6489
|
+
seen.add(c);
|
|
6490
|
+
out.push(c);
|
|
6491
|
+
if (out.length >= max) break;
|
|
6492
|
+
}
|
|
6493
|
+
return out;
|
|
6494
|
+
}
|
|
6495
|
+
function matchStageAny(detail, stages) {
|
|
6496
|
+
for (const cand of allCandidates(detail)) {
|
|
6497
|
+
for (const [name, pats] of Object.entries(stages)) {
|
|
6498
|
+
for (const p of pats) {
|
|
6499
|
+
p.lastIndex = 0;
|
|
6500
|
+
if (p.test(cand)) return name;
|
|
6501
|
+
}
|
|
6502
|
+
}
|
|
6503
|
+
}
|
|
6504
|
+
return "";
|
|
6505
|
+
}
|
|
6265
6506
|
|
|
6266
6507
|
// src/engine/formal-engine/ltl.ts
|
|
6267
6508
|
init_esm_shims();
|
|
@@ -6295,11 +6536,7 @@ function compileAps(apPatterns) {
|
|
|
6295
6536
|
}
|
|
6296
6537
|
function labelEvent(actionDetail) {
|
|
6297
6538
|
if (!AP_RE) compileAps(loadMC().ap_patterns || {});
|
|
6298
|
-
|
|
6299
|
-
try {
|
|
6300
|
-
candidates = (init_decode(), __toCommonJS(decode_exports)).allCandidates(actionDetail);
|
|
6301
|
-
} catch {
|
|
6302
|
-
}
|
|
6539
|
+
const candidates = allCandidates(actionDetail);
|
|
6303
6540
|
const hits = /* @__PURE__ */ new Set();
|
|
6304
6541
|
for (const [name, pats] of Object.entries(AP_RE || {})) {
|
|
6305
6542
|
for (const c of candidates) {
|
|
@@ -6626,7 +6863,7 @@ function checkSensitiveDataFlow(graph) {
|
|
|
6626
6863
|
const n = nodes[i];
|
|
6627
6864
|
if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
|
|
6628
6865
|
violations.push({
|
|
6629
|
-
propertyId: "sensitive-data-
|
|
6866
|
+
propertyId: "sensitive-data-flow",
|
|
6630
6867
|
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
6868
|
riskLevel: "critical",
|
|
6632
6869
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6677,7 +6914,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6677
6914
|
if (pre) {
|
|
6678
6915
|
const post = nodes[firstEv];
|
|
6679
6916
|
return [{
|
|
6680
|
-
propertyId: "no-
|
|
6917
|
+
propertyId: "no-exfil-after-privesc",
|
|
6681
6918
|
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
6919
|
riskLevel: "critical",
|
|
6683
6920
|
path: [
|
|
@@ -6693,7 +6930,7 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
6693
6930
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6694
6931
|
if (isExfilNode(nodes[j])) {
|
|
6695
6932
|
violations.push({
|
|
6696
|
-
propertyId: "no-
|
|
6933
|
+
propertyId: "no-exfil-after-privesc",
|
|
6697
6934
|
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
6935
|
riskLevel: "critical",
|
|
6699
6936
|
path: [
|
|
@@ -6726,7 +6963,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6726
6963
|
if (pre) {
|
|
6727
6964
|
const post = nodes[firstEv];
|
|
6728
6965
|
return [{
|
|
6729
|
-
propertyId: "no-
|
|
6966
|
+
propertyId: "no-exfil-after-credential",
|
|
6730
6967
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6731
6968
|
riskLevel: "critical",
|
|
6732
6969
|
path: [
|
|
@@ -6742,7 +6979,7 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
6742
6979
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
6743
6980
|
if (isExfilNode(nodes[j])) {
|
|
6744
6981
|
violations.push({
|
|
6745
|
-
propertyId: "no-
|
|
6982
|
+
propertyId: "no-exfil-after-credential",
|
|
6746
6983
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
6747
6984
|
riskLevel: "critical",
|
|
6748
6985
|
path: [
|
|
@@ -6812,7 +7049,7 @@ function checkLowIntegrityCommandExec(graph) {
|
|
|
6812
7049
|
const n = nodes[i];
|
|
6813
7050
|
if (lowAt.has(i) && isComputeNode(n)) {
|
|
6814
7051
|
violations.push({
|
|
6815
|
-
propertyId: "low-integrity-
|
|
7052
|
+
propertyId: "low-integrity-command-exec",
|
|
6816
7053
|
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
7054
|
riskLevel: "critical",
|
|
6818
7055
|
path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
|
|
@@ -6859,7 +7096,7 @@ function checkCrossSessionReachability(graph) {
|
|
|
6859
7096
|
const srcIdx = Math.min(...sources);
|
|
6860
7097
|
const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
|
|
6861
7098
|
violations.push({
|
|
6862
|
-
propertyId: "
|
|
7099
|
+
propertyId: "low-integrity-reachability",
|
|
6863
7100
|
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
7101
|
riskLevel: "high",
|
|
6865
7102
|
path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
|
|
@@ -6911,17 +7148,17 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
6911
7148
|
return violations;
|
|
6912
7149
|
}
|
|
6913
7150
|
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-
|
|
7151
|
+
{ id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
|
|
7152
|
+
{ id: "sensitive-data-flow", check: checkSensitiveDataFlow, shortName: "P2" },
|
|
7153
|
+
{ id: "low-integrity-reachability", check: checkCrossSessionReachability, shortName: "P3" },
|
|
7154
|
+
{ id: "no-exfil-after-privesc", check: checkNeverExfilAfterPrivesc, shortName: "P4" },
|
|
7155
|
+
{ id: "no-exfil-after-credential", check: checkNeverExfilAfterCred, shortName: "P5" },
|
|
6919
7156
|
// 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 }
|
|
7157
|
+
{ id: "mc-ltl-rules", check: checkMCRules, shortName: "P6" },
|
|
7158
|
+
// P7 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
|
|
7159
|
+
{ id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec, shortName: "P7" },
|
|
7160
|
+
// P8 敏感数据聚合外传(egress 聚合 ≥2 事件源)
|
|
7161
|
+
{ id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation, shortName: "P8" }
|
|
6925
7162
|
];
|
|
6926
7163
|
|
|
6927
7164
|
// src/engine/formal-engine/engine.ts
|
|
@@ -6984,7 +7221,6 @@ var FormalEngine = class {
|
|
|
6984
7221
|
// src/engine/formal-engine/stateful.ts
|
|
6985
7222
|
init_esm_shims();
|
|
6986
7223
|
init_rules_loader();
|
|
6987
|
-
init_decode();
|
|
6988
7224
|
|
|
6989
7225
|
// src/engine/formal-engine/chain_state.ts
|
|
6990
7226
|
init_esm_shims();
|
|
@@ -7123,7 +7359,7 @@ async function runAnalysis(input) {
|
|
|
7123
7359
|
riskType: node.l1RiskType,
|
|
7124
7360
|
confidence: 90,
|
|
7125
7361
|
source: "heuristic_script_scan",
|
|
7126
|
-
violatedLayers: ["
|
|
7362
|
+
violatedLayers: ["L1.2"]
|
|
7127
7363
|
};
|
|
7128
7364
|
}
|
|
7129
7365
|
}
|
|
@@ -7241,7 +7477,7 @@ async function runAnalysis(input) {
|
|
|
7241
7477
|
riskType: inlineJudgment.riskType,
|
|
7242
7478
|
confidence: 90,
|
|
7243
7479
|
source: "logic_rule",
|
|
7244
|
-
violatedLayers: ["1
|
|
7480
|
+
violatedLayers: ["L2.1"]
|
|
7245
7481
|
};
|
|
7246
7482
|
}
|
|
7247
7483
|
return {
|
|
@@ -7274,7 +7510,7 @@ async function runAnalysis(input) {
|
|
|
7274
7510
|
console.warn(
|
|
7275
7511
|
`[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
7512
|
);
|
|
7277
|
-
observedFormalLayers.push("2
|
|
7513
|
+
observedFormalLayers.push("L2.2");
|
|
7278
7514
|
} else {
|
|
7279
7515
|
return {
|
|
7280
7516
|
allowed: false,
|
|
@@ -7283,7 +7519,7 @@ async function runAnalysis(input) {
|
|
|
7283
7519
|
riskType: "formal_verification",
|
|
7284
7520
|
confidence: 90,
|
|
7285
7521
|
source: "formal_engine",
|
|
7286
|
-
violatedLayers: ["2
|
|
7522
|
+
violatedLayers: ["L2.2"]
|
|
7287
7523
|
};
|
|
7288
7524
|
}
|
|
7289
7525
|
}
|
|
@@ -7300,7 +7536,7 @@ async function runAnalysis(input) {
|
|
|
7300
7536
|
riskType: node.l2RiskType,
|
|
7301
7537
|
confidence: 85,
|
|
7302
7538
|
source: "logic_rule_script_scan",
|
|
7303
|
-
violatedLayers: ["
|
|
7539
|
+
violatedLayers: ["L2.3"]
|
|
7304
7540
|
};
|
|
7305
7541
|
}
|
|
7306
7542
|
}
|
|
@@ -7387,7 +7623,7 @@ async function runAnalysis(input) {
|
|
|
7387
7623
|
riskType: "",
|
|
7388
7624
|
confidence: 60,
|
|
7389
7625
|
source: "llm_failure_fallback",
|
|
7390
|
-
violatedLayers: ["
|
|
7626
|
+
violatedLayers: ["L3.1"]
|
|
7391
7627
|
};
|
|
7392
7628
|
}
|
|
7393
7629
|
if (!llmResult.allowed && llmResult.confidence && llmResult.confidence >= 90 && _brainExtractor) {
|
|
@@ -7417,4 +7653,14 @@ export {
|
|
|
7417
7653
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
7418
7654
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|
|
7419
7655
|
//! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
|
|
7656
|
+
//!
|
|
7657
|
+
//! shortName(P1~P8 连续短名)与 id(语义化英文)对照(与 Python 一致):
|
|
7658
|
+
//! P1 cross-session-chain 跨会话组合链
|
|
7659
|
+
//! P2 sensitive-data-flow 敏感数据传播
|
|
7660
|
+
//! P3 low-integrity-reachability 低完整性数据流可达
|
|
7661
|
+
//! P4 no-exfil-after-privesc 提权后禁外传
|
|
7662
|
+
//! P5 no-exfil-after-credential 凭据后禁外传
|
|
7663
|
+
//! P6 mc-ltl-rules LTL 模型检测
|
|
7664
|
+
//! P7 low-integrity-command-exec 低完整性命令执行
|
|
7665
|
+
//! P8 sensitive-data-aggregation 敏感数据聚合外传
|
|
7420
7666
|
//# sourceMappingURL=coordinator.js.map
|