@kenhkl/agent-moss 0.17.11 → 0.17.13
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 +50 -26
- package/dist/engine/coordinator.d.ts +2 -0
- package/dist/engine/coordinator.js +42 -22
- package/dist/rules/l1_rules.json +3 -3
- package/dist/scenarios/bank/rules.json +14 -2
- package/dist/server.d.ts +10 -1
- package/dist/server.js +50 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1295,7 +1295,7 @@ function sessionSweepAction() {
|
|
|
1295
1295
|
function noteSessionAccess(key, sessionId) {
|
|
1296
1296
|
const limit = sessionSweepThreshold();
|
|
1297
1297
|
if (limit <= 0 || !sessionId) return [0, false];
|
|
1298
|
-
const now =
|
|
1298
|
+
const now = performance.now() / 1e3;
|
|
1299
1299
|
const cutoff = now - sessionSweepWindow();
|
|
1300
1300
|
let entries = (sessionWindow.get(key) ?? []).filter(([t]) => t >= cutoff);
|
|
1301
1301
|
entries.push([now, sessionId]);
|
|
@@ -1317,7 +1317,7 @@ function queueAbuseAction() {
|
|
|
1317
1317
|
function noteQueueReject(key) {
|
|
1318
1318
|
const limit = queueAbuseThreshold();
|
|
1319
1319
|
if (limit <= 0 || !key) return [0, false];
|
|
1320
|
-
const now =
|
|
1320
|
+
const now = performance.now() / 1e3;
|
|
1321
1321
|
const cutoff = now - queueAbuseWindow();
|
|
1322
1322
|
let entries = (queueRejects.get(key) ?? []).filter((t) => t >= cutoff);
|
|
1323
1323
|
entries.push(now);
|
|
@@ -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,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
2162
2162
|
}
|
|
2163
2163
|
return null;
|
|
2164
2164
|
}
|
|
2165
|
-
function
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2165
|
+
function consentWithinTtl(asked, aNext) {
|
|
2166
|
+
const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
|
|
2167
|
+
const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
|
|
2168
|
+
const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
|
|
2169
|
+
if (ttl <= 0) return true;
|
|
2170
|
+
const askedAt = String(asked.calledAt || "").trim();
|
|
2171
|
+
const nextAt = String(aNext?.calledAt || "").trim();
|
|
2172
|
+
if (!askedAt || !nextAt) return true;
|
|
2173
|
+
const a = Date.parse(askedAt);
|
|
2174
|
+
const b = Date.parse(nextAt);
|
|
2175
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
|
|
2176
|
+
return Math.abs(b - a) <= ttl * 1e3;
|
|
2177
|
+
}
|
|
2178
|
+
function hasValidConsent(actionHistory, aNext) {
|
|
2179
|
+
if (!actionHistory.length) return false;
|
|
2180
|
+
const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
|
|
2181
|
+
for (let i = tail.length - 1; i >= 0; i--) {
|
|
2182
|
+
const h = tail[i];
|
|
2183
|
+
if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
|
|
2184
|
+
const output = String(h.output || "").trim();
|
|
2185
|
+
if (!output) return false;
|
|
2186
|
+
const question = String(h.actionDetail || "");
|
|
2187
|
+
if (!CONSENT_TOPIC.test(question)) return false;
|
|
2188
|
+
if (CONSENT_NEGATIVE.test(output)) return false;
|
|
2189
|
+
if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
|
|
2190
|
+
return consentWithinTtl(h, aNext);
|
|
2191
|
+
}
|
|
2192
|
+
return false;
|
|
2176
2193
|
}
|
|
2177
|
-
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
2194
|
+
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
2178
2195
|
if (actionType !== "bash") return null;
|
|
2179
2196
|
const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
|
|
2180
2197
|
const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
|
|
2181
2198
|
if (!isPasswordModify) return null;
|
|
2182
|
-
if (
|
|
2183
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
2199
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
2184
2200
|
return {
|
|
2185
2201
|
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\
|
|
2202
|
+
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
2203
|
riskLevel: "high",
|
|
2188
2204
|
riskType: "consent_missing",
|
|
2189
2205
|
confidence: 85,
|
|
@@ -2191,16 +2207,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
|
2191
2207
|
violatedLayers: ["L2.1"]
|
|
2192
2208
|
};
|
|
2193
2209
|
}
|
|
2194
|
-
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
|
|
2210
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
2195
2211
|
if (actionType !== "bash") return null;
|
|
2196
2212
|
const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
|
|
2197
2213
|
const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
|
|
2198
2214
|
if (!isUserDeletion) return null;
|
|
2199
|
-
if (
|
|
2200
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
2215
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
2201
2216
|
return {
|
|
2202
2217
|
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\
|
|
2218
|
+
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
2219
|
riskLevel: "high",
|
|
2205
2220
|
riskType: "consent_missing",
|
|
2206
2221
|
confidence: 85,
|
|
@@ -2299,7 +2314,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
2299
2314
|
violatedLayers: ["L2.1"]
|
|
2300
2315
|
};
|
|
2301
2316
|
}
|
|
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;
|
|
2317
|
+
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, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
2303
2318
|
var init_logic_rules = __esm({
|
|
2304
2319
|
"src/engine/logic-rules.ts"() {
|
|
2305
2320
|
"use strict";
|
|
@@ -2387,6 +2402,11 @@ var init_logic_rules = __esm({
|
|
|
2387
2402
|
}));
|
|
2388
2403
|
})();
|
|
2389
2404
|
CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
|
|
2405
|
+
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;
|
|
2406
|
+
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;
|
|
2407
|
+
CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
|
|
2408
|
+
CONSENT_MAX_DISTANCE = 5;
|
|
2409
|
+
CONSENT_TTL_SECONDS = 300;
|
|
2390
2410
|
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
2411
|
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
2392
2412
|
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
@@ -12459,7 +12479,10 @@ async function analyzeRoute(c) {
|
|
|
12459
12479
|
aNext: {
|
|
12460
12480
|
actionType: req.a_next.action_type,
|
|
12461
12481
|
actionDetail: req.a_next.action_detail,
|
|
12462
|
-
instanceId
|
|
12482
|
+
instanceId,
|
|
12483
|
+
// called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
|
|
12484
|
+
// 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
|
|
12485
|
+
calledAt: req.a_next.called_at
|
|
12463
12486
|
},
|
|
12464
12487
|
reason: req.reason,
|
|
12465
12488
|
osType,
|
|
@@ -13199,6 +13222,7 @@ var init_console = __esm({
|
|
|
13199
13222
|
// src/server.ts
|
|
13200
13223
|
var server_exports = {};
|
|
13201
13224
|
__export(server_exports, {
|
|
13225
|
+
RateLimiter: () => RateLimiter,
|
|
13202
13226
|
createApp: () => createApp,
|
|
13203
13227
|
findFreePort: () => findFreePort,
|
|
13204
13228
|
logDeploymentSelfCheck: () => logDeploymentSelfCheck,
|
|
@@ -13557,7 +13581,7 @@ var init_server = __esm({
|
|
|
13557
13581
|
allow(key) {
|
|
13558
13582
|
if (!this.enabled) return true;
|
|
13559
13583
|
const burst = this.burst ?? Math.max(this.qps, 1) * 2;
|
|
13560
|
-
const now =
|
|
13584
|
+
const now = performance.now() / 1e3;
|
|
13561
13585
|
const [tokens, last] = this.buckets.get(key) ?? [burst, now];
|
|
13562
13586
|
const next = Math.min(burst, tokens + (now - last) * this.qps);
|
|
13563
13587
|
if (next < 1) {
|
|
@@ -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,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1339
1339
|
}
|
|
1340
1340
|
return null;
|
|
1341
1341
|
}
|
|
1342
|
-
function
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1342
|
+
function consentWithinTtl(asked, aNext) {
|
|
1343
|
+
const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
|
|
1344
|
+
const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
|
|
1345
|
+
const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
|
|
1346
|
+
if (ttl <= 0) return true;
|
|
1347
|
+
const askedAt = String(asked.calledAt || "").trim();
|
|
1348
|
+
const nextAt = String(aNext?.calledAt || "").trim();
|
|
1349
|
+
if (!askedAt || !nextAt) return true;
|
|
1350
|
+
const a = Date.parse(askedAt);
|
|
1351
|
+
const b = Date.parse(nextAt);
|
|
1352
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
|
|
1353
|
+
return Math.abs(b - a) <= ttl * 1e3;
|
|
1354
|
+
}
|
|
1355
|
+
function hasValidConsent(actionHistory, aNext) {
|
|
1356
|
+
if (!actionHistory.length) return false;
|
|
1357
|
+
const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
|
|
1358
|
+
for (let i = tail.length - 1; i >= 0; i--) {
|
|
1359
|
+
const h = tail[i];
|
|
1360
|
+
if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
|
|
1361
|
+
const output = String(h.output || "").trim();
|
|
1362
|
+
if (!output) return false;
|
|
1363
|
+
const question = String(h.actionDetail || "");
|
|
1364
|
+
if (!CONSENT_TOPIC.test(question)) return false;
|
|
1365
|
+
if (CONSENT_NEGATIVE.test(output)) return false;
|
|
1366
|
+
if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
|
|
1367
|
+
return consentWithinTtl(h, aNext);
|
|
1368
|
+
}
|
|
1369
|
+
return false;
|
|
1353
1370
|
}
|
|
1354
|
-
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1371
|
+
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
1355
1372
|
if (actionType !== "bash") return null;
|
|
1356
1373
|
const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
|
|
1357
1374
|
const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
|
|
1358
1375
|
if (!isPasswordModify) return null;
|
|
1359
|
-
if (
|
|
1360
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
1376
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
1361
1377
|
return {
|
|
1362
1378
|
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\
|
|
1379
|
+
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
1380
|
riskLevel: "high",
|
|
1365
1381
|
riskType: "consent_missing",
|
|
1366
1382
|
confidence: 85,
|
|
@@ -1368,16 +1384,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
|
1368
1384
|
violatedLayers: ["L2.1"]
|
|
1369
1385
|
};
|
|
1370
1386
|
}
|
|
1371
|
-
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1387
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
1372
1388
|
if (actionType !== "bash") return null;
|
|
1373
1389
|
const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
|
|
1374
1390
|
const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
|
|
1375
1391
|
if (!isUserDeletion) return null;
|
|
1376
|
-
if (
|
|
1377
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
1392
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
1378
1393
|
return {
|
|
1379
1394
|
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\
|
|
1395
|
+
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
1396
|
riskLevel: "high",
|
|
1382
1397
|
riskType: "consent_missing",
|
|
1383
1398
|
confidence: 85,
|
|
@@ -1476,7 +1491,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
1476
1491
|
violatedLayers: ["L2.1"]
|
|
1477
1492
|
};
|
|
1478
1493
|
}
|
|
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;
|
|
1494
|
+
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, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
1480
1495
|
var init_logic_rules = __esm({
|
|
1481
1496
|
"src/engine/logic-rules.ts"() {
|
|
1482
1497
|
"use strict";
|
|
@@ -1564,6 +1579,11 @@ var init_logic_rules = __esm({
|
|
|
1564
1579
|
}));
|
|
1565
1580
|
})();
|
|
1566
1581
|
CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
|
|
1582
|
+
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;
|
|
1583
|
+
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;
|
|
1584
|
+
CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
|
|
1585
|
+
CONSENT_MAX_DISTANCE = 5;
|
|
1586
|
+
CONSENT_TTL_SECONDS = 300;
|
|
1567
1587
|
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
1588
|
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
1569
1589
|
_IFA = loadLogicRules().indirect_file_access ?? {};
|
package/dist/rules/l1_rules.json
CHANGED
|
@@ -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]*\\
|
|
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.d.ts
CHANGED
|
@@ -13,6 +13,15 @@ import { Hono } from 'hono';
|
|
|
13
13
|
* 与 Python `agent_moss/server/app.py::_log_deployment_selfcheck` 对齐。
|
|
14
14
|
*/
|
|
15
15
|
declare function logDeploymentSelfCheck(): void;
|
|
16
|
+
/** 令牌桶(按主体/来源)。qps<=0 时不生效(默认)。 */
|
|
17
|
+
declare class RateLimiter {
|
|
18
|
+
private qps;
|
|
19
|
+
private burst?;
|
|
20
|
+
private buckets;
|
|
21
|
+
constructor(qps: number, burst?: number | undefined);
|
|
22
|
+
get enabled(): boolean;
|
|
23
|
+
allow(key: string): boolean;
|
|
24
|
+
}
|
|
16
25
|
declare function createApp(): Hono;
|
|
17
26
|
/**
|
|
18
27
|
* 从 startPort 开始找第一个可用端口(照搬 OpenDesk findFreePort 策略)。
|
|
@@ -45,4 +54,4 @@ declare function startUnixSocketServer(socketPath?: string): Promise<{
|
|
|
45
54
|
close: () => Promise<void>;
|
|
46
55
|
}>;
|
|
47
56
|
|
|
48
|
-
export { createApp, findFreePort, logDeploymentSelfCheck, startServer, startUnixSocketServer };
|
|
57
|
+
export { RateLimiter, createApp, findFreePort, logDeploymentSelfCheck, startServer, startUnixSocketServer };
|
package/dist/server.js
CHANGED
|
@@ -1141,7 +1141,7 @@ function sessionSweepAction() {
|
|
|
1141
1141
|
function noteSessionAccess(key, sessionId) {
|
|
1142
1142
|
const limit = sessionSweepThreshold();
|
|
1143
1143
|
if (limit <= 0 || !sessionId) return [0, false];
|
|
1144
|
-
const now =
|
|
1144
|
+
const now = performance.now() / 1e3;
|
|
1145
1145
|
const cutoff = now - sessionSweepWindow();
|
|
1146
1146
|
let entries = (sessionWindow.get(key) ?? []).filter(([t]) => t >= cutoff);
|
|
1147
1147
|
entries.push([now, sessionId]);
|
|
@@ -1163,7 +1163,7 @@ function queueAbuseAction() {
|
|
|
1163
1163
|
function noteQueueReject(key) {
|
|
1164
1164
|
const limit = queueAbuseThreshold();
|
|
1165
1165
|
if (limit <= 0 || !key) return [0, false];
|
|
1166
|
-
const now =
|
|
1166
|
+
const now = performance.now() / 1e3;
|
|
1167
1167
|
const cutoff = now - queueAbuseWindow();
|
|
1168
1168
|
let entries = (queueRejects.get(key) ?? []).filter((t) => t >= cutoff);
|
|
1169
1169
|
entries.push(now);
|
|
@@ -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,44 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
1870
1870
|
}
|
|
1871
1871
|
return null;
|
|
1872
1872
|
}
|
|
1873
|
-
function
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1873
|
+
function consentWithinTtl(asked, aNext) {
|
|
1874
|
+
const raw = String(process.env.AGENT_MOSS_CONSENT_TTL_SECONDS || "").trim();
|
|
1875
|
+
const parsed = raw ? Number(raw) : CONSENT_TTL_SECONDS;
|
|
1876
|
+
const ttl = Number.isFinite(parsed) ? parsed : CONSENT_TTL_SECONDS;
|
|
1877
|
+
if (ttl <= 0) return true;
|
|
1878
|
+
const askedAt = String(asked.calledAt || "").trim();
|
|
1879
|
+
const nextAt = String(aNext?.calledAt || "").trim();
|
|
1880
|
+
if (!askedAt || !nextAt) return true;
|
|
1881
|
+
const a = Date.parse(askedAt);
|
|
1882
|
+
const b = Date.parse(nextAt);
|
|
1883
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return true;
|
|
1884
|
+
return Math.abs(b - a) <= ttl * 1e3;
|
|
1885
|
+
}
|
|
1886
|
+
function hasValidConsent(actionHistory, aNext) {
|
|
1887
|
+
if (!actionHistory.length) return false;
|
|
1888
|
+
const tail = actionHistory.slice(-CONSENT_MAX_DISTANCE);
|
|
1889
|
+
for (let i = tail.length - 1; i >= 0; i--) {
|
|
1890
|
+
const h = tail[i];
|
|
1891
|
+
if (String(h.actionType || "").toLowerCase() !== "ask_user_question") continue;
|
|
1892
|
+
const output = String(h.output || "").trim();
|
|
1893
|
+
if (!output) return false;
|
|
1894
|
+
const question = String(h.actionDetail || "");
|
|
1895
|
+
if (!CONSENT_TOPIC.test(question)) return false;
|
|
1896
|
+
if (CONSENT_NEGATIVE.test(output)) return false;
|
|
1897
|
+
if (!CONSENT_INPUT_REQUEST.test(question) && !CONSENT_AFFIRMATIVE.test(output)) return false;
|
|
1898
|
+
return consentWithinTtl(h, aNext);
|
|
1899
|
+
}
|
|
1900
|
+
return false;
|
|
1884
1901
|
}
|
|
1885
|
-
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1902
|
+
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
1886
1903
|
if (actionType !== "bash") return null;
|
|
1887
1904
|
const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
|
|
1888
1905
|
const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
|
|
1889
1906
|
if (!isPasswordModify) return null;
|
|
1890
|
-
if (
|
|
1891
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
1907
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
1892
1908
|
return {
|
|
1893
1909
|
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\
|
|
1910
|
+
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
1911
|
riskLevel: "high",
|
|
1896
1912
|
riskType: "consent_missing",
|
|
1897
1913
|
confidence: 85,
|
|
@@ -1899,16 +1915,15 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
|
1899
1915
|
violatedLayers: ["L2.1"]
|
|
1900
1916
|
};
|
|
1901
1917
|
}
|
|
1902
|
-
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1918
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType, aNext) {
|
|
1903
1919
|
if (actionType !== "bash") return null;
|
|
1904
1920
|
const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
|
|
1905
1921
|
const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
|
|
1906
1922
|
if (!isUserDeletion) return null;
|
|
1907
|
-
if (
|
|
1908
|
-
if (hasWeakConsent(actionHistory)) return null;
|
|
1923
|
+
if (hasValidConsent(actionHistory, aNext)) return null;
|
|
1909
1924
|
return {
|
|
1910
1925
|
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\
|
|
1926
|
+
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
1927
|
riskLevel: "high",
|
|
1913
1928
|
riskType: "consent_missing",
|
|
1914
1929
|
confidence: 85,
|
|
@@ -2007,7 +2022,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
|
2007
2022
|
violatedLayers: ["L2.1"]
|
|
2008
2023
|
};
|
|
2009
2024
|
}
|
|
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;
|
|
2025
|
+
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, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
|
|
2011
2026
|
var init_logic_rules = __esm({
|
|
2012
2027
|
"src/engine/logic-rules.ts"() {
|
|
2013
2028
|
"use strict";
|
|
@@ -2095,6 +2110,11 @@ var init_logic_rules = __esm({
|
|
|
2095
2110
|
}));
|
|
2096
2111
|
})();
|
|
2097
2112
|
CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
|
|
2113
|
+
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;
|
|
2114
|
+
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;
|
|
2115
|
+
CONSENT_INPUT_REQUEST = /(?:请|输入|提供|填写|给出|告诉我).{0,8}(?:新的?)?(?:密码|口令|passwd|password)/i;
|
|
2116
|
+
CONSENT_MAX_DISTANCE = 5;
|
|
2117
|
+
CONSENT_TTL_SECONDS = 300;
|
|
2098
2118
|
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
2119
|
DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
|
|
2100
2120
|
_IFA = loadLogicRules().indirect_file_access ?? {};
|
|
@@ -12130,7 +12150,10 @@ async function analyzeRoute(c) {
|
|
|
12130
12150
|
aNext: {
|
|
12131
12151
|
actionType: req.a_next.action_type,
|
|
12132
12152
|
actionDetail: req.a_next.action_detail,
|
|
12133
|
-
instanceId
|
|
12153
|
+
instanceId,
|
|
12154
|
+
// called_at 一并透传:L2 授权(consent)的时效窗需要「提问时刻」与
|
|
12155
|
+
// 「本次动作时刻」两侧都有时间戳才生效(对齐 Python routes.py)。
|
|
12156
|
+
calledAt: req.a_next.called_at
|
|
12134
12157
|
},
|
|
12135
12158
|
reason: req.reason,
|
|
12136
12159
|
osType,
|
|
@@ -12933,7 +12956,7 @@ var RateLimiter = class {
|
|
|
12933
12956
|
allow(key) {
|
|
12934
12957
|
if (!this.enabled) return true;
|
|
12935
12958
|
const burst = this.burst ?? Math.max(this.qps, 1) * 2;
|
|
12936
|
-
const now =
|
|
12959
|
+
const now = performance.now() / 1e3;
|
|
12937
12960
|
const [tokens, last] = this.buckets.get(key) ?? [burst, now];
|
|
12938
12961
|
const next = Math.min(burst, tokens + (now - last) * this.qps);
|
|
12939
12962
|
if (next < 1) {
|
|
@@ -13194,6 +13217,7 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
|
|
|
13194
13217
|
};
|
|
13195
13218
|
}
|
|
13196
13219
|
export {
|
|
13220
|
+
RateLimiter,
|
|
13197
13221
|
createApp,
|
|
13198
13222
|
findFreePort,
|
|
13199
13223
|
logDeploymentSelfCheck,
|