@kenhkl/agent-moss 0.17.12 → 0.17.14

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 CHANGED
@@ -1964,9 +1964,9 @@ function runLogicCheck(input, opts) {
1964
1964
  }
1965
1965
  }
1966
1966
  }
1967
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1967
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1968
1968
  if (pwdResult) return pwdResult;
1969
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1969
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1970
1970
  if (userDelResult) return userDelResult;
1971
1971
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1972
1972
  if (comboResult) return comboResult;
@@ -2162,28 +2162,48 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
2162
2162
  }
2163
2163
  return null;
2164
2164
  }
2165
- function hasUserConsent(actionHistory) {
2166
- return actionHistory.some((h) => {
2167
- const t = String(h.actionType || "").toLowerCase();
2168
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
2169
- });
2170
- }
2171
- function hasWeakConsent(actionHistory) {
2172
- return actionHistory.some((h) => {
2173
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
2174
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
2175
- });
2165
+ function isAskUserAction(actionType) {
2166
+ if (ASK_USER_ACTION_TYPES.includes(actionType)) return true;
2167
+ return actionType.endsWith("-askuser");
2168
+ }
2169
+ function consentWithinTtl(asked, aNext) {
2170
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
2171
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
2172
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
2173
+ if (ttl <= 0) return true;
2174
+ const askedAt = String(asked.calledAt || "").trim();
2175
+ const nextAt = String(aNext?.calledAt || "").trim();
2176
+ if (!askedAt || !nextAt) return true;
2177
+ const a = Date.parse(askedAt);
2178
+ const b = Date.parse(nextAt);
2179
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
2180
+ return Math.abs(b - a) <= ttl * 1e3;
2181
+ }
2182
+ function hasValidConsent(actionHistory, aNext) {
2183
+ if (!actionHistory.length) return false;
2184
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
2185
+ for (let i = tail.length - 1; i >= 0; i--) {
2186
+ const h = tail[i];
2187
+ if (!isAskUserAction(String(h.actionType || "").toLowerCase())) continue;
2188
+ const output = String(h.output || "").trim();
2189
+ if (!output) return false;
2190
+ const question = String(h.actionDetail || "");
2191
+ if (!CONSENT_TOPIC.test(question)) return false;
2192
+ if (CONSENT_NEGATIVE.test(output)) return false;
2193
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
2194
+ return consentWithinTtl(h, aNext);
2195
+ }
2196
+ return false;
2176
2197
  }
2177
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
2178
- if (actionType !== "bash") return null;
2198
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
2199
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
2179
2200
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
2180
2201
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
2181
2202
  if (!isPasswordModify) return null;
2182
- if (hasUserConsent(actionHistory)) return null;
2183
- if (hasWeakConsent(actionHistory)) return null;
2203
+ if (hasValidConsent(actionHistory, aNext)) return null;
2184
2204
  return {
2185
2205
  allowed: false,
2186
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
2206
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
2187
2207
  riskLevel: "high",
2188
2208
  riskType: "consent_missing",
2189
2209
  confidence: 85,
@@ -2191,16 +2211,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
2191
2211
  violatedLayers: ["L2.1"]
2192
2212
  };
2193
2213
  }
2194
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
2195
- if (actionType !== "bash") return null;
2214
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
2215
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
2196
2216
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
2197
2217
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
2198
2218
  if (!isUserDeletion) return null;
2199
- if (hasUserConsent(actionHistory)) return null;
2200
- if (hasWeakConsent(actionHistory)) return null;
2219
+ if (hasValidConsent(actionHistory, aNext)) return null;
2201
2220
  return {
2202
2221
  allowed: false,
2203
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
2222
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
2204
2223
  riskLevel: "high",
2205
2224
  riskType: "consent_missing",
2206
2225
  confidence: 85,
@@ -2299,7 +2318,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
2299
2318
  violatedLayers: ["L2.1"]
2300
2319
  };
2301
2320
  }
2302
- var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2321
+ var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, BASH_LIKE_ACTION_TYPES, ASK_USER_ACTION_TYPES, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2303
2322
  var init_logic_rules = __esm({
2304
2323
  "src/engine/logic-rules.ts"() {
2305
2324
  "use strict";
@@ -2387,6 +2406,13 @@ var init_logic_rules = __esm({
2387
2406
  }));
2388
2407
  })();
