@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.
@@ -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()?.L2_4?.ltl_rules?.rules || [];
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: ["2"]
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: ["2"]
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: ["2"]
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: ["2"]
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: ["2"]
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: ["2"]
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: ["2"],
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: ["2"]
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: ["2"]
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
- L2_4: {},
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
- L2_4: merged.L2_4 || {},
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
- L2_4: _ltlRulesToRuntime(),
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
- _mergeRuleCategories(cfg, srcDefaults, "L2_4");
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: ["1.1"]
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
- if (dangerousResult) return dangerousResult;
2736
- if (!isExcludedType) {
2737
- const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
2738
- if (pathResult) return pathResult;
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: ["1.1"]
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: ["1.1"]
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: ["1.1"],
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
- const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
2921
- const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
2922
- if (pathRe.test(pathScanText) || expandedRe.test(pathScanText)) {
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: ["1.1"],
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: ["1.1"],
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: ["1.1"],
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: ["1.1"]
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: ["1.1"]
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: ["1.1"]
3207
+ violatedLayers: ["L1.1"]
3023
3208
  };
3024
3209
  }
3025
- if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail)) {
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: ["1.1"]
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: ["1.1"]
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: ["1.1"]
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 m;
5317
- while ((m = re.exec(actionDetail)) !== null) {
5318
- const p = m[1];
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: ["3"]
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
- init_decode();
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
- let candidates = [actionDetail];
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-exfil",
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-privesc-then-exfil",
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-privesc-then-exfil",
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-cred-then-exfil",
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-cred-then-exfil",
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-controls-command-exec",
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: "cross-session-reachability",
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: "cross-session-reachability", check: checkCrossSessionReachability },
6917
- { id: "no-privesc-then-exfil", check: checkNeverExfilAfterPrivesc },
6918
- { id: "no-cred-then-exfil", check: checkNeverExfilAfterCred },
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
- // Rule 5 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
6922
- { id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec },
6923
- // Rule 12 敏感数据聚合外传(egress 聚合 ≥2 事件源)
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: ["1.1-script"]
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.2"]
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.4");
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.4"]
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: ["1.2-script"]
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: ["3"]
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