@kenhkl/agent-moss 0.15.2 → 0.15.5

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.
@@ -1434,6 +1434,7 @@ __export(runtime_config_exports, {
1434
1434
  getRuntimeConfigPath: () => getRuntimeConfigPath,
1435
1435
  getScenarioRules: () => getScenarioRules,
1436
1436
  hasRuntimeConfig: () => hasRuntimeConfig,
1437
+ invalidateRuntimeConfigCache: () => invalidateRuntimeConfigCache,
1437
1438
  isL2CategoryEnabled: () => isL2CategoryEnabled,
1438
1439
  isLayerEnabled: () => isLayerEnabled,
1439
1440
  loadRuntimeConfig: () => loadRuntimeConfig,
@@ -1452,7 +1453,7 @@ __export(runtime_config_exports, {
1452
1453
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1453
1454
  updateSkillEnabled: () => updateSkillEnabled
1454
1455
  });
1455
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1456
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
1456
1457
  import { join as join3 } from "path";
1457
1458
  import { fileURLToPath as fileURLToPath5 } from "url";
1458
1459
  import { dirname as dirname3 } from "path";
@@ -1469,6 +1470,7 @@ function saveRuntimeConfig(config) {
1469
1470
  const dir = p.substring(0, p.lastIndexOf("/"));
1470
1471
  if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
1471
1472
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
1473
+ invalidateRuntimeConfigCache();
1472
1474
  }
1473
1475
  function emptyRuntimeConfig() {
1474
1476
  return {
@@ -1482,6 +1484,9 @@ function emptyRuntimeConfig() {
1482
1484
  scenario_rules: {}
1483
1485
  };
1484
1486
  }
1487
+ function invalidateRuntimeConfigCache() {
1488
+ runtimeConfigCache = null;
1489
+ }
1485
1490
  function loadRuntimeConfig() {
1486
1491
  const p = getRuntimeConfigPath();
1487
1492
  if (!existsSync4(p)) {
@@ -1493,9 +1498,14 @@ function loadRuntimeConfig() {
1493
1498
  return seeded;
1494
1499
  }
1495
1500
  try {
1501
+ const st = statSync(p);
1502
+ const cacheKey = `${p}|${st.mtimeMs}|${st.size}`;
1503
+ if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
1504
+ return runtimeConfigCache.value;
1505
+ }
1496
1506
  const data = JSON.parse(readFileSync4(p, "utf-8"));
1497
1507
  const merged = mergeConfig(data, generateSourceDefaults());
1498
- return {
1508
+ const result = {
1499
1509
  version: data.version || 1,
1500
1510
  layers: {
1501
1511
  L1_heuristic: data.layers?.L1_heuristic ?? true,
@@ -1509,6 +1519,8 @@ function loadRuntimeConfig() {
1509
1519
  agent_rules: merged.agent_rules || {},
1510
1520
  scenario_rules: merged.scenario_rules || {}
1511
1521
  };
1522
+ runtimeConfigCache = { key: cacheKey, value: result };
1523
+ return result;
1512
1524
  } catch {
1513
1525
  return emptyRuntimeConfig();
1514
1526
  }
@@ -2498,7 +2510,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
2498
2510
  saveRuntimeConfig(runtime);
2499
2511
  return runtime;
2500
2512
  }
2501
- var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2513
+ var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2502
2514
  var init_runtime_config = __esm({
2503
2515
  "src/infra/runtime-config.ts"() {
2504
2516
  "use strict";
@@ -2528,6 +2540,7 @@ var init_runtime_config = __esm({
2528
2540
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2529
2541
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2530
2542
  };
2543
+ runtimeConfigCache = null;
2531
2544
  _AGENTS_DIR_PROD = join3(__dirname4, "agents");
2532
2545
  _AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
2533
2546
  _SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
@@ -2822,6 +2835,56 @@ function isInlineScriptCommand(actionDetail) {
2822
2835
  const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2823
2836
  return re.test(actionDetail);
2824
2837
  }
2838
+ function resolvePathTokens(segment) {
2839
+ const out = [];
2840
+ const low = segment.toLowerCase();
2841
+ for (const m of low.matchAll(HOME_PREFIX_RE)) {
2842
+ const prefix = m[0];
2843
+ const endIdx = (m.index ?? 0) + m[0].length;
2844
+ const tail = low.slice(endIdx);
2845
+ if (!tail || !(tail[0] === "/" || tail[0] === " ")) {
2846
+ out.push([prefix, prefix]);
2847
+ continue;
2848
+ }
2849
+ const token = tail.trim().split(/\s+/)[0] || "";
2850
+ if (!token.startsWith("/")) continue;
2851
+ const cleaned = token.replace(/\/+$/, "").replace(/^['"]|['"]$/g, "");
2852
+ const segments = cleaned.split("/").filter((s) => s && s !== ".");
2853
+ if (!segments.some((s) => s === "..")) continue;
2854
+ let depth = 0;
2855
+ const kept = [];
2856
+ let escaped = false;
2857
+ for (const s of segments) {
2858
+ if (s === "..") {
2859
+ depth -= 1;
2860
+ if (kept.length) kept.pop();
2861
+ if (depth < 0) {
2862
+ escaped = true;
2863
+ depth = 0;
2864
+ }
2865
+ } else {
2866
+ depth += 1;
2867
+ kept.push(s);
2868
+ }
2869
+ }
2870
+ let resolved = kept.length ? "~/" + kept.join("/") : "~";
2871
+ if (escaped) resolved += "\uFF08\u26A0\uFE0F .. \u5F39\u6808\u8D85\u51FA\u4E3B\u76EE\u5F55\u6839\uFF0C\u5B9E\u9645\u76EE\u6807\u5728\u4E3B\u76EE\u5F55\u4E4B\u5916\uFF09";
2872
+ out.push([prefix + token, resolved]);
2873
+ }
2874
+ return out;
2875
+ }
2876
+ function buildPathContext(actionDetail) {
2877
+ if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
2878
+ const stripped = stripLiteralTextSegments(actionDetail);
2879
+ const lines = [];
2880
+ for (const seg of splitCommandSegments(stripped)) {
2881
+ for (const [raw, lex] of resolvePathTokens(seg)) {
2882
+ lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
2883
+ }
2884
+ }
2885
+ if (!lines.length) return "";
2886
+ return "## \u8DEF\u5F84\u89E3\u6790\u4E8B\u5B9E\uFF08\u8BCD\u6CD5\u5206\u6790\uFF0C\u786E\u5B9A\u6027\u7ED3\u8BBA\uFF09\n" + lines.join("\n") + "\n\n## \u8FD0\u884C\u65F6\u672A\u9A8C\u8BC1\uFF08\u4F9B\u53C2\u8003\uFF0C\u975E\u4E8B\u5B9E\uFF09\n- \u5206\u6790\u5668\u4E0D\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A\u65E0\u6CD5\u786E\u8BA4\u8DEF\u5F84\u4E2D\u7684\u4E2D\u95F4\u76EE\u5F55\u662F\u5426\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF1B\u82E5\u4E2D\u95F4\u6BB5\u5728\u6267\u884C\u65F6\u662F\uFF08\u6216\u88AB\u66FF\u6362\u4E3A\uFF09\u6307\u5411\u4E3B\u76EE\u5F55\u5916\u7684\u8F6F\u94FE\uFF0C\u5B9E\u9645\u64CD\u4F5C\u76EE\u6807\u53EF\u80FD\u968F\u4E4B\u6539\u53D8\u3002\u8BCD\u6CD5\u89E3\u6790\u7ED3\u679C\u57FA\u4E8E\u6807\u51C6\u8DEF\u5F84\u8BED\u4E49\uFF0C\u4E0D\u5E94\u56E0\u731C\u6D4B\u300C\u89E3\u6790\u4F9D\u8D56\u67D0\u6BB5\u662F\u5426\u5B58\u5728\u300D\u800C\u5426\u5B9A\u5B83\u3002";
2887
+ }
2825
2888
  function stripLiteralTextSegments(actionDetail) {
2826
2889
  const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
2827
2890
  const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
@@ -3348,7 +3411,7 @@ function checkLateralMovement(actionDetail) {
3348
3411
  }
3349
3412
  return null;
3350
3413
  }
3351
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3414
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3352
3415
  var init_heuristic = __esm({
3353
3416
  "src/engine/heuristic.ts"() {
3354
3417
  "use strict";
@@ -3376,6 +3439,8 @@ var init_heuristic = __esm({
3376
3439
  description: c.reason
3377
3440
  }));
3378
3441
  })();
3442
+ PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
3443
+ HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
3379
3444
  SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
3380
3445
  SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
3381
3446
  HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
@@ -3791,7 +3856,7 @@ init_esm_shims();
3791
3856
  init_heuristic();
3792
3857
  init_logic_rules();
3793
3858
  init_inline_analyzer();
3794
- import { statSync as statSync2, existsSync as existsSync9 } from "fs";
3859
+ import { statSync as statSync3, existsSync as existsSync9 } from "fs";
3795
3860
  import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
3796
3861
  import { homedir as homedir4 } from "os";
3797
3862
 
@@ -5452,7 +5517,7 @@ function extractInlineScriptPath(actionDetail) {
5452
5517
  try {
5453
5518
  const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
5454
5519
  if (existsSync9(resolved)) {
5455
- const stat = statSync2(resolved);
5520
+ const stat = statSync3(resolved);
5456
5521
  if (stat.isFile()) return resolved;
5457
5522
  }
5458
5523
  } catch {
@@ -5466,7 +5531,7 @@ function extractInlineScriptPath(actionDetail) {
5466
5531
  try {
5467
5532
  const resolved = resolvePath2(p);
5468
5533
  if (existsSync9(resolved)) {
5469
- const stat = statSync2(resolved);
5534
+ const stat = statSync3(resolved);
5470
5535
  if (stat.isFile()) return resolved;
5471
5536
  }
5472
5537
  } catch {
@@ -5482,7 +5547,7 @@ function extractInlineScriptPath(actionDetail) {
5482
5547
  try {
5483
5548
  resolved = resolvePath2(p);
5484
5549
  if (existsSync9(resolved)) {
5485
- const stat = statSync2(resolved);
5550
+ const stat = statSync3(resolved);
5486
5551
  if (stat.isFile()) return resolved;
5487
5552
  }
5488
5553
  } catch {
@@ -5693,7 +5758,7 @@ async function runLLMAnalysis(input, previous) {
5693
5758
  } else {
5694
5759
  scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
5695
5760
  }
5696
- const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
5761
+ const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText, previous.pathContext ?? "");
5697
5762
  logPrompt(prompt, input.aNext, previous.llmLogPath);
5698
5763
  const result = await analyzeWithLLM(prompt, config);
5699
5764
  if (llmCache.size >= LLM_CACHE_MAX) {
@@ -5717,7 +5782,7 @@ ${prompt}
5717
5782
  console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
5718
5783
  }
5719
5784
  }
5720
- function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
5785
+ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "", pathContext = "") {
5721
5786
  const skillsText = formatSkillsForPrompt(matchedSkills);
5722
5787
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
5723
5788
  const promptHistoryText = formatPromptHistory(input.promptHistory);
@@ -5725,7 +5790,7 @@ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis,
5725
5790
  const hintsText = formatHints(previous);
5726
5791
  const securityJudgeTemplate = templates["security_judge"];
5727
5792
  if (securityJudgeTemplate) {
5728
- return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
5793
+ return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{path_context}", pathContext || "\uFF08\u672C\u547D\u4EE4\u65E0\u4E3B\u76EE\u5F55\u76F8\u5173\u8DEF\u5F84\u6BB5\uFF0C\u4E0D\u9002\u7528\uFF09").replaceAll("{{", "{").replaceAll("}}", "}");
5729
5794
  }
5730
5795
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
5731
5796
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -7609,6 +7674,21 @@ async function runAnalysis(input) {
7609
7674
  if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
7610
7675
  return effectiveResult;
7611
7676
  }
7677
+ } else {
7678
+ if (isInlineScriptCmd || isLiteralTextCmd) {
7679
+ const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
7680
+ if (inlineJudgment.actionable && !inlineJudgment.allowed) {
7681
+ return {
7682
+ allowed: false,
7683
+ reason: inlineJudgment.reason,
7684
+ riskLevel: inlineJudgment.riskLevel,
7685
+ riskType: inlineJudgment.riskType,
7686
+ confidence: 90,
7687
+ source: "logic_rule",
7688
+ violatedLayers: ["L2.1"]
7689
+ };
7690
+ }
7691
+ }
7612
7692
  }
7613
7693
  if (isLogicRulesEnabled()) {
7614
7694
  const formalSessions = [];
@@ -7710,12 +7790,14 @@ async function runAnalysis(input) {
7710
7790
  }
7711
7791
  let llmResult;
7712
7792
  try {
7793
+ const pathContext = buildPathContext(input.aNext.actionDetail);
7713
7794
  llmResult = await runLLMAnalysis(input, {
7714
7795
  heuristic: heuristicResult,
7715
7796
  logic: logicResult,
7716
7797
  skipL3Hints,
7717
7798
  scriptChain: scriptChain ?? void 0,
7718
- llmLogPath: input.llmLogPath
7799
+ llmLogPath: input.llmLogPath,
7800
+ pathContext
7719
7801
  });
7720
7802
  } catch (e) {
7721
7803
  const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));