2389
2408
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2409
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
2410
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
2411
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
2412
+ CONSENT_MAX_DISTANCE = 5;
2413
+ CONSENT_TTL_SECONDS = 300;
2414
+ BASH_LIKE_ACTION_TYPES = ["bash", "executebash", "terminal-executebash"];
2415
+ ASK_USER_ACTION_TYPES = ["ask_user_question", "askuser"];
2390
2416
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2391
2417
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2392
2418
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -12459,7 +12485,10 @@ async function analyzeRoute(c) {
12459
12485
  aNext: {
12460
12486
  actionType: req.a_next.action_type,
12461
12487
  actionDetail: req.a_next.action_detail,
12462
- instanceId
12488
+ instanceId,
12489
+ // called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
12490
+ // 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
12491
+ calledAt: req.a_next.called_at
12463
12492
  },
12464
12493
  reason: req.reason,
12465
12494
  osType,
@@ -66,6 +66,7 @@ interface AnalysisInput {
66
66
  actionType: string;
67
67
  actionDetail: string;
68
68
  output?: string;
69
+ calledAt?: string;
69
70
  }[];
70
71
  /** 完整的交错历史 prompt(可选),每轮 {text, actions} 或纯文本 string(AgentGate v3 旧格式),按时间序。
71
72
  * 供 L2 意图一致性与 L3 语义分析使用;空则回退单条 promptSession。 */
@@ -92,6 +93,7 @@ interface AnalysisInput {
92
93
  actionType: string;
93
94
  actionDetail: string;
94
95
  instanceId?: string;
96
+ calledAt?: string;
95
97
  };
96
98
  reason: string;
97
99
  osType: string;
@@ -1141,9 +1141,9 @@ function runLogicCheck(input, opts) {
1141
1141
  }
1142
1142
  }
1143
1143
  }
1144
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1144
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1145
1145
  if (pwdResult) return pwdResult;
1146
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1146
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1147
1147
  if (userDelResult) return userDelResult;
1148
1148
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1149
1149
  if (comboResult) return comboResult;
@@ -1339,28 +1339,48 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
1339
1339
  }
1340
1340
  return null;
1341
1341
  }
1342
- function hasUserConsent(actionHistory) {
1343
- return actionHistory.some((h) => {
1344
- const t = String(h.actionType || "").toLowerCase();
1345
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1346
- });
1347
- }
1348
- function hasWeakConsent(actionHistory) {
1349
- return actionHistory.some((h) => {
1350
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1351
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1352
- });
1342
+ function isAskUserAction(actionType) {
1343
+ if (ASK_USER_ACTION_TYPES.includes(actionType)) return true;
1344
+ return actionType.endsWith("-askuser");
1345
+ }
1346
+ function consentWithinTtl(asked, aNext) {
1347
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
1348
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
1349
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
1350
+ if (ttl <= 0) return true;
1351
+ const askedAt = String(asked.calledAt || "").trim();
1352
+ const nextAt = String(aNext?.calledAt || "").trim();
1353
+ if (!askedAt || !nextAt) return true;
1354
+ const a = Date.parse(askedAt);
1355
+ const b = Date.parse(nextAt);
1356
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
1357
+ return Math.abs(b - a) <= ttl * 1e3;
1358
+ }
1359
+ function hasValidConsent(actionHistory, aNext) {
1360
+ if (!actionHistory.length) return false;
1361
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
1362
+ for (let i = tail.length - 1; i >= 0; i--) {
1363
+ const h = tail[i];
1364
+ if (!isAskUserAction(String(h.actionType || "").toLowerCase())) continue;
1365
+ const output = String(h.output || "").trim();
1366
+ if (!output) return false;
1367
+ const question = String(h.actionDetail || "");
1368
+ if (!CONSENT_TOPIC.test(question)) return false;
1369
+ if (CONSENT_NEGATIVE.test(output)) return false;
1370
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
1371
+ return consentWithinTtl(h, aNext);
1372
+ }
1373
+ return false;
1353
1374
  }
1354
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1355
- if (actionType !== "bash") return null;
1375
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1376
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
1356
1377
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1357
1378
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1358
1379
  if (!isPasswordModify) return null;
1359
- if (hasUserConsent(actionHistory)) return null;
1360
- if (hasWeakConsent(actionHistory)) return null;
1380
+ if (hasValidConsent(actionHistory, aNext)) return null;
1361
1381
  return {
1362
1382
  allowed: false,
1363
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1383
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1364
1384
  riskLevel: "high",
1365
1385
  riskType: "consent_missing",
1366
1386
  confidence: 85,
@@ -1368,16 +1388,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1368
1388
  violatedLayers: ["L2.1"]
1369
1389
  };
1370
1390
  }
