@kenhkl/agent-moss 0.15.7 → 0.16.3
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 +1773 -1660
- package/dist/engine/coordinator.d.ts +12 -1
- package/dist/engine/coordinator.js +1663 -850
- package/dist/rules/l1_rules.json +1306 -4
- package/dist/rules/l2_rules.json +285 -243
- package/dist/scenarios/bank/rules.json +5 -5
- package/dist/server.d.ts +14 -1
- package/dist/server.js +3717 -3434
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,45 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
7
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
8
|
-
}) : x)(function(x) {
|
|
9
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
10
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
11
|
-
});
|
|
12
4
|
var __esm = (fn, res) => function __init() {
|
|
13
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
14
6
|
};
|
|
15
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
-
};
|
|
18
7
|
var __export = (target, all) => {
|
|
19
8
|
for (var name in all)
|
|
20
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
10
|
};
|
|
22
|
-
var __copyProps = (to, from, except, desc) => {
|
|
23
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
-
for (let key of __getOwnPropNames(from))
|
|
25
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
26
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
27
|
-
}
|
|
28
|
-
return to;
|
|
29
|
-
};
|
|
30
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
11
|
|
|
32
12
|
// node_modules/tsup/assets/esm_shims.js
|
|
33
13
|
import path from "path";
|
|
34
14
|
import { fileURLToPath } from "url";
|
|
35
|
-
var getFilename, getDirname, __dirname
|
|
15
|
+
var getFilename, getDirname, __dirname;
|
|
36
16
|
var init_esm_shims = __esm({
|
|
37
17
|
"node_modules/tsup/assets/esm_shims.js"() {
|
|
38
18
|
"use strict";
|
|
39
19
|
getFilename = () => fileURLToPath(import.meta.url);
|
|
40
20
|
getDirname = () => path.dirname(getFilename());
|
|
41
21
|
__dirname = /* @__PURE__ */ getDirname();
|
|
42
|
-
__filename = /* @__PURE__ */ getFilename();
|
|
43
22
|
}
|
|
44
23
|
});
|
|
45
24
|
|
|
@@ -127,278 +106,31 @@ var init_paths = __esm({
|
|
|
127
106
|
}
|
|
128
107
|
});
|
|
129
108
|
|
|
130
|
-
// src/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
detectProvider: () => detectProvider,
|
|
134
|
-
getCustomRules: () => getCustomRules,
|
|
135
|
-
getLLMConfig: () => getLLMConfig,
|
|
136
|
-
getLLMFailMode: () => getLLMFailMode,
|
|
137
|
-
getProviderHeaders: () => getProviderHeaders,
|
|
138
|
-
getRetryConfig: () => getRetryConfig,
|
|
139
|
-
getTimeoutConfig: () => getTimeoutConfig,
|
|
140
|
-
isBrainEnabled: () => isBrainEnabled,
|
|
141
|
-
isFormalObserveMode: () => isFormalObserveMode,
|
|
142
|
-
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
143
|
-
isLLMAvailable: () => isLLMAvailable,
|
|
144
|
-
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
145
|
-
probeLLMConnection: () => probeLLMConnection,
|
|
146
|
-
updateLLMConfig: () => updateLLMConfig
|
|
147
|
-
});
|
|
148
|
-
import fs from "fs";
|
|
149
|
-
import path2 from "path";
|
|
150
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
151
|
-
function loadConfigFile(filePath) {
|
|
152
|
-
try {
|
|
153
|
-
const raw = fs.readFileSync(filePath, "utf-8");
|
|
154
|
-
return JSON.parse(raw);
|
|
155
|
-
} catch {
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function resolveConfigPath() {
|
|
160
|
-
const envPath = process.env.AGENT_MOSS_CONFIG_PATH;
|
|
161
|
-
if (envPath) return envPath;
|
|
162
|
-
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
163
|
-
if (fs.existsSync(userConfig)) return userConfig;
|
|
164
|
-
const defaultCandidates = [
|
|
165
|
-
path2.join(__dirname, "config.json.example"),
|
|
166
|
-
path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example")
|
|
167
|
-
];
|
|
168
|
-
const defaultConfig = defaultCandidates.find((p) => fs.existsSync(p));
|
|
169
|
-
if (defaultConfig) {
|
|
170
|
-
const configDir = path2.dirname(userConfig);
|
|
171
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
172
|
-
const pkgConfig = loadConfigFile(defaultConfig);
|
|
173
|
-
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
174
|
-
fs.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
175
|
-
console.log(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
176
|
-
console.log(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
177
|
-
}
|
|
178
|
-
return userConfig;
|
|
179
|
-
}
|
|
180
|
-
function loadLLMConfigFromFile(configPath) {
|
|
181
|
-
const data = loadConfigFile(configPath);
|
|
182
|
-
if (!data?.llm) return {};
|
|
183
|
-
const llm = data.llm;
|
|
184
|
-
return {
|
|
185
|
-
provider: llm.provider || "",
|
|
186
|
-
baseUrl: llm.base_url || "",
|
|
187
|
-
apiKey: llm.api_key || "",
|
|
188
|
-
model: llm.model || "",
|
|
189
|
-
temperature: llm.temperature ?? 0.1,
|
|
190
|
-
maxTokens: llm.max_tokens ?? 4096,
|
|
191
|
-
timeout: llm.timeout ?? 40
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
function loadTimeoutRetryFromFile(configPath) {
|
|
195
|
-
const data = loadConfigFile(configPath);
|
|
196
|
-
const t = data?.timeout ?? {};
|
|
197
|
-
const r = data?.retry ?? {};
|
|
198
|
-
return {
|
|
199
|
-
timeout: {
|
|
200
|
-
totalTimeout: Number(t.total_timeout ?? 90),
|
|
201
|
-
prompt1Timeout: Number(t.prompt1_timeout ?? 40),
|
|
202
|
-
prompt2Timeout: Number(t.prompt2_timeout ?? 20),
|
|
203
|
-
stepInterval: Number(t.step_interval ?? 0)
|
|
204
|
-
},
|
|
205
|
-
retry: {
|
|
206
|
-
maxRetries: Number(r.max_retries ?? 2),
|
|
207
|
-
retryInterval: Number(r.retry_interval ?? 2)
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function getTimeoutConfig() {
|
|
212
|
-
return timeoutConfig;
|
|
213
|
-
}
|
|
214
|
-
function getRetryConfig() {
|
|
215
|
-
return retryConfig;
|
|
216
|
-
}
|
|
217
|
-
function wildcardToRegExp(pattern) {
|
|
218
|
-
return pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
219
|
-
}
|
|
220
|
-
function loadCustomRules() {
|
|
221
|
-
const rules = [];
|
|
222
|
-
try {
|
|
223
|
-
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
224
|
-
if (raw) {
|
|
225
|
-
const parsed = JSON.parse(raw);
|
|
226
|
-
if (Array.isArray(parsed)) {
|
|
227
|
-
for (const r of parsed) {
|
|
228
|
-
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} catch {
|
|
233
|
-
}
|
|
234
|
-
for (const rulesPath of USER_RULES_PATHS) {
|
|
235
|
-
try {
|
|
236
|
-
if (!fs.existsSync(rulesPath)) continue;
|
|
237
|
-
const data = JSON.parse(fs.readFileSync(rulesPath, "utf-8"));
|
|
238
|
-
const actions = Array.isArray(data?.sensitive_actions) ? data.sensitive_actions : [];
|
|
239
|
-
for (const r of actions) {
|
|
240
|
-
const p = r?.pattern;
|
|
241
|
-
if (!p) continue;
|
|
242
|
-
rules.push({ pattern: wildcardToRegExp(p), action: "Deny", severity: "high" });
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return rules;
|
|
249
|
-
}
|
|
250
|
-
function getLLMConfig() {
|
|
251
|
-
return llmConfig;
|
|
252
|
-
}
|
|
253
|
-
function updateLLMConfig(partial) {
|
|
254
|
-
llmConfig = { ...llmConfig, ...partial };
|
|
255
|
-
}
|
|
256
|
-
function getCustomRules() {
|
|
257
|
-
return customRules;
|
|
258
|
-
}
|
|
259
|
-
function isLLMAvailable() {
|
|
260
|
-
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
261
|
-
if (!layerEnabledOrDefault("L3_llm_analysis")) return false;
|
|
262
|
-
return llmConfig.apiKey.length > 0;
|
|
263
|
-
}
|
|
264
|
-
async function probeLLMConnection(timeoutMs = 8e3) {
|
|
265
|
-
if (!isLLMAvailable()) return false;
|
|
266
|
-
const OpenAI3 = (await import("openai")).default;
|
|
267
|
-
const config = getLLMConfig();
|
|
268
|
-
const provider = detectProvider(config.baseUrl, config.provider);
|
|
269
|
-
const extraHeaders = getProviderHeaders(provider);
|
|
270
|
-
const client = new OpenAI3({
|
|
271
|
-
baseURL: config.baseUrl,
|
|
272
|
-
apiKey: config.apiKey,
|
|
273
|
-
timeout: timeoutMs,
|
|
274
|
-
maxRetries: 0,
|
|
275
|
-
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
276
|
-
});
|
|
277
|
-
try {
|
|
278
|
-
const resp = await client.chat.completions.create({
|
|
279
|
-
model: config.model,
|
|
280
|
-
messages: [{ role: "user", content: "ping" }],
|
|
281
|
-
max_tokens: 1
|
|
282
|
-
});
|
|
283
|
-
return !!(resp && resp.choices && resp.choices[0]);
|
|
284
|
-
} catch {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function getLLMFailMode() {
|
|
289
|
-
const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
|
|
290
|
-
return val === "fail_closed" ? "fail_closed" : "fail_open";
|
|
291
|
-
}
|
|
292
|
-
function detectProvider(baseUrl, providerHint = "") {
|
|
293
|
-
if (providerHint) {
|
|
294
|
-
const hint = providerHint.toLowerCase();
|
|
295
|
-
if (["claude", "anthropic"].includes(hint)) return "anthropic";
|
|
296
|
-
if (["google", "gemini"].includes(hint)) return "gemini";
|
|
297
|
-
if (["zai-coding-plan", "zhipu-coding-plan", "zhipuai-coding-plan"].includes(hint)) return "zai-coding-plan";
|
|
298
|
-
if (["zai", "z-ai", "z.ai", "zai-cn", "zai-china", "zhipu", "glm-cn", "bigmodel"].includes(hint)) return "zhipu";
|
|
299
|
-
if (["xai", "xai-grok"].includes(hint)) return "xai";
|
|
300
|
-
if (["minimax", "minimax-openai"].includes(hint)) return "minimax";
|
|
301
|
-
if (["minimax-anthropic"].includes(hint)) return "minimax-anthropic";
|
|
302
|
-
if (["other", "custom", "local"].includes(hint)) return "openai-compatible";
|
|
303
|
-
return hint;
|
|
304
|
-
}
|
|
305
|
-
const url = baseUrl.toLowerCase();
|
|
306
|
-
if (url.includes("openrouter.ai")) return "openrouter";
|
|
307
|
-
if (url.includes("api.openai.com")) return "openai";
|
|
308
|
-
if (url.includes("anthropic.com") || url.includes("api.anthropic.com")) return "anthropic";
|
|
309
|
-
if (url.includes("generativelanguage.googleapis.com") || url.includes("gemini")) return "gemini";
|
|
310
|
-
if (url.includes("api.deepseek.com")) return "deepseek";
|
|
311
|
-
if (url.includes("api.groq.com")) return "groq";
|
|
312
|
-
if (url.includes("api.mistral.ai")) return "mistral";
|
|
313
|
-
if (url.includes("api.together.xyz")) return "together";
|
|
314
|
-
if (url.includes("api.x.ai")) return "xai";
|
|
315
|
-
if (url.includes("api.minimaxi.com")) return "minimax";
|
|
316
|
-
if (url.includes("api-ai.gitcode.com")) return "gitcode";
|
|
317
|
-
if (url.includes("api.z.ai")) return "zai-coding-plan";
|
|
318
|
-
if (url.includes("open.bigmodel.cn") || url.includes("bigmodel")) return "zhipu";
|
|
319
|
-
if (url.includes(":11434")) return "ollama";
|
|
320
|
-
return "openai-compatible";
|
|
321
|
-
}
|
|
322
|
-
function getProviderHeaders(provider) {
|
|
323
|
-
return PROVIDER_HEADERS[provider] || {};
|
|
324
|
-
}
|
|
325
|
-
function isHeuristicEnabled() {
|
|
326
|
-
if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
|
|
327
|
-
return layerEnabledOrDefault("L1_heuristic");
|
|
328
|
-
}
|
|
329
|
-
function layerEnabledOrDefault(key) {
|
|
330
|
-
try {
|
|
331
|
-
const { isLayerEnabled: isLayerEnabled2 } = __require("../infra/runtime-config");
|
|
332
|
-
return isLayerEnabled2(key);
|
|
333
|
-
} catch {
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function isBrainEnabled() {
|
|
338
|
-
return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
|
|
109
|
+
// src/engine/shell-split.ts
|
|
110
|
+
function hasCommandSubstitution(text) {
|
|
111
|
+
return CMD_SUBSTITUTION.test(text);
|
|
339
112
|
}
|
|
340
|
-
function
|
|
341
|
-
|
|
342
|
-
return layerEnabledOrDefault("L2_logic_rules");
|
|
113
|
+
function splitCommandSegments(command) {
|
|
114
|
+
return command.split(CMD_SEPARATORS).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
343
115
|
}
|
|
344
|
-
function
|
|
345
|
-
|
|
116
|
+
function extractFirstCommand(command) {
|
|
117
|
+
const segments = splitCommandSegments(command);
|
|
118
|
+
return segments.length ? segments[0] : "";
|
|
346
119
|
}
|
|
347
|
-
var
|
|
348
|
-
var
|
|
349
|
-
"src/
|
|
120
|
+
var CMD_SEPARATORS, CMD_SUBSTITUTION;
|
|
121
|
+
var init_shell_split = __esm({
|
|
122
|
+
"src/engine/shell-split.ts"() {
|
|
350
123
|
"use strict";
|
|
351
124
|
init_esm_shims();
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const configPath = resolveConfigPath();
|
|
355
|
-
const fileConfig = fs.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
356
|
-
const tr = loadTimeoutRetryFromFile(configPath);
|
|
357
|
-
timeoutConfig = tr.timeout;
|
|
358
|
-
retryConfig = tr.retry;
|
|
359
|
-
return {
|
|
360
|
-
provider: process.env.AGENT_MOSS_LLM_PROVIDER || fileConfig.provider || "openai",
|
|
361
|
-
baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || fileConfig.baseUrl || "https://api.openai.com/v1",
|
|
362
|
-
apiKey: process.env.AGENT_MOSS_LLM_API_KEY || process.env.OPENAI_API_KEY || fileConfig.apiKey || "",
|
|
363
|
-
model: process.env.AGENT_MOSS_LLM_MODEL || fileConfig.model || "gpt-4o",
|
|
364
|
-
temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || String(fileConfig.temperature ?? "0.1")),
|
|
365
|
-
maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || String(fileConfig.maxTokens ?? "4096"), 10),
|
|
366
|
-
// 优先用 env,否则用 timeout 块的 prompt1Timeout(对齐 py)
|
|
367
|
-
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
368
|
-
};
|
|
369
|
-
})();
|
|
370
|
-
__configModuleDir = (() => {
|
|
371
|
-
try {
|
|
372
|
-
return path2.dirname(fileURLToPath2(import.meta.url));
|
|
373
|
-
} catch {
|
|
374
|
-
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
375
|
-
}
|
|
376
|
-
})();
|
|
377
|
-
USER_RULES_PATHS = [
|
|
378
|
-
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
379
|
-
// 源在 ts/src/rules(一般不存在)
|
|
380
|
-
path2.join(__configModuleDir, "..", "rules", "user_rules.json"),
|
|
381
|
-
// dist: dist/engine/../rules = dist/rules ✅
|
|
382
|
-
path2.join(__configModuleDir, "..", "..", "agent_moss", "rules", "user_rules.json")
|
|
383
|
-
// dev src: ts/src/../../agent_moss
|
|
384
|
-
];
|
|
385
|
-
customRules = loadCustomRules();
|
|
386
|
-
PROVIDER_HEADERS = {
|
|
387
|
-
"openrouter": {
|
|
388
|
-
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM",
|
|
389
|
-
"X-OpenRouter-Title": "AgentMoss"
|
|
390
|
-
},
|
|
391
|
-
"xai": {
|
|
392
|
-
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM"
|
|
393
|
-
}
|
|
394
|
-
};
|
|
125
|
+
CMD_SEPARATORS = /\|\||&&|[|;&\n]/;
|
|
126
|
+
CMD_SUBSTITUTION = /\$\(|`|</;
|
|
395
127
|
}
|
|
396
128
|
});
|
|
397
129
|
|
|
398
130
|
// src/engine/inline-analyzer.ts
|
|
399
131
|
function isLiteralTextCommand(command) {
|
|
400
|
-
|
|
401
|
-
if (!
|
|
132
|
+
const segments = splitCommandSegments(command);
|
|
133
|
+
if (segments.length === 0 || segments.some((s) => !ECHO_SEG_RE.test(s))) return false;
|
|
402
134
|
if (command.includes(">")) {
|
|
403
135
|
let d = command.replace(/<=>|>=|<=|<>|=>/g, " ");
|
|
404
136
|
d = d.replace(/(?:2|&|1)?\s*>\s*\/dev\/null\b/g, "");
|
|
@@ -488,14 +220,15 @@ function analyzeInlineScript(command) {
|
|
|
488
220
|
riskType: "file_access"
|
|
489
221
|
};
|
|
490
222
|
}
|
|
491
|
-
var INLINE_CODE_RE, ECHO_STRING_RE,
|
|
223
|
+
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_SEG_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SHELL_RC_FRAGMENTS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
492
224
|
var init_inline_analyzer = __esm({
|
|
493
225
|
"src/engine/inline-analyzer.ts"() {
|
|
494
226
|
"use strict";
|
|
495
227
|
init_esm_shims();
|
|
228
|
+
init_shell_split();
|
|
496
229
|
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
497
230
|
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
498
|
-
|
|
231
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
499
232
|
READ_WRITE_KEYWORDS = [
|
|
500
233
|
// 函数调用形态(关键词后跟 '(')
|
|
501
234
|
"open(",
|
|
@@ -544,9 +277,34 @@ var init_inline_analyzer = __esm({
|
|
|
544
277
|
"passthru",
|
|
545
278
|
"shell_exec",
|
|
546
279
|
"proc_open",
|
|
547
|
-
// Perl
|
|
280
|
+
// Perl/Ruby
|
|
548
281
|
"<stdin>",
|
|
549
|
-
"open my"
|
|
282
|
+
"open my",
|
|
283
|
+
// Node/JS(2026-09 补,与 Python _READ_WRITE_KEYWORDS 同步):历史缺口 ——
|
|
284
|
+
// 识别表里一条 Node API 都没有,于是 node -e "require('fs').readFileSync('/etc/shadow')"
|
|
285
|
+
// 判为纯文本字面量 → 放行(真漏报)。
|
|
286
|
+
"readfilesync(",
|
|
287
|
+
"writefilesync(",
|
|
288
|
+
"appendfilesync(",
|
|
289
|
+
"unlinksync(",
|
|
290
|
+
"rmsync(",
|
|
291
|
+
"mkdirsync(",
|
|
292
|
+
"execsync(",
|
|
293
|
+
"spawnsync(",
|
|
294
|
+
"execfile",
|
|
295
|
+
"createreadstream",
|
|
296
|
+
"createwritestream",
|
|
297
|
+
"fs.read",
|
|
298
|
+
"fs.write",
|
|
299
|
+
"fs.promises",
|
|
300
|
+
"require('fs')",
|
|
301
|
+
'require("fs")',
|
|
302
|
+
"require('node:fs')",
|
|
303
|
+
'require("node:fs")',
|
|
304
|
+
"require('child_process')",
|
|
305
|
+
'require("child_process")',
|
|
306
|
+
"require('https')",
|
|
307
|
+
'require("https")'
|
|
550
308
|
];
|
|
551
309
|
WRITE_EXEC_CHAIN_PATTERNS = [
|
|
552
310
|
// echo/cat/printf "..." > file && bash/sh/python file
|
|
@@ -601,15 +359,53 @@ var init_inline_analyzer = __esm({
|
|
|
601
359
|
}
|
|
602
360
|
});
|
|
603
361
|
|
|
604
|
-
// src/infra/
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
362
|
+
// src/infra/runtime-config-bridge.ts
|
|
363
|
+
function registerRuntimeConfigBridge(bridge) {
|
|
364
|
+
runtimeConfigBridge.current = bridge;
|
|
365
|
+
}
|
|
366
|
+
var runtimeConfigBridge;
|
|
367
|
+
var init_runtime_config_bridge = __esm({
|
|
368
|
+
"src/infra/runtime-config-bridge.ts"() {
|
|
369
|
+
"use strict";
|
|
370
|
+
init_esm_shims();
|
|
371
|
+
runtimeConfigBridge = { current: null };
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// src/infra/rules-loader.ts
|
|
376
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
377
|
+
import { resolve, dirname } from "path";
|
|
378
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
379
|
+
function dataRootDir() {
|
|
380
|
+
return getDataRoot();
|
|
381
|
+
}
|
|
382
|
+
function overrideRulesDir() {
|
|
383
|
+
return resolve(dataRootDir(), "rules");
|
|
384
|
+
}
|
|
385
|
+
function warnOverride(name, path6, data) {
|
|
386
|
+
if (warnedOverride.has(path6)) return;
|
|
387
|
+
warnedOverride.add(path6);
|
|
388
|
+
console.warn(
|
|
389
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6\u88AB\u7528\u6237\u6570\u636E\u76EE\u5F55\u8986\u76D6: ${path6}\uFF08\u5185\u7F6E\u9ED8\u8BA4\u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4 \u2014\u2014 **\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762**\uFF1B\u5220\u9664\u8BE5\u6587\u4EF6\u5373\u6062\u590D\u5185\u7F6E\u89C4\u5219\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002`
|
|
390
|
+
);
|
|
391
|
+
const missing = (EXPECTED_KEYS[name] || []).filter((k) => !(k in data));
|
|
392
|
+
if (missing.length > 0) {
|
|
393
|
+
console.warn(
|
|
394
|
+
`\u26A0\uFE0F [AgentMoss] \u8986\u76D6\u6587\u4EF6 ${path6} \u7F3A\u5C11\u9876\u5C42\u952E [${missing.join(", ")}] \u2014\u2014 \u5BF9\u5E94\u7C7B\u522B\u4F1A**\u4E00\u6761\u89C4\u5219\u90FD\u4E0D\u8DD1**\uFF08\u9759\u9ED8\u5931\u6548\uFF09\u3002\u82E5\u53EA\u60F3\u6539\u5176\u4E2D\u4E00\u7C7B\uFF0C\u8BF7\u5148\u62F7\u8D1D\u5B8C\u6574\u7684\u5185\u7F6E\u6587\u4EF6\u518D\u6539\u3002`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function overriddenRuleFiles() {
|
|
399
|
+
const out = [];
|
|
400
|
+
for (const name of Object.keys(EXPECTED_KEYS)) {
|
|
401
|
+
const p = resolve(overrideRulesDir(), name);
|
|
402
|
+
if (existsSync(p)) out.push(p);
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
611
405
|
}
|
|
612
406
|
function findRulesFile(name) {
|
|
407
|
+
const override = resolve(overrideRulesDir(), name);
|
|
408
|
+
if (existsSync(override)) return override;
|
|
613
409
|
for (const dir of RULES_DIR_CANDS) {
|
|
614
410
|
const p = resolve(dir, name);
|
|
615
411
|
if (existsSync(p)) return p;
|
|
@@ -619,11 +415,16 @@ function findRulesFile(name) {
|
|
|
619
415
|
function loadJson(name) {
|
|
620
416
|
const p = findRulesFile(name);
|
|
621
417
|
if (!p) return {};
|
|
418
|
+
const isOverride = p === resolve(overrideRulesDir(), name);
|
|
419
|
+
let data;
|
|
622
420
|
try {
|
|
623
|
-
|
|
421
|
+
data = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
624
422
|
} catch {
|
|
423
|
+
if (isOverride) warnOverride(name, p, {});
|
|
625
424
|
return {};
|
|
626
425
|
}
|
|
426
|
+
if (isOverride) warnOverride(name, p, data);
|
|
427
|
+
return data;
|
|
627
428
|
}
|
|
628
429
|
function loadL1() {
|
|
629
430
|
return loadJson("l1_rules.json");
|
|
@@ -638,8 +439,8 @@ function loadMC() {
|
|
|
638
439
|
const mc = loadJson("mc_rules.json");
|
|
639
440
|
const rules = mc.rules || [];
|
|
640
441
|
try {
|
|
641
|
-
const rt =
|
|
642
|
-
if (rules.length > 0 &&
|
|
442
|
+
const rt = runtimeConfigBridge.current;
|
|
443
|
+
if (rules.length > 0 && rt?.hasRuntimeConfig()) {
|
|
643
444
|
const ltl = rt.loadRuntimeConfig()?.L2_2?.ltl_rules || {};
|
|
644
445
|
const rtRules = ltl.rules || [];
|
|
645
446
|
const enabledById = {};
|
|
@@ -657,16 +458,26 @@ function loadMC() {
|
|
|
657
458
|
function loadToolRegistry() {
|
|
658
459
|
return loadJson("tool_registry.json");
|
|
659
460
|
}
|
|
660
|
-
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
461
|
+
var __filename2, __dirname2, EXPECTED_KEYS, warnedOverride, RULES_DIR_CANDS;
|
|
661
462
|
var init_rules_loader = __esm({
|
|
662
463
|
"src/infra/rules-loader.ts"() {
|
|
663
464
|
"use strict";
|
|
664
465
|
init_esm_shims();
|
|
665
|
-
|
|
466
|
+
init_paths();
|
|
467
|
+
init_runtime_config_bridge();
|
|
468
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
666
469
|
__dirname2 = dirname(__filename2);
|
|
470
|
+
EXPECTED_KEYS = {
|
|
471
|
+
"l1_rules.json": ["dangerous_commands", "injection_detection", "sensitive_path_access"],
|
|
472
|
+
"l2_rules.json": ["intent_consistency", "password_modify_consent", "user_deletion_consent"],
|
|
473
|
+
"l3_rules.json": [],
|
|
474
|
+
"mc_rules.json": ["rules"],
|
|
475
|
+
"tool_registry.json": ["tools"]
|
|
476
|
+
};
|
|
477
|
+
warnedOverride = /* @__PURE__ */ new Set();
|
|
667
478
|
RULES_DIR_CANDS = (() => {
|
|
668
479
|
const cands = [
|
|
669
|
-
// 用户数据目录覆盖优先(与 Python rules_loader.
|
|
480
|
+
// 用户数据目录覆盖优先(与 Python rules_loader.resolve_rules_file 的
|
|
670
481
|
// <data_root>/rules/<name> 优先逻辑对齐)。用户安装 pip/npm 包后把自定义
|
|
671
482
|
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
672
483
|
resolve(dataRootDir(), "rules"),
|
|
@@ -685,21 +496,455 @@ var init_rules_loader = __esm({
|
|
|
685
496
|
}
|
|
686
497
|
});
|
|
687
498
|
|
|
499
|
+
// src/engine/platform-traits.ts
|
|
500
|
+
function getPlatformTraits(osType) {
|
|
501
|
+
const accepted = acceptedOsValues(osType);
|
|
502
|
+
if (accepted.includes("windows")) return WINDOWS_TRAITS;
|
|
503
|
+
if (accepted.includes("macos")) return MACOS_TRAITS;
|
|
504
|
+
if (accepted.includes("harmony")) return HARMONY_TRAITS;
|
|
505
|
+
return LINUX_TRAITS;
|
|
506
|
+
}
|
|
507
|
+
var LINUX_TRAITS, WINDOWS_TRAITS, MACOS_TRAITS, HARMONY_TRAITS;
|
|
508
|
+
var init_platform_traits = __esm({
|
|
509
|
+
"src/engine/platform-traits.ts"() {
|
|
510
|
+
"use strict";
|
|
511
|
+
init_esm_shims();
|
|
512
|
+
init_logic_rules();
|
|
513
|
+
LINUX_TRAITS = {
|
|
514
|
+
writeKeywords: [
|
|
515
|
+
"write",
|
|
516
|
+
"\u5199\u5165",
|
|
517
|
+
"save",
|
|
518
|
+
"\u4FDD\u5B58",
|
|
519
|
+
"modify",
|
|
520
|
+
"\u4FEE\u6539",
|
|
521
|
+
"update",
|
|
522
|
+
"\u66F4\u65B0",
|
|
523
|
+
"delete",
|
|
524
|
+
"\u5220\u9664",
|
|
525
|
+
"remove",
|
|
526
|
+
"\u79FB\u9664",
|
|
527
|
+
"create",
|
|
528
|
+
"\u521B\u5EFA",
|
|
529
|
+
"append",
|
|
530
|
+
"\u8FFD\u52A0",
|
|
531
|
+
"truncate",
|
|
532
|
+
"\u622A\u65AD",
|
|
533
|
+
"overwrite",
|
|
534
|
+
"\u8986\u76D6",
|
|
535
|
+
"replace",
|
|
536
|
+
"\u66FF\u6362",
|
|
537
|
+
"edit"
|
|
538
|
+
],
|
|
539
|
+
readKeywords: [
|
|
540
|
+
"read",
|
|
541
|
+
"\u8BFB\u53D6",
|
|
542
|
+
"cat",
|
|
543
|
+
"head",
|
|
544
|
+
"tail",
|
|
545
|
+
"less",
|
|
546
|
+
"more",
|
|
547
|
+
"view",
|
|
548
|
+
"\u67E5\u770B",
|
|
549
|
+
"open",
|
|
550
|
+
"\u6253\u5F00",
|
|
551
|
+
"load",
|
|
552
|
+
"\u52A0\u8F7D",
|
|
553
|
+
"grep",
|
|
554
|
+
"search",
|
|
555
|
+
"\u641C\u7D22",
|
|
556
|
+
"find",
|
|
557
|
+
"\u67E5\u627E"
|
|
558
|
+
],
|
|
559
|
+
writeCommandRes: [
|
|
560
|
+
/\brm\b/,
|
|
561
|
+
/\bunlink\b/,
|
|
562
|
+
/\brmdir\b/,
|
|
563
|
+
/\bshred\b/,
|
|
564
|
+
/\bcp\b/,
|
|
565
|
+
/\bmv\b/,
|
|
566
|
+
/\binstall\b/,
|
|
567
|
+
/\btee\b/,
|
|
568
|
+
/\bdd\b/,
|
|
569
|
+
/\bmkfs\b/,
|
|
570
|
+
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
571
|
+
/\bchmod\b/,
|
|
572
|
+
/\bchown\b/,
|
|
573
|
+
/\btruncate\b/
|
|
574
|
+
],
|
|
575
|
+
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
576
|
+
"cat",
|
|
577
|
+
"head",
|
|
578
|
+
"tail",
|
|
579
|
+
"less",
|
|
580
|
+
"more",
|
|
581
|
+
"grep",
|
|
582
|
+
"find",
|
|
583
|
+
"awk",
|
|
584
|
+
"sed",
|
|
585
|
+
"sort",
|
|
586
|
+
"uniq",
|
|
587
|
+
"wc",
|
|
588
|
+
"cut",
|
|
589
|
+
"strings",
|
|
590
|
+
"od",
|
|
591
|
+
"xxd",
|
|
592
|
+
"hexdump",
|
|
593
|
+
"tr",
|
|
594
|
+
"file",
|
|
595
|
+
"stat",
|
|
596
|
+
"du",
|
|
597
|
+
"df",
|
|
598
|
+
"ls",
|
|
599
|
+
"dir",
|
|
600
|
+
"tree",
|
|
601
|
+
"nl",
|
|
602
|
+
"tac",
|
|
603
|
+
"rev",
|
|
604
|
+
"echo",
|
|
605
|
+
"printf",
|
|
606
|
+
"lsblk",
|
|
607
|
+
"blockdev",
|
|
608
|
+
"smartctl",
|
|
609
|
+
"udevadm",
|
|
610
|
+
"dmidecode",
|
|
611
|
+
"lscpu",
|
|
612
|
+
"lspci",
|
|
613
|
+
"lsusb",
|
|
614
|
+
"lsmem",
|
|
615
|
+
"lsns",
|
|
616
|
+
"lsof",
|
|
617
|
+
"hwinfo",
|
|
618
|
+
"inxi",
|
|
619
|
+
"fdisk",
|
|
620
|
+
"parted",
|
|
621
|
+
"dmesg",
|
|
622
|
+
"journalctl",
|
|
623
|
+
"systemctl",
|
|
624
|
+
"hostnamectl",
|
|
625
|
+
"localectl",
|
|
626
|
+
"timedatectl",
|
|
627
|
+
"ps",
|
|
628
|
+
"top",
|
|
629
|
+
"free",
|
|
630
|
+
"vmstat",
|
|
631
|
+
"iostat",
|
|
632
|
+
"mpstat",
|
|
633
|
+
"pidof",
|
|
634
|
+
"pgrep",
|
|
635
|
+
"ip",
|
|
636
|
+
"ifconfig",
|
|
637
|
+
"route",
|
|
638
|
+
"ss",
|
|
639
|
+
"netstat",
|
|
640
|
+
"arp",
|
|
641
|
+
"ethtool",
|
|
642
|
+
"nmcli",
|
|
643
|
+
"uname",
|
|
644
|
+
"arch",
|
|
645
|
+
"nproc",
|
|
646
|
+
"getconf",
|
|
647
|
+
"getent",
|
|
648
|
+
"id",
|
|
649
|
+
"whoami",
|
|
650
|
+
"who",
|
|
651
|
+
"w",
|
|
652
|
+
"last",
|
|
653
|
+
"uptime",
|
|
654
|
+
"lsmod",
|
|
655
|
+
"modinfo",
|
|
656
|
+
"sysctl"
|
|
657
|
+
]),
|
|
658
|
+
fullySafeCommands: [
|
|
659
|
+
/^ls(?:\s|$)/i,
|
|
660
|
+
/^dir(?:\s|$)/i,
|
|
661
|
+
/^pwd(?:\s|$)/i,
|
|
662
|
+
/^which\s+/i,
|
|
663
|
+
/^whereis\s+/i,
|
|
664
|
+
/^realpath\s+/i,
|
|
665
|
+
/^basename\s+/i,
|
|
666
|
+
/^dirname\s+/i,
|
|
667
|
+
/^file\s+/i,
|
|
668
|
+
/^stat\s+/i,
|
|
669
|
+
/^du(?:\s|$)/i,
|
|
670
|
+
/^echo\s+/i,
|
|
671
|
+
/^printf\s+/i,
|
|
672
|
+
/^type\s+/i,
|
|
673
|
+
/^command\s+/i,
|
|
674
|
+
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
675
|
+
/^whoami(?:\s|$)/i,
|
|
676
|
+
/^id(?:\s|$)/i,
|
|
677
|
+
/^hostname(?:\s|$)/i,
|
|
678
|
+
/^uname(?:\s|$)/i,
|
|
679
|
+
/^date(?:\s|$)/i,
|
|
680
|
+
/^env(?:\s|$)/i,
|
|
681
|
+
/^printenv(?:\s|$)/i,
|
|
682
|
+
/^tty(?:\s|$)/i,
|
|
683
|
+
/^arch(?:\s|$)/i,
|
|
684
|
+
/^uptime(?:\s|$)/i,
|
|
685
|
+
/^groups(?:\s|$)/i,
|
|
686
|
+
/^logname(?:\s|$)/i,
|
|
687
|
+
// 系统资源/硬件信息(只读,不读文件内容、无状态变更选项)
|
|
688
|
+
// 与上面 uptime/uname/id 同性质;缺了它们时开 L3 的部署每查一次磁盘/内存
|
|
689
|
+
// 都要走 LLM(实测 df -h 从 5ms 变成十几秒)。
|
|
690
|
+
/^df(?:\s|$)/i,
|
|
691
|
+
/^free(?:\s|$)/i,
|
|
692
|
+
/^vmstat(?:\s|$)/i,
|
|
693
|
+
/^nproc(?:\s|$)/i,
|
|
694
|
+
/^lscpu(?:\s|$)/i,
|
|
695
|
+
/^lsblk(?:\s|$)/i,
|
|
696
|
+
/^lspci(?:\s|$)/i,
|
|
697
|
+
/^lsusb(?:\s|$)/i,
|
|
698
|
+
/^lsmod(?:\s|$)/i
|
|
699
|
+
],
|
|
700
|
+
readonlySensitiveCommands: [
|
|
701
|
+
/^cat\s+/i,
|
|
702
|
+
/^head(?:\s|$)/i,
|
|
703
|
+
/^tail(?:\s|$)/i,
|
|
704
|
+
/^less(?:\s|$)/i,
|
|
705
|
+
/^more(?:\s|$)/i,
|
|
706
|
+
/^wc(?:\s|$)/i,
|
|
707
|
+
/^grep(?:\s|$)/i,
|
|
708
|
+
/^find\s+/i,
|
|
709
|
+
/^ag(?:\s|$)/i,
|
|
710
|
+
/^rg(?:\s|$)/i,
|
|
711
|
+
/^awk\s+/i,
|
|
712
|
+
/^sed\s+/i,
|
|
713
|
+
/^cut\s+/i,
|
|
714
|
+
/^sort(?:\s|$)/i,
|
|
715
|
+
/^uniq(?:\s|$)/i,
|
|
716
|
+
// 进程/网络/打开文件信息:只读,但会暴露进程命令行、连接对端、打开的文件
|
|
717
|
+
// 路径,故只跳 L3、保留 L2(L1 敏感路径检测照常生效)。
|
|
718
|
+
/^ps(?:\s|$)/i,
|
|
719
|
+
/^ss(?:\s|$)/i,
|
|
720
|
+
/^netstat(?:\s|$)/i,
|
|
721
|
+
/^lsof(?:\s|$)/i,
|
|
722
|
+
// 只读子命令级白名单:匹配的是「第一段命令」而非首词(extractFirstCommand
|
|
723
|
+
// 按 | && || 切分),所以能精确到子命令。systemctl stop/disable 等不在此列。
|
|
724
|
+
/^systemctl\s+(?:status|is-active|is-enabled|is-failed|list-units|list-unit-files|list-dependencies|show|cat)\b/i,
|
|
725
|
+
/^service\s+\S+\s+status\b/i,
|
|
726
|
+
// journalctl 整族是只读的,但 --vacuum-*/--rotate/--flush/--sync 会删改日志,
|
|
727
|
+
// 用否定前瞻挡掉后再放行。注意前瞻里必须用 .* 扫描**整段**:只写 (?!\s+--...)
|
|
728
|
+
// 的话只看紧跟 journalctl 的第一个参数,
|
|
729
|
+
// `journalctl -u sshd --vacuum-size=100M` 就漏过去了。
|
|
730
|
+
/^journalctl\b(?!.*--(?:vacuum-size|vacuum-time|vacuum-files|rotate|flush|sync)\b)/i
|
|
731
|
+
]
|
|
732
|
+
};
|
|
733
|
+
WINDOWS_TRAITS = {
|
|
734
|
+
writeKeywords: [
|
|
735
|
+
"write",
|
|
736
|
+
"\u5199\u5165",
|
|
737
|
+
"save",
|
|
738
|
+
"\u4FDD\u5B58",
|
|
739
|
+
"modify",
|
|
740
|
+
"\u4FEE\u6539",
|
|
741
|
+
"update",
|
|
742
|
+
"\u66F4\u65B0",
|
|
743
|
+
"delete",
|
|
744
|
+
"\u5220\u9664",
|
|
745
|
+
"remove",
|
|
746
|
+
"\u79FB\u9664",
|
|
747
|
+
"create",
|
|
748
|
+
"\u521B\u5EFA",
|
|
749
|
+
"append",
|
|
750
|
+
"\u8FFD\u52A0",
|
|
751
|
+
"overwrite",
|
|
752
|
+
"\u8986\u76D6",
|
|
753
|
+
"replace",
|
|
754
|
+
"\u66FF\u6362",
|
|
755
|
+
"set",
|
|
756
|
+
"\u8BBE\u7F6E"
|
|
757
|
+
// 注意:这里**不能放 `reg` / `注册表`**。关键词是子串包含匹配,放进去等于
|
|
758
|
+
// 「只要命令里出现 reg 字样就算写操作」——`reg query`(读)会被判成写,
|
|
759
|
+
// 进而误拦对 deny_write 路径的只读查询。注册表的写判定交给下面的
|
|
760
|
+
// writeCommandRes(只认写子命令)。两端语义必须一致。
|
|
761
|
+
],
|
|
762
|
+
readKeywords: [
|
|
763
|
+
"read",
|
|
764
|
+
"\u8BFB\u53D6",
|
|
765
|
+
"type",
|
|
766
|
+
"findstr",
|
|
767
|
+
"dir",
|
|
768
|
+
"view",
|
|
769
|
+
"\u67E5\u770B",
|
|
770
|
+
"open",
|
|
771
|
+
"\u6253\u5F00",
|
|
772
|
+
"load",
|
|
773
|
+
"\u52A0\u8F7D",
|
|
774
|
+
"search",
|
|
775
|
+
"\u641C\u7D22",
|
|
776
|
+
"find",
|
|
777
|
+
"\u67E5\u627E",
|
|
778
|
+
"get",
|
|
779
|
+
"\u83B7\u53D6"
|
|
780
|
+
],
|
|
781
|
+
// Windows 写/删命令:del/copy/move/xcopy/replace/rmdir/icacls/reg delete 等
|
|
782
|
+
writeCommandRes: [
|
|
783
|
+
/\b(del|erase)\b/,
|
|
784
|
+
/\brmdir\b/,
|
|
785
|
+
/\b(rd)\b/,
|
|
786
|
+
/\bcopy\b/,
|
|
787
|
+
/\b(xcopy|robocopy)\b/,
|
|
788
|
+
/\b(move|movefile)\b/,
|
|
789
|
+
/\breplace\b/,
|
|
790
|
+
/\bicacls\b/,
|
|
791
|
+
/\battrib\b/,
|
|
792
|
+
// 注册表:**只认写子命令**(对齐 Python WindowsProfile.write_command_res)。
|
|
793
|
+
// 此前是 /\breg\b/,把 reg query(读)也判成写 → 误拦对 deny_write 路径的只读查询。
|
|
794
|
+
// 写子命令 add/delete/copy/save/restore/import/load/unload;
|
|
795
|
+
// 读子命令 query/compare/export/flags/help 不在此列。
|
|
796
|
+
/\breg(?:\.exe)?\s+(?:add|delete|copy|save|restore|import|load|unload)\b/,
|
|
797
|
+
/\bformat\b/,
|
|
798
|
+
/\bdiskpart\b/,
|
|
799
|
+
/\bmkdir\b/,
|
|
800
|
+
/\bmd\b/,
|
|
801
|
+
/\bsetx\b/,
|
|
802
|
+
/\bwmic\b/
|
|
803
|
+
],
|
|
804
|
+
// Windows 只读命令(重定向写判定用):type/findstr/more/tree/dir 等
|
|
805
|
+
readOnlyCommands: /* @__PURE__ */ new Set([
|
|
806
|
+
"type",
|
|
807
|
+
"findstr",
|
|
808
|
+
"more",
|
|
809
|
+
"tree",
|
|
810
|
+
"dir",
|
|
811
|
+
"where",
|
|
812
|
+
"ver",
|
|
813
|
+
"cls",
|
|
814
|
+
"echo",
|
|
815
|
+
"help",
|
|
816
|
+
"fc",
|
|
817
|
+
"comp",
|
|
818
|
+
"find",
|
|
819
|
+
"sort",
|
|
820
|
+
"powershell",
|
|
821
|
+
"cmd",
|
|
822
|
+
"netstat",
|
|
823
|
+
"ipconfig",
|
|
824
|
+
"tasklist",
|
|
825
|
+
"systeminfo",
|
|
826
|
+
"driverquery",
|
|
827
|
+
"reg",
|
|
828
|
+
"wmic",
|
|
829
|
+
"whoami",
|
|
830
|
+
"hostname",
|
|
831
|
+
"getmac",
|
|
832
|
+
"query",
|
|
833
|
+
"w32tm",
|
|
834
|
+
"diskpart"
|
|
835
|
+
]),
|
|
836
|
+
// Windows 完全安全命令(对齐 WindowsProfile.fully_safe_commands)
|
|
837
|
+
fullySafeCommands: [
|
|
838
|
+
/^dir(?:\s|$)/,
|
|
839
|
+
/^where\s+/,
|
|
840
|
+
/^echo\s+/,
|
|
841
|
+
/^cd(?:\s|$)/,
|
|
842
|
+
/^tree(?:\s|$)/,
|
|
843
|
+
/^help(?:\s|$)/,
|
|
844
|
+
/^cls(?:\s|$)/,
|
|
845
|
+
/^ver(?:\s|$)/,
|
|
846
|
+
// 系统信息查询(只读,不读文件内容、无状态变更选项),与 Linux 侧 df/free 对齐
|
|
847
|
+
/^whoami(?:\s|$)/,
|
|
848
|
+
/^hostname(?:\s|$)/,
|
|
849
|
+
/^systeminfo(?:\s|$)/,
|
|
850
|
+
/^tasklist(?:\s|$)/,
|
|
851
|
+
/^getmac(?:\s|$)/,
|
|
852
|
+
/^driverquery(?:\s|$)/,
|
|
853
|
+
/^netstat(?:\s|$)/
|
|
854
|
+
],
|
|
855
|
+
// Windows 安全但可能访问敏感路径的命令(对齐 WindowsProfile.readonly_sensitive_commands)
|
|
856
|
+
readonlySensitiveCommands: [
|
|
857
|
+
/^type\s+/,
|
|
858
|
+
/^findstr\s+/,
|
|
859
|
+
// 只读子命令级白名单(匹配「第一段命令」而非首词);
|
|
860
|
+
// sc create/delete/start/stop、reg add/delete/set 均不在此列。
|
|
861
|
+
/^sc\s+query\b/,
|
|
862
|
+
/^reg\s+query\b/
|
|
863
|
+
]
|
|
864
|
+
};
|
|
865
|
+
MACOS_TRAITS = LINUX_TRAITS;
|
|
866
|
+
HARMONY_TRAITS = LINUX_TRAITS;
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
|
|
688
870
|
// src/engine/logic-rules.ts
|
|
689
|
-
import * as
|
|
690
|
-
function
|
|
871
|
+
import * as fs from "fs";
|
|
872
|
+
function acceptedOsValues(osType) {
|
|
873
|
+
const t = String(osType || "").toLowerCase();
|
|
874
|
+
if (t === "windows") return ["windows"];
|
|
875
|
+
if (t === "macos" || t === "darwin" || t === "osx" || t === "mac") return ["linux", "macos"];
|
|
876
|
+
if (t === "harmony" || t === "hmos" || t === "openharmony" || t === "hongmeng") return ["linux", "harmony"];
|
|
877
|
+
return ["linux"];
|
|
878
|
+
}
|
|
879
|
+
function _loadSensitivePathScope(scope, osType = "linux") {
|
|
880
|
+
const accepted = acceptedOsValues(osType);
|
|
691
881
|
const sps = loadL1().sensitive_path_access || [];
|
|
692
|
-
return sps.filter((sp) => sp.scope === scope).map((sp) => ({
|
|
882
|
+
return sps.filter((sp) => sp.scope === scope).filter((sp) => accepted.includes(String(sp.os || "linux").toLowerCase())).map((sp) => ({
|
|
693
883
|
path: sp.path,
|
|
694
884
|
riskLevel: sp.risk_level,
|
|
695
885
|
desc: sp.desc,
|
|
696
886
|
credential: sp.deny_mode === "deny_both" ? true : void 0,
|
|
697
|
-
|
|
887
|
+
readOnly: sp.read_only === true || sp.deny_mode === "deny_read" ? true : void 0,
|
|
888
|
+
denyMode: sp.deny_mode || void 0,
|
|
889
|
+
dirDelOnly: sp.dir_del_only === true ? true : void 0,
|
|
890
|
+
// 保留条目的原始平台归属(非查询用的 osType)—— seed 分组去重用
|
|
891
|
+
os: String(sp.os || "linux").toLowerCase()
|
|
698
892
|
}));
|
|
699
893
|
}
|
|
700
|
-
function
|
|
894
|
+
function _scopeOf(os4, path6) {
|
|
895
|
+
if (!_scopeIndex) {
|
|
896
|
+
_scopeIndex = /* @__PURE__ */ new Map();
|
|
897
|
+
for (const sp of loadL1().sensitive_path_access || []) {
|
|
898
|
+
const key = `${String(sp.os || "linux").toLowerCase()}|${sp.path}`;
|
|
899
|
+
if (!_scopeIndex.has(key)) _scopeIndex.set(key, sp.scope);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return _scopeIndex.get(`${String(os4 || "linux").toLowerCase()}|${path6}`) || "root";
|
|
903
|
+
}
|
|
904
|
+
function getSensitivePathsForOs(osType) {
|
|
905
|
+
const effective = runtimeConfigBridge.current?.effectiveSensitivePaths(osType);
|
|
906
|
+
if (effective) {
|
|
907
|
+
const root = [];
|
|
908
|
+
const home = [];
|
|
909
|
+
const credential = [];
|
|
910
|
+
for (const r of effective) {
|
|
911
|
+
const entry = {
|
|
912
|
+
path: r.path,
|
|
913
|
+
riskLevel: r.riskLevel,
|
|
914
|
+
desc: r.desc,
|
|
915
|
+
// ⚠️ 不要强制 credential:用户在 Console 把某条从 deny_both 改成 deny_write
|
|
916
|
+
// 之后,强制 credential 会让读操作仍被拦(改了不生效)。一律以 runtime 为准。
|
|
917
|
+
credential: r.credential === true ? true : void 0,
|
|
918
|
+
readOnly: r.readOnly === true ? true : void 0,
|
|
919
|
+
denyMode: r.denyMode || void 0,
|
|
920
|
+
dirDelOnly: r.dirDelOnly === true ? true : void 0
|
|
921
|
+
};
|
|
922
|
+
const scope = _scopeOf(r.os, r.path);
|
|
923
|
+
if (scope === "home") home.push(entry);
|
|
924
|
+
else if (scope === "credential") credential.push(entry);
|
|
925
|
+
else root.push(entry);
|
|
926
|
+
}
|
|
927
|
+
return { root, home, credential };
|
|
928
|
+
}
|
|
929
|
+
return getSensitivePathsForOsBuiltin(osType);
|
|
930
|
+
}
|
|
931
|
+
function getSensitivePathsForOsBuiltin(osType) {
|
|
932
|
+
const t = String(osType || "").toLowerCase();
|
|
933
|
+
if (t === "windows") {
|
|
934
|
+
return { root: ROOT_SENSITIVE_PATHS_WINDOWS, home: HOME_SENSITIVE_PATHS_WINDOWS, credential: CREDENTIAL_PATHS_WINDOWS };
|
|
935
|
+
}
|
|
936
|
+
if (t === "macos" || t === "darwin" || t === "osx" || t === "mac") {
|
|
937
|
+
return { root: ROOT_SENSITIVE_PATHS_MACOS, home: HOME_SENSITIVE_PATHS_MACOS, credential: CREDENTIAL_PATHS_MACOS };
|
|
938
|
+
}
|
|
939
|
+
if (t === "harmony" || t === "hmos" || t === "openharmony" || t === "hongmeng") {
|
|
940
|
+
return { root: ROOT_SENSITIVE_PATHS_HARMONY, home: HOME_SENSITIVE_PATHS_HARMONY, credential: CREDENTIAL_PATHS_HARMONY };
|
|
941
|
+
}
|
|
942
|
+
return { root: ROOT_SENSITIVE_PATHS, home: HOME_SENSITIVE_PATHS, credential: CREDENTIAL_PATHS };
|
|
943
|
+
}
|
|
944
|
+
function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWORDS, osType) {
|
|
701
945
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
702
|
-
|
|
946
|
+
const { writeCommandRes } = getPlatformTraits(osType);
|
|
947
|
+
if (writeCommandRes.some((rx) => rx.test(actionDetail))) return true;
|
|
703
948
|
if (/\bsed\s+-[a-zA-Z]*i/i.test(actionDetail)) return true;
|
|
704
949
|
for (const seg of actionDetail.split(/&&|\|\||;|\n/)) {
|
|
705
950
|
const trimmed = seg.trim();
|
|
@@ -733,6 +978,12 @@ function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWOR
|
|
|
733
978
|
}
|
|
734
979
|
return false;
|
|
735
980
|
}
|
|
981
|
+
function getIntentPatternsForOs(osType) {
|
|
982
|
+
const effective = runtimeConfigBridge.current?.effectiveIntentPatterns(osType);
|
|
983
|
+
if (effective) return effective;
|
|
984
|
+
const accepted = acceptedOsValues(osType);
|
|
985
|
+
return INTENT_DEVIATION_PATTERNS.filter((p) => accepted.includes(p.os));
|
|
986
|
+
}
|
|
736
987
|
function matchDangerousAction(actionDetail, action) {
|
|
737
988
|
if (/[^\x00-\x7f]/.test(action)) {
|
|
738
989
|
return actionDetail.includes(action);
|
|
@@ -767,38 +1018,47 @@ function collectIntentTexts(promptSession, promptHistory) {
|
|
|
767
1018
|
}
|
|
768
1019
|
return deduped;
|
|
769
1020
|
}
|
|
770
|
-
function
|
|
1021
|
+
function checkIntentConsistency(input) {
|
|
1022
|
+
const { aNext, promptSession, promptHistory } = input;
|
|
1023
|
+
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
1024
|
+
const actionType = aNext.actionType.toLowerCase();
|
|
1025
|
+
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
1026
|
+
if (EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
1027
|
+
if (INLINE_SCRIPT_RE.test(actionDetail)) return null;
|
|
1028
|
+
const intentTexts = collectIntentTexts(promptSession, promptHistory);
|
|
1029
|
+
for (const pattern of getIntentPatternsForOs(input.osType)) {
|
|
1030
|
+
const intentMatched = intentTexts.some(
|
|
1031
|
+
(text) => pattern.intentKeywords.some((kw) => text.includes(kw))
|
|
1032
|
+
);
|
|
1033
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
1034
|
+
if (intentMatched && dangerousMatched) {
|
|
1035
|
+
return {
|
|
1036
|
+
allowed: false,
|
|
1037
|
+
reason: pattern.reason,
|
|
1038
|
+
riskLevel: "high",
|
|
1039
|
+
riskType: "intent_deviation",
|
|
1040
|
+
confidence: 85,
|
|
1041
|
+
source: "logic_rule",
|
|
1042
|
+
violatedLayers: ["L2.1"]
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
function runLogicCheck(input, opts) {
|
|
771
1049
|
const { actionHistory, aNext, cwd } = input;
|
|
772
1050
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
773
1051
|
const actionType = aNext.actionType.toLowerCase();
|
|
774
1052
|
const resolvedCwd = cwd || "/";
|
|
775
1053
|
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
776
1054
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (!INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
781
|
-
const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
|
|
782
|
-
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
783
|
-
const intentMatched = intentTexts.some(
|
|
784
|
-
(text) => pattern.intentKeywords.some((kw) => text.includes(kw))
|
|
785
|
-
);
|
|
786
|
-
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
787
|
-
if (intentMatched && dangerousMatched) {
|
|
788
|
-
return {
|
|
789
|
-
allowed: false,
|
|
790
|
-
reason: pattern.reason,
|
|
791
|
-
riskLevel: "high",
|
|
792
|
-
riskType: "intent_deviation",
|
|
793
|
-
confidence: 85,
|
|
794
|
-
source: "logic_rule",
|
|
795
|
-
violatedLayers: ["L2.1"]
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
}
|
|
1055
|
+
if (!opts?.skipReadBeforeWrite) {
|
|
1056
|
+
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
1057
|
+
if (rbwResult) return rbwResult;
|
|
800
1058
|
}
|
|
801
|
-
const
|
|
1059
|
+
const intentResult = checkIntentConsistency(input);
|
|
1060
|
+
if (intentResult) return intentResult;
|
|
1061
|
+
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd, input.osType);
|
|
802
1062
|
if (indirectResult) return indirectResult;
|
|
803
1063
|
if (INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
804
1064
|
} else if (containsPrivilegeEscalation(actionDetail)) {
|
|
@@ -821,9 +1081,9 @@ function runLogicCheck(input) {
|
|
|
821
1081
|
}
|
|
822
1082
|
}
|
|
823
1083
|
}
|
|
824
|
-
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
|
|
1084
|
+
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
|
|
825
1085
|
if (pwdResult) return pwdResult;
|
|
826
|
-
const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory);
|
|
1086
|
+
const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
|
|
827
1087
|
if (userDelResult) return userDelResult;
|
|
828
1088
|
const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
|
|
829
1089
|
if (comboResult) return comboResult;
|
|
@@ -874,22 +1134,33 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
874
1134
|
}
|
|
875
1135
|
return null;
|
|
876
1136
|
}
|
|
877
|
-
function extractFilePaths(text) {
|
|
1137
|
+
function extractFilePaths(text, osType) {
|
|
878
1138
|
const paths = [];
|
|
879
1139
|
const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
|
|
880
1140
|
if (unixPaths) paths.push(...unixPaths.map((p) => p.trim()));
|
|
1141
|
+
const isWindows = String(osType || "").toLowerCase() === "windows";
|
|
1142
|
+
if (isWindows) {
|
|
1143
|
+
const winPaths = text.match(/(?:^|\s)([A-Za-z]:[\\\/][^\s;|&><'"]+)/g);
|
|
1144
|
+
if (winPaths) paths.push(...winPaths.map((p) => p.trim()));
|
|
1145
|
+
const uncPaths = text.match(/(?:^|\s)(\\{2}[^\\\s]+(?:\\[^\s;|&><'"]*))/g);
|
|
1146
|
+
if (uncPaths) paths.push(...uncPaths.map((p) => p.trim()));
|
|
1147
|
+
}
|
|
881
1148
|
const textNoQuotes = text.replace(/'[^']*'|"[^"]*"/g, "");
|
|
882
1149
|
const kvPaths = textNoQuotes.match(/(?:=)(\/[^\s;|&><'"]+)/g);
|
|
883
1150
|
if (kvPaths) paths.push(...kvPaths.map((p) => p.replace(/^=/, "")));
|
|
884
1151
|
const quotedPaths = text.match(/["']\/([^"']+)["']/g);
|
|
885
1152
|
if (quotedPaths) paths.push(...quotedPaths.map((p) => p.replace(/["']/g, "")));
|
|
1153
|
+
if (isWindows) {
|
|
1154
|
+
const quotedWinPaths = text.match(/["']([A-Za-z]:[\\\/][^"']+)["']/g);
|
|
1155
|
+
if (quotedWinPaths) paths.push(...quotedWinPaths.map((p) => p.replace(/["']/g, "")));
|
|
1156
|
+
}
|
|
886
1157
|
return [...new Set(paths)];
|
|
887
1158
|
}
|
|
888
|
-
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd) {
|
|
1159
|
+
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd, osType) {
|
|
889
1160
|
if (NON_WRITE_TOOL_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
890
1161
|
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
891
1162
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
892
|
-
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail) :
|
|
1163
|
+
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
893
1164
|
if (!isWrite) return null;
|
|
894
1165
|
const WRITE_CMDS = /* @__PURE__ */ new Set(["cp", "mv", "install", "tee", "dd", "truncate", "mkfs", "fdisk", "parted"]);
|
|
895
1166
|
if (isBashLike) {
|
|
@@ -933,7 +1204,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
933
1204
|
if (firstWord === "cd") {
|
|
934
1205
|
continue;
|
|
935
1206
|
}
|
|
936
|
-
if (
|
|
1207
|
+
if (getPlatformTraits(osType).readOnlyCommands.has(firstWord)) {
|
|
937
1208
|
const segUnquoted = trimmed.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
938
1209
|
const segNoDevnull = segUnquoted.replace(DEVNULL_REDIRECT_RE, "");
|
|
939
1210
|
const segNoQuotes = segNoDevnull.replace(/'[^']*'|"[^"]*"/g, "");
|
|
@@ -941,7 +1212,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
941
1212
|
if (m) targetPaths.push(m[1] || m[2] || m[3]);
|
|
942
1213
|
continue;
|
|
943
1214
|
}
|
|
944
|
-
const segPaths = extractFilePaths(trimmed);
|
|
1215
|
+
const segPaths = extractFilePaths(trimmed, osType);
|
|
945
1216
|
const segPathsFiltered = firstWord === "wget" || firstWord === "curl" ? segPaths.filter((p) => p !== "/dev/null") : segPaths;
|
|
946
1217
|
if (SOURCE_ARG_CMDS.has(firstWord)) {
|
|
947
1218
|
if (segPathsFiltered.length > 1) targetPaths.push(...segPathsFiltered.slice(1));
|
|
@@ -963,20 +1234,30 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
963
1234
|
for (const action of actionHistory) {
|
|
964
1235
|
const histDetail = action.actionDetail.toLowerCase();
|
|
965
1236
|
const histType = action.actionType.toLowerCase();
|
|
966
|
-
const isRead =
|
|
1237
|
+
const isRead = getPlatformTraits(osType).readKeywords.some((kw) => histType.includes(kw) || histDetail.includes(kw));
|
|
967
1238
|
if (isRead) {
|
|
968
|
-
for (const p of extractFilePaths(histDetail)) {
|
|
1239
|
+
for (const p of extractFilePaths(histDetail, osType)) {
|
|
969
1240
|
historyReadPaths.add(p);
|
|
970
1241
|
}
|
|
971
1242
|
}
|
|
972
1243
|
}
|
|
1244
|
+
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/dev/shm/", "/run/"];
|
|
1245
|
+
let filteredPaths = uniqueTargetPaths;
|
|
1246
|
+
if (/\bdd\b/i.test(actionDetail) && /\bif=\/dev\/zero\b/i.test(actionDetail)) {
|
|
1247
|
+
filteredPaths = uniqueTargetPaths.filter(
|
|
1248
|
+
(p) => !TEMP_DIRS.some((td) => p.startsWith(td))
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
if (filteredPaths.length === 0) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
973
1254
|
const unreadPaths = [];
|
|
974
|
-
for (const path6 of
|
|
1255
|
+
for (const path6 of filteredPaths) {
|
|
975
1256
|
const hasRead = [...historyReadPaths].some(
|
|
976
1257
|
(rp) => path6 === rp || path6.startsWith(rp) || rp.startsWith(path6)
|
|
977
1258
|
);
|
|
978
1259
|
if (!hasRead) {
|
|
979
|
-
let isCreate = !
|
|
1260
|
+
let isCreate = !fs.existsSync(path6);
|
|
980
1261
|
if (!isCreate) {
|
|
981
1262
|
isCreate = CREATE_KEYWORDS.some((kw) => reason.toLowerCase().includes(kw) || actionDetail.includes(kw));
|
|
982
1263
|
}
|
|
@@ -998,14 +1279,18 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
998
1279
|
}
|
|
999
1280
|
return null;
|
|
1000
1281
|
}
|
|
1001
|
-
function
|
|
1282
|
+
function hasUserConsent(actionHistory) {
|
|
1283
|
+
return actionHistory.some((h) => {
|
|
1284
|
+
const t = String(h.actionType || "").toLowerCase();
|
|
1285
|
+
return t === "ask_user_question" && String(h.output || "").trim().length > 0;
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1002
1289
|
if (actionType !== "bash") return null;
|
|
1003
|
-
const
|
|
1290
|
+
const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
|
|
1291
|
+
const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
|
|
1004
1292
|
if (!isPasswordModify) return null;
|
|
1005
|
-
|
|
1006
|
-
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1007
|
-
);
|
|
1008
|
-
if (hasConsent) return null;
|
|
1293
|
+
if (hasUserConsent(actionHistory)) return null;
|
|
1009
1294
|
return {
|
|
1010
1295
|
allowed: false,
|
|
1011
1296
|
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",
|
|
@@ -1016,14 +1301,12 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
1016
1301
|
violatedLayers: ["L2.1"]
|
|
1017
1302
|
};
|
|
1018
1303
|
}
|
|
1019
|
-
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
1304
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1020
1305
|
if (actionType !== "bash") return null;
|
|
1021
|
-
const
|
|
1306
|
+
const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
|
|
1307
|
+
const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
|
|
1022
1308
|
if (!isUserDeletion) return null;
|
|
1023
|
-
|
|
1024
|
-
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1025
|
-
);
|
|
1026
|
-
if (hasConsent) return null;
|
|
1309
|
+
if (hasUserConsent(actionHistory)) return null;
|
|
1027
1310
|
return {
|
|
1028
1311
|
allowed: false,
|
|
1029
1312
|
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",
|
|
@@ -1046,7 +1329,7 @@ function containsPrivilegeEscalation(actionDetail) {
|
|
|
1046
1329
|
}
|
|
1047
1330
|
return false;
|
|
1048
1331
|
}
|
|
1049
|
-
function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
1332
|
+
function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
1050
1333
|
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
|
|
1051
1334
|
return null;
|
|
1052
1335
|
}
|
|
@@ -1059,14 +1342,14 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1059
1342
|
if (!hasTraversal) {
|
|
1060
1343
|
return null;
|
|
1061
1344
|
}
|
|
1062
|
-
const
|
|
1345
|
+
const READ_KEYWORDS = ["read(", "readline", "readlines", "fread", "fgets", "load(", "getpass", "file_get_contents"];
|
|
1063
1346
|
const WRITE_KEYWORDS2 = ["write(", "writelines", "fwrite", "file_put_contents"];
|
|
1064
1347
|
const EXEC_KEYWORDS = ["system(", "exec(", "eval(", "popen(", "os.system", "os.popen", "subprocess", "os.exec", "os.spawn", "shell=true", "check_output", "check_call", "run(", "passthru", "shell_exec", "proc_open"];
|
|
1065
1348
|
const OPEN_KEYWORDS = ["open(", "fopen("];
|
|
1066
1349
|
const code = extractInlineCode(actionDetail);
|
|
1067
1350
|
if (!code) return null;
|
|
1068
1351
|
const codeLower = code.toLowerCase();
|
|
1069
|
-
const hasAnyOp =
|
|
1352
|
+
const hasAnyOp = READ_KEYWORDS.some((kw) => codeLower.includes(kw)) || WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw)) || EXEC_KEYWORDS.some((kw) => codeLower.includes(kw)) || OPEN_KEYWORDS.some((kw) => codeLower.includes(kw));
|
|
1070
1353
|
if (!hasAnyOp) {
|
|
1071
1354
|
return null;
|
|
1072
1355
|
}
|
|
@@ -1074,15 +1357,16 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1074
1357
|
const hasExec = EXEC_KEYWORDS.some((kw) => codeLower.includes(kw));
|
|
1075
1358
|
const blockAllSensitive = hasWrite || hasExec;
|
|
1076
1359
|
let actualFiles = [];
|
|
1077
|
-
if (cwd
|
|
1360
|
+
if (cwd) {
|
|
1078
1361
|
try {
|
|
1079
|
-
actualFiles =
|
|
1362
|
+
actualFiles = fs.readdirSync(cwd);
|
|
1080
1363
|
} catch {
|
|
1081
1364
|
}
|
|
1082
1365
|
}
|
|
1083
1366
|
if (actualFiles.length > 0) {
|
|
1084
|
-
|
|
1085
|
-
|
|
1367
|
+
const { root: rootPaths, home: homePaths, credential: credentialPaths } = getSensitivePathsForOs(osType);
|
|
1368
|
+
for (const cp of credentialPaths) {
|
|
1369
|
+
const fname = cp.path.split(/[/\\]/).pop() || cp.path;
|
|
1086
1370
|
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1087
1371
|
return {
|
|
1088
1372
|
allowed: false,
|
|
@@ -1097,8 +1381,8 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1097
1381
|
}
|
|
1098
1382
|
}
|
|
1099
1383
|
if (blockAllSensitive) {
|
|
1100
|
-
for (const sp of [...
|
|
1101
|
-
const fname = sp.path.split(
|
|
1384
|
+
for (const sp of [...rootPaths, ...homePaths]) {
|
|
1385
|
+
const fname = sp.path.split(/[/\\]/).pop() || sp.path;
|
|
1102
1386
|
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1103
1387
|
return {
|
|
1104
1388
|
allowed: false,
|
|
@@ -1124,16 +1408,28 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1124
1408
|
violatedLayers: ["L2.1"]
|
|
1125
1409
|
};
|
|
1126
1410
|
}
|
|
1127
|
-
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS,
|
|
1411
|
+
var 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, WRITE_KEYWORDS, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1128
1412
|
var init_logic_rules = __esm({
|
|
1129
1413
|
"src/engine/logic-rules.ts"() {
|
|
1130
1414
|
"use strict";
|
|
1131
1415
|
init_esm_shims();
|
|
1132
1416
|
init_inline_analyzer();
|
|
1417
|
+
init_runtime_config_bridge();
|
|
1133
1418
|
init_rules_loader();
|
|
1419
|
+
init_platform_traits();
|
|
1134
1420
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1135
1421
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1136
1422
|
CREDENTIAL_PATHS = _loadSensitivePathScope("credential").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1423
|
+
ROOT_SENSITIVE_PATHS_WINDOWS = _loadSensitivePathScope("root", "windows");
|
|
1424
|
+
HOME_SENSITIVE_PATHS_WINDOWS = _loadSensitivePathScope("home", "windows");
|
|
1425
|
+
CREDENTIAL_PATHS_WINDOWS = _loadSensitivePathScope("credential", "windows").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1426
|
+
ROOT_SENSITIVE_PATHS_MACOS = _loadSensitivePathScope("root", "macos");
|
|
1427
|
+
HOME_SENSITIVE_PATHS_MACOS = _loadSensitivePathScope("home", "macos");
|
|
1428
|
+
CREDENTIAL_PATHS_MACOS = _loadSensitivePathScope("credential", "macos").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1429
|
+
ROOT_SENSITIVE_PATHS_HARMONY = _loadSensitivePathScope("root", "harmony");
|
|
1430
|
+
HOME_SENSITIVE_PATHS_HARMONY = _loadSensitivePathScope("home", "harmony");
|
|
1431
|
+
CREDENTIAL_PATHS_HARMONY = _loadSensitivePathScope("credential", "harmony").map(({ path: path6, riskLevel, desc, dirDelOnly }) => ({ path: path6, riskLevel, desc, credential: true, dirDelOnly }));
|
|
1432
|
+
_scopeIndex = null;
|
|
1137
1433
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1138
1434
|
"ask_user_question",
|
|
1139
1435
|
"glob",
|
|
@@ -1155,111 +1451,7 @@ var init_logic_rules = __esm({
|
|
|
1155
1451
|
]);
|
|
1156
1452
|
NON_WRITE_TOOL_PREFIXES = ["memory-", "skill-manager-", "mailbox-", "app-"];
|
|
1157
1453
|
WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362", "edit"];
|
|
1158
|
-
WRITE_COMMAND_RES = [
|
|
1159
|
-
/\brm\b/,
|
|
1160
|
-
/\bunlink\b/,
|
|
1161
|
-
/\brmdir\b/,
|
|
1162
|
-
/\bshred\b/,
|
|
1163
|
-
/\bcp\b/,
|
|
1164
|
-
/\bmv\b/,
|
|
1165
|
-
/\binstall\b/,
|
|
1166
|
-
/\btee\b/,
|
|
1167
|
-
/\bdd\b/,
|
|
1168
|
-
/\bmkfs\b/,
|
|
1169
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
1170
|
-
/\bchmod\b/,
|
|
1171
|
-
/\bchown\b/,
|
|
1172
|
-
/\btruncate\b/
|
|
1173
|
-
];
|
|
1174
|
-
READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
1175
|
-
"cat",
|
|
1176
|
-
"head",
|
|
1177
|
-
"tail",
|
|
1178
|
-
"less",
|
|
1179
|
-
"more",
|
|
1180
|
-
"grep",
|
|
1181
|
-
"find",
|
|
1182
|
-
"awk",
|
|
1183
|
-
"sed",
|
|
1184
|
-
"sort",
|
|
1185
|
-
"uniq",
|
|
1186
|
-
"wc",
|
|
1187
|
-
"cut",
|
|
1188
|
-
"strings",
|
|
1189
|
-
"od",
|
|
1190
|
-
"xxd",
|
|
1191
|
-
"hexdump",
|
|
1192
|
-
"tr",
|
|
1193
|
-
"file",
|
|
1194
|
-
"stat",
|
|
1195
|
-
"du",
|
|
1196
|
-
"df",
|
|
1197
|
-
"ls",
|
|
1198
|
-
"dir",
|
|
1199
|
-
"tree",
|
|
1200
|
-
"nl",
|
|
1201
|
-
"tac",
|
|
1202
|
-
"rev",
|
|
1203
|
-
// NEW-FP-I: echo/printf 是文本输出命令,参数是字面量非写目标。加入此集让
|
|
1204
|
-
// read_before_write 的 echo 段走只读命令分支(只提取重定向目标,不提取参数路径),
|
|
1205
|
-
// 避免 echo "cat /etc/shadow" > /tmp/x.sh 的引号内 /etc/shadow 被当写目标误报。
|
|
1206
|
-
// echo "x" > /tmp/x.sh 的重定向目标 /tmp/x.sh 仍被提取(真实写不漏报)。
|
|
1207
|
-
"echo",
|
|
1208
|
-
"printf",
|
|
1209
|
-
"lsblk",
|
|
1210
|
-
"blockdev",
|
|
1211
|
-
"smartctl",
|
|
1212
|
-
"udevadm",
|
|
1213
|
-
"dmidecode",
|
|
1214
|
-
"lscpu",
|
|
1215
|
-
"lspci",
|
|
1216
|
-
"lsusb",
|
|
1217
|
-
"lsmem",
|
|
1218
|
-
"lsns",
|
|
1219
|
-
"lsof",
|
|
1220
|
-
"hwinfo",
|
|
1221
|
-
"inxi",
|
|
1222
|
-
"fdisk",
|
|
1223
|
-
"parted",
|
|
1224
|
-
"dmesg",
|
|
1225
|
-
"journalctl",
|
|
1226
|
-
"systemctl",
|
|
1227
|
-
"hostnamectl",
|
|
1228
|
-
"localectl",
|
|
1229
|
-
"timedatectl",
|
|
1230
|
-
"ps",
|
|
1231
|
-
"top",
|
|
1232
|
-
"free",
|
|
1233
|
-
"vmstat",
|
|
1234
|
-
"iostat",
|
|
1235
|
-
"mpstat",
|
|
1236
|
-
"pidof",
|
|
1237
|
-
"pgrep",
|
|
1238
|
-
"ip",
|
|
1239
|
-
"ifconfig",
|
|
1240
|
-
"route",
|
|
1241
|
-
"ss",
|
|
1242
|
-
"netstat",
|
|
1243
|
-
"arp",
|
|
1244
|
-
"ethtool",
|
|
1245
|
-
"nmcli",
|
|
1246
|
-
"uname",
|
|
1247
|
-
"arch",
|
|
1248
|
-
"nproc",
|
|
1249
|
-
"getconf",
|
|
1250
|
-
"getent",
|
|
1251
|
-
"id",
|
|
1252
|
-
"whoami",
|
|
1253
|
-
"who",
|
|
1254
|
-
"w",
|
|
1255
|
-
"last",
|
|
1256
|
-
"uptime",
|
|
1257
|
-
"lsmod",
|
|
1258
|
-
"modinfo",
|
|
1259
|
-
"sysctl"
|
|
1260
|
-
]);
|
|
1261
1454
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
1262
|
-
READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
|
|
1263
1455
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1264
1456
|
PASSWORD_MODIFY_PATTERNS = (() => {
|
|
1265
1457
|
const pats = loadL2().password_modify_consent || [];
|
|
@@ -1274,7 +1466,8 @@ var init_logic_rules = __esm({
|
|
|
1274
1466
|
return pats.map((p) => ({
|
|
1275
1467
|
intentKeywords: p.intent_keywords,
|
|
1276
1468
|
dangerousActions: p.dangerous_actions,
|
|
1277
|
-
reason: p.reason
|
|
1469
|
+
reason: p.reason,
|
|
1470
|
+
os: String(p.os || "linux").toLowerCase()
|
|
1278
1471
|
}));
|
|
1279
1472
|
})();
|
|
1280
1473
|
COMBO_STAGES = (() => {
|
|
@@ -1334,9 +1527,9 @@ var init_asset_dir = __esm({
|
|
|
1334
1527
|
});
|
|
1335
1528
|
|
|
1336
1529
|
// src/engine/skill-loader.ts
|
|
1337
|
-
import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
1530
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync4 } from "fs";
|
|
1338
1531
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1339
|
-
import { fileURLToPath as
|
|
1532
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1340
1533
|
function loadAllSkills() {
|
|
1341
1534
|
if (cachedSkills) return cachedSkills;
|
|
1342
1535
|
cachedSkills = {};
|
|
@@ -1354,14 +1547,47 @@ function loadAllSkills() {
|
|
|
1354
1547
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1355
1548
|
return cachedSkills;
|
|
1356
1549
|
}
|
|
1550
|
+
function userSkillsDir() {
|
|
1551
|
+
return resolve3(getDataRoot(), "agent_moss_skills");
|
|
1552
|
+
}
|
|
1553
|
+
function runtimeCustomSkillKeywords() {
|
|
1554
|
+
try {
|
|
1555
|
+
const rt = runtimeConfigBridge.current?.loadRuntimeConfig?.();
|
|
1556
|
+
if (!rt) return {};
|
|
1557
|
+
const out = {};
|
|
1558
|
+
for (const cat of Object.values(rt.L3_skills || {})) {
|
|
1559
|
+
if (!cat || typeof cat !== "object" || cat.category_enabled === false) continue;
|
|
1560
|
+
for (const sk of cat.skills || []) {
|
|
1561
|
+
if (!sk || sk.builtin !== false || sk.enabled === false || !sk.id) continue;
|
|
1562
|
+
out[sk.id] = sk.keywords || [];
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return out;
|
|
1566
|
+
} catch {
|
|
1567
|
+
return {};
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
function loadUserSkill(name) {
|
|
1571
|
+
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1572
|
+
try {
|
|
1573
|
+
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
1574
|
+
userSkillCache[name] = existsSync4(p) ? readFileSync3(p, "utf-8") : "";
|
|
1575
|
+
} catch {
|
|
1576
|
+
userSkillCache[name] = "";
|
|
1577
|
+
}
|
|
1578
|
+
return userSkillCache[name];
|
|
1579
|
+
}
|
|
1357
1580
|
function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
1358
1581
|
const skills = loadAllSkills();
|
|
1359
1582
|
const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
|
|
1360
1583
|
const actionDetailLower = actionDetail.toLowerCase();
|
|
1361
1584
|
const actionTypeLower = actionType.toLowerCase();
|
|
1362
1585
|
const reasonLower = reason.toLowerCase();
|
|
1586
|
+
const disabledSkillIds = runtimeConfigBridge.current?.disabledSkillIds() ?? /* @__PURE__ */ new Set();
|
|
1587
|
+
const skillMap = { ...SKILL_KEYWORD_MAP, ...runtimeCustomSkillKeywords() };
|
|
1363
1588
|
const scores = {};
|
|
1364
|
-
for (const [skillName, keywords] of Object.entries(
|
|
1589
|
+
for (const [skillName, keywords] of Object.entries(skillMap)) {
|
|
1590
|
+
if (disabledSkillIds.has(skillName)) continue;
|
|
1365
1591
|
let score = 0;
|
|
1366
1592
|
for (const kw of keywords) {
|
|
1367
1593
|
if (combined.includes(kw)) {
|
|
@@ -1378,7 +1604,7 @@ function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
|
1378
1604
|
const maxScore = top[0]?.[1] || 1;
|
|
1379
1605
|
const results = [];
|
|
1380
1606
|
for (const [name, score] of top) {
|
|
1381
|
-
const content = skills[name];
|
|
1607
|
+
const content = skills[name] || loadUserSkill(name);
|
|
1382
1608
|
if (content) {
|
|
1383
1609
|
results.push({
|
|
1384
1610
|
skillName: name,
|
|
@@ -1403,14 +1629,16 @@ function formatSkillsForPrompt(skills) {
|
|
|
1403
1629
|
${s.skillContent}`
|
|
1404
1630
|
).join("\n\n---\n\n");
|
|
1405
1631
|
}
|
|
1406
|
-
var __filename3, __dirname3, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1632
|
+
var __filename3, __dirname3, SKILL_KEYWORD_MAP, cachedSkills, userSkillCache;
|
|
1407
1633
|
var init_skill_loader = __esm({
|
|
1408
1634
|
"src/engine/skill-loader.ts"() {
|
|
1409
1635
|
"use strict";
|
|
1410
1636
|
init_esm_shims();
|
|
1411
1637
|
init_rules_loader();
|
|
1638
|
+
init_paths();
|
|
1412
1639
|
init_asset_dir();
|
|
1413
|
-
|
|
1640
|
+
init_runtime_config_bridge();
|
|
1641
|
+
__filename3 = fileURLToPath3(import.meta.url);
|
|
1414
1642
|
__dirname3 = dirname2(__filename3);
|
|
1415
1643
|
SKILL_KEYWORD_MAP = (() => {
|
|
1416
1644
|
const map = {};
|
|
@@ -1424,6 +1652,7 @@ var init_skill_loader = __esm({
|
|
|
1424
1652
|
return map;
|
|
1425
1653
|
})();
|
|
1426
1654
|
cachedSkills = null;
|
|
1655
|
+
userSkillCache = {};
|
|
1427
1656
|
}
|
|
1428
1657
|
});
|
|
1429
1658
|
|
|
@@ -1440,6 +1669,12 @@ __export(runtime_config_exports, {
|
|
|
1440
1669
|
deleteCustomRule: () => deleteCustomRule,
|
|
1441
1670
|
deleteCustomSkill: () => deleteCustomSkill,
|
|
1442
1671
|
deleteScenarioRule: () => deleteScenarioRule,
|
|
1672
|
+
effectiveDangerousPatterns: () => effectiveDangerousPatterns,
|
|
1673
|
+
effectiveInjectionKeywords: () => effectiveInjectionKeywords,
|
|
1674
|
+
effectiveIntentPatterns: () => effectiveIntentPatterns,
|
|
1675
|
+
effectivePasswordPatterns: () => effectivePasswordPatterns,
|
|
1676
|
+
effectiveSensitivePaths: () => effectiveSensitivePaths,
|
|
1677
|
+
effectiveUserDeletionPatterns: () => effectiveUserDeletionPatterns,
|
|
1443
1678
|
filterEnabledRules: () => filterEnabledRules,
|
|
1444
1679
|
generateSourceDefaults: () => generateSourceDefaults,
|
|
1445
1680
|
getAgentRules: () => getAgentRules,
|
|
@@ -1483,9 +1718,9 @@ __export(runtime_config_exports, {
|
|
|
1483
1718
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1484
1719
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1485
1720
|
});
|
|
1486
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as
|
|
1721
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
|
|
1487
1722
|
import { join as join3 } from "path";
|
|
1488
|
-
import { fileURLToPath as
|
|
1723
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1489
1724
|
import { dirname as dirname3 } from "path";
|
|
1490
1725
|
function runtimeDir() {
|
|
1491
1726
|
return getRuntimeConfigDir();
|
|
@@ -1498,7 +1733,7 @@ function getRuntimeConfigPath() {
|
|
|
1498
1733
|
function saveRuntimeConfig(config) {
|
|
1499
1734
|
const p = getRuntimeConfigPath();
|
|
1500
1735
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1501
|
-
if (dir && !
|
|
1736
|
+
if (dir && !existsSync5(dir)) mkdirSync(dir, { recursive: true });
|
|
1502
1737
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1503
1738
|
invalidateRuntimeConfigCache();
|
|
1504
1739
|
}
|
|
@@ -1516,10 +1751,11 @@ function emptyRuntimeConfig() {
|
|
|
1516
1751
|
}
|
|
1517
1752
|
function invalidateRuntimeConfigCache() {
|
|
1518
1753
|
runtimeConfigCache = null;
|
|
1754
|
+
hasRuntimeConfigCache.clear();
|
|
1519
1755
|
}
|
|
1520
1756
|
function loadRuntimeConfig() {
|
|
1521
1757
|
const p = getRuntimeConfigPath();
|
|
1522
|
-
if (!
|
|
1758
|
+
if (!existsSync5(p)) {
|
|
1523
1759
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1524
1760
|
try {
|
|
1525
1761
|
saveRuntimeConfig(seeded);
|
|
@@ -1540,7 +1776,13 @@ function loadRuntimeConfig() {
|
|
|
1540
1776
|
layers: {
|
|
1541
1777
|
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
1542
1778
|
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
1543
|
-
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
1779
|
+
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true,
|
|
1780
|
+
// ⚠️ agent_rules_enabled / scenario_rules_enabled 必须一并带上:
|
|
1781
|
+
// isLayerEnabled 的判据是 `layerKey in layers`,键丢了就恒真,
|
|
1782
|
+
// 于是控制台的「agent 规则 / 场景规则」总开关**永远是开**(点了没反应)。
|
|
1783
|
+
// 这两个键本来就写在 seed 里(DEFAULT_LAYERS),只是这里没透传。
|
|
1784
|
+
agent_rules_enabled: data.layers?.agent_rules_enabled ?? true,
|
|
1785
|
+
scenario_rules_enabled: data.layers?.scenario_rules_enabled ?? true
|
|
1544
1786
|
},
|
|
1545
1787
|
L1_rules: merged.L1_rules || {},
|
|
1546
1788
|
L2_rules: merged.L2_rules || {},
|
|
@@ -1582,21 +1824,48 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
1582
1824
|
if (!categoryEnabled) return [];
|
|
1583
1825
|
return rules.filter((r) => r.enabled !== false);
|
|
1584
1826
|
}
|
|
1585
|
-
function
|
|
1827
|
+
function _filterRulesByOs(rules, osType) {
|
|
1828
|
+
const accepted = acceptedOsValues(osType);
|
|
1829
|
+
return rules.filter((r) => accepted.includes(String(r.os || "linux").toLowerCase()));
|
|
1830
|
+
}
|
|
1831
|
+
function _userCustomRules(runtime, layers, requiredKey) {
|
|
1832
|
+
const list = Array.isArray(layers) ? layers : [layers];
|
|
1833
|
+
const out = [];
|
|
1834
|
+
for (const layer of list) {
|
|
1835
|
+
const cat = runtime?.[layer]?.user_custom;
|
|
1836
|
+
const rules = filterEnabledRules(cat?.rules || [], cat?.category_enabled);
|
|
1837
|
+
for (const r of rules) {
|
|
1838
|
+
if (!r[requiredKey]) continue;
|
|
1839
|
+
out.push(r.risk_level ? r : { ...r, risk_level: "high" });
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
return out;
|
|
1843
|
+
}
|
|
1844
|
+
function _mergeUserCustom(primary, runtime, layers, key) {
|
|
1845
|
+
const extra = _userCustomRules(runtime, layers, key);
|
|
1846
|
+
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
1847
|
+
}
|
|
1848
|
+
function getEnabledL1CommandPatterns(runtime, osType) {
|
|
1586
1849
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
1587
|
-
|
|
1850
|
+
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
1851
|
+
rules = _mergeUserCustom(rules, runtime, "L1_rules", "pattern");
|
|
1852
|
+
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
1853
|
+
return rules.map((r) => ({
|
|
1588
1854
|
pattern: r.pattern || "",
|
|
1589
1855
|
risk_level: r.risk_level || "",
|
|
1590
1856
|
risk_type: r.risk_type || "",
|
|
1591
1857
|
reason: r.reason || ""
|
|
1592
1858
|
}));
|
|
1593
1859
|
}
|
|
1594
|
-
function getEnabledL2SensitivePaths(runtime) {
|
|
1860
|
+
function getEnabledL2SensitivePaths(runtime, osType) {
|
|
1595
1861
|
let cat = runtime.L1_rules?.sensitive_path_access;
|
|
1596
1862
|
if (!cat || !cat.rules || cat.rules.length === 0) {
|
|
1597
1863
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
1598
1864
|
}
|
|
1599
|
-
|
|
1865
|
+
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
1866
|
+
rules = _mergeUserCustom(rules, runtime, ["L1_rules", "L2_rules"], "path");
|
|
1867
|
+
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
1868
|
+
return rules.map((r) => {
|
|
1600
1869
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1601
1870
|
if (r.credential) out.credential = true;
|
|
1602
1871
|
if (r.read_only) out.read_only = true;
|
|
@@ -1606,13 +1875,24 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1606
1875
|
}
|
|
1607
1876
|
function hasRuntimeConfig() {
|
|
1608
1877
|
const p = getRuntimeConfigPath();
|
|
1609
|
-
|
|
1878
|
+
let key;
|
|
1610
1879
|
try {
|
|
1611
|
-
|
|
1612
|
-
|
|
1880
|
+
const st = statSync(p);
|
|
1881
|
+
key = `${p}|${st.mtimeMs}|${st.size}`;
|
|
1613
1882
|
} catch {
|
|
1614
1883
|
return false;
|
|
1615
1884
|
}
|
|
1885
|
+
const cached = hasRuntimeConfigCache.get(key);
|
|
1886
|
+
if (cached !== void 0) return cached;
|
|
1887
|
+
let ok = false;
|
|
1888
|
+
try {
|
|
1889
|
+
JSON.parse(readFileSync4(p, "utf-8"));
|
|
1890
|
+
ok = true;
|
|
1891
|
+
} catch {
|
|
1892
|
+
ok = false;
|
|
1893
|
+
}
|
|
1894
|
+
hasRuntimeConfigCache.set(key, ok);
|
|
1895
|
+
return ok;
|
|
1616
1896
|
}
|
|
1617
1897
|
function getDisabledL2RulesWithSkipL3() {
|
|
1618
1898
|
let runtime;
|
|
@@ -1677,15 +1957,28 @@ function _syncDenyModeFields(rule) {
|
|
|
1677
1957
|
}
|
|
1678
1958
|
}
|
|
1679
1959
|
function _l1CommandPatternsToRules() {
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1960
|
+
const out = [];
|
|
1961
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1962
|
+
for (const osName of ["linux", "windows", "macos", "harmony"]) {
|
|
1963
|
+
for (const p of getDangerousPatternsForOs(osName)) {
|
|
1964
|
+
const src = p.pattern instanceof RegExp ? p.pattern.source : String(p.pattern);
|
|
1965
|
+
const rule = {
|
|
1966
|
+
id: _generatePatternId(src),
|
|
1967
|
+
pattern: src,
|
|
1968
|
+
risk_level: p.severity,
|
|
1969
|
+
risk_type: p.riskType,
|
|
1970
|
+
reason: p.description,
|
|
1971
|
+
enabled: true,
|
|
1972
|
+
builtin: true,
|
|
1973
|
+
os: p.os || "linux"
|
|
1974
|
+
};
|
|
1975
|
+
const fp = _ruleFingerprint(rule);
|
|
1976
|
+
if (seen.has(fp)) continue;
|
|
1977
|
+
seen.add(fp);
|
|
1978
|
+
out.push(rule);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
return out;
|
|
1689
1982
|
}
|
|
1690
1983
|
function _l1InjectionKeywordsToRules() {
|
|
1691
1984
|
return INJECTION_KEYWORDS.map((kw) => ({
|
|
@@ -1698,28 +1991,42 @@ function _l1InjectionKeywordsToRules() {
|
|
|
1698
1991
|
}));
|
|
1699
1992
|
}
|
|
1700
1993
|
function _l2SensitivePathsToRules() {
|
|
1701
|
-
const
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1994
|
+
const out = [];
|
|
1995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1996
|
+
for (const osName of ["linux", "windows", "macos", "harmony"]) {
|
|
1997
|
+
const { root, home, credential } = getSensitivePathsForOsBuiltin(osName);
|
|
1998
|
+
const all = [...root, ...home, ...credential];
|
|
1999
|
+
for (const sp of all) {
|
|
2000
|
+
const os4 = String(sp.os || "linux").toLowerCase();
|
|
2001
|
+
const denyMode = _deriveDenyMode({
|
|
2002
|
+
deny_mode: sp.deny_mode,
|
|
2003
|
+
credential: sp.credential,
|
|
2004
|
+
read_only: sp.read_only
|
|
2005
|
+
});
|
|
2006
|
+
const rule = {
|
|
2007
|
+
// id = path_<平台>_<slug>_<hash8>:平台段消除跨平台同名(.ssh/.aws 各一条),
|
|
2008
|
+
// hash 段消除同平台 slug 撞车(macOS 的 /Library/LaunchAgents 与
|
|
2009
|
+
// Library/LaunchAgents)—— Console 按 id 操作,撞 id 会误改别的规则。
|
|
2010
|
+
id: `path_${os4}_${_slugify(sp.path)}_${_fnv1a32(sp.path)}`,
|
|
2011
|
+
path: sp.path,
|
|
2012
|
+
risk_level: sp.riskLevel,
|
|
2013
|
+
desc: sp.desc,
|
|
2014
|
+
deny_mode: denyMode,
|
|
2015
|
+
source_deny_mode: denyMode,
|
|
2016
|
+
skip_l3_on_disabled: true,
|
|
2017
|
+
enabled: true,
|
|
2018
|
+
builtin: true,
|
|
2019
|
+
os: os4
|
|
2020
|
+
};
|
|
2021
|
+
_syncDenyModeFields(rule);
|
|
2022
|
+
if (sp.dirDelOnly) rule.dir_del_only = true;
|
|
2023
|
+
const fp = _ruleFingerprint(rule);
|
|
2024
|
+
if (seen.has(fp)) continue;
|
|
2025
|
+
seen.add(fp);
|
|
2026
|
+
out.push(rule);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return out;
|
|
1723
2030
|
}
|
|
1724
2031
|
function _l2IntentPatternsToRules() {
|
|
1725
2032
|
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
@@ -1796,8 +2103,8 @@ function _l3SkillsToRules() {
|
|
|
1796
2103
|
}
|
|
1797
2104
|
function _scenarioRulesToDefaults() {
|
|
1798
2105
|
const out = {};
|
|
1799
|
-
const scenDir =
|
|
1800
|
-
if (!
|
|
2106
|
+
const scenDir = existsSync5(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync5(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
|
|
2107
|
+
if (!existsSync5(scenDir)) return out;
|
|
1801
2108
|
let entries;
|
|
1802
2109
|
try {
|
|
1803
2110
|
entries = readdirSync3(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1806,7 +2113,7 @@ function _scenarioRulesToDefaults() {
|
|
|
1806
2113
|
}
|
|
1807
2114
|
for (const scenarioId of entries) {
|
|
1808
2115
|
const rulesFile = join3(scenDir, scenarioId, "rules.json");
|
|
1809
|
-
if (!
|
|
2116
|
+
if (!existsSync5(rulesFile)) continue;
|
|
1810
2117
|
let data;
|
|
1811
2118
|
try {
|
|
1812
2119
|
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
@@ -1845,8 +2152,8 @@ function _scenarioRulesToDefaults() {
|
|
|
1845
2152
|
}
|
|
1846
2153
|
function _agentRulesToDefaults() {
|
|
1847
2154
|
const out = {};
|
|
1848
|
-
const agentsDir =
|
|
1849
|
-
if (!
|
|
2155
|
+
const agentsDir = existsSync5(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync5(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
|
|
2156
|
+
if (!existsSync5(agentsDir)) return out;
|
|
1850
2157
|
let entries;
|
|
1851
2158
|
try {
|
|
1852
2159
|
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1855,7 +2162,7 @@ function _agentRulesToDefaults() {
|
|
|
1855
2162
|
}
|
|
1856
2163
|
for (const agentId of entries) {
|
|
1857
2164
|
const rulesFile = join3(agentsDir, agentId, "rules.json");
|
|
1858
|
-
if (!
|
|
2165
|
+
if (!existsSync5(rulesFile)) continue;
|
|
1859
2166
|
let data;
|
|
1860
2167
|
try {
|
|
1861
2168
|
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
@@ -1951,7 +2258,7 @@ function _ltlRulesToRuntime() {
|
|
|
1951
2258
|
let mc = {};
|
|
1952
2259
|
for (const dir of cands) {
|
|
1953
2260
|
const p = join3(dir, "mc_rules.json");
|
|
1954
|
-
if (
|
|
2261
|
+
if (existsSync5(p)) {
|
|
1955
2262
|
try {
|
|
1956
2263
|
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
1957
2264
|
} catch {
|
|
@@ -1980,17 +2287,27 @@ function normalizePatternForFingerprint(pattern) {
|
|
|
1980
2287
|
return pattern.replace(/\\\//g, "/");
|
|
1981
2288
|
}
|
|
1982
2289
|
function _ruleFingerprint(rule) {
|
|
2290
|
+
const osTag = String(rule.os || "linux").toLowerCase();
|
|
1983
2291
|
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1984
2292
|
return JSON.stringify([
|
|
2293
|
+
osTag,
|
|
1985
2294
|
[...rule.intent_keywords || []].sort(),
|
|
1986
2295
|
[...rule.dangerous_actions || []].sort()
|
|
1987
2296
|
]);
|
|
1988
2297
|
}
|
|
1989
|
-
if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
|
|
1990
|
-
if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
|
|
1991
|
-
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1992
|
-
if (rule.value) return JSON.stringify([rule.value]);
|
|
1993
|
-
return JSON.stringify([rule.id || ""]);
|
|
2298
|
+
if (rule.path) return JSON.stringify([osTag, normalizePatternForFingerprint(rule.path)]);
|
|
2299
|
+
if (rule.pattern) return JSON.stringify([osTag, normalizePatternForFingerprint(rule.pattern)]);
|
|
2300
|
+
if (rule.keyword) return JSON.stringify([osTag, rule.keyword, rule.lang || ""]);
|
|
2301
|
+
if (rule.value) return JSON.stringify([osTag, rule.value]);
|
|
2302
|
+
return JSON.stringify([osTag, rule.id || ""]);
|
|
2303
|
+
}
|
|
2304
|
+
function _applyRuleEnabled(rule, enabled) {
|
|
2305
|
+
rule.enabled = enabled;
|
|
2306
|
+
if (enabled) {
|
|
2307
|
+
delete rule.user_disabled;
|
|
2308
|
+
} else {
|
|
2309
|
+
rule.user_disabled = true;
|
|
2310
|
+
}
|
|
1994
2311
|
}
|
|
1995
2312
|
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
1996
2313
|
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
@@ -2013,13 +2330,14 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
2013
2330
|
srcFps.add(fp);
|
|
2014
2331
|
if (userByFp.has(fp)) {
|
|
2015
2332
|
const existing = userByFp.get(fp);
|
|
2016
|
-
const
|
|
2333
|
+
const userDisabled = existing.user_disabled === true;
|
|
2017
2334
|
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
2018
2335
|
const userDenyMode = existing.deny_mode;
|
|
2019
2336
|
const userSourceDeny = existing.source_deny_mode;
|
|
2020
2337
|
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
2021
2338
|
Object.assign(existing, srcR);
|
|
2022
|
-
existing.enabled =
|
|
2339
|
+
existing.enabled = !userDisabled;
|
|
2340
|
+
if (userDisabled) existing.user_disabled = true;
|
|
2023
2341
|
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
2024
2342
|
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
2025
2343
|
existing.deny_mode = userDenyMode;
|
|
@@ -2045,7 +2363,19 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
2045
2363
|
}
|
|
2046
2364
|
for (const catName of Object.keys(userLayer)) {
|
|
2047
2365
|
if (catName === "user_custom") continue;
|
|
2048
|
-
if (!(catName in srcLayer))
|
|
2366
|
+
if (!(catName in srcLayer)) {
|
|
2367
|
+
const kept = (userLayer[catName].rules || []).filter((r) => r.builtin !== true);
|
|
2368
|
+
if (kept.length) {
|
|
2369
|
+
const uc = userLayer.user_custom || (userLayer.user_custom = {
|
|
2370
|
+
category_enabled: true,
|
|
2371
|
+
category_desc: "",
|
|
2372
|
+
rules: []
|
|
2373
|
+
});
|
|
2374
|
+
const existing = new Set((uc.rules || []).map((r) => r.id));
|
|
2375
|
+
uc.rules = [...uc.rules || [], ...kept.filter((r) => !existing.has(r.id))];
|
|
2376
|
+
}
|
|
2377
|
+
delete userLayer[catName];
|
|
2378
|
+
}
|
|
2049
2379
|
}
|
|
2050
2380
|
}
|
|
2051
2381
|
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
@@ -2093,10 +2423,11 @@ function _mergeAgentRules(userCfg, srcDefaults, partition = "agent_rules") {
|
|
|
2093
2423
|
srcFps.add(fp);
|
|
2094
2424
|
if (userByFp.has(fp)) {
|
|
2095
2425
|
const existing = userByFp.get(fp);
|
|
2096
|
-
const
|
|
2426
|
+
const userDisabled = existing.user_disabled === true;
|
|
2097
2427
|
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
2098
2428
|
Object.assign(existing, srcR);
|
|
2099
|
-
existing.enabled =
|
|
2429
|
+
existing.enabled = !userDisabled;
|
|
2430
|
+
if (userDisabled) existing.user_disabled = true;
|
|
2100
2431
|
} else {
|
|
2101
2432
|
userRules.push({ ...srcR, enabled: true });
|
|
2102
2433
|
}
|
|
@@ -2160,17 +2491,23 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2160
2491
|
delete src[catName];
|
|
2161
2492
|
}
|
|
2162
2493
|
}
|
|
2163
|
-
function getEnabledL1InjectionKeywords(runtime) {
|
|
2494
|
+
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2164
2495
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2165
|
-
|
|
2496
|
+
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2497
|
+
rules = _mergeUserCustom(rules, runtime, "L1_rules", "keyword");
|
|
2498
|
+
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2499
|
+
return rules.map((r) => ({
|
|
2166
2500
|
keyword: r.keyword || "",
|
|
2167
2501
|
risk_level: r.risk_level || "",
|
|
2168
2502
|
lang: r.lang || "en"
|
|
2169
2503
|
}));
|
|
2170
2504
|
}
|
|
2171
|
-
function getEnabledL2IntentPatterns(runtime) {
|
|
2505
|
+
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2172
2506
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2173
|
-
|
|
2507
|
+
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2508
|
+
rules = _mergeUserCustom(rules, runtime, "L2_rules", "intent_keywords");
|
|
2509
|
+
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2510
|
+
return rules.map((r) => ({
|
|
2174
2511
|
intent_keywords: r.intent_keywords || [],
|
|
2175
2512
|
dangerous_actions: r.dangerous_actions || [],
|
|
2176
2513
|
reason: r.reason || ""
|
|
@@ -2178,11 +2515,13 @@ function getEnabledL2IntentPatterns(runtime) {
|
|
|
2178
2515
|
}
|
|
2179
2516
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2180
2517
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2181
|
-
|
|
2518
|
+
const rules = _mergeUserCustom(filterEnabledRules(cat.rules || [], cat.category_enabled), runtime, "L2_rules", "pattern");
|
|
2519
|
+
return rules.map((r) => r.pattern || "");
|
|
2182
2520
|
}
|
|
2183
2521
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2184
2522
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2185
|
-
|
|
2523
|
+
const rules = _mergeUserCustom(filterEnabledRules(cat.rules || [], cat.category_enabled), runtime, "L2_rules", "pattern");
|
|
2524
|
+
return rules.map((r) => r.pattern || "");
|
|
2186
2525
|
}
|
|
2187
2526
|
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
2188
2527
|
const cat = runtime.L2_rules?.[categoryKey];
|
|
@@ -2201,45 +2540,126 @@ function getDisabledSkillIds(runtime) {
|
|
|
2201
2540
|
}
|
|
2202
2541
|
return disabled;
|
|
2203
2542
|
}
|
|
2204
|
-
function
|
|
2205
|
-
const
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
if (
|
|
2214
|
-
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2543
|
+
function _filterDefaultsByOs(rules, osType) {
|
|
2544
|
+
const accepted = acceptedOsValues(osType);
|
|
2545
|
+
return rules.filter((r) => !r.os || accepted.includes(String(r.os).toLowerCase()));
|
|
2546
|
+
}
|
|
2547
|
+
function _hasRulesForOs(rules, osType) {
|
|
2548
|
+
return _filterRulesByOs(rules || [], osType || "linux").length > 0;
|
|
2549
|
+
}
|
|
2550
|
+
function getEnabledL1CommandPatternsOrDefault(runtime, osType) {
|
|
2551
|
+
const cat = runtime.L1_rules?.dangerous_commands;
|
|
2552
|
+
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2553
|
+
return getEnabledL1CommandPatterns(runtime, osType);
|
|
2554
|
+
}
|
|
2555
|
+
return _mergeUserCustom(_filterDefaultsByOs(_l1CommandPatternsToRules(), osType), runtime, "L1_rules", "pattern");
|
|
2556
|
+
}
|
|
2557
|
+
function getEnabledL1InjectionKeywordsOrDefault(runtime, osType) {
|
|
2558
|
+
const cat = runtime.L1_rules?.injection_detection;
|
|
2559
|
+
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2560
|
+
return getEnabledL1InjectionKeywords(runtime, osType);
|
|
2561
|
+
}
|
|
2562
|
+
return _mergeUserCustom(_l1InjectionKeywordsToRules(), runtime, "L1_rules", "keyword");
|
|
2563
|
+
}
|
|
2564
|
+
function getEnabledL2SensitivePathsOrDefault(runtime, osType) {
|
|
2565
|
+
const spCat = _sensitivePathCategoryOf(runtime);
|
|
2566
|
+
if (_hasRulesForOs(spCat?.rules, osType)) {
|
|
2567
|
+
return getEnabledL2SensitivePaths(runtime, osType);
|
|
2568
|
+
}
|
|
2569
|
+
return _mergeUserCustom(_filterDefaultsByOs(_l2SensitivePathsToRules(), osType), runtime, ["L1_rules", "L2_rules"], "path");
|
|
2570
|
+
}
|
|
2571
|
+
function getEnabledL2IntentPatternsOrDefault(runtime, osType) {
|
|
2572
|
+
const cat = runtime.L2_rules?.intent_consistency;
|
|
2573
|
+
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2574
|
+
return getEnabledL2IntentPatterns(runtime, osType);
|
|
2575
|
+
}
|
|
2576
|
+
return _mergeUserCustom(_filterDefaultsByOs(_l2IntentPatternsToRules(), osType), runtime, "L2_rules", "intent_keywords");
|
|
2577
|
+
}
|
|
2578
|
+
function getEnabledL2PasswordPatternsOrDefault(runtime, osType) {
|
|
2579
|
+
const cat = runtime.L2_rules?.password_modify_consent;
|
|
2580
|
+
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2581
|
+
return getEnabledL2PasswordPatterns(runtime);
|
|
2582
|
+
}
|
|
2583
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
|
|
2584
|
+
}
|
|
2585
|
+
function getEnabledL2UserDeletionPatternsOrDefault(runtime, osType) {
|
|
2586
|
+
const cat = runtime.L2_rules?.user_deletion_consent;
|
|
2587
|
+
if (_hasRulesForOs(cat?.rules, osType)) {
|
|
2588
|
+
return getEnabledL2UserDeletionPatterns(runtime);
|
|
2589
|
+
}
|
|
2590
|
+
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
2591
|
+
}
|
|
2592
|
+
function _effective(osType, key, compute) {
|
|
2593
|
+
const runtime = loadRuntimeConfig();
|
|
2594
|
+
let bucket = _effectiveCache.get(runtime);
|
|
2595
|
+
if (!bucket) {
|
|
2596
|
+
bucket = /* @__PURE__ */ new Map();
|
|
2597
|
+
_effectiveCache.set(runtime, bucket);
|
|
2598
|
+
}
|
|
2599
|
+
const k = `${key}|${osType || ""}`;
|
|
2600
|
+
if (bucket.has(k)) return bucket.get(k);
|
|
2601
|
+
const value = compute(runtime);
|
|
2602
|
+
bucket.set(k, value);
|
|
2603
|
+
return value;
|
|
2604
|
+
}
|
|
2605
|
+
function effectiveDangerousPatterns(osType) {
|
|
2606
|
+
return _effective(osType, "danger", (rt) => {
|
|
2607
|
+
const rules = getEnabledL1CommandPatternsOrDefault(rt, osType);
|
|
2608
|
+
return rules.filter((r) => r.pattern).map((r) => ({
|
|
2609
|
+
pattern: new RegExp(String(r.pattern), "i"),
|
|
2610
|
+
severity: String(r.risk_level || "medium"),
|
|
2611
|
+
riskType: String(r.risk_type || ""),
|
|
2612
|
+
description: String(r.reason || ""),
|
|
2613
|
+
os: String(r.os || "linux").toLowerCase()
|
|
2614
|
+
}));
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
function effectiveInjectionKeywords(osType) {
|
|
2618
|
+
return _effective(osType, "injection", (rt) => {
|
|
2619
|
+
const rules = getEnabledL1InjectionKeywordsOrDefault(rt, osType);
|
|
2620
|
+
return rules.map((r) => ({
|
|
2621
|
+
keyword: String(r.keyword || ""),
|
|
2622
|
+
riskLevel: String(r.risk_level || ""),
|
|
2623
|
+
lang: String(r.lang || "en")
|
|
2624
|
+
})).filter((r) => r.keyword);
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
function effectiveSensitivePaths(osType) {
|
|
2628
|
+
return _effective(osType, "sensitive", (rt) => {
|
|
2629
|
+
const rules = getEnabledL2SensitivePathsOrDefault(rt, osType);
|
|
2630
|
+
return rules.filter((r) => r.path).map((r) => ({
|
|
2631
|
+
path: String(r.path),
|
|
2632
|
+
riskLevel: String(r.risk_level || "high"),
|
|
2633
|
+
desc: String(r.desc || ""),
|
|
2634
|
+
credential: r.credential === true ? true : void 0,
|
|
2635
|
+
readOnly: r.read_only === true ? true : void 0,
|
|
2636
|
+
dirDelOnly: r.dir_del_only === true ? true : void 0,
|
|
2637
|
+
denyMode: String(r.deny_mode || ""),
|
|
2638
|
+
os: String(r.os || "linux").toLowerCase()
|
|
2639
|
+
}));
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
function effectiveIntentPatterns(osType) {
|
|
2643
|
+
return _effective(osType, "intent", (rt) => {
|
|
2644
|
+
const rules = getEnabledL2IntentPatternsOrDefault(rt, osType);
|
|
2645
|
+
return rules.map((r) => ({
|
|
2646
|
+
intentKeywords: r.intent_keywords || [],
|
|
2647
|
+
dangerousActions: r.dangerous_actions || [],
|
|
2648
|
+
reason: String(r.reason || ""),
|
|
2649
|
+
os: String(r.os || "linux").toLowerCase()
|
|
2650
|
+
}));
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
function effectivePasswordPatterns(osType) {
|
|
2654
|
+
return _effective(osType, "password", (rt) => getEnabledL2PasswordPatternsOrDefault(rt, osType).map((p) => new RegExp(p, "i")));
|
|
2655
|
+
}
|
|
2656
|
+
function effectiveUserDeletionPatterns(osType) {
|
|
2657
|
+
return _effective(osType, "userdel", (rt) => getEnabledL2UserDeletionPatternsOrDefault(rt, osType).map((p) => new RegExp(p, "i")));
|
|
2658
|
+
}
|
|
2659
|
+
function _sensitivePathCategoryOf(runtime) {
|
|
2660
|
+
const l1 = runtime.L1_rules?.sensitive_path_access;
|
|
2661
|
+
if (l1 && (l1.rules || []).length > 0) return l1;
|
|
2662
|
+
return runtime.L2_rules?.sensitive_path_access;
|
|
2243
2663
|
}
|
|
2244
2664
|
function _validateSkillId(skillId) {
|
|
2245
2665
|
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
@@ -2256,7 +2676,7 @@ function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
|
2256
2676
|
const runtime = loadRuntimeConfig();
|
|
2257
2677
|
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2258
2678
|
if (r.id === ruleId) {
|
|
2259
|
-
r
|
|
2679
|
+
_applyRuleEnabled(r, enabled);
|
|
2260
2680
|
break;
|
|
2261
2681
|
}
|
|
2262
2682
|
}
|
|
@@ -2326,7 +2746,7 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2326
2746
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2327
2747
|
_validateSkillId(skillId);
|
|
2328
2748
|
const runtime = loadRuntimeConfig();
|
|
2329
|
-
const skillsDir = join3(
|
|
2749
|
+
const skillsDir = join3(getRuntimeConfigDir(), "agent_moss_skills");
|
|
2330
2750
|
try {
|
|
2331
2751
|
mkdirSync(skillsDir, { recursive: true });
|
|
2332
2752
|
} catch {
|
|
@@ -2346,9 +2766,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2346
2766
|
if (idx >= 0) {
|
|
2347
2767
|
if (skills[idx].builtin) return null;
|
|
2348
2768
|
skills.splice(idx, 1);
|
|
2349
|
-
const skillPath = join3(
|
|
2769
|
+
const skillPath = join3(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
2350
2770
|
try {
|
|
2351
|
-
if (
|
|
2771
|
+
if (existsSync5(skillPath)) unlinkSync(skillPath);
|
|
2352
2772
|
} catch {
|
|
2353
2773
|
}
|
|
2354
2774
|
saveRuntimeConfig(runtime);
|
|
@@ -2427,7 +2847,7 @@ function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
|
|
|
2427
2847
|
const targetFp = _ruleFingerprint(ruleFp);
|
|
2428
2848
|
for (const r of cat.rules || []) {
|
|
2429
2849
|
if (_ruleFingerprint(r) === targetFp) {
|
|
2430
|
-
r
|
|
2850
|
+
_applyRuleEnabled(r, enabled);
|
|
2431
2851
|
break;
|
|
2432
2852
|
}
|
|
2433
2853
|
}
|
|
@@ -2455,137 +2875,419 @@ function deleteAgentRule(agentId, category, ruleFp) {
|
|
|
2455
2875
|
saveRuntimeConfig(runtime);
|
|
2456
2876
|
return runtime;
|
|
2457
2877
|
}
|
|
2458
|
-
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
2459
|
-
const runtime = loadRuntimeConfig();
|
|
2460
|
-
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
2461
|
-
if (cat) cat.category_enabled = enabled;
|
|
2462
|
-
saveRuntimeConfig(runtime);
|
|
2463
|
-
return runtime;
|
|
2878
|
+
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
2879
|
+
const runtime = loadRuntimeConfig();
|
|
2880
|
+
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
2881
|
+
if (cat) cat.category_enabled = enabled;
|
|
2882
|
+
saveRuntimeConfig(runtime);
|
|
2883
|
+
return runtime;
|
|
2884
|
+
}
|
|
2885
|
+
function getScenarioRules(runtime) {
|
|
2886
|
+
return runtime.scenario_rules || {};
|
|
2887
|
+
}
|
|
2888
|
+
function getEnabledScenarioExtensions(runtime, scenarioId) {
|
|
2889
|
+
const scenData = runtime.scenario_rules?.[scenarioId];
|
|
2890
|
+
if (!scenData) return null;
|
|
2891
|
+
const cmdPatterns = _agentCategoryEnabledRules(scenData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
2892
|
+
pattern: r.pattern,
|
|
2893
|
+
risk_level: r.risk_level || "",
|
|
2894
|
+
risk_type: r.risk_type || "",
|
|
2895
|
+
reason: r.reason || ""
|
|
2896
|
+
}));
|
|
2897
|
+
const injKeywords = _agentCategoryEnabledRules(scenData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
2898
|
+
keyword: r.keyword,
|
|
2899
|
+
risk_level: r.risk_level || "",
|
|
2900
|
+
lang: r.lang || "en"
|
|
2901
|
+
}));
|
|
2902
|
+
const sensitivePaths = _agentCategoryEnabledRules(scenData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
2903
|
+
path: r.path,
|
|
2904
|
+
risk_level: r.risk_level || "",
|
|
2905
|
+
desc: r.desc || "",
|
|
2906
|
+
credential: r.credential,
|
|
2907
|
+
read_only: r.read_only,
|
|
2908
|
+
deny_mode: r.deny_mode
|
|
2909
|
+
}));
|
|
2910
|
+
return {
|
|
2911
|
+
agent_id: scenarioId,
|
|
2912
|
+
description: "",
|
|
2913
|
+
extra_command_patterns: cmdPatterns,
|
|
2914
|
+
extra_injection_keywords: injKeywords,
|
|
2915
|
+
extra_sensitive_paths: sensitivePaths,
|
|
2916
|
+
builtin_safe_skills: [],
|
|
2917
|
+
builtin_safe_action_types: [],
|
|
2918
|
+
extra_skills: []
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
function updateScenarioRuleEnabled(scenarioId, category, ruleFp, enabled) {
|
|
2922
|
+
const runtime = loadRuntimeConfig();
|
|
2923
|
+
const cat = runtime.scenario_rules?.[scenarioId]?.[category];
|
|
2924
|
+
if (cat) {
|
|
2925
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
2926
|
+
for (const r of cat.rules || []) {
|
|
2927
|
+
if (_ruleFingerprint(r) === targetFp) {
|
|
2928
|
+
_applyRuleEnabled(r, enabled);
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
saveRuntimeConfig(runtime);
|
|
2934
|
+
return runtime;
|
|
2935
|
+
}
|
|
2936
|
+
function addScenarioRule(scenarioId, category, rule) {
|
|
2937
|
+
const runtime = loadRuntimeConfig();
|
|
2938
|
+
if (!runtime.scenario_rules) runtime.scenario_rules = {};
|
|
2939
|
+
if (!runtime.scenario_rules[scenarioId]) runtime.scenario_rules[scenarioId] = {};
|
|
2940
|
+
const cats = runtime.scenario_rules[scenarioId];
|
|
2941
|
+
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
2942
|
+
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
2943
|
+
saveRuntimeConfig(runtime);
|
|
2944
|
+
return runtime;
|
|
2945
|
+
}
|
|
2946
|
+
function deleteScenarioRule(scenarioId, category, ruleFp) {
|
|
2947
|
+
const runtime = loadRuntimeConfig();
|
|
2948
|
+
const cat = runtime.scenario_rules?.[scenarioId]?.[category];
|
|
2949
|
+
if (cat) {
|
|
2950
|
+
const targetFp = _ruleFingerprint(ruleFp);
|
|
2951
|
+
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
2952
|
+
}
|
|
2953
|
+
saveRuntimeConfig(runtime);
|
|
2954
|
+
return runtime;
|
|
2955
|
+
}
|
|
2956
|
+
function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
2957
|
+
const runtime = loadRuntimeConfig();
|
|
2958
|
+
const cat = runtime.scenario_rules?.[scenarioId]?.[category];
|
|
2959
|
+
if (cat) cat.category_enabled = enabled;
|
|
2960
|
+
saveRuntimeConfig(runtime);
|
|
2961
|
+
return runtime;
|
|
2962
|
+
}
|
|
2963
|
+
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, hasRuntimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, _effectiveCache, SKILL_ID_RE;
|
|
2964
|
+
var init_runtime_config = __esm({
|
|
2965
|
+
"src/infra/runtime-config.ts"() {
|
|
2966
|
+
"use strict";
|
|
2967
|
+
init_esm_shims();
|
|
2968
|
+
init_paths();
|
|
2969
|
+
init_heuristic();
|
|
2970
|
+
init_logic_rules();
|
|
2971
|
+
init_skill_loader();
|
|
2972
|
+
init_runtime_config_bridge();
|
|
2973
|
+
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
2974
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2975
|
+
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
2976
|
+
RUNTIME_CONFIG_PATH = join3(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2977
|
+
LAYER_DISABLE_ENV = {
|
|
2978
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2979
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2980
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2981
|
+
};
|
|
2982
|
+
DEFAULT_LAYERS = {
|
|
2983
|
+
L1_heuristic: true,
|
|
2984
|
+
L2_logic_rules: true,
|
|
2985
|
+
L3_llm_analysis: true,
|
|
2986
|
+
agent_rules_enabled: true,
|
|
2987
|
+
scenario_rules_enabled: true
|
|
2988
|
+
};
|
|
2989
|
+
DENY_MODE_LABELS = {
|
|
2990
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2991
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2992
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2993
|
+
};
|
|
2994
|
+
runtimeConfigCache = null;
|
|
2995
|
+
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
2996
|
+
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2997
|
+
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2998
|
+
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
2999
|
+
_SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
3000
|
+
_effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
3001
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
3002
|
+
registerRuntimeConfigBridge({
|
|
3003
|
+
hasRuntimeConfig,
|
|
3004
|
+
loadRuntimeConfig,
|
|
3005
|
+
effectiveDangerousPatterns,
|
|
3006
|
+
effectiveInjectionKeywords,
|
|
3007
|
+
effectiveSensitivePaths,
|
|
3008
|
+
effectiveIntentPatterns,
|
|
3009
|
+
effectivePasswordPatterns,
|
|
3010
|
+
effectiveUserDeletionPatterns,
|
|
3011
|
+
disabledSkillIds: () => getDisabledSkillIds(loadRuntimeConfig())
|
|
3012
|
+
});
|
|
3013
|
+
}
|
|
3014
|
+
});
|
|
3015
|
+
|
|
3016
|
+
// src/config.ts
|
|
3017
|
+
var config_exports = {};
|
|
3018
|
+
__export(config_exports, {
|
|
3019
|
+
detectProvider: () => detectProvider,
|
|
3020
|
+
getCustomRules: () => getCustomRules,
|
|
3021
|
+
getLLMConfig: () => getLLMConfig,
|
|
3022
|
+
getLLMFailMode: () => getLLMFailMode,
|
|
3023
|
+
getProviderHeaders: () => getProviderHeaders,
|
|
3024
|
+
getRetryConfig: () => getRetryConfig,
|
|
3025
|
+
getTimeoutConfig: () => getTimeoutConfig,
|
|
3026
|
+
isBrainEnabled: () => isBrainEnabled,
|
|
3027
|
+
isFormalObserveMode: () => isFormalObserveMode,
|
|
3028
|
+
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
3029
|
+
isLLMAvailable: () => isLLMAvailable,
|
|
3030
|
+
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
3031
|
+
probeLLMConnection: () => probeLLMConnection,
|
|
3032
|
+
updateLLMConfig: () => updateLLMConfig
|
|
3033
|
+
});
|
|
3034
|
+
import fs2 from "fs";
|
|
3035
|
+
import path2 from "path";
|
|
3036
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3037
|
+
function loadConfigFile(filePath) {
|
|
3038
|
+
try {
|
|
3039
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
3040
|
+
return JSON.parse(raw);
|
|
3041
|
+
} catch {
|
|
3042
|
+
return null;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
function resolveConfigPath() {
|
|
3046
|
+
const envPath = process.env.AGENT_MOSS_CONFIG_PATH;
|
|
3047
|
+
if (envPath) return envPath;
|
|
3048
|
+
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
3049
|
+
if (fs2.existsSync(userConfig)) return userConfig;
|
|
3050
|
+
const defaultCandidates = [
|
|
3051
|
+
path2.join(__dirname, "config.json.example"),
|
|
3052
|
+
path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example")
|
|
3053
|
+
];
|
|
3054
|
+
const defaultConfig = defaultCandidates.find((p) => fs2.existsSync(p));
|
|
3055
|
+
if (defaultConfig) {
|
|
3056
|
+
const configDir = path2.dirname(userConfig);
|
|
3057
|
+
fs2.mkdirSync(configDir, { recursive: true });
|
|
3058
|
+
const pkgConfig = loadConfigFile(defaultConfig);
|
|
3059
|
+
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
3060
|
+
fs2.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
3061
|
+
console.log(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
3062
|
+
console.log(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
3063
|
+
}
|
|
3064
|
+
return userConfig;
|
|
3065
|
+
}
|
|
3066
|
+
function loadLLMConfigFromFile(configPath) {
|
|
3067
|
+
const data = loadConfigFile(configPath);
|
|
3068
|
+
if (!data?.llm) return {};
|
|
3069
|
+
const llm = data.llm;
|
|
3070
|
+
return {
|
|
3071
|
+
provider: llm.provider || "",
|
|
3072
|
+
baseUrl: llm.base_url || "",
|
|
3073
|
+
apiKey: llm.api_key || "",
|
|
3074
|
+
model: llm.model || "",
|
|
3075
|
+
temperature: llm.temperature ?? 0.1,
|
|
3076
|
+
maxTokens: llm.max_tokens ?? 4096,
|
|
3077
|
+
timeout: llm.timeout ?? 40
|
|
3078
|
+
};
|
|
3079
|
+
}
|
|
3080
|
+
function loadTimeoutRetryFromFile(configPath) {
|
|
3081
|
+
const data = loadConfigFile(configPath);
|
|
3082
|
+
const t = data?.timeout ?? {};
|
|
3083
|
+
const r = data?.retry ?? {};
|
|
3084
|
+
return {
|
|
3085
|
+
timeout: {
|
|
3086
|
+
totalTimeout: Number(t.total_timeout ?? 90),
|
|
3087
|
+
prompt1Timeout: Number(t.prompt1_timeout ?? 40),
|
|
3088
|
+
prompt2Timeout: Number(t.prompt2_timeout ?? 20),
|
|
3089
|
+
stepInterval: Number(t.step_interval ?? 0)
|
|
3090
|
+
},
|
|
3091
|
+
retry: {
|
|
3092
|
+
maxRetries: Number(r.max_retries ?? 2),
|
|
3093
|
+
retryInterval: Number(r.retry_interval ?? 2)
|
|
3094
|
+
}
|
|
3095
|
+
};
|
|
3096
|
+
}
|
|
3097
|
+
function getTimeoutConfig() {
|
|
3098
|
+
return timeoutConfig;
|
|
3099
|
+
}
|
|
3100
|
+
function getRetryConfig() {
|
|
3101
|
+
return retryConfig;
|
|
3102
|
+
}
|
|
3103
|
+
function wildcardToRegExp(pattern) {
|
|
3104
|
+
return pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
3105
|
+
}
|
|
3106
|
+
function loadCustomRules() {
|
|
3107
|
+
const rules = [];
|
|
3108
|
+
try {
|
|
3109
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
3110
|
+
if (raw) {
|
|
3111
|
+
const parsed = JSON.parse(raw);
|
|
3112
|
+
if (Array.isArray(parsed)) {
|
|
3113
|
+
for (const r of parsed) {
|
|
3114
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
} catch {
|
|
3119
|
+
}
|
|
3120
|
+
for (const rulesPath of USER_RULES_PATHS) {
|
|
3121
|
+
try {
|
|
3122
|
+
if (!fs2.existsSync(rulesPath)) continue;
|
|
3123
|
+
const data = JSON.parse(fs2.readFileSync(rulesPath, "utf-8"));
|
|
3124
|
+
const actions = Array.isArray(data?.sensitive_actions) ? data.sensitive_actions : [];
|
|
3125
|
+
for (const r of actions) {
|
|
3126
|
+
const p = r?.pattern;
|
|
3127
|
+
if (!p) continue;
|
|
3128
|
+
rules.push({ pattern: wildcardToRegExp(p), action: "Deny", severity: "high" });
|
|
3129
|
+
}
|
|
3130
|
+
break;
|
|
3131
|
+
} catch {
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return rules;
|
|
3135
|
+
}
|
|
3136
|
+
function getLLMConfig() {
|
|
3137
|
+
return llmConfig;
|
|
3138
|
+
}
|
|
3139
|
+
function updateLLMConfig(partial) {
|
|
3140
|
+
llmConfig = { ...llmConfig, ...partial };
|
|
3141
|
+
}
|
|
3142
|
+
function getCustomRules() {
|
|
3143
|
+
return customRules;
|
|
3144
|
+
}
|
|
3145
|
+
function isLLMAvailable() {
|
|
3146
|
+
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
3147
|
+
if (!layerEnabledOrDefault("L3_llm_analysis")) return false;
|
|
3148
|
+
return llmConfig.apiKey.length > 0;
|
|
3149
|
+
}
|
|
3150
|
+
async function probeLLMConnection(timeoutMs = 3e4) {
|
|
3151
|
+
if (!isLLMAvailable()) return false;
|
|
3152
|
+
const OpenAI3 = (await import("openai")).default;
|
|
3153
|
+
const config = getLLMConfig();
|
|
3154
|
+
const provider = detectProvider(config.baseUrl, config.provider);
|
|
3155
|
+
const extraHeaders = getProviderHeaders(provider);
|
|
3156
|
+
const client = new OpenAI3({
|
|
3157
|
+
baseURL: config.baseUrl,
|
|
3158
|
+
apiKey: config.apiKey,
|
|
3159
|
+
timeout: timeoutMs,
|
|
3160
|
+
maxRetries: 0,
|
|
3161
|
+
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
3162
|
+
});
|
|
3163
|
+
try {
|
|
3164
|
+
const resp = await client.chat.completions.create({
|
|
3165
|
+
model: config.model,
|
|
3166
|
+
messages: [{ role: "user", content: "ping" }],
|
|
3167
|
+
max_tokens: 1
|
|
3168
|
+
});
|
|
3169
|
+
return !!(resp && resp.choices && resp.choices[0]);
|
|
3170
|
+
} catch {
|
|
3171
|
+
return false;
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
function getLLMFailMode() {
|
|
3175
|
+
const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
|
|
3176
|
+
return val === "fail_closed" ? "fail_closed" : "fail_open";
|
|
3177
|
+
}
|
|
3178
|
+
function detectProvider(baseUrl, providerHint = "") {
|
|
3179
|
+
if (providerHint) {
|
|
3180
|
+
const hint = providerHint.toLowerCase();
|
|
3181
|
+
if (["claude", "anthropic"].includes(hint)) return "anthropic";
|
|
3182
|
+
if (["google", "gemini"].includes(hint)) return "gemini";
|
|
3183
|
+
if (["zai-coding-plan", "zhipu-coding-plan", "zhipuai-coding-plan"].includes(hint)) return "zai-coding-plan";
|
|
3184
|
+
if (["zai", "z-ai", "z.ai", "zai-cn", "zai-china", "zhipu", "glm-cn", "bigmodel"].includes(hint)) return "zhipu";
|
|
3185
|
+
if (["xai", "xai-grok"].includes(hint)) return "xai";
|
|
3186
|
+
if (["minimax", "minimax-openai"].includes(hint)) return "minimax";
|
|
3187
|
+
if (["minimax-anthropic"].includes(hint)) return "minimax-anthropic";
|
|
3188
|
+
if (["other", "custom", "local"].includes(hint)) return "openai-compatible";
|
|
3189
|
+
return hint;
|
|
3190
|
+
}
|
|
3191
|
+
const url = baseUrl.toLowerCase();
|
|
3192
|
+
if (url.includes("openrouter.ai")) return "openrouter";
|
|
3193
|
+
if (url.includes("api.openai.com")) return "openai";
|
|
3194
|
+
if (url.includes("anthropic.com") || url.includes("api.anthropic.com")) return "anthropic";
|
|
3195
|
+
if (url.includes("generativelanguage.googleapis.com") || url.includes("gemini")) return "gemini";
|
|
3196
|
+
if (url.includes("api.deepseek.com")) return "deepseek";
|
|
3197
|
+
if (url.includes("api.groq.com")) return "groq";
|
|
3198
|
+
if (url.includes("api.mistral.ai")) return "mistral";
|
|
3199
|
+
if (url.includes("api.together.xyz")) return "together";
|
|
3200
|
+
if (url.includes("api.x.ai")) return "xai";
|
|
3201
|
+
if (url.includes("api.minimaxi.com")) return "minimax";
|
|
3202
|
+
if (url.includes("api-ai.gitcode.com")) return "gitcode";
|
|
3203
|
+
if (url.includes("api.z.ai")) return "zai-coding-plan";
|
|
3204
|
+
if (url.includes("open.bigmodel.cn") || url.includes("bigmodel")) return "zhipu";
|
|
3205
|
+
if (url.includes(":11434")) return "ollama";
|
|
3206
|
+
return "openai-compatible";
|
|
2464
3207
|
}
|
|
2465
|
-
function
|
|
2466
|
-
return
|
|
3208
|
+
function getProviderHeaders(provider) {
|
|
3209
|
+
return PROVIDER_HEADERS[provider] || {};
|
|
2467
3210
|
}
|
|
2468
|
-
function
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
const cmdPatterns = _agentCategoryEnabledRules(scenData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
2472
|
-
pattern: r.pattern,
|
|
2473
|
-
risk_level: r.risk_level || "",
|
|
2474
|
-
risk_type: r.risk_type || "",
|
|
2475
|
-
reason: r.reason || ""
|
|
2476
|
-
}));
|
|
2477
|
-
const injKeywords = _agentCategoryEnabledRules(scenData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
2478
|
-
keyword: r.keyword,
|
|
2479
|
-
risk_level: r.risk_level || "",
|
|
2480
|
-
lang: r.lang || "en"
|
|
2481
|
-
}));
|
|
2482
|
-
const sensitivePaths = _agentCategoryEnabledRules(scenData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
2483
|
-
path: r.path,
|
|
2484
|
-
risk_level: r.risk_level || "",
|
|
2485
|
-
desc: r.desc || "",
|
|
2486
|
-
credential: r.credential,
|
|
2487
|
-
read_only: r.read_only,
|
|
2488
|
-
deny_mode: r.deny_mode
|
|
2489
|
-
}));
|
|
2490
|
-
return {
|
|
2491
|
-
agent_id: scenarioId,
|
|
2492
|
-
description: "",
|
|
2493
|
-
extra_command_patterns: cmdPatterns,
|
|
2494
|
-
extra_injection_keywords: injKeywords,
|
|
2495
|
-
extra_sensitive_paths: sensitivePaths,
|
|
2496
|
-
builtin_safe_skills: [],
|
|
2497
|
-
builtin_safe_action_types: [],
|
|
2498
|
-
extra_skills: []
|
|
2499
|
-
};
|
|
3211
|
+
function isHeuristicEnabled() {
|
|
3212
|
+
if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
|
|
3213
|
+
return layerEnabledOrDefault("L1_heuristic");
|
|
2500
3214
|
}
|
|
2501
|
-
function
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
for (const r of cat.rules || []) {
|
|
2507
|
-
if (_ruleFingerprint(r) === targetFp) {
|
|
2508
|
-
r.enabled = enabled;
|
|
2509
|
-
break;
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
3215
|
+
function layerEnabledOrDefault(key) {
|
|
3216
|
+
try {
|
|
3217
|
+
return isLayerEnabled(key);
|
|
3218
|
+
} catch {
|
|
3219
|
+
return true;
|
|
2512
3220
|
}
|
|
2513
|
-
saveRuntimeConfig(runtime);
|
|
2514
|
-
return runtime;
|
|
2515
3221
|
}
|
|
2516
|
-
function
|
|
2517
|
-
|
|
2518
|
-
if (!runtime.scenario_rules) runtime.scenario_rules = {};
|
|
2519
|
-
if (!runtime.scenario_rules[scenarioId]) runtime.scenario_rules[scenarioId] = {};
|
|
2520
|
-
const cats = runtime.scenario_rules[scenarioId];
|
|
2521
|
-
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
2522
|
-
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
2523
|
-
saveRuntimeConfig(runtime);
|
|
2524
|
-
return runtime;
|
|
3222
|
+
function isBrainEnabled() {
|
|
3223
|
+
return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
|
|
2525
3224
|
}
|
|
2526
|
-
function
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
if (cat) {
|
|
2530
|
-
const targetFp = _ruleFingerprint(ruleFp);
|
|
2531
|
-
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
2532
|
-
}
|
|
2533
|
-
saveRuntimeConfig(runtime);
|
|
2534
|
-
return runtime;
|
|
3225
|
+
function isLogicRulesEnabled() {
|
|
3226
|
+
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
3227
|
+
return layerEnabledOrDefault("L2_logic_rules");
|
|
2535
3228
|
}
|
|
2536
|
-
function
|
|
2537
|
-
|
|
2538
|
-
const cat = runtime.scenario_rules?.[scenarioId]?.[category];
|
|
2539
|
-
if (cat) cat.category_enabled = enabled;
|
|
2540
|
-
saveRuntimeConfig(runtime);
|
|
2541
|
-
return runtime;
|
|
3229
|
+
function isFormalObserveMode() {
|
|
3230
|
+
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
2542
3231
|
}
|
|
2543
|
-
var
|
|
2544
|
-
var
|
|
2545
|
-
"src/
|
|
3232
|
+
var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
3233
|
+
var init_config = __esm({
|
|
3234
|
+
"src/config.ts"() {
|
|
2546
3235
|
"use strict";
|
|
2547
3236
|
init_esm_shims();
|
|
2548
3237
|
init_paths();
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
3238
|
+
init_runtime_config();
|
|
3239
|
+
llmConfig = (() => {
|
|
3240
|
+
const configPath = resolveConfigPath();
|
|
3241
|
+
const fileConfig = fs2.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
3242
|
+
const tr = loadTimeoutRetryFromFile(configPath);
|
|
3243
|
+
timeoutConfig = tr.timeout;
|
|
3244
|
+
retryConfig = tr.retry;
|
|
3245
|
+
return {
|
|
3246
|
+
provider: process.env.AGENT_MOSS_LLM_PROVIDER || fileConfig.provider || "openai",
|
|
3247
|
+
baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || fileConfig.baseUrl || "https://api.openai.com/v1",
|
|
3248
|
+
apiKey: process.env.AGENT_MOSS_LLM_API_KEY || process.env.OPENAI_API_KEY || fileConfig.apiKey || "",
|
|
3249
|
+
model: process.env.AGENT_MOSS_LLM_MODEL || fileConfig.model || "gpt-4o",
|
|
3250
|
+
temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || String(fileConfig.temperature ?? "0.1")),
|
|
3251
|
+
maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || String(fileConfig.maxTokens ?? "4096"), 10),
|
|
3252
|
+
// 优先用 env,否则用 timeout 块的 prompt1Timeout(对齐 py)
|
|
3253
|
+
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
3254
|
+
};
|
|
3255
|
+
})();
|
|
3256
|
+
__configModuleDir = (() => {
|
|
3257
|
+
try {
|
|
3258
|
+
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3259
|
+
} catch {
|
|
3260
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3261
|
+
}
|
|
3262
|
+
})();
|
|
3263
|
+
USER_RULES_PATHS = [
|
|
3264
|
+
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
3265
|
+
// 源在 ts/src/rules(一般不存在)
|
|
3266
|
+
path2.join(__configModuleDir, "..", "rules", "user_rules.json"),
|
|
3267
|
+
// dist: dist/engine/../rules = dist/rules ✅
|
|
3268
|
+
path2.join(__configModuleDir, "..", "..", "agent_moss", "rules", "user_rules.json")
|
|
3269
|
+
// dev src: ts/src/../../agent_moss
|
|
3270
|
+
];
|
|
3271
|
+
customRules = loadCustomRules();
|
|
3272
|
+
PROVIDER_HEADERS = {
|
|
3273
|
+
"openrouter": {
|
|
3274
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM",
|
|
3275
|
+
"X-OpenRouter-Title": "AgentMoss"
|
|
3276
|
+
},
|
|
3277
|
+
"xai": {
|
|
3278
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM"
|
|
3279
|
+
}
|
|
2572
3280
|
};
|
|
2573
|
-
runtimeConfigCache = null;
|
|
2574
|
-
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2575
|
-
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2576
|
-
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
2577
|
-
_SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
2578
|
-
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2579
3281
|
}
|
|
2580
3282
|
});
|
|
2581
3283
|
|
|
2582
3284
|
// src/agents/index.ts
|
|
2583
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
3285
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
2584
3286
|
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
2585
3287
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2586
3288
|
function resolveAgentsDir() {
|
|
2587
|
-
if (
|
|
2588
|
-
if (
|
|
3289
|
+
if (existsSync6(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3290
|
+
if (existsSync6(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
2589
3291
|
return AGENTS_DIR_PROD;
|
|
2590
3292
|
}
|
|
2591
3293
|
function getAgentExtensions(agentId) {
|
|
@@ -2593,16 +3295,15 @@ function getAgentExtensions(agentId) {
|
|
|
2593
3295
|
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
2594
3296
|
let ext = null;
|
|
2595
3297
|
try {
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
|
|
3298
|
+
if (hasRuntimeConfig()) {
|
|
3299
|
+
ext = getEnabledAgentExtensions(loadRuntimeConfig(), agentId);
|
|
2599
3300
|
}
|
|
2600
3301
|
} catch {
|
|
2601
3302
|
}
|
|
2602
3303
|
if (ext === null) {
|
|
2603
3304
|
const agentsDir = resolveAgentsDir();
|
|
2604
3305
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
2605
|
-
if (!
|
|
3306
|
+
if (!existsSync6(rulesFile)) {
|
|
2606
3307
|
cache.set(agentId, null);
|
|
2607
3308
|
return null;
|
|
2608
3309
|
}
|
|
@@ -2621,7 +3322,7 @@ function getAgentExtensions(agentId) {
|
|
|
2621
3322
|
}
|
|
2622
3323
|
}
|
|
2623
3324
|
cache.set(agentId, ext);
|
|
2624
|
-
console.
|
|
3325
|
+
console.error(
|
|
2625
3326
|
`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
|
|
2626
3327
|
);
|
|
2627
3328
|
return ext;
|
|
@@ -2646,6 +3347,7 @@ var init_agents = __esm({
|
|
|
2646
3347
|
"src/agents/index.ts"() {
|
|
2647
3348
|
"use strict";
|
|
2648
3349
|
init_esm_shims();
|
|
3350
|
+
init_runtime_config();
|
|
2649
3351
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
2650
3352
|
__dirname5 = dirname4(__filename4);
|
|
2651
3353
|
AGENTS_DIR_PROD = resolve4(__dirname5, "agents");
|
|
@@ -2655,12 +3357,12 @@ var init_agents = __esm({
|
|
|
2655
3357
|
});
|
|
2656
3358
|
|
|
2657
3359
|
// src/scenarios/index.ts
|
|
2658
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
3360
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
2659
3361
|
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
2660
3362
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
2661
3363
|
function resolveScenariosDir() {
|
|
2662
|
-
if (
|
|
2663
|
-
if (
|
|
3364
|
+
if (existsSync7(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3365
|
+
if (existsSync7(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
2664
3366
|
return SCENARIOS_DIR_PROD;
|
|
2665
3367
|
}
|
|
2666
3368
|
function getScenarioExtensions(scenarioId) {
|
|
@@ -2668,16 +3370,15 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2668
3370
|
if (cache2.has(scenarioId)) return cache2.get(scenarioId) ?? null;
|
|
2669
3371
|
let ext = null;
|
|
2670
3372
|
try {
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
ext = getEnabledScenarioExtensions2(loadRuntimeConfig2(), scenarioId);
|
|
3373
|
+
if (hasRuntimeConfig()) {
|
|
3374
|
+
ext = getEnabledScenarioExtensions(loadRuntimeConfig(), scenarioId);
|
|
2674
3375
|
}
|
|
2675
3376
|
} catch {
|
|
2676
3377
|
}
|
|
2677
3378
|
if (ext === null) {
|
|
2678
3379
|
const scenariosDir = resolveScenariosDir();
|
|
2679
3380
|
const rulesFile = resolve5(scenariosDir, scenarioId, "rules.json");
|
|
2680
|
-
if (!
|
|
3381
|
+
if (!existsSync7(rulesFile)) {
|
|
2681
3382
|
cache2.set(scenarioId, null);
|
|
2682
3383
|
return null;
|
|
2683
3384
|
}
|
|
@@ -2707,7 +3408,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2707
3408
|
}
|
|
2708
3409
|
}
|
|
2709
3410
|
cache2.set(scenarioId, ext);
|
|
2710
|
-
console.
|
|
3411
|
+
console.error(
|
|
2711
3412
|
`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths`
|
|
2712
3413
|
);
|
|
2713
3414
|
return ext;
|
|
@@ -2727,6 +3428,7 @@ var init_scenarios = __esm({
|
|
|
2727
3428
|
"use strict";
|
|
2728
3429
|
init_esm_shims();
|
|
2729
3430
|
init_agents();
|
|
3431
|
+
init_runtime_config();
|
|
2730
3432
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
2731
3433
|
__dirname6 = dirname5(__filename5);
|
|
2732
3434
|
SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
|
|
@@ -2736,6 +3438,62 @@ var init_scenarios = __esm({
|
|
|
2736
3438
|
});
|
|
2737
3439
|
|
|
2738
3440
|
// src/engine/heuristic.ts
|
|
3441
|
+
function _effectiveDangerousPatterns(osType) {
|
|
3442
|
+
return runtimeConfigBridge.current?.effectiveDangerousPatterns(osType) ?? getDangerousPatternsForOs(osType);
|
|
3443
|
+
}
|
|
3444
|
+
function _loadDangerousPatterns(osType) {
|
|
3445
|
+
const accepted = acceptedOsValues(osType);
|
|
3446
|
+
const cmds = loadL1().dangerous_commands || [];
|
|
3447
|
+
return cmds.filter((c) => accepted.includes(String(c.os || "linux").toLowerCase())).map((c) => ({
|
|
3448
|
+
pattern: new RegExp(c.pattern, "i"),
|
|
3449
|
+
severity: c.risk_level,
|
|
3450
|
+
riskType: c.risk_type,
|
|
3451
|
+
description: c.reason,
|
|
3452
|
+
// 保留条目的原始平台归属(非查询用的 osType)—— seed 生成据此分组去重
|
|
3453
|
+
os: String(c.os || "linux").toLowerCase()
|
|
3454
|
+
}));
|
|
3455
|
+
}
|
|
3456
|
+
function getDangerousPatternsForOs(osType) {
|
|
3457
|
+
const t = String(osType || "").toLowerCase();
|
|
3458
|
+
if (t === "windows") return DANGEROUS_PATTERNS_WINDOWS;
|
|
3459
|
+
if (t === "macos") return DANGEROUS_PATTERNS_MACOS;
|
|
3460
|
+
if (t === "harmony" || t === "hmos" || t === "openharmony" || t === "hongmeng") return DANGEROUS_PATTERNS_HARMONY;
|
|
3461
|
+
return DANGEROUS_PATTERNS2;
|
|
3462
|
+
}
|
|
3463
|
+
function normalizeShellObfuscation(text) {
|
|
3464
|
+
let t = text.replace(/(?<=\w)(?:''|"")(?=\w)/g, "");
|
|
3465
|
+
t = t.replace(/\$\{IFS\}|\$IFS/gi, " ");
|
|
3466
|
+
return t;
|
|
3467
|
+
}
|
|
3468
|
+
function scanDangerousCommandPatterns(actionType, actionDetail, osType) {
|
|
3469
|
+
const scanDetail = normalizePathSegments(stripLiteralTextSegments(actionDetail));
|
|
3470
|
+
const scanCommands = String(osType || "").toLowerCase() === "windows" ? [
|
|
3471
|
+
`${actionType}: ${scanDetail}`,
|
|
3472
|
+
scanDetail,
|
|
3473
|
+
`${actionType}: ${scanDetail.replace(/\//g, "\\")}`,
|
|
3474
|
+
scanDetail.replace(/\//g, "\\")
|
|
3475
|
+
] : [`${actionType}: ${scanDetail}`, scanDetail];
|
|
3476
|
+
const deob = normalizeShellObfuscation(scanDetail);
|
|
3477
|
+
if (deob !== scanDetail) scanCommands.push(`${actionType}: ${deob}`, deob);
|
|
3478
|
+
let top = null;
|
|
3479
|
+
for (const rule of _effectiveDangerousPatterns(osType)) {
|
|
3480
|
+
if (scanCommands.some((c) => rule.pattern.test(c))) {
|
|
3481
|
+
if (!top || severityRank(rule.severity) > severityRank(top.sev)) {
|
|
3482
|
+
top = { sev: rule.severity, type: rule.riskType, desc: rule.description };
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
if (!top) return null;
|
|
3487
|
+
return {
|
|
3488
|
+
allowed: false,
|
|
3489
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${top.desc}`,
|
|
3490
|
+
riskLevel: top.sev,
|
|
3491
|
+
riskType: top.type,
|
|
3492
|
+
confidence: 95,
|
|
3493
|
+
source: "heuristic",
|
|
3494
|
+
violatedLayers: ["L1.1"]
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
2739
3497
|
function runHeuristicCheck(input) {
|
|
2740
3498
|
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
2741
3499
|
const matched = [];
|
|
@@ -2762,11 +3520,23 @@ function runHeuristicCheck(input) {
|
|
|
2762
3520
|
}
|
|
2763
3521
|
}
|
|
2764
3522
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2765
|
-
const
|
|
3523
|
+
const scanCommands = String(input.osType || "").toLowerCase() === "windows" ? [
|
|
3524
|
+
`${input.aNext.actionType}: ${scanDetail}`,
|
|
3525
|
+
scanDetail,
|
|
3526
|
+
`${input.aNext.actionType}: ${scanDetail.replace(/\//g, "\\")}`,
|
|
3527
|
+
scanDetail.replace(/\//g, "\\")
|
|
3528
|
+
] : [`${input.aNext.actionType}: ${scanDetail}`, scanDetail];
|
|
3529
|
+
const deobfuscated = normalizeShellObfuscation(scanDetail);
|
|
3530
|
+
if (deobfuscated !== scanDetail) {
|
|
3531
|
+
scanCommands.push(
|
|
3532
|
+
`${input.aNext.actionType}: ${deobfuscated}`,
|
|
3533
|
+
deobfuscated
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
2766
3536
|
let topRiskType = "";
|
|
2767
3537
|
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2768
|
-
for (const rule of
|
|
2769
|
-
if (rule.pattern.test(
|
|
3538
|
+
for (const rule of _effectiveDangerousPatterns(input.osType)) {
|
|
3539
|
+
if (scanCommands.some((c) => rule.pattern.test(c))) {
|
|
2770
3540
|
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2771
3541
|
matched.push(rule.description);
|
|
2772
3542
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
@@ -2784,7 +3554,7 @@ function runHeuristicCheck(input) {
|
|
|
2784
3554
|
for (const rule of agentExt.extra_command_patterns) {
|
|
2785
3555
|
try {
|
|
2786
3556
|
const re = new RegExp(rule.pattern, "i");
|
|
2787
|
-
if (re.test(
|
|
3557
|
+
if (scanCommands.some((c) => re.test(c))) {
|
|
2788
3558
|
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
2789
3559
|
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
2790
3560
|
highestSeverity = rule.risk_level;
|
|
@@ -2796,7 +3566,7 @@ function runHeuristicCheck(input) {
|
|
|
2796
3566
|
}
|
|
2797
3567
|
}
|
|
2798
3568
|
const injectionText = isSearchActionType(input.aNext.actionType) ? `${input.aNext.actionType} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase() : `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
2799
|
-
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
3569
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords, input.osType);
|
|
2800
3570
|
if (injectionHits.length > 0) {
|
|
2801
3571
|
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
2802
3572
|
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
@@ -2809,7 +3579,7 @@ function runHeuristicCheck(input) {
|
|
|
2809
3579
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2810
3580
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2811
3581
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2812
|
-
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
3582
|
+
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt, input.osType);
|
|
2813
3583
|
const homeResult = checkHomeDeletion(actionTypeL, detailL);
|
|
2814
3584
|
const mergeResults = [dangerousResult, pathResult, homeResult].filter(Boolean);
|
|
2815
3585
|
if (mergeResults.length > 0) {
|
|
@@ -2845,9 +3615,10 @@ function runHeuristicCheck(input) {
|
|
|
2845
3615
|
if (lateralResult) return lateralResult;
|
|
2846
3616
|
return null;
|
|
2847
3617
|
}
|
|
2848
|
-
function checkInjectionKeywords(text, extraKeywords) {
|
|
3618
|
+
function checkInjectionKeywords(text, extraKeywords, osType) {
|
|
2849
3619
|
const hits = [];
|
|
2850
|
-
|
|
3620
|
+
const baseKeywords = runtimeConfigBridge.current?.effectiveInjectionKeywords(osType) ?? INJECTION_KEYWORDS;
|
|
3621
|
+
for (const entry of baseKeywords) {
|
|
2851
3622
|
if (text.includes(entry.keyword.toLowerCase())) {
|
|
2852
3623
|
hits.push(entry);
|
|
2853
3624
|
}
|
|
@@ -2907,7 +3678,7 @@ function buildPathContext(actionDetail) {
|
|
|
2907
3678
|
if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
|
|
2908
3679
|
const stripped = stripLiteralTextSegments(actionDetail);
|
|
2909
3680
|
const lines = [];
|
|
2910
|
-
for (const seg of
|
|
3681
|
+
for (const seg of splitCommandSegments2(stripped)) {
|
|
2911
3682
|
for (const [raw, lex] of resolvePathTokens(seg)) {
|
|
2912
3683
|
lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
|
|
2913
3684
|
}
|
|
@@ -2922,7 +3693,7 @@ function stripLiteralTextSegments(actionDetail) {
|
|
|
2922
3693
|
return actionDetail;
|
|
2923
3694
|
}
|
|
2924
3695
|
const resultParts = [];
|
|
2925
|
-
for (const seg of
|
|
3696
|
+
for (const seg of splitCommandSegments2(actionDetail)) {
|
|
2926
3697
|
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
2927
3698
|
if (fw === "echo" || fw === "printf") {
|
|
2928
3699
|
const segUnquotedRedirect = seg.replace(
|
|
@@ -2942,7 +3713,7 @@ function stripHeredocContent(actionDetail) {
|
|
|
2942
3713
|
"<<heredoc>>\n"
|
|
2943
3714
|
);
|
|
2944
3715
|
}
|
|
2945
|
-
function normalizePathSegments(text) {
|
|
3716
|
+
function normalizePathSegments(text, foldQuotes = false) {
|
|
2946
3717
|
const protTokens = [];
|
|
2947
3718
|
let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
|
|
2948
3719
|
const scheme = m.slice(0, m.indexOf(":"));
|
|
@@ -2950,10 +3721,12 @@ function normalizePathSegments(text) {
|
|
|
2950
3721
|
return `${scheme}__PROT${protTokens.length - 1}__`;
|
|
2951
3722
|
});
|
|
2952
3723
|
const quoteTokens = [];
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3724
|
+
if (!foldQuotes) {
|
|
3725
|
+
t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
|
|
3726
|
+
quoteTokens.push(m);
|
|
3727
|
+
return `__Q${quoteTokens.length - 1}__`;
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
2957
3730
|
t = t.replace(/\/{2,}/g, "/");
|
|
2958
3731
|
t = t.replace(/\/\.\//g, "/");
|
|
2959
3732
|
for (let i = 0; i < quoteTokens.length; i++) {
|
|
@@ -2964,7 +3737,12 @@ function normalizePathSegments(text) {
|
|
|
2964
3737
|
}
|
|
2965
3738
|
return t;
|
|
2966
3739
|
}
|
|
2967
|
-
function
|
|
3740
|
+
function pathScanVariants(actionDetailLower) {
|
|
3741
|
+
const base = stripLiteralTextSegments(actionDetailLower);
|
|
3742
|
+
const deep = normalizePathSegments(base, true);
|
|
3743
|
+
return deep === base ? [base] : [base, deep];
|
|
3744
|
+
}
|
|
3745
|
+
function splitCommandSegments2(actionDetail) {
|
|
2968
3746
|
const segments = [];
|
|
2969
3747
|
let cur = "";
|
|
2970
3748
|
let quote = null;
|
|
@@ -3013,11 +3791,15 @@ function isSearchActionType(actionType) {
|
|
|
3013
3791
|
const at = actionType.toLowerCase();
|
|
3014
3792
|
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || ["websearch", "web_search", "search", "query"].includes(at);
|
|
3015
3793
|
}
|
|
3016
|
-
function checkSensitivePath(command, cwd, agentExt) {
|
|
3794
|
+
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
3017
3795
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
3018
|
-
const
|
|
3796
|
+
const { root: rootPaths, home: homePaths, credential: credentialPaths } = getSensitivePathsForOs(osType);
|
|
3797
|
+
const isWriteOpFlag = isWriteOperation("", command, void 0, osType);
|
|
3019
3798
|
const keygenGenerate = isSshKeygenGenerate(command);
|
|
3020
|
-
const
|
|
3799
|
+
const pathScanTexts = pathScanVariants(command);
|
|
3800
|
+
if (String(osType || "").toLowerCase() === "windows") {
|
|
3801
|
+
pathScanTexts.push(...pathScanTexts.map((t) => t.replace(/\//g, "\\")));
|
|
3802
|
+
}
|
|
3021
3803
|
const pathMatches = (text, path6) => {
|
|
3022
3804
|
const parts = path6.split(/(\(\?!.*?\))/);
|
|
3023
3805
|
const escaped = parts.map((part) => {
|
|
@@ -3027,8 +3809,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3027
3809
|
const re = path6.startsWith(".") || path6.startsWith("/") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`, "i") : new RegExp(`\\b${escaped}\\b`, "i");
|
|
3028
3810
|
return re.test(text);
|
|
3029
3811
|
};
|
|
3030
|
-
|
|
3031
|
-
|
|
3812
|
+
const pathMatchesAny = (path6) => pathScanTexts.some((t) => pathMatches(t, path6));
|
|
3813
|
+
for (const sp of rootPaths) {
|
|
3814
|
+
if (pathMatchesAny(sp.path.toLowerCase())) {
|
|
3032
3815
|
if (keygenGenerate) continue;
|
|
3033
3816
|
if (sp.path === "/dev/random") {
|
|
3034
3817
|
if (isWriteOpFlag) {
|
|
@@ -3044,9 +3827,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3044
3827
|
violatedLayers: ["L1.1"]
|
|
3045
3828
|
};
|
|
3046
3829
|
}
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3830
|
+
const denyMode = sp.denyMode || (sp.credential ? "deny_both" : sp.readOnly ? "deny_read" : "deny_write");
|
|
3831
|
+
if (denyMode === "deny_write" && !isWriteOpFlag) continue;
|
|
3832
|
+
if (denyMode === "deny_read" && isWriteOpFlag) continue;
|
|
3050
3833
|
return {
|
|
3051
3834
|
allowed: false,
|
|
3052
3835
|
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
@@ -3059,17 +3842,19 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3059
3842
|
};
|
|
3060
3843
|
}
|
|
3061
3844
|
}
|
|
3062
|
-
for (const sp of
|
|
3845
|
+
for (const sp of homePaths) {
|
|
3063
3846
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
3064
3847
|
let pPath = sp.path;
|
|
3065
3848
|
if (!sp.path.endsWith(".pub")) pPath = `${sp.path}(?!\\.pub)`;
|
|
3066
3849
|
let dirPrefixMatch = null;
|
|
3067
3850
|
if (sp.dirDelOnly) {
|
|
3068
3851
|
const dirRe = (t) => new RegExp(`(?:^|[\\s/\\\\'"])${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/|[\\s'"\\\\]|$)`, "i");
|
|
3069
|
-
dirPrefixMatch =
|
|
3852
|
+
dirPrefixMatch = pathScanTexts.some(
|
|
3853
|
+
(t) => dirRe(sp.path).test(t) || dirRe(expanded.toLowerCase()).test(t)
|
|
3854
|
+
);
|
|
3070
3855
|
}
|
|
3071
|
-
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch :
|
|
3072
|
-
const expandedReMatch = sp.dirDelOnly ? false :
|
|
3856
|
+
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch : pathMatchesAny(pPath);
|
|
3857
|
+
const expandedReMatch = sp.dirDelOnly ? false : pathMatchesAny(expanded + (sp.path.endsWith(".pub") ? "" : "(?!\\.pub)"));
|
|
3073
3858
|
if (pathReMatch || expandedReMatch) {
|
|
3074
3859
|
if (keygenGenerate) continue;
|
|
3075
3860
|
if (sp.dirDelOnly) {
|
|
@@ -3099,9 +3884,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3099
3884
|
}
|
|
3100
3885
|
continue;
|
|
3101
3886
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3887
|
+
const denyMode = sp.denyMode || (sp.credential ? "deny_both" : sp.readOnly ? "deny_read" : "deny_write");
|
|
3888
|
+
if (denyMode === "deny_write" && !isWriteOpFlag) continue;
|
|
3889
|
+
if (denyMode === "deny_read" && isWriteOpFlag) continue;
|
|
3105
3890
|
return {
|
|
3106
3891
|
allowed: false,
|
|
3107
3892
|
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
@@ -3138,7 +3923,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3138
3923
|
}
|
|
3139
3924
|
}
|
|
3140
3925
|
}
|
|
3141
|
-
for (const cp of
|
|
3926
|
+
for (const cp of credentialPaths) {
|
|
3142
3927
|
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3143
3928
|
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
3144
3929
|
if (re.test(command)) {
|
|
@@ -3258,7 +4043,7 @@ function isDeleteActionForDir(command, dirPath) {
|
|
|
3258
4043
|
}
|
|
3259
4044
|
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3260
4045
|
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3261
|
-
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") &&
|
|
4046
|
+
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") && seg.includes(dpath)) {
|
|
3262
4047
|
return true;
|
|
3263
4048
|
}
|
|
3264
4049
|
}
|
|
@@ -3441,7 +4226,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
3441
4226
|
}
|
|
3442
4227
|
return null;
|
|
3443
4228
|
}
|
|
3444
|
-
var INJECTION_KEYWORDS,
|
|
4229
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS2, DANGEROUS_PATTERNS_WINDOWS, DANGEROUS_PATTERNS_MACOS, DANGEROUS_PATTERNS_HARMONY, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
|
|
3445
4230
|
var init_heuristic = __esm({
|
|
3446
4231
|
"src/engine/heuristic.ts"() {
|
|
3447
4232
|
"use strict";
|
|
@@ -3450,6 +4235,7 @@ var init_heuristic = __esm({
|
|
|
3450
4235
|
init_agents();
|
|
3451
4236
|
init_scenarios();
|
|
3452
4237
|
init_runtime_config();
|
|
4238
|
+
init_runtime_config_bridge();
|
|
3453
4239
|
init_rules_loader();
|
|
3454
4240
|
init_logic_rules();
|
|
3455
4241
|
INJECTION_KEYWORDS = (() => {
|
|
@@ -3460,15 +4246,10 @@ var init_heuristic = __esm({
|
|
|
3460
4246
|
lang: k.lang || "en"
|
|
3461
4247
|
}));
|
|
3462
4248
|
})();
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
severity: c.risk_level,
|
|
3468
|
-
riskType: c.risk_type,
|
|
3469
|
-
description: c.reason
|
|
3470
|
-
}));
|
|
3471
|
-
})();
|
|
4249
|
+
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
4250
|
+
DANGEROUS_PATTERNS_WINDOWS = _loadDangerousPatterns("windows");
|
|
4251
|
+
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4252
|
+
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
3472
4253
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
3473
4254
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
3474
4255
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
@@ -3479,7 +4260,7 @@ var init_heuristic = __esm({
|
|
|
3479
4260
|
});
|
|
3480
4261
|
|
|
3481
4262
|
// src/engine/template-loader.ts
|
|
3482
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
4263
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
3483
4264
|
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
3484
4265
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
3485
4266
|
function loadTemplates() {
|
|
@@ -3493,7 +4274,7 @@ function loadTemplates() {
|
|
|
3493
4274
|
}
|
|
3494
4275
|
for (const file of TEMPLATE_FILES) {
|
|
3495
4276
|
const filePath = resolve6(dir, file);
|
|
3496
|
-
if (
|
|
4277
|
+
if (existsSync8(filePath)) {
|
|
3497
4278
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3498
4279
|
cachedTemplates[key] = readFileSync7(filePath, "utf-8");
|
|
3499
4280
|
}
|
|
@@ -3569,7 +4350,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3569
4350
|
}
|
|
3570
4351
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3571
4352
|
try {
|
|
3572
|
-
const expandedPath = scriptPath.startsWith("
|
|
4353
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join4(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
3573
4354
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
3574
4355
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3575
4356
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -4519,9 +5300,20 @@ function evalExpr2(text, env) {
|
|
|
4519
5300
|
const result = [];
|
|
4520
5301
|
for (const p of parts) {
|
|
4521
5302
|
if (!p) continue;
|
|
4522
|
-
const brace = p.match(/^\$\{([^}]
|
|
5303
|
+
const brace = p.match(/^\$\{([^}]*)\}$/);
|
|
4523
5304
|
if (brace) {
|
|
4524
5305
|
const inner = brace[1];
|
|
5306
|
+
const dm = inner.match(/^([A-Za-z_][A-Za-z0-9_]*|\d+):?-(.*)$/s);
|
|
5307
|
+
if (dm) {
|
|
5308
|
+
const key = /^\d+$/.test(dm[1]) ? `$${dm[1]}` : dm[1];
|
|
5309
|
+
const bound = env.get(key);
|
|
5310
|
+
if (bound && !(bound.kind === "concrete" && !bound.value)) {
|
|
5311
|
+
result.push(bound);
|
|
5312
|
+
} else {
|
|
5313
|
+
result.push(evalExpr2(dm[2], env));
|
|
5314
|
+
}
|
|
5315
|
+
continue;
|
|
5316
|
+
}
|
|
4525
5317
|
if (/^\d+$/.test(inner)) {
|
|
4526
5318
|
const v = env.get(`$${inner}`);
|
|
4527
5319
|
result.push(v || svalSym(`$${inner}`));
|
|
@@ -4737,7 +5529,7 @@ var init_symbolic = __esm({
|
|
|
4737
5529
|
LOOP_DONE_RE = /\bdone\b/g;
|
|
4738
5530
|
FN_DEF_RE = /^\s*(?:function\s+)?[A-Za-z_][A-Za-z0-9_]*\s*\(\s*\)\s*\{/;
|
|
4739
5531
|
FN_DEF_KW_RE = /^\s*function\s+[A-Za-z_][A-Za-z0-9_]*\s*\{?/;
|
|
4740
|
-
EXPR_SPLIT_RE = /(\$\{
|
|
5532
|
+
EXPR_SPLIT_RE = /(\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$\d+)/;
|
|
4741
5533
|
HAS_GLOB = /[*?[]/;
|
|
4742
5534
|
}
|
|
4743
5535
|
});
|
|
@@ -5018,6 +5810,14 @@ var init_backends = __esm({
|
|
|
5018
5810
|
import * as fs3 from "fs";
|
|
5019
5811
|
import * as path3 from "path";
|
|
5020
5812
|
import * as os from "os";
|
|
5813
|
+
function maxFileSizeFromEnv() {
|
|
5814
|
+
const raw = (process.env.AGENT_MOSS_SCRIPT_MAX_FILE_SIZE || "").trim();
|
|
5815
|
+
if (raw) {
|
|
5816
|
+
const v = Number(raw);
|
|
5817
|
+
if (Number.isFinite(v) && v > 0) return v;
|
|
5818
|
+
}
|
|
5819
|
+
return 2 * 1024 * 1024;
|
|
5820
|
+
}
|
|
5021
5821
|
function pickBackend(pathStr) {
|
|
5022
5822
|
const lower = pathStr.toLowerCase();
|
|
5023
5823
|
for (const ext of Object.keys(BACKENDS)) {
|
|
@@ -5060,6 +5860,7 @@ function emptyNode(pathStr, depth) {
|
|
|
5060
5860
|
depth,
|
|
5061
5861
|
childPaths: [],
|
|
5062
5862
|
error: "",
|
|
5863
|
+
unanalyzable: false,
|
|
5063
5864
|
l1Hit: false,
|
|
5064
5865
|
l1RiskLevel: "",
|
|
5065
5866
|
l1RiskType: "",
|
|
@@ -5085,7 +5886,7 @@ var init_tracker = __esm({
|
|
|
5085
5886
|
init_backends();
|
|
5086
5887
|
DEFAULT_MAX_DEPTH = 14;
|
|
5087
5888
|
DEFAULT_MAX_NODES = 64;
|
|
5088
|
-
DEFAULT_MAX_FILE_SIZE =
|
|
5889
|
+
DEFAULT_MAX_FILE_SIZE = maxFileSizeFromEnv();
|
|
5089
5890
|
DEFAULT_MAX_TOTAL_SIZE = 8 * 1024 * 1024;
|
|
5090
5891
|
BACKENDS = {
|
|
5091
5892
|
".py": new PythonBackend()
|
|
@@ -5156,6 +5957,7 @@ var init_tracker = __esm({
|
|
|
5156
5957
|
node2.error = error;
|
|
5157
5958
|
node2.fileSize = fileSize;
|
|
5158
5959
|
node2.lineCount = lineCount;
|
|
5960
|
+
node2.unanalyzable = error.startsWith("\u811A\u672C\u6587\u4EF6\u8FC7\u5927");
|
|
5159
5961
|
this.result.nodes.push(node2);
|
|
5160
5962
|
this.visiting.delete(pathStr);
|
|
5161
5963
|
return;
|
|
@@ -5202,9 +6004,9 @@ var init_tracker = __esm({
|
|
|
5202
6004
|
});
|
|
5203
6005
|
|
|
5204
6006
|
// src/engine/script-content-analyzer.ts
|
|
5205
|
-
import { statSync as statSync3, existsSync as
|
|
6007
|
+
import { statSync as statSync3, existsSync as existsSync10 } from "fs";
|
|
5206
6008
|
import { resolve as resolve9, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
5207
|
-
import { homedir as
|
|
6009
|
+
import { homedir as homedir3 } from "os";
|
|
5208
6010
|
function emptyChain() {
|
|
5209
6011
|
return {
|
|
5210
6012
|
nodes: [],
|
|
@@ -5310,12 +6112,12 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5310
6112
|
const subShellRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*(['"])(.*?)\1[^)]*?shell\s*=\s*true/gi;
|
|
5311
6113
|
while ((m = subShellRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5312
6114
|
for (const cand of candidates) {
|
|
5313
|
-
const tokens = cand.includes(",") ? cand.split(/\s*,\s*/).map((t) => t.trim().replace(/^['"]|['"]$/g, "")) : cand
|
|
6115
|
+
const tokens = cand.includes(",") ? cand.split(/\s*,\s*/).map((t) => t.trim().replace(/^['"]|['"]$/g, "")) : shlexSplit(cand);
|
|
5314
6116
|
for (const tok of tokens) {
|
|
5315
6117
|
if (looksLikeScriptPath(tok)) {
|
|
5316
6118
|
try {
|
|
5317
6119
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5318
|
-
if (
|
|
6120
|
+
if (existsSync10(resolved)) {
|
|
5319
6121
|
const stat = statSync3(resolved);
|
|
5320
6122
|
if (stat.isFile()) return resolved;
|
|
5321
6123
|
}
|
|
@@ -5329,7 +6131,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5329
6131
|
const p = m[1];
|
|
5330
6132
|
try {
|
|
5331
6133
|
const resolved = resolvePath2(p);
|
|
5332
|
-
if (
|
|
6134
|
+
if (existsSync10(resolved)) {
|
|
5333
6135
|
const stat = statSync3(resolved);
|
|
5334
6136
|
if (stat.isFile()) return resolved;
|
|
5335
6137
|
}
|
|
@@ -5345,7 +6147,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5345
6147
|
let resolved;
|
|
5346
6148
|
try {
|
|
5347
6149
|
resolved = resolvePath2(p);
|
|
5348
|
-
if (
|
|
6150
|
+
if (existsSync10(resolved)) {
|
|
5349
6151
|
const stat = statSync3(resolved);
|
|
5350
6152
|
if (stat.isFile()) return resolved;
|
|
5351
6153
|
}
|
|
@@ -5354,10 +6156,39 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5354
6156
|
}
|
|
5355
6157
|
return null;
|
|
5356
6158
|
}
|
|
6159
|
+
function shlexSplit(s) {
|
|
6160
|
+
const out = [];
|
|
6161
|
+
let cur = "";
|
|
6162
|
+
let quote = null;
|
|
6163
|
+
let quoted = false;
|
|
6164
|
+
for (let i = 0; i < s.length; i++) {
|
|
6165
|
+
const ch = s[i];
|
|
6166
|
+
if (quote) {
|
|
6167
|
+
if (ch === quote) quote = null;
|
|
6168
|
+
else if (ch === "\\" && quote === '"' && i + 1 < s.length) cur += s[++i];
|
|
6169
|
+
else cur += ch;
|
|
6170
|
+
} else if (ch === '"' || ch === "'") {
|
|
6171
|
+
quote = ch;
|
|
6172
|
+
quoted = true;
|
|
6173
|
+
} else if (ch === "\\" && i + 1 < s.length) {
|
|
6174
|
+
cur += s[++i];
|
|
6175
|
+
} else if (/\s/.test(ch)) {
|
|
6176
|
+
if (cur || quoted) {
|
|
6177
|
+
out.push(cur);
|
|
6178
|
+
cur = "";
|
|
6179
|
+
quoted = false;
|
|
6180
|
+
}
|
|
6181
|
+
} else {
|
|
6182
|
+
cur += ch;
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
if (cur || quoted) out.push(cur);
|
|
6186
|
+
return out;
|
|
6187
|
+
}
|
|
5357
6188
|
function resolvePath2(p) {
|
|
5358
6189
|
let expanded = p;
|
|
5359
6190
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
5360
|
-
expanded = join6(
|
|
6191
|
+
expanded = join6(homedir3(), expanded.slice(1));
|
|
5361
6192
|
}
|
|
5362
6193
|
return resolve9(expanded);
|
|
5363
6194
|
}
|
|
@@ -5368,7 +6199,7 @@ function scanScriptChainWithL1(chain, input) {
|
|
|
5368
6199
|
...input,
|
|
5369
6200
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
5370
6201
|
};
|
|
5371
|
-
const l1 =
|
|
6202
|
+
const l1 = scanDangerousCommandPatterns("bash", node.preprocessedContent, input.osType);
|
|
5372
6203
|
if (l1 && !l1.allowed) {
|
|
5373
6204
|
node.l1Hit = true;
|
|
5374
6205
|
node.l1RiskLevel = l1.riskLevel;
|
|
@@ -5391,18 +6222,25 @@ function scanScriptChainWithL2(chain, input) {
|
|
|
5391
6222
|
...input,
|
|
5392
6223
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
5393
6224
|
};
|
|
5394
|
-
|
|
5395
|
-
if (
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
6225
|
+
let l2 = null;
|
|
6226
|
+
if (isLogicRulesEnabled()) {
|
|
6227
|
+
l2 = checkSensitivePath(
|
|
6228
|
+
node.preprocessedContent.toLowerCase(),
|
|
6229
|
+
input.cwd || "/",
|
|
6230
|
+
null,
|
|
6231
|
+
input.osType
|
|
6232
|
+
) || checkIntentConsistency(virtualInput);
|
|
6233
|
+
}
|
|
6234
|
+
if (!l2 || l2.allowed) continue;
|
|
6235
|
+
node.l2Hit = true;
|
|
6236
|
+
node.l2ViolatedRule = `script_logic:${node.scriptPath}`;
|
|
6237
|
+
node.l2RiskLevel = l2.riskLevel;
|
|
6238
|
+
node.l2RiskType = l2.riskType;
|
|
6239
|
+
node.l2Reason = `\u811A\u672C ${node.scriptPath} (\u6DF1\u5EA6 ${node.depth}) \u4E2D: ${l2.reason}`;
|
|
6240
|
+
if (l2.riskLevel === "critical") {
|
|
6241
|
+
chain.hasCritical = true;
|
|
6242
|
+
chain.firstHitNode = node;
|
|
6243
|
+
break;
|
|
5406
6244
|
}
|
|
5407
6245
|
}
|
|
5408
6246
|
if (!chain.hasHigh) {
|
|
@@ -5441,13 +6279,16 @@ function analyzeScriptContent2(input) {
|
|
|
5441
6279
|
resolvedPath = join6(input.cwd || process.cwd(), resolvedPath);
|
|
5442
6280
|
}
|
|
5443
6281
|
const l1Adapter = (content) => {
|
|
5444
|
-
const
|
|
5445
|
-
...input,
|
|
5446
|
-
aNext: { actionType: "bash", actionDetail: content }
|
|
5447
|
-
};
|
|
5448
|
-
const j = runHeuristicCheck(virtualInput);
|
|
6282
|
+
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
5449
6283
|
if (j && !j.allowed) {
|
|
5450
|
-
return {
|
|
6284
|
+
return {
|
|
6285
|
+
hit: true,
|
|
6286
|
+
riskLevel: j.riskLevel,
|
|
6287
|
+
riskType: j.riskType,
|
|
6288
|
+
reason: j.reason,
|
|
6289
|
+
matchedPatterns: []
|
|
6290
|
+
// SecurityJudgment 不带模式列表(见 types.ts);留空即可
|
|
6291
|
+
};
|
|
5451
6292
|
}
|
|
5452
6293
|
return { hit: false, riskLevel: "", riskType: "", reason: "", matchedPatterns: [] };
|
|
5453
6294
|
};
|
|
@@ -5526,6 +6367,7 @@ var init_script_content_analyzer = __esm({
|
|
|
5526
6367
|
"use strict";
|
|
5527
6368
|
init_esm_shims();
|
|
5528
6369
|
init_heuristic();
|
|
6370
|
+
init_config();
|
|
5529
6371
|
init_logic_rules();
|
|
5530
6372
|
init_inline_analyzer();
|
|
5531
6373
|
init_tracker();
|
|
@@ -5624,14 +6466,14 @@ __export(token_stats_exports, {
|
|
|
5624
6466
|
recordTokenUsage: () => recordTokenUsage,
|
|
5625
6467
|
resetTokenStats: () => resetTokenStats
|
|
5626
6468
|
});
|
|
5627
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync2, existsSync as
|
|
6469
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync2, existsSync as existsSync11, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
5628
6470
|
function getStatsPath() {
|
|
5629
6471
|
const envPath = process.env[ENV_STATS_PATH];
|
|
5630
6472
|
if (envPath) return envPath;
|
|
5631
6473
|
return getTokenStatsPath();
|
|
5632
6474
|
}
|
|
5633
6475
|
function loadAllRecords(statsPath) {
|
|
5634
|
-
if (!
|
|
6476
|
+
if (!existsSync11(statsPath)) return [];
|
|
5635
6477
|
try {
|
|
5636
6478
|
const data = JSON.parse(readFileSync11(statsPath, "utf-8"));
|
|
5637
6479
|
if (Array.isArray(data)) return data;
|
|
@@ -5644,7 +6486,7 @@ function loadAllRecords(statsPath) {
|
|
|
5644
6486
|
function saveRecords(statsPath, records) {
|
|
5645
6487
|
try {
|
|
5646
6488
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
5647
|
-
if (dir && !
|
|
6489
|
+
if (dir && !existsSync11(dir)) mkdirSync2(dir, { recursive: true });
|
|
5648
6490
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
5649
6491
|
} catch (e) {
|
|
5650
6492
|
}
|
|
@@ -5771,7 +6613,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
5771
6613
|
}
|
|
5772
6614
|
function resetTokenStats() {
|
|
5773
6615
|
const statsPath = getStatsPath();
|
|
5774
|
-
if (
|
|
6616
|
+
if (existsSync11(statsPath)) {
|
|
5775
6617
|
try {
|
|
5776
6618
|
unlinkSync2(statsPath);
|
|
5777
6619
|
} catch {
|
|
@@ -6028,9 +6870,9 @@ var init_llm_analyzer = __esm({
|
|
|
6028
6870
|
});
|
|
6029
6871
|
|
|
6030
6872
|
// src/brain/rule-store.ts
|
|
6031
|
-
import { existsSync as
|
|
6873
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
6032
6874
|
import { join as join7 } from "path";
|
|
6033
|
-
import { homedir as
|
|
6875
|
+
import { homedir as homedir4 } from "os";
|
|
6034
6876
|
function getRulesPath() {
|
|
6035
6877
|
const envPath = process.env[ENV_RULES_PATH];
|
|
6036
6878
|
if (envPath) return envPath;
|
|
@@ -6048,7 +6890,7 @@ var init_rule_store = __esm({
|
|
|
6048
6890
|
"src/brain/rule-store.ts"() {
|
|
6049
6891
|
"use strict";
|
|
6050
6892
|
init_esm_shims();
|
|
6051
|
-
DEFAULT_RULES_PATH = join7(
|
|
6893
|
+
DEFAULT_RULES_PATH = join7(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
6052
6894
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
6053
6895
|
STATUS_DRAFT = "draft";
|
|
6054
6896
|
STATUS_APPROVED = "approved";
|
|
@@ -6065,7 +6907,7 @@ var init_rule_store = __esm({
|
|
|
6065
6907
|
this.data = this.load();
|
|
6066
6908
|
}
|
|
6067
6909
|
load() {
|
|
6068
|
-
if (!
|
|
6910
|
+
if (!existsSync12(this.path)) {
|
|
6069
6911
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
6070
6912
|
}
|
|
6071
6913
|
try {
|
|
@@ -8080,18 +8922,21 @@ var coordinator_exports = {};
|
|
|
8080
8922
|
__export(coordinator_exports, {
|
|
8081
8923
|
TRUSTED_SKILLS_BASE: () => TRUSTED_SKILLS_BASE,
|
|
8082
8924
|
_resetBrainForTest: () => _resetBrainForTest,
|
|
8083
|
-
|
|
8925
|
+
extractFirstCommand: () => extractFirstCommand,
|
|
8926
|
+
matchesAny: () => matchesAny,
|
|
8927
|
+
runAnalysis: () => runAnalysis,
|
|
8928
|
+
splitCommandSegments: () => splitCommandSegments,
|
|
8929
|
+
whitelistTier: () => whitelistTier
|
|
8084
8930
|
});
|
|
8085
|
-
function
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
return main2;
|
|
8931
|
+
function whitelistTier(command, fullySafe, readonlySensitive) {
|
|
8932
|
+
const segments = splitCommandSegments(command);
|
|
8933
|
+
if (segments.length === 0) return "";
|
|
8934
|
+
if (segments.some((seg) => hasCommandSubstitution(seg))) return "";
|
|
8935
|
+
const hitFull = (seg) => matchesAny(seg, fullySafe);
|
|
8936
|
+
const hitAny = (seg) => hitFull(seg) || matchesAny(seg, readonlySensitive);
|
|
8937
|
+
if (segments.every(hitFull)) return "full";
|
|
8938
|
+
if (segments.every(hitAny)) return "readonly";
|
|
8939
|
+
return "";
|
|
8095
8940
|
}
|
|
8096
8941
|
function extractRawCommand(detail) {
|
|
8097
8942
|
const m = detail.match(/command=([^,]+)/);
|
|
@@ -8122,7 +8967,7 @@ async function runAnalysis(input) {
|
|
|
8122
8967
|
if (heuristicResult && !heuristicResult.allowed) {
|
|
8123
8968
|
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && heuristicResult.riskType === "file_access";
|
|
8124
8969
|
if (!skipInlineFileAccess) {
|
|
8125
|
-
const shouldDenyL1 = heuristicResult.riskLevel === "high" || heuristicResult.riskLevel === "critical" || heuristicResult.riskLevel === "medium" && heuristicResult.riskType === "file_access";
|
|
8970
|
+
const shouldDenyL1 = heuristicResult.riskLevel === "high" || heuristicResult.riskLevel === "critical" || heuristicResult.riskLevel === "medium" && heuristicResult.riskType === "file_access" || heuristicResult.riskLevel === "medium" && heuristicResult.riskType === "resource_exhaustion";
|
|
8126
8971
|
if (shouldDenyL1) {
|
|
8127
8972
|
return heuristicResult;
|
|
8128
8973
|
}
|
|
@@ -8132,6 +8977,19 @@ async function runAnalysis(input) {
|
|
|
8132
8977
|
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
8133
8978
|
scriptChain = analyzeScriptContent2(input);
|
|
8134
8979
|
if (scriptChain.nodes.length > 0) {
|
|
8980
|
+
for (const node of scriptChain.nodes) {
|
|
8981
|
+
if (node.unanalyzable) {
|
|
8982
|
+
return {
|
|
8983
|
+
allowed: false,
|
|
8984
|
+
reason: `\u811A\u672C\u5B58\u5728\u4F46\u5185\u5BB9\u65E0\u6CD5\u9759\u6001\u5206\u6790\uFF08${node.error}\uFF09\u2014\u2014 \u65E0\u6CD5\u786E\u8BA4\u5176\u5B89\u5168\u6027\uFF0C\u6309\u4E0D\u53EF\u4FE1\u5904\u7406\u3002\u6587\u4EF6: ${node.scriptPath}\u3002\u5982\u786E\u5C5E\u6B63\u5E38\u5927\u811A\u672C\uFF0C\u53EF\u8BBE AGENT_MOSS_SCRIPT_MAX_FILE_SIZE \u653E\u5BBD\u4E0A\u9650\u3002`,
|
|
8985
|
+
riskLevel: "high",
|
|
8986
|
+
riskType: "script_execution",
|
|
8987
|
+
confidence: 80,
|
|
8988
|
+
source: "heuristic_script_scan",
|
|
8989
|
+
violatedLayers: ["L1.2"]
|
|
8990
|
+
};
|
|
8991
|
+
}
|
|
8992
|
+
}
|
|
8135
8993
|
for (const node of scriptChain.nodes) {
|
|
8136
8994
|
if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
|
|
8137
8995
|
return {
|
|
@@ -8219,11 +9077,15 @@ async function runAnalysis(input) {
|
|
|
8219
9077
|
let skipLLM = READONLY_SENSITIVE_TOOLS.has(actionType);
|
|
8220
9078
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
8221
9079
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
8222
|
-
|
|
8223
|
-
|
|
9080
|
+
const { fullySafeCommands, readonlySensitiveCommands } = getPlatformTraits(input.osType);
|
|
9081
|
+
const tier = whitelistTier(lowerDetail, fullySafeCommands, readonlySensitiveCommands);
|
|
9082
|
+
if (tier === "full") {
|
|
9083
|
+
const fastPassScanText = normalizePathSegments(stripLiteralTextSegments(lowerDetail));
|
|
9084
|
+
const osPatterns = getDangerousPatternsForOs(input.osType);
|
|
9085
|
+
const fullCmdHit = osPatterns.some(
|
|
8224
9086
|
(p) => p.severity === "critical" || p.severity === "high"
|
|
8225
|
-
) &&
|
|
8226
|
-
(p) => (p.severity === "critical" || p.severity === "high") && p.pattern.test(
|
|
9087
|
+
) && osPatterns.some(
|
|
9088
|
+
(p) => (p.severity === "critical" || p.severity === "high") && p.pattern.test(fastPassScanText)
|
|
8227
9089
|
);
|
|
8228
9090
|
const hasRedirectWrite = isWriteOperation(firstCmd, lowerDetail);
|
|
8229
9091
|
if (!fullCmdHit && !hasRedirectWrite) {
|
|
@@ -8238,7 +9100,7 @@ async function runAnalysis(input) {
|
|
|
8238
9100
|
};
|
|
8239
9101
|
}
|
|
8240
9102
|
}
|
|
8241
|
-
if (
|
|
9103
|
+
if (tier !== "") {
|
|
8242
9104
|
skipLLM = true;
|
|
8243
9105
|
}
|
|
8244
9106
|
}
|
|
@@ -8247,8 +9109,8 @@ async function runAnalysis(input) {
|
|
|
8247
9109
|
const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
|
|
8248
9110
|
if (effectiveResult) {
|
|
8249
9111
|
const isCredentialPath = !!effectiveResult.isCredential;
|
|
8250
|
-
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) &&
|
|
8251
|
-
if (
|
|
9112
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && l1FileAccessHit;
|
|
9113
|
+
if (isInlineScriptCmd || isLiteralTextCmd) {
|
|
8252
9114
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
8253
9115
|
if (inlineJudgment.actionable) {
|
|
8254
9116
|
if (!inlineJudgment.allowed) {
|
|
@@ -8444,13 +9306,15 @@ async function runAnalysis(input) {
|
|
|
8444
9306
|
}
|
|
8445
9307
|
return llmResult;
|
|
8446
9308
|
}
|
|
8447
|
-
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS,
|
|
9309
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainInitialized, formalEngine;
|
|
8448
9310
|
var init_coordinator = __esm({
|
|
8449
9311
|
"src/engine/coordinator.ts"() {
|
|
8450
9312
|
"use strict";
|
|
8451
9313
|
init_esm_shims();
|
|
8452
9314
|
init_heuristic();
|
|
8453
9315
|
init_logic_rules();
|
|
9316
|
+
init_platform_traits();
|
|
9317
|
+
init_shell_split();
|
|
8454
9318
|
init_inline_analyzer();
|
|
8455
9319
|
init_llm_analyzer();
|
|
8456
9320
|
init_script_content_analyzer();
|
|
@@ -8480,53 +9344,6 @@ var init_coordinator = __esm({
|
|
|
8480
9344
|
"filemgr-readfile",
|
|
8481
9345
|
"filemgr-grepfiles"
|
|
8482
9346
|
]);
|
|
8483
|
-
FULLY_SAFE_BASH_PATTERNS = [
|
|
8484
|
-
/^ls(?:\s|$)/i,
|
|
8485
|
-
/^dir(?:\s|$)/i,
|
|
8486
|
-
/^pwd(?:\s|$)/i,
|
|
8487
|
-
/^which\s+/i,
|
|
8488
|
-
/^whereis\s+/i,
|
|
8489
|
-
/^realpath\s+/i,
|
|
8490
|
-
/^basename\s+/i,
|
|
8491
|
-
/^dirname\s+/i,
|
|
8492
|
-
/^file\s+/i,
|
|
8493
|
-
/^stat\s+/i,
|
|
8494
|
-
/^du(?:\s|$)/i,
|
|
8495
|
-
/^echo\s+/i,
|
|
8496
|
-
/^printf\s+/i,
|
|
8497
|
-
/^type\s+/i,
|
|
8498
|
-
/^command\s+/i,
|
|
8499
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
8500
|
-
/^whoami(?:\s|$)/i,
|
|
8501
|
-
/^id(?:\s|$)/i,
|
|
8502
|
-
/^hostname(?:\s|$)/i,
|
|
8503
|
-
/^uname(?:\s|$)/i,
|
|
8504
|
-
/^date(?:\s|$)/i,
|
|
8505
|
-
/^env(?:\s|$)/i,
|
|
8506
|
-
/^printenv(?:\s|$)/i,
|
|
8507
|
-
/^tty(?:\s|$)/i,
|
|
8508
|
-
/^arch(?:\s|$)/i,
|
|
8509
|
-
/^uptime(?:\s|$)/i,
|
|
8510
|
-
/^groups(?:\s|$)/i,
|
|
8511
|
-
/^logname(?:\s|$)/i
|
|
8512
|
-
];
|
|
8513
|
-
READONLY_SENSITIVE_BASH_PATTERNS = [
|
|
8514
|
-
/^cat\s+/i,
|
|
8515
|
-
/^head(?:\s|$)/i,
|
|
8516
|
-
/^tail(?:\s|$)/i,
|
|
8517
|
-
/^less(?:\s|$)/i,
|
|
8518
|
-
/^more(?:\s|$)/i,
|
|
8519
|
-
/^wc(?:\s|$)/i,
|
|
8520
|
-
/^grep(?:\s|$)/i,
|
|
8521
|
-
/^find\s+/i,
|
|
8522
|
-
/^ag(?:\s|$)/i,
|
|
8523
|
-
/^rg(?:\s|$)/i,
|
|
8524
|
-
/^awk\s+/i,
|
|
8525
|
-
/^sed\s+/i,
|
|
8526
|
-
/^cut\s+/i,
|
|
8527
|
-
/^sort(?:\s|$)/i,
|
|
8528
|
-
/^uniq(?:\s|$)/i
|
|
8529
|
-
];
|
|
8530
9347
|
_brainStore = null;
|
|
8531
9348
|
_brainExtractor = null;
|
|
8532
9349
|
_brainInitialized = false;
|
|
@@ -8534,795 +9351,6 @@ var init_coordinator = __esm({
|
|
|
8534
9351
|
}
|
|
8535
9352
|
});
|
|
8536
9353
|
|
|
8537
|
-
// node_modules/better-sqlite3/lib/util.js
|
|
8538
|
-
var require_util = __commonJS({
|
|
8539
|
-
"node_modules/better-sqlite3/lib/util.js"(exports) {
|
|
8540
|
-
"use strict";
|
|
8541
|
-
init_esm_shims();
|
|
8542
|
-
exports.getBooleanOption = (options, key) => {
|
|
8543
|
-
let value = false;
|
|
8544
|
-
if (key in options && typeof (value = options[key]) !== "boolean") {
|
|
8545
|
-
throw new TypeError(`Expected the "${key}" option to be a boolean`);
|
|
8546
|
-
}
|
|
8547
|
-
return value;
|
|
8548
|
-
};
|
|
8549
|
-
exports.cppdb = /* @__PURE__ */ Symbol();
|
|
8550
|
-
exports.inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
8551
|
-
}
|
|
8552
|
-
});
|
|
8553
|
-
|
|
8554
|
-
// node_modules/better-sqlite3/lib/sqlite-error.js
|
|
8555
|
-
var require_sqlite_error = __commonJS({
|
|
8556
|
-
"node_modules/better-sqlite3/lib/sqlite-error.js"(exports, module) {
|
|
8557
|
-
"use strict";
|
|
8558
|
-
init_esm_shims();
|
|
8559
|
-
var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
|
|
8560
|
-
function SqliteError(message, code) {
|
|
8561
|
-
if (new.target !== SqliteError) {
|
|
8562
|
-
return new SqliteError(message, code);
|
|
8563
|
-
}
|
|
8564
|
-
if (typeof code !== "string") {
|
|
8565
|
-
throw new TypeError("Expected second argument to be a string");
|
|
8566
|
-
}
|
|
8567
|
-
Error.call(this, message);
|
|
8568
|
-
descriptor.value = "" + message;
|
|
8569
|
-
Object.defineProperty(this, "message", descriptor);
|
|
8570
|
-
Error.captureStackTrace(this, SqliteError);
|
|
8571
|
-
this.code = code;
|
|
8572
|
-
}
|
|
8573
|
-
Object.setPrototypeOf(SqliteError, Error);
|
|
8574
|
-
Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
|
|
8575
|
-
Object.defineProperty(SqliteError.prototype, "name", descriptor);
|
|
8576
|
-
module.exports = SqliteError;
|
|
8577
|
-
}
|
|
8578
|
-
});
|
|
8579
|
-
|
|
8580
|
-
// node_modules/file-uri-to-path/index.js
|
|
8581
|
-
var require_file_uri_to_path = __commonJS({
|
|
8582
|
-
"node_modules/file-uri-to-path/index.js"(exports, module) {
|
|
8583
|
-
"use strict";
|
|
8584
|
-
init_esm_shims();
|
|
8585
|
-
var sep = __require("path").sep || "/";
|
|
8586
|
-
module.exports = fileUriToPath;
|
|
8587
|
-
function fileUriToPath(uri) {
|
|
8588
|
-
if ("string" != typeof uri || uri.length <= 7 || "file://" != uri.substring(0, 7)) {
|
|
8589
|
-
throw new TypeError("must pass in a file:// URI to convert to a file path");
|
|
8590
|
-
}
|
|
8591
|
-
var rest = decodeURI(uri.substring(7));
|
|
8592
|
-
var firstSlash = rest.indexOf("/");
|
|
8593
|
-
var host = rest.substring(0, firstSlash);
|
|
8594
|
-
var path6 = rest.substring(firstSlash + 1);
|
|
8595
|
-
if ("localhost" == host) host = "";
|
|
8596
|
-
if (host) {
|
|
8597
|
-
host = sep + sep + host;
|
|
8598
|
-
}
|
|
8599
|
-
path6 = path6.replace(/^(.+)\|/, "$1:");
|
|
8600
|
-
if (sep == "\\") {
|
|
8601
|
-
path6 = path6.replace(/\//g, "\\");
|
|
8602
|
-
}
|
|
8603
|
-
if (/^.+\:/.test(path6)) {
|
|
8604
|
-
} else {
|
|
8605
|
-
path6 = sep + path6;
|
|
8606
|
-
}
|
|
8607
|
-
return host + path6;
|
|
8608
|
-
}
|
|
8609
|
-
}
|
|
8610
|
-
});
|
|
8611
|
-
|
|
8612
|
-
// node_modules/bindings/bindings.js
|
|
8613
|
-
var require_bindings = __commonJS({
|
|
8614
|
-
"node_modules/bindings/bindings.js"(exports, module) {
|
|
8615
|
-
"use strict";
|
|
8616
|
-
init_esm_shims();
|
|
8617
|
-
var fs7 = __require("fs");
|
|
8618
|
-
var path6 = __require("path");
|
|
8619
|
-
var fileURLToPath11 = require_file_uri_to_path();
|
|
8620
|
-
var join11 = path6.join;
|
|
8621
|
-
var dirname11 = path6.dirname;
|
|
8622
|
-
var exists = fs7.accessSync && function(path7) {
|
|
8623
|
-
try {
|
|
8624
|
-
fs7.accessSync(path7);
|
|
8625
|
-
} catch (e) {
|
|
8626
|
-
return false;
|
|
8627
|
-
}
|
|
8628
|
-
return true;
|
|
8629
|
-
} || fs7.existsSync || path6.existsSync;
|
|
8630
|
-
var defaults = {
|
|
8631
|
-
arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
|
|
8632
|
-
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
|
|
8633
|
-
platform: process.platform,
|
|
8634
|
-
arch: process.arch,
|
|
8635
|
-
nodePreGyp: "node-v" + process.versions.modules + "-" + process.platform + "-" + process.arch,
|
|
8636
|
-
version: process.versions.node,
|
|
8637
|
-
bindings: "bindings.node",
|
|
8638
|
-
try: [
|
|
8639
|
-
// node-gyp's linked version in the "build" dir
|
|
8640
|
-
["module_root", "build", "bindings"],
|
|
8641
|
-
// node-waf and gyp_addon (a.k.a node-gyp)
|
|
8642
|
-
["module_root", "build", "Debug", "bindings"],
|
|
8643
|
-
["module_root", "build", "Release", "bindings"],
|
|
8644
|
-
// Debug files, for development (legacy behavior, remove for node v0.9)
|
|
8645
|
-
["module_root", "out", "Debug", "bindings"],
|
|
8646
|
-
["module_root", "Debug", "bindings"],
|
|
8647
|
-
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
|
|
8648
|
-
["module_root", "out", "Release", "bindings"],
|
|
8649
|
-
["module_root", "Release", "bindings"],
|
|
8650
|
-
// Legacy from node-waf, node <= 0.4.x
|
|
8651
|
-
["module_root", "build", "default", "bindings"],
|
|
8652
|
-
// Production "Release" buildtype binary (meh...)
|
|
8653
|
-
["module_root", "compiled", "version", "platform", "arch", "bindings"],
|
|
8654
|
-
// node-qbs builds
|
|
8655
|
-
["module_root", "addon-build", "release", "install-root", "bindings"],
|
|
8656
|
-
["module_root", "addon-build", "debug", "install-root", "bindings"],
|
|
8657
|
-
["module_root", "addon-build", "default", "install-root", "bindings"],
|
|
8658
|
-
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
|
|
8659
|
-
["module_root", "lib", "binding", "nodePreGyp", "bindings"]
|
|
8660
|
-
]
|
|
8661
|
-
};
|
|
8662
|
-
function bindings(opts) {
|
|
8663
|
-
if (typeof opts == "string") {
|
|
8664
|
-
opts = { bindings: opts };
|
|
8665
|
-
} else if (!opts) {
|
|
8666
|
-
opts = {};
|
|
8667
|
-
}
|
|
8668
|
-
Object.keys(defaults).map(function(i2) {
|
|
8669
|
-
if (!(i2 in opts)) opts[i2] = defaults[i2];
|
|
8670
|
-
});
|
|
8671
|
-
if (!opts.module_root) {
|
|
8672
|
-
opts.module_root = exports.getRoot(exports.getFileName());
|
|
8673
|
-
}
|
|
8674
|
-
if (path6.extname(opts.bindings) != ".node") {
|
|
8675
|
-
opts.bindings += ".node";
|
|
8676
|
-
}
|
|
8677
|
-
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
8678
|
-
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
8679
|
-
for (; i < l; i++) {
|
|
8680
|
-
n = join11.apply(
|
|
8681
|
-
null,
|
|
8682
|
-
opts.try[i].map(function(p) {
|
|
8683
|
-
return opts[p] || p;
|
|
8684
|
-
})
|
|
8685
|
-
);
|
|
8686
|
-
tries.push(n);
|
|
8687
|
-
try {
|
|
8688
|
-
b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
|
|
8689
|
-
if (!opts.path) {
|
|
8690
|
-
b.path = n;
|
|
8691
|
-
}
|
|
8692
|
-
return b;
|
|
8693
|
-
} catch (e) {
|
|
8694
|
-
if (e.code !== "MODULE_NOT_FOUND" && e.code !== "QUALIFIED_PATH_RESOLUTION_FAILED" && !/not find/i.test(e.message)) {
|
|
8695
|
-
throw e;
|
|
8696
|
-
}
|
|
8697
|
-
}
|
|
8698
|
-
}
|
|
8699
|
-
err = new Error(
|
|
8700
|
-
"Could not locate the bindings file. Tried:\n" + tries.map(function(a) {
|
|
8701
|
-
return opts.arrow + a;
|
|
8702
|
-
}).join("\n")
|
|
8703
|
-
);
|
|
8704
|
-
err.tries = tries;
|
|
8705
|
-
throw err;
|
|
8706
|
-
}
|
|
8707
|
-
module.exports = exports = bindings;
|
|
8708
|
-
exports.getFileName = function getFileName(calling_file) {
|
|
8709
|
-
var origPST = Error.prepareStackTrace, origSTL = Error.stackTraceLimit, dummy = {}, fileName;
|
|
8710
|
-
Error.stackTraceLimit = 10;
|
|
8711
|
-
Error.prepareStackTrace = function(e, st) {
|
|
8712
|
-
for (var i = 0, l = st.length; i < l; i++) {
|
|
8713
|
-
fileName = st[i].getFileName();
|
|
8714
|
-
if (fileName !== __filename) {
|
|
8715
|
-
if (calling_file) {
|
|
8716
|
-
if (fileName !== calling_file) {
|
|
8717
|
-
return;
|
|
8718
|
-
}
|
|
8719
|
-
} else {
|
|
8720
|
-
return;
|
|
8721
|
-
}
|
|
8722
|
-
}
|
|
8723
|
-
}
|
|
8724
|
-
};
|
|
8725
|
-
Error.captureStackTrace(dummy);
|
|
8726
|
-
dummy.stack;
|
|
8727
|
-
Error.prepareStackTrace = origPST;
|
|
8728
|
-
Error.stackTraceLimit = origSTL;
|
|
8729
|
-
var fileSchema = "file://";
|
|
8730
|
-
if (fileName.indexOf(fileSchema) === 0) {
|
|
8731
|
-
fileName = fileURLToPath11(fileName);
|
|
8732
|
-
}
|
|
8733
|
-
return fileName;
|
|
8734
|
-
};
|
|
8735
|
-
exports.getRoot = function getRoot(file) {
|
|
8736
|
-
var dir = dirname11(file), prev;
|
|
8737
|
-
while (true) {
|
|
8738
|
-
if (dir === ".") {
|
|
8739
|
-
dir = process.cwd();
|
|
8740
|
-
}
|
|
8741
|
-
if (exists(join11(dir, "package.json")) || exists(join11(dir, "node_modules"))) {
|
|
8742
|
-
return dir;
|
|
8743
|
-
}
|
|
8744
|
-
if (prev === dir) {
|
|
8745
|
-
throw new Error(
|
|
8746
|
-
'Could not find module root given file: "' + file + '". Do you have a `package.json` file? '
|
|
8747
|
-
);
|
|
8748
|
-
}
|
|
8749
|
-
prev = dir;
|
|
8750
|
-
dir = join11(dir, "..");
|
|
8751
|
-
}
|
|
8752
|
-
};
|
|
8753
|
-
}
|
|
8754
|
-
});
|
|
8755
|
-
|
|
8756
|
-
// node_modules/better-sqlite3/lib/methods/wrappers.js
|
|
8757
|
-
var require_wrappers = __commonJS({
|
|
8758
|
-
"node_modules/better-sqlite3/lib/methods/wrappers.js"(exports) {
|
|
8759
|
-
"use strict";
|
|
8760
|
-
init_esm_shims();
|
|
8761
|
-
var { cppdb } = require_util();
|
|
8762
|
-
exports.prepare = function prepare(sql) {
|
|
8763
|
-
return this[cppdb].prepare(sql, this, false);
|
|
8764
|
-
};
|
|
8765
|
-
exports.exec = function exec(sql) {
|
|
8766
|
-
this[cppdb].exec(sql);
|
|
8767
|
-
return this;
|
|
8768
|
-
};
|
|
8769
|
-
exports.close = function close() {
|
|
8770
|
-
this[cppdb].close();
|
|
8771
|
-
return this;
|
|
8772
|
-
};
|
|
8773
|
-
exports.loadExtension = function loadExtension(...args) {
|
|
8774
|
-
this[cppdb].loadExtension(...args);
|
|
8775
|
-
return this;
|
|
8776
|
-
};
|
|
8777
|
-
exports.defaultSafeIntegers = function defaultSafeIntegers(...args) {
|
|
8778
|
-
this[cppdb].defaultSafeIntegers(...args);
|
|
8779
|
-
return this;
|
|
8780
|
-
};
|
|
8781
|
-
exports.unsafeMode = function unsafeMode(...args) {
|
|
8782
|
-
this[cppdb].unsafeMode(...args);
|
|
8783
|
-
return this;
|
|
8784
|
-
};
|
|
8785
|
-
exports.getters = {
|
|
8786
|
-
name: {
|
|
8787
|
-
get: function name() {
|
|
8788
|
-
return this[cppdb].name;
|
|
8789
|
-
},
|
|
8790
|
-
enumerable: true
|
|
8791
|
-
},
|
|
8792
|
-
open: {
|
|
8793
|
-
get: function open() {
|
|
8794
|
-
return this[cppdb].open;
|
|
8795
|
-
},
|
|
8796
|
-
enumerable: true
|
|
8797
|
-
},
|
|
8798
|
-
inTransaction: {
|
|
8799
|
-
get: function inTransaction() {
|
|
8800
|
-
return this[cppdb].inTransaction;
|
|
8801
|
-
},
|
|
8802
|
-
enumerable: true
|
|
8803
|
-
},
|
|
8804
|
-
readonly: {
|
|
8805
|
-
get: function readonly() {
|
|
8806
|
-
return this[cppdb].readonly;
|
|
8807
|
-
},
|
|
8808
|
-
enumerable: true
|
|
8809
|
-
},
|
|
8810
|
-
memory: {
|
|
8811
|
-
get: function memory() {
|
|
8812
|
-
return this[cppdb].memory;
|
|
8813
|
-
},
|
|
8814
|
-
enumerable: true
|
|
8815
|
-
}
|
|
8816
|
-
};
|
|
8817
|
-
}
|
|
8818
|
-
});
|
|
8819
|
-
|
|
8820
|
-
// node_modules/better-sqlite3/lib/methods/transaction.js
|
|
8821
|
-
var require_transaction = __commonJS({
|
|
8822
|
-
"node_modules/better-sqlite3/lib/methods/transaction.js"(exports, module) {
|
|
8823
|
-
"use strict";
|
|
8824
|
-
init_esm_shims();
|
|
8825
|
-
var { cppdb } = require_util();
|
|
8826
|
-
var controllers = /* @__PURE__ */ new WeakMap();
|
|
8827
|
-
module.exports = function transaction(fn) {
|
|
8828
|
-
if (typeof fn !== "function") throw new TypeError("Expected first argument to be a function");
|
|
8829
|
-
const db = this[cppdb];
|
|
8830
|
-
const controller = getController(db, this);
|
|
8831
|
-
const { apply } = Function.prototype;
|
|
8832
|
-
const properties = {
|
|
8833
|
-
default: { value: wrapTransaction(apply, fn, db, controller.default) },
|
|
8834
|
-
deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
|
|
8835
|
-
immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
|
|
8836
|
-
exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
|
|
8837
|
-
database: { value: this, enumerable: true }
|
|
8838
|
-
};
|
|
8839
|
-
Object.defineProperties(properties.default.value, properties);
|
|
8840
|
-
Object.defineProperties(properties.deferred.value, properties);
|
|
8841
|
-
Object.defineProperties(properties.immediate.value, properties);
|
|
8842
|
-
Object.defineProperties(properties.exclusive.value, properties);
|
|
8843
|
-
return properties.default.value;
|
|
8844
|
-
};
|
|
8845
|
-
var getController = (db, self) => {
|
|
8846
|
-
let controller = controllers.get(db);
|
|
8847
|
-
if (!controller) {
|
|
8848
|
-
const shared = {
|
|
8849
|
-
commit: db.prepare("COMMIT", self, false),
|
|
8850
|
-
rollback: db.prepare("ROLLBACK", self, false),
|
|
8851
|
-
savepoint: db.prepare("SAVEPOINT ` _bs3. `", self, false),
|
|
8852
|
-
release: db.prepare("RELEASE ` _bs3. `", self, false),
|
|
8853
|
-
rollbackTo: db.prepare("ROLLBACK TO ` _bs3. `", self, false)
|
|
8854
|
-
};
|
|
8855
|
-
controllers.set(db, controller = {
|
|
8856
|
-
default: Object.assign({ begin: db.prepare("BEGIN", self, false) }, shared),
|
|
8857
|
-
deferred: Object.assign({ begin: db.prepare("BEGIN DEFERRED", self, false) }, shared),
|
|
8858
|
-
immediate: Object.assign({ begin: db.prepare("BEGIN IMMEDIATE", self, false) }, shared),
|
|
8859
|
-
exclusive: Object.assign({ begin: db.prepare("BEGIN EXCLUSIVE", self, false) }, shared)
|
|
8860
|
-
});
|
|
8861
|
-
}
|
|
8862
|
-
return controller;
|
|
8863
|
-
};
|
|
8864
|
-
var wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
|
|
8865
|
-
let before, after, undo;
|
|
8866
|
-
if (db.inTransaction) {
|
|
8867
|
-
before = savepoint;
|
|
8868
|
-
after = release;
|
|
8869
|
-
undo = rollbackTo;
|
|
8870
|
-
} else {
|
|
8871
|
-
before = begin;
|
|
8872
|
-
after = commit;
|
|
8873
|
-
undo = rollback;
|
|
8874
|
-
}
|
|
8875
|
-
before.run();
|
|
8876
|
-
try {
|
|
8877
|
-
const result = apply.call(fn, this, arguments);
|
|
8878
|
-
if (result && typeof result.then === "function") {
|
|
8879
|
-
throw new TypeError("Transaction function cannot return a promise");
|
|
8880
|
-
}
|
|
8881
|
-
after.run();
|
|
8882
|
-
return result;
|
|
8883
|
-
} catch (ex) {
|
|
8884
|
-
if (db.inTransaction) {
|
|
8885
|
-
undo.run();
|
|
8886
|
-
if (undo !== rollback) after.run();
|
|
8887
|
-
}
|
|
8888
|
-
throw ex;
|
|
8889
|
-
}
|
|
8890
|
-
};
|
|
8891
|
-
}
|
|
8892
|
-
});
|
|
8893
|
-
|
|
8894
|
-
// node_modules/better-sqlite3/lib/methods/pragma.js
|
|
8895
|
-
var require_pragma = __commonJS({
|
|
8896
|
-
"node_modules/better-sqlite3/lib/methods/pragma.js"(exports, module) {
|
|
8897
|
-
"use strict";
|
|
8898
|
-
init_esm_shims();
|
|
8899
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
8900
|
-
module.exports = function pragma(source, options) {
|
|
8901
|
-
if (options == null) options = {};
|
|
8902
|
-
if (typeof source !== "string") throw new TypeError("Expected first argument to be a string");
|
|
8903
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
8904
|
-
const simple = getBooleanOption(options, "simple");
|
|
8905
|
-
const stmt = this[cppdb].prepare(`PRAGMA ${source}`, this, true);
|
|
8906
|
-
return simple ? stmt.pluck().get() : stmt.all();
|
|
8907
|
-
};
|
|
8908
|
-
}
|
|
8909
|
-
});
|
|
8910
|
-
|
|
8911
|
-
// node_modules/better-sqlite3/lib/methods/backup.js
|
|
8912
|
-
var require_backup = __commonJS({
|
|
8913
|
-
"node_modules/better-sqlite3/lib/methods/backup.js"(exports, module) {
|
|
8914
|
-
"use strict";
|
|
8915
|
-
init_esm_shims();
|
|
8916
|
-
var fs7 = __require("fs");
|
|
8917
|
-
var path6 = __require("path");
|
|
8918
|
-
var { promisify } = __require("util");
|
|
8919
|
-
var { cppdb } = require_util();
|
|
8920
|
-
var fsAccess = promisify(fs7.access);
|
|
8921
|
-
module.exports = async function backup(filename, options) {
|
|
8922
|
-
if (options == null) options = {};
|
|
8923
|
-
if (typeof filename !== "string") throw new TypeError("Expected first argument to be a string");
|
|
8924
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
8925
|
-
filename = filename.trim();
|
|
8926
|
-
const attachedName = "attached" in options ? options.attached : "main";
|
|
8927
|
-
const handler = "progress" in options ? options.progress : null;
|
|
8928
|
-
if (!filename) throw new TypeError("Backup filename cannot be an empty string");
|
|
8929
|
-
if (filename === ":memory:") throw new TypeError('Invalid backup filename ":memory:"');
|
|
8930
|
-
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8931
|
-
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8932
|
-
if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
|
|
8933
|
-
await fsAccess(path6.dirname(filename)).catch(() => {
|
|
8934
|
-
throw new TypeError("Cannot save backup because the directory does not exist");
|
|
8935
|
-
});
|
|
8936
|
-
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
8937
|
-
return runBackup(this[cppdb].backup(this, attachedName, filename, isNewFile), handler || null);
|
|
8938
|
-
};
|
|
8939
|
-
var runBackup = (backup, handler) => {
|
|
8940
|
-
let rate = 0;
|
|
8941
|
-
let useDefault = true;
|
|
8942
|
-
return new Promise((resolve10, reject) => {
|
|
8943
|
-
setImmediate(function step() {
|
|
8944
|
-
try {
|
|
8945
|
-
const progress = backup.transfer(rate);
|
|
8946
|
-
if (!progress.remainingPages) {
|
|
8947
|
-
backup.close();
|
|
8948
|
-
resolve10(progress);
|
|
8949
|
-
return;
|
|
8950
|
-
}
|
|
8951
|
-
if (useDefault) {
|
|
8952
|
-
useDefault = false;
|
|
8953
|
-
rate = 100;
|
|
8954
|
-
}
|
|
8955
|
-
if (handler) {
|
|
8956
|
-
const ret = handler(progress);
|
|
8957
|
-
if (ret !== void 0) {
|
|
8958
|
-
if (typeof ret === "number" && ret === ret) rate = Math.max(0, Math.min(2147483647, Math.round(ret)));
|
|
8959
|
-
else throw new TypeError("Expected progress callback to return a number or undefined");
|
|
8960
|
-
}
|
|
8961
|
-
}
|
|
8962
|
-
setImmediate(step);
|
|
8963
|
-
} catch (err) {
|
|
8964
|
-
backup.close();
|
|
8965
|
-
reject(err);
|
|
8966
|
-
}
|
|
8967
|
-
});
|
|
8968
|
-
});
|
|
8969
|
-
};
|
|
8970
|
-
}
|
|
8971
|
-
});
|
|
8972
|
-
|
|
8973
|
-
// node_modules/better-sqlite3/lib/methods/serialize.js
|
|
8974
|
-
var require_serialize = __commonJS({
|
|
8975
|
-
"node_modules/better-sqlite3/lib/methods/serialize.js"(exports, module) {
|
|
8976
|
-
"use strict";
|
|
8977
|
-
init_esm_shims();
|
|
8978
|
-
var { cppdb } = require_util();
|
|
8979
|
-
module.exports = function serialize(options) {
|
|
8980
|
-
if (options == null) options = {};
|
|
8981
|
-
if (typeof options !== "object") throw new TypeError("Expected first argument to be an options object");
|
|
8982
|
-
const attachedName = "attached" in options ? options.attached : "main";
|
|
8983
|
-
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8984
|
-
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8985
|
-
return this[cppdb].serialize(attachedName);
|
|
8986
|
-
};
|
|
8987
|
-
}
|
|
8988
|
-
});
|
|
8989
|
-
|
|
8990
|
-
// node_modules/better-sqlite3/lib/methods/function.js
|
|
8991
|
-
var require_function = __commonJS({
|
|
8992
|
-
"node_modules/better-sqlite3/lib/methods/function.js"(exports, module) {
|
|
8993
|
-
"use strict";
|
|
8994
|
-
init_esm_shims();
|
|
8995
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
8996
|
-
module.exports = function defineFunction(name, options, fn) {
|
|
8997
|
-
if (options == null) options = {};
|
|
8998
|
-
if (typeof options === "function") {
|
|
8999
|
-
fn = options;
|
|
9000
|
-
options = {};
|
|
9001
|
-
}
|
|
9002
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9003
|
-
if (typeof fn !== "function") throw new TypeError("Expected last argument to be a function");
|
|
9004
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
9005
|
-
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
9006
|
-
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
9007
|
-
const deterministic = getBooleanOption(options, "deterministic");
|
|
9008
|
-
const directOnly = getBooleanOption(options, "directOnly");
|
|
9009
|
-
const varargs = getBooleanOption(options, "varargs");
|
|
9010
|
-
let argCount = -1;
|
|
9011
|
-
if (!varargs) {
|
|
9012
|
-
argCount = fn.length;
|
|
9013
|
-
if (!Number.isInteger(argCount) || argCount < 0) throw new TypeError("Expected function.length to be a positive integer");
|
|
9014
|
-
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
9015
|
-
}
|
|
9016
|
-
this[cppdb].function(fn, name, argCount, safeIntegers, deterministic, directOnly);
|
|
9017
|
-
return this;
|
|
9018
|
-
};
|
|
9019
|
-
}
|
|
9020
|
-
});
|
|
9021
|
-
|
|
9022
|
-
// node_modules/better-sqlite3/lib/methods/aggregate.js
|
|
9023
|
-
var require_aggregate = __commonJS({
|
|
9024
|
-
"node_modules/better-sqlite3/lib/methods/aggregate.js"(exports, module) {
|
|
9025
|
-
"use strict";
|
|
9026
|
-
init_esm_shims();
|
|
9027
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
9028
|
-
module.exports = function defineAggregate(name, options) {
|
|
9029
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9030
|
-
if (typeof options !== "object" || options === null) throw new TypeError("Expected second argument to be an options object");
|
|
9031
|
-
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
9032
|
-
const start = "start" in options ? options.start : null;
|
|
9033
|
-
const step = getFunctionOption(options, "step", true);
|
|
9034
|
-
const inverse = getFunctionOption(options, "inverse", false);
|
|
9035
|
-
const result = getFunctionOption(options, "result", false);
|
|
9036
|
-
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
9037
|
-
const deterministic = getBooleanOption(options, "deterministic");
|
|
9038
|
-
const directOnly = getBooleanOption(options, "directOnly");
|
|
9039
|
-
const varargs = getBooleanOption(options, "varargs");
|
|
9040
|
-
let argCount = -1;
|
|
9041
|
-
if (!varargs) {
|
|
9042
|
-
argCount = Math.max(getLength(step), inverse ? getLength(inverse) : 0);
|
|
9043
|
-
if (argCount > 0) argCount -= 1;
|
|
9044
|
-
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
9045
|
-
}
|
|
9046
|
-
this[cppdb].aggregate(start, step, inverse, result, name, argCount, safeIntegers, deterministic, directOnly);
|
|
9047
|
-
return this;
|
|
9048
|
-
};
|
|
9049
|
-
var getFunctionOption = (options, key, required) => {
|
|
9050
|
-
const value = key in options ? options[key] : null;
|
|
9051
|
-
if (typeof value === "function") return value;
|
|
9052
|
-
if (value != null) throw new TypeError(`Expected the "${key}" option to be a function`);
|
|
9053
|
-
if (required) throw new TypeError(`Missing required option "${key}"`);
|
|
9054
|
-
return null;
|
|
9055
|
-
};
|
|
9056
|
-
var getLength = ({ length }) => {
|
|
9057
|
-
if (Number.isInteger(length) && length >= 0) return length;
|
|
9058
|
-
throw new TypeError("Expected function.length to be a positive integer");
|
|
9059
|
-
};
|
|
9060
|
-
}
|
|
9061
|
-
});
|
|
9062
|
-
|
|
9063
|
-
// node_modules/better-sqlite3/lib/methods/table.js
|
|
9064
|
-
var require_table = __commonJS({
|
|
9065
|
-
"node_modules/better-sqlite3/lib/methods/table.js"(exports, module) {
|
|
9066
|
-
"use strict";
|
|
9067
|
-
init_esm_shims();
|
|
9068
|
-
var { cppdb } = require_util();
|
|
9069
|
-
module.exports = function defineTable(name, factory) {
|
|
9070
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9071
|
-
if (!name) throw new TypeError("Virtual table module name cannot be an empty string");
|
|
9072
|
-
let eponymous = false;
|
|
9073
|
-
if (typeof factory === "object" && factory !== null) {
|
|
9074
|
-
eponymous = true;
|
|
9075
|
-
factory = defer(parseTableDefinition(factory, "used", name));
|
|
9076
|
-
} else {
|
|
9077
|
-
if (typeof factory !== "function") throw new TypeError("Expected second argument to be a function or a table definition object");
|
|
9078
|
-
factory = wrapFactory(factory);
|
|
9079
|
-
}
|
|
9080
|
-
this[cppdb].table(factory, name, eponymous);
|
|
9081
|
-
return this;
|
|
9082
|
-
};
|
|
9083
|
-
function wrapFactory(factory) {
|
|
9084
|
-
return function virtualTableFactory(moduleName, databaseName, tableName, ...args) {
|
|
9085
|
-
const thisObject = {
|
|
9086
|
-
module: moduleName,
|
|
9087
|
-
database: databaseName,
|
|
9088
|
-
table: tableName
|
|
9089
|
-
};
|
|
9090
|
-
const def = apply.call(factory, thisObject, args);
|
|
9091
|
-
if (typeof def !== "object" || def === null) {
|
|
9092
|
-
throw new TypeError(`Virtual table module "${moduleName}" did not return a table definition object`);
|
|
9093
|
-
}
|
|
9094
|
-
return parseTableDefinition(def, "returned", moduleName);
|
|
9095
|
-
};
|
|
9096
|
-
}
|
|
9097
|
-
function parseTableDefinition(def, verb, moduleName) {
|
|
9098
|
-
if (!hasOwnProperty.call(def, "rows")) {
|
|
9099
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "rows" property`);
|
|
9100
|
-
}
|
|
9101
|
-
if (!hasOwnProperty.call(def, "columns")) {
|
|
9102
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "columns" property`);
|
|
9103
|
-
}
|
|
9104
|
-
const rows = def.rows;
|
|
9105
|
-
if (typeof rows !== "function" || Object.getPrototypeOf(rows) !== GeneratorFunctionPrototype) {
|
|
9106
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "rows" property (should be a generator function)`);
|
|
9107
|
-
}
|
|
9108
|
-
let columns = def.columns;
|
|
9109
|
-
if (!Array.isArray(columns) || !(columns = [...columns]).every((x) => typeof x === "string")) {
|
|
9110
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "columns" property (should be an array of strings)`);
|
|
9111
|
-
}
|
|
9112
|
-
if (columns.length !== new Set(columns).size) {
|
|
9113
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate column names`);
|
|
9114
|
-
}
|
|
9115
|
-
if (!columns.length) {
|
|
9116
|
-
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with zero columns`);
|
|
9117
|
-
}
|
|
9118
|
-
let parameters;
|
|
9119
|
-
if (hasOwnProperty.call(def, "parameters")) {
|
|
9120
|
-
parameters = def.parameters;
|
|
9121
|
-
if (!Array.isArray(parameters) || !(parameters = [...parameters]).every((x) => typeof x === "string")) {
|
|
9122
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "parameters" property (should be an array of strings)`);
|
|
9123
|
-
}
|
|
9124
|
-
} else {
|
|
9125
|
-
parameters = inferParameters(rows);
|
|
9126
|
-
}
|
|
9127
|
-
if (parameters.length !== new Set(parameters).size) {
|
|
9128
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate parameter names`);
|
|
9129
|
-
}
|
|
9130
|
-
if (parameters.length > 32) {
|
|
9131
|
-
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with more than the maximum number of 32 parameters`);
|
|
9132
|
-
}
|
|
9133
|
-
for (const parameter of parameters) {
|
|
9134
|
-
if (columns.includes(parameter)) {
|
|
9135
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with column "${parameter}" which was ambiguously defined as both a column and parameter`);
|
|
9136
|
-
}
|
|
9137
|
-
}
|
|
9138
|
-
let safeIntegers = 2;
|
|
9139
|
-
if (hasOwnProperty.call(def, "safeIntegers")) {
|
|
9140
|
-
const bool = def.safeIntegers;
|
|
9141
|
-
if (typeof bool !== "boolean") {
|
|
9142
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "safeIntegers" property (should be a boolean)`);
|
|
9143
|
-
}
|
|
9144
|
-
safeIntegers = +bool;
|
|
9145
|
-
}
|
|
9146
|
-
let directOnly = false;
|
|
9147
|
-
if (hasOwnProperty.call(def, "directOnly")) {
|
|
9148
|
-
directOnly = def.directOnly;
|
|
9149
|
-
if (typeof directOnly !== "boolean") {
|
|
9150
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "directOnly" property (should be a boolean)`);
|
|
9151
|
-
}
|
|
9152
|
-
}
|
|
9153
|
-
const columnDefinitions = [
|
|
9154
|
-
...parameters.map(identifier).map((str) => `${str} HIDDEN`),
|
|
9155
|
-
...columns.map(identifier)
|
|
9156
|
-
];
|
|
9157
|
-
return [
|
|
9158
|
-
`CREATE TABLE x(${columnDefinitions.join(", ")});`,
|
|
9159
|
-
wrapGenerator(rows, new Map(columns.map((x, i) => [x, parameters.length + i])), moduleName),
|
|
9160
|
-
parameters,
|
|
9161
|
-
safeIntegers,
|
|
9162
|
-
directOnly
|
|
9163
|
-
];
|
|
9164
|
-
}
|
|
9165
|
-
function wrapGenerator(generator, columnMap, moduleName) {
|
|
9166
|
-
return function* virtualTable(...args) {
|
|
9167
|
-
const output = args.map((x) => Buffer.isBuffer(x) ? Buffer.from(x) : x);
|
|
9168
|
-
for (let i = 0; i < columnMap.size; ++i) {
|
|
9169
|
-
output.push(null);
|
|
9170
|
-
}
|
|
9171
|
-
for (const row of generator(...args)) {
|
|
9172
|
-
if (Array.isArray(row)) {
|
|
9173
|
-
extractRowArray(row, output, columnMap.size, moduleName);
|
|
9174
|
-
yield output;
|
|
9175
|
-
} else if (typeof row === "object" && row !== null) {
|
|
9176
|
-
extractRowObject(row, output, columnMap, moduleName);
|
|
9177
|
-
yield output;
|
|
9178
|
-
} else {
|
|
9179
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded something that isn't a valid row object`);
|
|
9180
|
-
}
|
|
9181
|
-
}
|
|
9182
|
-
};
|
|
9183
|
-
}
|
|
9184
|
-
function extractRowArray(row, output, columnCount, moduleName) {
|
|
9185
|
-
if (row.length !== columnCount) {
|
|
9186
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an incorrect number of columns`);
|
|
9187
|
-
}
|
|
9188
|
-
const offset = output.length - columnCount;
|
|
9189
|
-
for (let i = 0; i < columnCount; ++i) {
|
|
9190
|
-
output[i + offset] = row[i];
|
|
9191
|
-
}
|
|
9192
|
-
}
|
|
9193
|
-
function extractRowObject(row, output, columnMap, moduleName) {
|
|
9194
|
-
let count = 0;
|
|
9195
|
-
for (const key of Object.keys(row)) {
|
|
9196
|
-
const index = columnMap.get(key);
|
|
9197
|
-
if (index === void 0) {
|
|
9198
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an undeclared column "${key}"`);
|
|
9199
|
-
}
|
|
9200
|
-
output[index] = row[key];
|
|
9201
|
-
count += 1;
|
|
9202
|
-
}
|
|
9203
|
-
if (count !== columnMap.size) {
|
|
9204
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with missing columns`);
|
|
9205
|
-
}
|
|
9206
|
-
}
|
|
9207
|
-
function inferParameters({ length }) {
|
|
9208
|
-
if (!Number.isInteger(length) || length < 0) {
|
|
9209
|
-
throw new TypeError("Expected function.length to be a positive integer");
|
|
9210
|
-
}
|
|
9211
|
-
const params = [];
|
|
9212
|
-
for (let i = 0; i < length; ++i) {
|
|
9213
|
-
params.push(`$${i + 1}`);
|
|
9214
|
-
}
|
|
9215
|
-
return params;
|
|
9216
|
-
}
|
|
9217
|
-
var { hasOwnProperty } = Object.prototype;
|
|
9218
|
-
var { apply } = Function.prototype;
|
|
9219
|
-
var GeneratorFunctionPrototype = Object.getPrototypeOf(function* () {
|
|
9220
|
-
});
|
|
9221
|
-
var identifier = (str) => `"${str.replace(/"/g, '""')}"`;
|
|
9222
|
-
var defer = (x) => () => x;
|
|
9223
|
-
}
|
|
9224
|
-
});
|
|
9225
|
-
|
|
9226
|
-
// node_modules/better-sqlite3/lib/methods/inspect.js
|
|
9227
|
-
var require_inspect = __commonJS({
|
|
9228
|
-
"node_modules/better-sqlite3/lib/methods/inspect.js"(exports, module) {
|
|
9229
|
-
"use strict";
|
|
9230
|
-
init_esm_shims();
|
|
9231
|
-
var DatabaseInspection = function Database() {
|
|
9232
|
-
};
|
|
9233
|
-
module.exports = function inspect(depth, opts) {
|
|
9234
|
-
return Object.assign(new DatabaseInspection(), this);
|
|
9235
|
-
};
|
|
9236
|
-
}
|
|
9237
|
-
});
|
|
9238
|
-
|
|
9239
|
-
// node_modules/better-sqlite3/lib/database.js
|
|
9240
|
-
var require_database = __commonJS({
|
|
9241
|
-
"node_modules/better-sqlite3/lib/database.js"(exports, module) {
|
|
9242
|
-
"use strict";
|
|
9243
|
-
init_esm_shims();
|
|
9244
|
-
var fs7 = __require("fs");
|
|
9245
|
-
var path6 = __require("path");
|
|
9246
|
-
var util = require_util();
|
|
9247
|
-
var SqliteError = require_sqlite_error();
|
|
9248
|
-
var DEFAULT_ADDON;
|
|
9249
|
-
function Database(filenameGiven, options) {
|
|
9250
|
-
if (new.target == null) {
|
|
9251
|
-
return new Database(filenameGiven, options);
|
|
9252
|
-
}
|
|
9253
|
-
let buffer;
|
|
9254
|
-
if (Buffer.isBuffer(filenameGiven)) {
|
|
9255
|
-
buffer = filenameGiven;
|
|
9256
|
-
filenameGiven = ":memory:";
|
|
9257
|
-
}
|
|
9258
|
-
if (filenameGiven == null) filenameGiven = "";
|
|
9259
|
-
if (options == null) options = {};
|
|
9260
|
-
if (typeof filenameGiven !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9261
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
9262
|
-
if ("readOnly" in options) throw new TypeError('Misspelled option "readOnly" should be "readonly"');
|
|
9263
|
-
if ("memory" in options) throw new TypeError('Option "memory" was removed in v7.0.0 (use ":memory:" filename instead)');
|
|
9264
|
-
const filename = filenameGiven.trim();
|
|
9265
|
-
const anonymous = filename === "" || filename === ":memory:";
|
|
9266
|
-
const readonly = util.getBooleanOption(options, "readonly");
|
|
9267
|
-
const fileMustExist = util.getBooleanOption(options, "fileMustExist");
|
|
9268
|
-
const timeout = "timeout" in options ? options.timeout : 5e3;
|
|
9269
|
-
const verbose = "verbose" in options ? options.verbose : null;
|
|
9270
|
-
const nativeBinding = "nativeBinding" in options ? options.nativeBinding : null;
|
|
9271
|
-
if (readonly && anonymous && !buffer) throw new TypeError("In-memory/temporary databases cannot be readonly");
|
|
9272
|
-
if (!Number.isInteger(timeout) || timeout < 0) throw new TypeError('Expected the "timeout" option to be a positive integer');
|
|
9273
|
-
if (timeout > 2147483647) throw new RangeError('Option "timeout" cannot be greater than 2147483647');
|
|
9274
|
-
if (verbose != null && typeof verbose !== "function") throw new TypeError('Expected the "verbose" option to be a function');
|
|
9275
|
-
if (nativeBinding != null && typeof nativeBinding !== "string" && typeof nativeBinding !== "object") throw new TypeError('Expected the "nativeBinding" option to be a string or addon object');
|
|
9276
|
-
let addon;
|
|
9277
|
-
if (nativeBinding == null) {
|
|
9278
|
-
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
|
|
9279
|
-
} else if (typeof nativeBinding === "string") {
|
|
9280
|
-
const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
9281
|
-
addon = requireFunc(path6.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
|
|
9282
|
-
} else {
|
|
9283
|
-
addon = nativeBinding;
|
|
9284
|
-
}
|
|
9285
|
-
if (!addon.isInitialized) {
|
|
9286
|
-
addon.setErrorConstructor(SqliteError);
|
|
9287
|
-
addon.isInitialized = true;
|
|
9288
|
-
}
|
|
9289
|
-
if (!anonymous && !fs7.existsSync(path6.dirname(filename))) {
|
|
9290
|
-
throw new TypeError("Cannot open database because the directory does not exist");
|
|
9291
|
-
}
|
|
9292
|
-
Object.defineProperties(this, {
|
|
9293
|
-
[util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) },
|
|
9294
|
-
...wrappers.getters
|
|
9295
|
-
});
|
|
9296
|
-
}
|
|
9297
|
-
var wrappers = require_wrappers();
|
|
9298
|
-
Database.prototype.prepare = wrappers.prepare;
|
|
9299
|
-
Database.prototype.transaction = require_transaction();
|
|
9300
|
-
Database.prototype.pragma = require_pragma();
|
|
9301
|
-
Database.prototype.backup = require_backup();
|
|
9302
|
-
Database.prototype.serialize = require_serialize();
|
|
9303
|
-
Database.prototype.function = require_function();
|
|
9304
|
-
Database.prototype.aggregate = require_aggregate();
|
|
9305
|
-
Database.prototype.table = require_table();
|
|
9306
|
-
Database.prototype.loadExtension = wrappers.loadExtension;
|
|
9307
|
-
Database.prototype.exec = wrappers.exec;
|
|
9308
|
-
Database.prototype.close = wrappers.close;
|
|
9309
|
-
Database.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
|
|
9310
|
-
Database.prototype.unsafeMode = wrappers.unsafeMode;
|
|
9311
|
-
Database.prototype[util.inspect] = require_inspect();
|
|
9312
|
-
module.exports = Database;
|
|
9313
|
-
}
|
|
9314
|
-
});
|
|
9315
|
-
|
|
9316
|
-
// node_modules/better-sqlite3/lib/index.js
|
|
9317
|
-
var require_lib = __commonJS({
|
|
9318
|
-
"node_modules/better-sqlite3/lib/index.js"(exports, module) {
|
|
9319
|
-
"use strict";
|
|
9320
|
-
init_esm_shims();
|
|
9321
|
-
module.exports = require_database();
|
|
9322
|
-
module.exports.SqliteError = require_sqlite_error();
|
|
9323
|
-
}
|
|
9324
|
-
});
|
|
9325
|
-
|
|
9326
9354
|
// src/cli.ts
|
|
9327
9355
|
init_esm_shims();
|
|
9328
9356
|
|
|
@@ -9350,7 +9378,11 @@ var version = pkg.version;
|
|
|
9350
9378
|
// src/models.ts
|
|
9351
9379
|
var ActionItemSchema = z.object({
|
|
9352
9380
|
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
9353
|
-
action_detail: z.string().describe("Action detail / command content")
|
|
9381
|
+
action_detail: z.string().describe("Action detail / command content"),
|
|
9382
|
+
// 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
|
|
9383
|
+
// 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
|
|
9384
|
+
// 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
|
|
9385
|
+
output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
|
|
9354
9386
|
});
|
|
9355
9387
|
var PromptTurnSchema = z.object({
|
|
9356
9388
|
text: z.string().default("").describe("User prompt text at this turn"),
|
|
@@ -9420,6 +9452,16 @@ init_coordinator();
|
|
|
9420
9452
|
// src/actrail.ts
|
|
9421
9453
|
init_esm_shims();
|
|
9422
9454
|
import * as fs5 from "fs";
|
|
9455
|
+
|
|
9456
|
+
// src/infra/esm-require.ts
|
|
9457
|
+
init_esm_shims();
|
|
9458
|
+
import { createRequire as createRequire2 } from "module";
|
|
9459
|
+
var requireFromHere = createRequire2(import.meta.url);
|
|
9460
|
+
function esmRequire(id) {
|
|
9461
|
+
return requireFromHere(id);
|
|
9462
|
+
}
|
|
9463
|
+
|
|
9464
|
+
// src/actrail.ts
|
|
9423
9465
|
function escape(value) {
|
|
9424
9466
|
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/=/g, "\\e");
|
|
9425
9467
|
}
|
|
@@ -9444,7 +9486,7 @@ function reportEnforcementEvent(opts) {
|
|
|
9444
9486
|
try {
|
|
9445
9487
|
let Database;
|
|
9446
9488
|
try {
|
|
9447
|
-
Database =
|
|
9489
|
+
Database = esmRequire("better-sqlite3");
|
|
9448
9490
|
} catch {
|
|
9449
9491
|
console.debug("[AgentMoss] better-sqlite3 not installed, AcTrail reporting disabled");
|
|
9450
9492
|
return false;
|
|
@@ -9645,7 +9687,7 @@ function readJsonDir(dirPath, limit = 1e5) {
|
|
|
9645
9687
|
function readSqlite(dbPath, limit = 1e5) {
|
|
9646
9688
|
let Database;
|
|
9647
9689
|
try {
|
|
9648
|
-
Database =
|
|
9690
|
+
Database = esmRequire("better-sqlite3");
|
|
9649
9691
|
} catch {
|
|
9650
9692
|
return [];
|
|
9651
9693
|
}
|
|
@@ -9713,15 +9755,21 @@ async function analyzeRoute(c) {
|
|
|
9713
9755
|
const result = await runAnalysis({
|
|
9714
9756
|
sessionId: req.session_id,
|
|
9715
9757
|
promptSession: req.prompt_session,
|
|
9758
|
+
// output 必须透传:L2 的「授权(consent)」判定看的是**用户对提问的回答**,
|
|
9759
|
+
// 不是提问正文。历史 bug:这里丢了 output,TS 的判定退化成 actionDetail 非空,
|
|
9760
|
+
// 于是只要会话里问过任何问题(无论用户答没答、答的是什么、问的是不是改密)
|
|
9761
|
+
// 都算「已授权」→ 未授权的改密/删用户被放行(fail-open)。
|
|
9716
9762
|
actionHistory: req.action_history.map((h) => ({
|
|
9717
9763
|
actionType: h.action_type,
|
|
9718
|
-
actionDetail: h.action_detail
|
|
9764
|
+
actionDetail: h.action_detail,
|
|
9765
|
+
output: h.output
|
|
9719
9766
|
})),
|
|
9720
9767
|
sessionHistory: (req.session_history || []).map((s) => ({
|
|
9721
9768
|
sessionId: s.session_id,
|
|
9722
9769
|
actionHistory: (s.action_history || []).map((h) => ({
|
|
9723
9770
|
actionType: h.action_type,
|
|
9724
|
-
actionDetail: h.action_detail
|
|
9771
|
+
actionDetail: h.action_detail,
|
|
9772
|
+
output: h.output
|
|
9725
9773
|
}))
|
|
9726
9774
|
})),
|
|
9727
9775
|
aNext: {
|
|
@@ -9915,7 +9963,7 @@ init_runtime_config();
|
|
|
9915
9963
|
init_token_stats();
|
|
9916
9964
|
init_rules_loader();
|
|
9917
9965
|
import { Hono } from "hono";
|
|
9918
|
-
import { readFileSync as readFileSync15, existsSync as
|
|
9966
|
+
import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
9919
9967
|
import { join as join10, dirname as dirname10 } from "path";
|
|
9920
9968
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
9921
9969
|
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
@@ -9944,7 +9992,7 @@ function createConsoleRouter() {
|
|
|
9944
9992
|
console2.use("/api/*", authMiddleware());
|
|
9945
9993
|
const serveIndex = (c) => {
|
|
9946
9994
|
const indexFile = join10(STATIC_DIR, "index.html");
|
|
9947
|
-
if (
|
|
9995
|
+
if (existsSync15(indexFile)) {
|
|
9948
9996
|
return c.html(readFileSync15(indexFile, "utf-8"));
|
|
9949
9997
|
}
|
|
9950
9998
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
@@ -10110,9 +10158,9 @@ function createConsoleRouter() {
|
|
|
10110
10158
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
10111
10159
|
}
|
|
10112
10160
|
const skillPath = join10(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
10113
|
-
if (!
|
|
10161
|
+
if (!existsSync15(skillPath)) {
|
|
10114
10162
|
const builtinPath = join10(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
10115
|
-
if (
|
|
10163
|
+
if (existsSync15(builtinPath)) return c.text(readFileSync15(builtinPath, "utf-8"));
|
|
10116
10164
|
return c.json({ error: "skill not found" }, 404);
|
|
10117
10165
|
}
|
|
10118
10166
|
return c.text(readFileSync15(skillPath, "utf-8"));
|
|
@@ -10231,6 +10279,38 @@ function createConsoleRouter() {
|
|
|
10231
10279
|
}
|
|
10232
10280
|
|
|
10233
10281
|
// src/server.ts
|
|
10282
|
+
init_runtime_config();
|
|
10283
|
+
init_rules_loader();
|
|
10284
|
+
function logDeploymentSelfCheck() {
|
|
10285
|
+
try {
|
|
10286
|
+
console.log("================ AgentMoss \u90E8\u7F72\u81EA\u68C0 ================");
|
|
10287
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}`);
|
|
10288
|
+
const layerKeys = ["L1_heuristic", "L2_logic_rules", "L3_llm_analysis", "agent_rules_enabled", "scenario_rules_enabled"];
|
|
10289
|
+
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
10290
|
+
try {
|
|
10291
|
+
const overrides = overriddenRuleFiles();
|
|
10292
|
+
if (overrides.length > 0) {
|
|
10293
|
+
console.error(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u7528\u6237\u89C4\u5219\u8986\u76D6\u76EE\u5F55\u4E0B\u6709 ${overrides.length} \u4E2A\u6587\u4EF6\uFF0C**\u6574\u4EFD\u66FF\u6362**\u4E86\u5185\u7F6E\u89C4\u5219\uFF1A`);
|
|
10294
|
+
for (const p of overrides) console.error(` ${p}`);
|
|
10295
|
+
console.error(
|
|
10296
|
+
" \u8FD9\u4E9B\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C**\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762**\uFF1B\u5220\u9664\u5BF9\u5E94\u6587\u4EF6\u5373\u6062\u590D\u5185\u7F6E\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002"
|
|
10297
|
+
);
|
|
10298
|
+
}
|
|
10299
|
+
} catch {
|
|
10300
|
+
}
|
|
10301
|
+
const scenarioRaw = (process.env.AGENT_MOSS_SCENARIO || "").trim();
|
|
10302
|
+
if (scenarioRaw) {
|
|
10303
|
+
console.log(`\u573A\u666F\u89C4\u5219\u5DF2\u542F\u7528: ${scenarioRaw}\uFF08\u6765\u6E90\uFF1A\u73AF\u5883\u53D8\u91CF AGENT_MOSS_SCENARIO\uFF09`);
|
|
10304
|
+
} else {
|
|
10305
|
+
console.error(
|
|
10306
|
+
"\u26A0\uFE0F \u573A\u666F\u89C4\u5219\u3010\u672A\u542F\u7528\u3011\u2014\u2014 \u73AF\u5883\u53D8\u91CF AGENT_MOSS_SCENARIO \u672A\u8BBE\u7F6E\u3002\n \u573A\u666F\u5B9A\u5236\u89C4\u5219\uFF08\u5982\u94F6\u884C\u6C99\u7BB1\u7684\u76EE\u5F55\u767D\u540D\u5355 / \u7981 ssh\uFF09**\u4E0D\u4F1A\u751F\u6548**\uFF0C\u767D\u540D\u5355\u5916\u7684\u8DEF\u5F84\u4F1A\u88AB\u653E\u884C\u3002\n \u542F\u7528\u65B9\u5F0F\uFF1A\u8BBE AGENT_MOSS_SCENARIO=<\u573A\u666Fid>\uFF08\u5982 bank\uFF1B\u591A\u573A\u666F\u9017\u53F7\u5206\u9694\uFF09\u540E**\u91CD\u542F\u672C\u670D\u52A1**\u3002\n \uFF08HTTP \u8BF7\u6C42\u65E0\u6CD5\u6307\u5B9A\u573A\u666F id\uFF0C\u573A\u666F\u662F\u90E8\u7F72\u7EA7\u914D\u7F6E\u3002\uFF09"
|
|
10307
|
+
);
|
|
10308
|
+
}
|
|
10309
|
+
console.log("==================================================");
|
|
10310
|
+
} catch (exc) {
|
|
10311
|
+
console.error(`\u90E8\u7F72\u81EA\u68C0\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u670D\u52A1\uFF09: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
10312
|
+
}
|
|
10313
|
+
}
|
|
10234
10314
|
function createApp() {
|
|
10235
10315
|
const app = new Hono2();
|
|
10236
10316
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
@@ -10260,6 +10340,37 @@ async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
|
10260
10340
|
}
|
|
10261
10341
|
return startPort;
|
|
10262
10342
|
}
|
|
10343
|
+
var WARMUP_COMMANDS = {
|
|
10344
|
+
linux: "whoami",
|
|
10345
|
+
macos: "whoami",
|
|
10346
|
+
harmony: "whoami",
|
|
10347
|
+
windows: "dir"
|
|
10348
|
+
};
|
|
10349
|
+
async function warmUpEngine() {
|
|
10350
|
+
const prevSavePolicy = process.env.AGENT_MOSS_SAVE_POLICY;
|
|
10351
|
+
try {
|
|
10352
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_coordinator(), coordinator_exports));
|
|
10353
|
+
process.env.AGENT_MOSS_SAVE_POLICY = "0";
|
|
10354
|
+
for (const [osType, command] of Object.entries(WARMUP_COMMANDS)) {
|
|
10355
|
+
await runAnalysis2({
|
|
10356
|
+
sessionId: "__startup_warmup__",
|
|
10357
|
+
promptSession: "",
|
|
10358
|
+
actionHistory: [],
|
|
10359
|
+
aNext: { actionType: "bash", actionDetail: command },
|
|
10360
|
+
reason: "",
|
|
10361
|
+
osType,
|
|
10362
|
+
cwd: "",
|
|
10363
|
+
agentId: ""
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
console.log("[AgentMoss] \u5F15\u64CE\u9884\u70ED\u5B8C\u6210\uFF08\u9996\u4E2A\u8BF7\u6C42\u4E0D\u518D\u4ED8\u51B7\u542F\u52A8\u5F00\u9500\uFF09");
|
|
10367
|
+
} catch (err) {
|
|
10368
|
+
console.warn(`[AgentMoss] \u5F15\u64CE\u9884\u70ED\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u670D\u52A1\uFF09: ${String(err)}`);
|
|
10369
|
+
} finally {
|
|
10370
|
+
if (prevSavePolicy === void 0) delete process.env.AGENT_MOSS_SAVE_POLICY;
|
|
10371
|
+
else process.env.AGENT_MOSS_SAVE_POLICY = prevSavePolicy;
|
|
10372
|
+
}
|
|
10373
|
+
}
|
|
10263
10374
|
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
10264
10375
|
const actualPort = await findFreePort(port, host);
|
|
10265
10376
|
const app = createApp();
|
|
@@ -10267,6 +10378,8 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
10267
10378
|
port: actualPort,
|
|
10268
10379
|
serve: async () => {
|
|
10269
10380
|
console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
|
|
10381
|
+
logDeploymentSelfCheck();
|
|
10382
|
+
await warmUpEngine();
|
|
10270
10383
|
const { serve: honoServe } = await import("@hono/node-server");
|
|
10271
10384
|
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
10272
10385
|
}
|
|
@@ -10354,7 +10467,7 @@ async function main() {
|
|
|
10354
10467
|
console.error(`\u9519\u8BEF: \u4E0D\u662F\u6587\u4EF6: ${target}`);
|
|
10355
10468
|
process.exit(2);
|
|
10356
10469
|
}
|
|
10357
|
-
if (/\.(json|ya?ml|toml)$/i.test(target)) {
|
|
10470
|
+
if (/\.(json|ya?ml|toml)$/i.test(target) && !/\s/.test(target.trim())) {
|
|
10358
10471
|
console.error(`\u9519\u8BEF: \u6587\u4EF6\u4E0D\u5B58\u5728: ${target}`);
|
|
10359
10472
|
process.exit(2);
|
|
10360
10473
|
}
|