1371
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
1372
- if (actionType !== "bash") return null;
1391
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1392
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
1373
1393
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
1374
1394
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1375
1395
  if (!isUserDeletion) return null;
1376
- if (hasUserConsent(actionHistory)) return null;
1377
- if (hasWeakConsent(actionHistory)) return null;
1396
+ if (hasValidConsent(actionHistory, aNext)) return null;
1378
1397
  return {
1379
1398
  allowed: false,
1380
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1399
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1381
1400
  riskLevel: "high",
1382
1401
  riskType: "consent_missing",
1383
1402
  confidence: 85,
@@ -1476,7 +1495,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
1476
1495
  violatedLayers: ["L2.1"]
1477
1496
  };
1478
1497
  }
1479
- var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1498
+ var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, BASH_LIKE_ACTION_TYPES, ASK_USER_ACTION_TYPES, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1480
1499
  var init_logic_rules = __esm({
1481
1500
  "src/engine/logic-rules.ts"() {
1482
1501
  "use strict";
@@ -1564,6 +1583,13 @@ var init_logic_rules = __esm({
1564
1583
  }));
1565
1584
  })();
1566
1585
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
1586
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
1587
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
1588
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
1589
+ CONSENT_MAX_DISTANCE = 5;
1590
+ CONSENT_TTL_SECONDS = 300;
1591
+ BASH_LIKE_ACTION_TYPES = ["bash", "executebash", "terminal-executebash"];
1592
+ ASK_USER_ACTION_TYPES = ["ask_user_question", "askuser"];
1567
1593
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1568
1594
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
1569
1595
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -697,14 +697,14 @@
697
697
  "reason": "检测到 MongoDB 删除集合 (db.<col>.drop())"
698
698
  },
699
699
  {
700
- "pattern": "(?:del|rmdir|rd)\\s+[\\\\/][a-zA-Z]:",
700
+ "pattern": "(?:del|rmdir|rd)\\s+[\\\\/]?[a-zA-Z]:",
701
701
  "risk_level": "critical",
702
702
  "risk_type": "script_execution",
703
703
  "reason": "检测到递归删除关键路径 (del/rmdir C:\\)",
704
704
  "os": "windows"
705
705
  },
706
706
  {
707
- "pattern": "del\\s+[\\\\/][a-zA-Z]:",
707
+ "pattern": "del\\s+[\\\\/]?[a-zA-Z]:",
708
708
  "risk_level": "critical",
709
709
  "risk_type": "script_execution",
710
710
  "reason": "检测到删除卷根目录文件 (del C:\\)",
@@ -816,7 +816,7 @@
816
816
  "os": "windows"
817
817
  },
818
818
  {
819
- "pattern": "\\bSet-Content\\b[^;&|\\n]*\\b-[A-Za-z]*Stream\\b",
819
+ "pattern": "\\bSet-Content\\b[^;&|\\n]*\\s-[A-Za-z]*Stream\\b",
820
820
  "risk_level": "high",
821
821
  "risk_type": "script_execution",
822
822
  "reason": "检测到写入 NTFS 备用数据流(用于隐藏载荷)",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "scenario_id": "bank",
3
- "description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync)不受影响。另放行 /dev/null、/dev/stdout、/dev/stderr —— 这三个是 666/可写、仅作流丢弃与输出重定向用的标准设备文件(`2>/dev/null` 是最常见的运维写法),不放行会让大量正常命令被误拦;/dev 下**其余**文件(/dev/mem、/dev/sda、/dev/shm…)照常拦。",
3
+ "description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync)不受影响。另放行 /dev/null、/dev/stdout、/dev/stderr —— 这三个是 666/可写、仅作流丢弃与输出重定向用的标准设备文件(`2>/dev/null` 是最常见的运维写法),不放行会让大量正常命令被误拦;/dev 下**其余**文件(/dev/mem、/dev/sda、/dev/shm…)照常拦。。另:`/bin/ls`、`/usr/bin/python3` 这类**命令的绝对路径写法**属正常执行,不算路径访问(2026-10 修误报);但用 `>/cp/mv/tee/install/ln` 往这些目录**写文件**仍然拦。",
4
4
  "extra_command_patterns": [
5
5
  {
6
- "pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$)|dev/null(?=[\\s;&|<>)\"'`]|$)|dev/stdout(?=[\\s;&|<>)\"'`]|$)|dev/stderr(?=[\\s;&|<>)\"'`]|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
6
+ "pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$)|dev/null(?=[\\s;&|<>)\"'`]|$)|dev/stdout(?=[\\s;&|<>)\"'`]|$)|dev/stderr(?=[\\s;&|<>)\"'`]|$)|(?:usr/)?(?:local/)?s?bin/(?!.*\\.\\.)[^\\s;&|>\"'`]*(?=[\\s;&|<>)\"'`]|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
7
7
  "risk_level": "high",
8
8
  "risk_type": "file_access",
9
9
  "reason": "bank 场景:访问允许目录(/home/sandbox、/opt/sandbox、/tmp,以及 /dev/null、/dev/stdout、/dev/stderr 三个标准流重定向目标)之外的路径(含根目录 /),或用 .. 逃逸 / ~其它用户"
@@ -13,6 +13,18 @@
13
13
  "risk_level": "critical",
14
14
  "risk_type": "network_access",
15
15
  "reason": "bank 场景:禁止远程访问 / 横向移动(ssh / scp / sftp / sshpass / ssh-copy-id / rsync -e ssh)"
16
+ },
17
+ {
18
+ "pattern": "(?:^|[\\s;&|(])(?:>|>>|\\btee\\b|\\bcp\\b|\\bmv\\b|\\binstall\\b|\\bln\\b)[^;&|\\n]*?/(?:usr/)?(?:local/)?s?bin/",
19
+ "risk_level": "high",
20
+ "risk_type": "file_access",
21
+ "reason": "bank 场景:向系统可执行目录(/bin、/sbin、/usr/bin、/usr/local/bin 等)写入文件 —— 可执行路径本身作为命令运行是放行的,但把它当写目标不行"
22
+ },
23
+ {
24
+ "pattern": "(?:^|[;&|(\\n])\\s*(?:sudo\\s+)?crontab\\s+(?!-l\\b)\\S",
25
+ "risk_level": "high",
26
+ "risk_type": "persistence",
27
+ "reason": "bank 场景:禁止写系统定时任务(crontab 安装/编辑/删除)——写操作落在 /var/spool/cron,属白名单外,且是持久化后门;`crontab -l` 只读放行"
16
28
  }
17
29
  ],
18
30
  "extra_injection_keywords": [],
package/dist/server.js CHANGED
@@ -1672,9 +1672,9 @@ function runLogicCheck(input, opts) {
1672
1672
  }
1673
1673
  }
1674
1674
  }
1675
- const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
1675
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1676
1676
  if (pwdResult) return pwdResult;
1677
- const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
1677
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType, aNext);
1678
1678
  if (userDelResult) return userDelResult;
1679
1679
  const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
1680
1680
  if (comboResult) return comboResult;
@@ -1870,28 +1870,48 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
1870
1870
  }
1871
1871
  return null;
1872
1872
  }
1873
- function hasUserConsent(actionHistory) {
1874
- return actionHistory.some((h) => {
1875
- const t = String(h.actionType || "").toLowerCase();
1876
- return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1877
- });
1878
- }
1879
- function hasWeakConsent(actionHistory) {
1880
- return actionHistory.some((h) => {
1881
- if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1882
- return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1883
- });
1873
+ function isAskUserAction(actionType) {
1874
+ if (ASK_USER_ACTION_TYPES.includes(actionType)) return true;
1875
+ return actionType.endsWith("-askuser");
1876
+ }
1877
+ function consentWithinTtl(asked, aNext) {
1878
+ const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
1879
+ const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
1880
+ const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
1881
+ if (ttl <= 0) return true;
1882
+ const askedAt = String(asked.calledAt || "").trim();
1883
+ const nextAt = String(aNext?.calledAt || "").trim();
1884
+ if (!askedAt || !nextAt) return true;
1885
+ const a = Date.parse(askedAt);
1886
+ const b = Date.parse(nextAt);
1887
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
1888
+ return Math.abs(b - a) <= ttl * 1e3;
1889
+ }
1890
+ function hasValidConsent(actionHistory, aNext) {
1891
+ if (!actionHistory.length) return false;
1892
+ const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
1893
+ for (let i = tail.length - 1; i >= 0; i--) {
1894
+ const h = tail[i];
1895
+ if (!isAskUserAction(String(h.actionType || "").toLowerCase())) continue;
1896
+ const output = String(h.output || "").trim();
1897
+ if (!output) return false;
1898
+ const question = String(h.actionDetail || "");
1899
+ if (!CONSENT_TOPIC.test(question)) return false;
1900
+ if (CONSENT_NEGATIVE.test(output)) return false;
1901
+ if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
1902
+ return consentWithinTtl(h, aNext);
1903
+ }
1904
+ return false;
1884
1905
  }
1885
- function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1886
- if (actionType !== "bash") return null;
1906
+ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1907
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
1887
1908
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1888
1909
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1889
1910
  if (!isPasswordModify) return null;
1890
- if (hasUserConsent(actionHistory)) return null;
1891
- if (hasWeakConsent(actionHistory)) return null;
1911
+ if (hasValidConsent(actionHistory, aNext)) return null;
1892
1912
  return {
1893
1913
  allowed: false,
1894
- reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1914
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
1895
1915
  riskLevel: "high",
1896
1916
  riskType: "consent_missing",
1897
1917
  confidence: 85,
@@ -1899,16 +1919,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1899
1919
  violatedLayers: ["L2.1"]
1900
1920
  };
1901
1921
  }
1902
- function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
1903
- if (actionType !== "bash") return null;
1922
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
1923
+ if (!BASH_LIKE_ACTION_TYPES.includes(actionType)) return null;
1904
1924
  const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
1905
1925
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1906
1926
  if (!isUserDeletion) return null;
1907
- if (hasUserConsent(actionHistory)) return null;
1908
- if (hasWeakConsent(actionHistory)) return null;
1927
+ if (hasValidConsent(actionHistory, aNext)) return null;
1909
1928
  return {
1910
1929
  allowed: false,
1911
- reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1930
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u7528\u6237\u660E\u786E\u6388\u6743\u7684 ask_user_question\uFF08\u987B\u7528\u6237\u6B63\u9762\u56DE\u7B54\u4E14\u4E0E\u672C\u6B21\u64CD\u4F5C\u76F8\u5173\uFF09\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
1912
1931
  riskLevel: "high",
1913
1932
  riskType: "consent_missing",
1914
1933
  confidence: 85,
@@ -2007,7 +2026,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
2007
2026
  violatedLayers: ["L2.1"]
2008
2027
  };
2009
2028
  }
2010
- var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2029
+ var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, CONSENT_TOPIC, CONSENT_NEGATIVE, CONSENT_AFFIRMATIVE, CONSENT_INPUT_REQUEST, CONSENT_MAX_DISTANCE, CONSENT_TTL_SECONDS, BASH_LIKE_ACTION_TYPES, ASK_USER_ACTION_TYPES, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2011
2030
  var init_logic_rules = __esm({
2012
2031
  "src/engine/logic-rules.ts"() {
2013
2032
  "use strict";
@@ -2095,6 +2114,13 @@ var init_logic_rules = __esm({
2095
2114
  }));
2096
2115
  })();
2097
2116
  CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2117
+ CONSENT_NEGATIVE = /(?:否|不|别|取消|拒绝|停止|终止|算了|\bno\b|\bnope\b|\bcancel\b|\babort\b|\bstop\b|\bdeny\b|\breject\b|\bdecline\b|\bdon'?t\b|\bdo\s+not\b)/i;
2118
+ CONSENT_AFFIRMATIVE = /(?:是|好|可以|行|嗯|对|确认|同意|继续|执行|删除|删掉|移除|修改|重置|改密|\byes\b|\by\b|\byep\b|\byeah\b|\bok\b|\bokay\b|\bconfirm(?:ed)?\b|\bproceed\b|\bcontinue\b|\bsure\b|\bapprove\b|\baccept\b|\bagree\b|\bdo\s+it\b|\bgo\s+ahead\b)/i;
2119
+ CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
2120
+ CONSENT_MAX_DISTANCE = 5;
2121
+ CONSENT_TTL_SECONDS = 300;
2122
+ BASH_LIKE_ACTION_TYPES = ["bash", "executebash", "terminal-executebash"];
2123
+ ASK_USER_ACTION_TYPES = ["ask_user_question", "askuser"];
2098
2124
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2099
2125
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2100
2126
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -12130,7 +12156,10 @@ async function analyzeRoute(c) {
12130
12156
  aNext: {
12131
12157
  actionType: req.a_next.action_type,
12132
12158
  actionDetail: req.a_next.action_detail,
12133
- instanceId
12159
+ instanceId,
12160
+ // called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
12161
+ // 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
12162
+ calledAt: req.a_next.called_at
12134
12163
  },
12135
12164
  reason: req.reason,
12136
12165
  osType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.17.12",
3
+ "version": "0.17.14",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",