@kenhkl/agent-moss 0.15.5 → 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 +1832 -1695
- package/dist/engine/coordinator.d.ts +12 -1
- package/dist/engine/coordinator.js +1713 -876
- package/dist/rules/l1_rules.json +1364 -6
- 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 +3727 -3420
- package/package.json +3 -2
- package/dist/agents/__init__.py +0 -105
- package/dist/agents/__pycache__/__init__.cpython-310.pyc +0 -0
- package/dist/agents/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-310.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-311.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-312.pyc +0 -0
- package/dist/agents/base.py +0 -158
- package/dist/cli.js.map +0 -1
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/scenarios/__init__.py +0 -148
- package/dist/scenarios/__pycache__/__init__.cpython-310.pyc +0 -0
- package/dist/scenarios/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/scenarios/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/server.js.map +0 -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, "");
|
|
@@ -408,6 +140,9 @@ function isLiteralTextCommand(command) {
|
|
|
408
140
|
return false;
|
|
409
141
|
}
|
|
410
142
|
}
|
|
143
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
411
146
|
return true;
|
|
412
147
|
}
|
|
413
148
|
function extractInlineCode(command) {
|
|
@@ -485,14 +220,15 @@ function analyzeInlineScript(command) {
|
|
|
485
220
|
riskType: "file_access"
|
|
486
221
|
};
|
|
487
222
|
}
|
|
488
|
-
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;
|
|
489
224
|
var init_inline_analyzer = __esm({
|
|
490
225
|
"src/engine/inline-analyzer.ts"() {
|
|
491
226
|
"use strict";
|
|
492
227
|
init_esm_shims();
|
|
228
|
+
init_shell_split();
|
|
493
229
|
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
494
230
|
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
495
|
-
|
|
231
|
+
ECHO_SEG_RE = /^\s*(?:echo|printf)(?:\s|$)/i;
|
|
496
232
|
READ_WRITE_KEYWORDS = [
|
|
497
233
|
// 函数调用形态(关键词后跟 '(')
|
|
498
234
|
"open(",
|
|
@@ -541,9 +277,34 @@ var init_inline_analyzer = __esm({
|
|
|
541
277
|
"passthru",
|
|
542
278
|
"shell_exec",
|
|
543
279
|
"proc_open",
|
|
544
|
-
// Perl
|
|
280
|
+
// Perl/Ruby
|
|
545
281
|
"<stdin>",
|
|
546
|
-
"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")'
|
|
547
308
|
];
|
|
548
309
|
WRITE_EXEC_CHAIN_PATTERNS = [
|
|
549
310
|
// echo/cat/printf "..." > file && bash/sh/python file
|
|
@@ -598,15 +359,53 @@ var init_inline_analyzer = __esm({
|
|
|
598
359
|
}
|
|
599
360
|
});
|
|
600
361
|
|
|
601
|
-
// src/infra/
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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";
|
|
606
379
|
function dataRootDir() {
|
|
607
|
-
return
|
|
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;
|
|
608
405
|
}
|
|
609
406
|
function findRulesFile(name) {
|
|
407
|
+
const override = resolve(overrideRulesDir(), name);
|
|
408
|
+
if (existsSync(override)) return override;
|
|
610
409
|
for (const dir of RULES_DIR_CANDS) {
|
|
611
410
|
const p = resolve(dir, name);
|
|
612
411
|
if (existsSync(p)) return p;
|
|
@@ -616,11 +415,16 @@ function findRulesFile(name) {
|
|
|
616
415
|
function loadJson(name) {
|
|
617
416
|
const p = findRulesFile(name);
|
|
618
417
|
if (!p) return {};
|
|
418
|
+
const isOverride = p === resolve(overrideRulesDir(), name);
|
|
419
|
+
let data;
|
|
619
420
|
try {
|
|
620
|
-
|
|
421
|
+
data = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
621
422
|
} catch {
|
|
423
|
+
if (isOverride) warnOverride(name, p, {});
|
|
622
424
|
return {};
|
|
623
425
|
}
|
|
426
|
+
if (isOverride) warnOverride(name, p, data);
|
|
427
|
+
return data;
|
|
624
428
|
}
|
|
625
429
|
function loadL1() {
|
|
626
430
|
return loadJson("l1_rules.json");
|
|
@@ -635,8 +439,8 @@ function loadMC() {
|
|
|
635
439
|
const mc = loadJson("mc_rules.json");
|
|
636
440
|
const rules = mc.rules || [];
|
|
637
441
|
try {
|
|
638
|
-
const rt =
|
|
639
|
-
if (rules.length > 0 &&
|
|
442
|
+
const rt = runtimeConfigBridge.current;
|
|
443
|
+
if (rules.length > 0 && rt?.hasRuntimeConfig()) {
|
|
640
444
|
const ltl = rt.loadRuntimeConfig()?.L2_2?.ltl_rules || {};
|
|
641
445
|
const rtRules = ltl.rules || [];
|
|
642
446
|
const enabledById = {};
|
|
@@ -654,16 +458,26 @@ function loadMC() {
|
|
|
654
458
|
function loadToolRegistry() {
|
|
655
459
|
return loadJson("tool_registry.json");
|
|
656
460
|
}
|
|
657
|
-
var __filename2, __dirname2, RULES_DIR_CANDS;
|
|
461
|
+
var __filename2, __dirname2, EXPECTED_KEYS, warnedOverride, RULES_DIR_CANDS;
|
|
658
462
|
var init_rules_loader = __esm({
|
|
659
463
|
"src/infra/rules-loader.ts"() {
|
|
660
464
|
"use strict";
|
|
661
465
|
init_esm_shims();
|
|
662
|
-
|
|
466
|
+
init_paths();
|
|
467
|
+
init_runtime_config_bridge();
|
|
468
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
663
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();
|
|
664
478
|
RULES_DIR_CANDS = (() => {
|
|
665
479
|
const cands = [
|
|
666
|
-
// 用户数据目录覆盖优先(与 Python rules_loader.
|
|
480
|
+
// 用户数据目录覆盖优先(与 Python rules_loader.resolve_rules_file 的
|
|
667
481
|
// <data_root>/rules/<name> 优先逻辑对齐)。用户安装 pip/npm 包后把自定义
|
|
668
482
|
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
669
483
|
resolve(dataRootDir(), "rules"),
|
|
@@ -682,21 +496,455 @@ var init_rules_loader = __esm({
|
|
|
682
496
|
}
|
|
683
497
|
});
|
|
684
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
|
+
|
|
685
870
|
// src/engine/logic-rules.ts
|
|
686
|
-
import * as
|
|
687
|
-
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);
|
|
688
881
|
const sps = loadL1().sensitive_path_access || [];
|
|
689
|
-
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) => ({
|
|
690
883
|
path: sp.path,
|
|
691
884
|
riskLevel: sp.risk_level,
|
|
692
885
|
desc: sp.desc,
|
|
693
886
|
credential: sp.deny_mode === "deny_both" ? true : void 0,
|
|
694
|
-
|
|
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()
|
|
695
892
|
}));
|
|
696
893
|
}
|
|
697
|
-
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) {
|
|
698
945
|
if (writeKeywords.some((kw) => actionType.includes(kw) || actionDetail.includes(kw))) return true;
|
|
699
|
-
|
|
946
|
+
const { writeCommandRes } = getPlatformTraits(osType);
|
|
947
|
+
if (writeCommandRes.some((rx) => rx.test(actionDetail))) return true;
|
|
700
948
|
if (/\bsed\s+-[a-zA-Z]*i/i.test(actionDetail)) return true;
|
|
701
949
|
for (const seg of actionDetail.split(/&&|\|\||;|\n/)) {
|
|
702
950
|
const trimmed = seg.trim();
|
|
@@ -730,6 +978,12 @@ function isWriteOperation(actionType, actionDetail, writeKeywords = WRITE_KEYWOR
|
|
|
730
978
|
}
|
|
731
979
|
return false;
|
|
732
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
|
+
}
|
|
733
987
|
function matchDangerousAction(actionDetail, action) {
|
|
734
988
|
if (/[^\x00-\x7f]/.test(action)) {
|
|
735
989
|
return actionDetail.includes(action);
|
|
@@ -764,38 +1018,47 @@ function collectIntentTexts(promptSession, promptHistory) {
|
|
|
764
1018
|
}
|
|
765
1019
|
return deduped;
|
|
766
1020
|
}
|
|
767
|
-
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) {
|
|
768
1049
|
const { actionHistory, aNext, cwd } = input;
|
|
769
1050
|
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
770
1051
|
const actionType = aNext.actionType.toLowerCase();
|
|
771
1052
|
const resolvedCwd = cwd || "/";
|
|
772
1053
|
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
773
1054
|
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (!INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
778
|
-
const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
|
|
779
|
-
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
780
|
-
const intentMatched = intentTexts.some(
|
|
781
|
-
(text) => pattern.intentKeywords.some((kw) => text.includes(kw))
|
|
782
|
-
);
|
|
783
|
-
const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
|
|
784
|
-
if (intentMatched && dangerousMatched) {
|
|
785
|
-
return {
|
|
786
|
-
allowed: false,
|
|
787
|
-
reason: pattern.reason,
|
|
788
|
-
riskLevel: "high",
|
|
789
|
-
riskType: "intent_deviation",
|
|
790
|
-
confidence: 85,
|
|
791
|
-
source: "logic_rule",
|
|
792
|
-
violatedLayers: ["L2.1"]
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
1055
|
+
if (!opts?.skipReadBeforeWrite) {
|
|
1056
|
+
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd, input.osType);
|
|
1057
|
+
if (rbwResult) return rbwResult;
|
|
797
1058
|
}
|
|
798
|
-
const
|
|
1059
|
+
const intentResult = checkIntentConsistency(input);
|
|
1060
|
+
if (intentResult) return intentResult;
|
|
1061
|
+
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd, input.osType);
|
|
799
1062
|
if (indirectResult) return indirectResult;
|
|
800
1063
|
if (INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
801
1064
|
} else if (containsPrivilegeEscalation(actionDetail)) {
|
|
@@ -818,9 +1081,9 @@ function runLogicCheck(input) {
|
|
|
818
1081
|
}
|
|
819
1082
|
}
|
|
820
1083
|
}
|
|
821
|
-
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
|
|
1084
|
+
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory, input.osType);
|
|
822
1085
|
if (pwdResult) return pwdResult;
|
|
823
|
-
const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory);
|
|
1086
|
+
const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory, input.osType);
|
|
824
1087
|
if (userDelResult) return userDelResult;
|
|
825
1088
|
const comboResult = checkComboChain(actionHistory, aNext.actionDetail);
|
|
826
1089
|
if (comboResult) return comboResult;
|
|
@@ -871,22 +1134,33 @@ function checkComboChain(actionHistory, actionDetailRaw) {
|
|
|
871
1134
|
}
|
|
872
1135
|
return null;
|
|
873
1136
|
}
|
|
874
|
-
function extractFilePaths(text) {
|
|
1137
|
+
function extractFilePaths(text, osType) {
|
|
875
1138
|
const paths = [];
|
|
876
1139
|
const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
|
|
877
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
|
+
}
|
|
878
1148
|
const textNoQuotes = text.replace(/'[^']*'|"[^"]*"/g, "");
|
|
879
1149
|
const kvPaths = textNoQuotes.match(/(?:=)(\/[^\s;|&><'"]+)/g);
|
|
880
1150
|
if (kvPaths) paths.push(...kvPaths.map((p) => p.replace(/^=/, "")));
|
|
881
1151
|
const quotedPaths = text.match(/["']\/([^"']+)["']/g);
|
|
882
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
|
+
}
|
|
883
1157
|
return [...new Set(paths)];
|
|
884
1158
|
}
|
|
885
|
-
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd) {
|
|
1159
|
+
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd, osType) {
|
|
886
1160
|
if (NON_WRITE_TOOL_PREFIXES.some((p) => actionType.startsWith(p))) return null;
|
|
887
1161
|
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
888
1162
|
const isBashLike = actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash";
|
|
889
|
-
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail) :
|
|
1163
|
+
const isWrite = isBashLike ? isWriteOperation(actionType, actionDetail, void 0, osType) : getPlatformTraits(osType).writeKeywords.some((kw) => actionType.includes(kw));
|
|
890
1164
|
if (!isWrite) return null;
|
|
891
1165
|
const WRITE_CMDS = /* @__PURE__ */ new Set(["cp", "mv", "install", "tee", "dd", "truncate", "mkfs", "fdisk", "parted"]);
|
|
892
1166
|
if (isBashLike) {
|
|
@@ -930,7 +1204,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
930
1204
|
if (firstWord === "cd") {
|
|
931
1205
|
continue;
|
|
932
1206
|
}
|
|
933
|
-
if (
|
|
1207
|
+
if (getPlatformTraits(osType).readOnlyCommands.has(firstWord)) {
|
|
934
1208
|
const segUnquoted = trimmed.replace(new RegExp(`>{1,2}\\s*(['"])([~./][^'"]+)\\1`, "g"), (_m, _q, p) => ">" + p);
|
|
935
1209
|
const segNoDevnull = segUnquoted.replace(DEVNULL_REDIRECT_RE, "");
|
|
936
1210
|
const segNoQuotes = segNoDevnull.replace(/'[^']*'|"[^"]*"/g, "");
|
|
@@ -938,7 +1212,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
938
1212
|
if (m) targetPaths.push(m[1] || m[2] || m[3]);
|
|
939
1213
|
continue;
|
|
940
1214
|
}
|
|
941
|
-
const segPaths = extractFilePaths(trimmed);
|
|
1215
|
+
const segPaths = extractFilePaths(trimmed, osType);
|
|
942
1216
|
const segPathsFiltered = firstWord === "wget" || firstWord === "curl" ? segPaths.filter((p) => p !== "/dev/null") : segPaths;
|
|
943
1217
|
if (SOURCE_ARG_CMDS.has(firstWord)) {
|
|
944
1218
|
if (segPathsFiltered.length > 1) targetPaths.push(...segPathsFiltered.slice(1));
|
|
@@ -960,20 +1234,30 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
960
1234
|
for (const action of actionHistory) {
|
|
961
1235
|
const histDetail = action.actionDetail.toLowerCase();
|
|
962
1236
|
const histType = action.actionType.toLowerCase();
|
|
963
|
-
const isRead =
|
|
1237
|
+
const isRead = getPlatformTraits(osType).readKeywords.some((kw) => histType.includes(kw) || histDetail.includes(kw));
|
|
964
1238
|
if (isRead) {
|
|
965
|
-
for (const p of extractFilePaths(histDetail)) {
|
|
1239
|
+
for (const p of extractFilePaths(histDetail, osType)) {
|
|
966
1240
|
historyReadPaths.add(p);
|
|
967
1241
|
}
|
|
968
1242
|
}
|
|
969
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
|
+
}
|
|
970
1254
|
const unreadPaths = [];
|
|
971
|
-
for (const path6 of
|
|
1255
|
+
for (const path6 of filteredPaths) {
|
|
972
1256
|
const hasRead = [...historyReadPaths].some(
|
|
973
1257
|
(rp) => path6 === rp || path6.startsWith(rp) || rp.startsWith(path6)
|
|
974
1258
|
);
|
|
975
1259
|
if (!hasRead) {
|
|
976
|
-
let isCreate = !
|
|
1260
|
+
let isCreate = !fs.existsSync(path6);
|
|
977
1261
|
if (!isCreate) {
|
|
978
1262
|
isCreate = CREATE_KEYWORDS.some((kw) => reason.toLowerCase().includes(kw) || actionDetail.includes(kw));
|
|
979
1263
|
}
|
|
@@ -995,14 +1279,18 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
|
|
|
995
1279
|
}
|
|
996
1280
|
return null;
|
|
997
1281
|
}
|
|
998
|
-
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) {
|
|
999
1289
|
if (actionType !== "bash") return null;
|
|
1000
|
-
const
|
|
1290
|
+
const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
|
|
1291
|
+
const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
|
|
1001
1292
|
if (!isPasswordModify) return null;
|
|
1002
|
-
|
|
1003
|
-
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1004
|
-
);
|
|
1005
|
-
if (hasConsent) return null;
|
|
1293
|
+
if (hasUserConsent(actionHistory)) return null;
|
|
1006
1294
|
return {
|
|
1007
1295
|
allowed: false,
|
|
1008
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",
|
|
@@ -1013,14 +1301,12 @@ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
|
1013
1301
|
violatedLayers: ["L2.1"]
|
|
1014
1302
|
};
|
|
1015
1303
|
}
|
|
1016
|
-
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
1304
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osType) {
|
|
1017
1305
|
if (actionType !== "bash") return null;
|
|
1018
|
-
const
|
|
1306
|
+
const udPatterns = runtimeConfigBridge.current?.effectiveUserDeletionPatterns(osType) ?? USER_DELETION_PATTERNS;
|
|
1307
|
+
const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
|
|
1019
1308
|
if (!isUserDeletion) return null;
|
|
1020
|
-
|
|
1021
|
-
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1022
|
-
);
|
|
1023
|
-
if (hasConsent) return null;
|
|
1309
|
+
if (hasUserConsent(actionHistory)) return null;
|
|
1024
1310
|
return {
|
|
1025
1311
|
allowed: false,
|
|
1026
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",
|
|
@@ -1043,7 +1329,7 @@ function containsPrivilegeEscalation(actionDetail) {
|
|
|
1043
1329
|
}
|
|
1044
1330
|
return false;
|
|
1045
1331
|
}
|
|
1046
|
-
function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
1332
|
+
function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
|
|
1047
1333
|
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
|
|
1048
1334
|
return null;
|
|
1049
1335
|
}
|
|
@@ -1056,14 +1342,14 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1056
1342
|
if (!hasTraversal) {
|
|
1057
1343
|
return null;
|
|
1058
1344
|
}
|
|
1059
|
-
const
|
|
1345
|
+
const READ_KEYWORDS = ["read(", "readline", "readlines", "fread", "fgets", "load(", "getpass", "file_get_contents"];
|
|
1060
1346
|
const WRITE_KEYWORDS2 = ["write(", "writelines", "fwrite", "file_put_contents"];
|
|
1061
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"];
|
|
1062
1348
|
const OPEN_KEYWORDS = ["open(", "fopen("];
|
|
1063
1349
|
const code = extractInlineCode(actionDetail);
|
|
1064
1350
|
if (!code) return null;
|
|
1065
1351
|
const codeLower = code.toLowerCase();
|
|
1066
|
-
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));
|
|
1067
1353
|
if (!hasAnyOp) {
|
|
1068
1354
|
return null;
|
|
1069
1355
|
}
|
|
@@ -1071,15 +1357,16 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1071
1357
|
const hasExec = EXEC_KEYWORDS.some((kw) => codeLower.includes(kw));
|
|
1072
1358
|
const blockAllSensitive = hasWrite || hasExec;
|
|
1073
1359
|
let actualFiles = [];
|
|
1074
|
-
if (cwd
|
|
1360
|
+
if (cwd) {
|
|
1075
1361
|
try {
|
|
1076
|
-
actualFiles =
|
|
1362
|
+
actualFiles = fs.readdirSync(cwd);
|
|
1077
1363
|
} catch {
|
|
1078
1364
|
}
|
|
1079
1365
|
}
|
|
1080
1366
|
if (actualFiles.length > 0) {
|
|
1081
|
-
|
|
1082
|
-
|
|
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;
|
|
1083
1370
|
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1084
1371
|
return {
|
|
1085
1372
|
allowed: false,
|
|
@@ -1094,8 +1381,8 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1094
1381
|
}
|
|
1095
1382
|
}
|
|
1096
1383
|
if (blockAllSensitive) {
|
|
1097
|
-
for (const sp of [...
|
|
1098
|
-
const fname = sp.path.split(
|
|
1384
|
+
for (const sp of [...rootPaths, ...homePaths]) {
|
|
1385
|
+
const fname = sp.path.split(/[/\\]/).pop() || sp.path;
|
|
1099
1386
|
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1100
1387
|
return {
|
|
1101
1388
|
allowed: false,
|
|
@@ -1121,16 +1408,28 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
|
1121
1408
|
violatedLayers: ["L2.1"]
|
|
1122
1409
|
};
|
|
1123
1410
|
}
|
|
1124
|
-
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;
|
|
1125
1412
|
var init_logic_rules = __esm({
|
|
1126
1413
|
"src/engine/logic-rules.ts"() {
|
|
1127
1414
|
"use strict";
|
|
1128
1415
|
init_esm_shims();
|
|
1129
1416
|
init_inline_analyzer();
|
|
1417
|
+
init_runtime_config_bridge();
|
|
1130
1418
|
init_rules_loader();
|
|
1419
|
+
init_platform_traits();
|
|
1131
1420
|
ROOT_SENSITIVE_PATHS = _loadSensitivePathScope("root");
|
|
1132
1421
|
HOME_SENSITIVE_PATHS = _loadSensitivePathScope("home");
|
|
1133
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;
|
|
1134
1433
|
NON_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
1135
1434
|
"ask_user_question",
|
|
1136
1435
|
"glob",
|
|
@@ -1152,111 +1451,7 @@ var init_logic_rules = __esm({
|
|
|
1152
1451
|
]);
|
|
1153
1452
|
NON_WRITE_TOOL_PREFIXES = ["memory-", "skill-manager-", "mailbox-", "app-"];
|
|
1154
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"];
|
|
1155
|
-
WRITE_COMMAND_RES = [
|
|
1156
|
-
/\brm\b/,
|
|
1157
|
-
/\bunlink\b/,
|
|
1158
|
-
/\brmdir\b/,
|
|
1159
|
-
/\bshred\b/,
|
|
1160
|
-
/\bcp\b/,
|
|
1161
|
-
/\bmv\b/,
|
|
1162
|
-
/\binstall\b/,
|
|
1163
|
-
/\btee\b/,
|
|
1164
|
-
/\bdd\b/,
|
|
1165
|
-
/\bmkfs\b/,
|
|
1166
|
-
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
1167
|
-
/\bchmod\b/,
|
|
1168
|
-
/\bchown\b/,
|
|
1169
|
-
/\btruncate\b/
|
|
1170
|
-
];
|
|
1171
|
-
READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
1172
|
-
"cat",
|
|
1173
|
-
"head",
|
|
1174
|
-
"tail",
|
|
1175
|
-
"less",
|
|
1176
|
-
"more",
|
|
1177
|
-
"grep",
|
|
1178
|
-
"find",
|
|
1179
|
-
"awk",
|
|
1180
|
-
"sed",
|
|
1181
|
-
"sort",
|
|
1182
|
-
"uniq",
|
|
1183
|
-
"wc",
|
|
1184
|
-
"cut",
|
|
1185
|
-
"strings",
|
|
1186
|
-
"od",
|
|
1187
|
-
"xxd",
|
|
1188
|
-
"hexdump",
|
|
1189
|
-
"tr",
|
|
1190
|
-
"file",
|
|
1191
|
-
"stat",
|
|
1192
|
-
"du",
|
|
1193
|
-
"df",
|
|
1194
|
-
"ls",
|
|
1195
|
-
"dir",
|
|
1196
|
-
"tree",
|
|
1197
|
-
"nl",
|
|
1198
|
-
"tac",
|
|
1199
|
-
"rev",
|
|
1200
|
-
// NEW-FP-I: echo/printf 是文本输出命令,参数是字面量非写目标。加入此集让
|
|
1201
|
-
// read_before_write 的 echo 段走只读命令分支(只提取重定向目标,不提取参数路径),
|
|
1202
|
-
// 避免 echo "cat /etc/shadow" > /tmp/x.sh 的引号内 /etc/shadow 被当写目标误报。
|
|
1203
|
-
// echo "x" > /tmp/x.sh 的重定向目标 /tmp/x.sh 仍被提取(真实写不漏报)。
|
|
1204
|
-
"echo",
|
|
1205
|
-
"printf",
|
|
1206
|
-
"lsblk",
|
|
1207
|
-
"blockdev",
|
|
1208
|
-
"smartctl",
|
|
1209
|
-
"udevadm",
|
|
1210
|
-
"dmidecode",
|
|
1211
|
-
"lscpu",
|
|
1212
|
-
"lspci",
|
|
1213
|
-
"lsusb",
|
|
1214
|
-
"lsmem",
|
|
1215
|
-
"lsns",
|
|
1216
|
-
"lsof",
|
|
1217
|
-
"hwinfo",
|
|
1218
|
-
"inxi",
|
|
1219
|
-
"fdisk",
|
|
1220
|
-
"parted",
|
|
1221
|
-
"dmesg",
|
|
1222
|
-
"journalctl",
|
|
1223
|
-
"systemctl",
|
|
1224
|
-
"hostnamectl",
|
|
1225
|
-
"localectl",
|
|
1226
|
-
"timedatectl",
|
|
1227
|
-
"ps",
|
|
1228
|
-
"top",
|
|
1229
|
-
"free",
|
|
1230
|
-
"vmstat",
|
|
1231
|
-
"iostat",
|
|
1232
|
-
"mpstat",
|
|
1233
|
-
"pidof",
|
|
1234
|
-
"pgrep",
|
|
1235
|
-
"ip",
|
|
1236
|
-
"ifconfig",
|
|
1237
|
-
"route",
|
|
1238
|
-
"ss",
|
|
1239
|
-
"netstat",
|
|
1240
|
-
"arp",
|
|
1241
|
-
"ethtool",
|
|
1242
|
-
"nmcli",
|
|
1243
|
-
"uname",
|
|
1244
|
-
"arch",
|
|
1245
|
-
"nproc",
|
|
1246
|
-
"getconf",
|
|
1247
|
-
"getent",
|
|
1248
|
-
"id",
|
|
1249
|
-
"whoami",
|
|
1250
|
-
"who",
|
|
1251
|
-
"w",
|
|
1252
|
-
"last",
|
|
1253
|
-
"uptime",
|
|
1254
|
-
"lsmod",
|
|
1255
|
-
"modinfo",
|
|
1256
|
-
"sysctl"
|
|
1257
|
-
]);
|
|
1258
1454
|
DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
|
|
1259
|
-
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"];
|
|
1260
1455
|
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1261
1456
|
PASSWORD_MODIFY_PATTERNS = (() => {
|
|
1262
1457
|
const pats = loadL2().password_modify_consent || [];
|
|
@@ -1271,7 +1466,8 @@ var init_logic_rules = __esm({
|
|
|
1271
1466
|
return pats.map((p) => ({
|
|
1272
1467
|
intentKeywords: p.intent_keywords,
|
|
1273
1468
|
dangerousActions: p.dangerous_actions,
|
|
1274
|
-
reason: p.reason
|
|
1469
|
+
reason: p.reason,
|
|
1470
|
+
os: String(p.os || "linux").toLowerCase()
|
|
1275
1471
|
}));
|
|
1276
1472
|
})();
|
|
1277
1473
|
COMBO_STAGES = (() => {
|
|
@@ -1308,34 +1504,90 @@ var init_logic_rules = __esm({
|
|
|
1308
1504
|
}
|
|
1309
1505
|
});
|
|
1310
1506
|
|
|
1507
|
+
// src/engine/asset-dir.ts
|
|
1508
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1509
|
+
import { resolve as resolve2 } from "path";
|
|
1510
|
+
function assetDirCandidates(moduleDir, name) {
|
|
1511
|
+
return [
|
|
1512
|
+
resolve2(moduleDir, name),
|
|
1513
|
+
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
1514
|
+
];
|
|
1515
|
+
}
|
|
1516
|
+
function resolveAssetDir(moduleDir, name) {
|
|
1517
|
+
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
1518
|
+
if (existsSync3(dir)) return dir;
|
|
1519
|
+
}
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
var init_asset_dir = __esm({
|
|
1523
|
+
"src/engine/asset-dir.ts"() {
|
|
1524
|
+
"use strict";
|
|
1525
|
+
init_esm_shims();
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1311
1529
|
// src/engine/skill-loader.ts
|
|
1312
|
-
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as
|
|
1313
|
-
import { resolve as
|
|
1314
|
-
import { fileURLToPath as
|
|
1530
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, existsSync as existsSync4 } from "fs";
|
|
1531
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1532
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1315
1533
|
function loadAllSkills() {
|
|
1316
1534
|
if (cachedSkills) return cachedSkills;
|
|
1317
1535
|
cachedSkills = {};
|
|
1318
|
-
const dir =
|
|
1319
|
-
if (!
|
|
1320
|
-
|
|
1536
|
+
const dir = resolveAssetDir(__dirname3, "skills");
|
|
1537
|
+
if (!dir) {
|
|
1538
|
+
const [primary, fallback] = assetDirCandidates(__dirname3, "skills");
|
|
1539
|
+
console.warn("[AgentMoss] Skills directory not found:", primary, "and fallback:", fallback);
|
|
1321
1540
|
return cachedSkills;
|
|
1322
1541
|
}
|
|
1323
1542
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1324
1543
|
for (const f of files) {
|
|
1325
1544
|
const name = f.replace(".md", "");
|
|
1326
|
-
cachedSkills[name] = readFileSync3(
|
|
1545
|
+
cachedSkills[name] = readFileSync3(resolve3(dir, f), "utf-8");
|
|
1327
1546
|
}
|
|
1328
1547
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1329
1548
|
return cachedSkills;
|
|
1330
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
|
+
}
|
|
1331
1580
|
function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
1332
1581
|
const skills = loadAllSkills();
|
|
1333
1582
|
const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
|
|
1334
1583
|
const actionDetailLower = actionDetail.toLowerCase();
|
|
1335
1584
|
const actionTypeLower = actionType.toLowerCase();
|
|
1336
1585
|
const reasonLower = reason.toLowerCase();
|
|
1586
|
+
const disabledSkillIds = runtimeConfigBridge.current?.disabledSkillIds() ?? /* @__PURE__ */ new Set();
|
|
1587
|
+
const skillMap = { ...SKILL_KEYWORD_MAP, ...runtimeCustomSkillKeywords() };
|
|
1337
1588
|
const scores = {};
|
|
1338
|
-
for (const [skillName, keywords] of Object.entries(
|
|
1589
|
+
for (const [skillName, keywords] of Object.entries(skillMap)) {
|
|
1590
|
+
if (disabledSkillIds.has(skillName)) continue;
|
|
1339
1591
|
let score = 0;
|
|
1340
1592
|
for (const kw of keywords) {
|
|
1341
1593
|
if (combined.includes(kw)) {
|
|
@@ -1352,7 +1604,7 @@ function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
|
1352
1604
|
const maxScore = top[0]?.[1] || 1;
|
|
1353
1605
|
const results = [];
|
|
1354
1606
|
for (const [name, score] of top) {
|
|
1355
|
-
const content = skills[name];
|
|
1607
|
+
const content = skills[name] || loadUserSkill(name);
|
|
1356
1608
|
if (content) {
|
|
1357
1609
|
results.push({
|
|
1358
1610
|
skillName: name,
|
|
@@ -1377,16 +1629,17 @@ function formatSkillsForPrompt(skills) {
|
|
|
1377
1629
|
${s.skillContent}`
|
|
1378
1630
|
).join("\n\n---\n\n");
|
|
1379
1631
|
}
|
|
1380
|
-
var __filename3, __dirname3,
|
|
1632
|
+
var __filename3, __dirname3, SKILL_KEYWORD_MAP, cachedSkills, userSkillCache;
|
|
1381
1633
|
var init_skill_loader = __esm({
|
|
1382
1634
|
"src/engine/skill-loader.ts"() {
|
|
1383
1635
|
"use strict";
|
|
1384
1636
|
init_esm_shims();
|
|
1385
1637
|
init_rules_loader();
|
|
1386
|
-
|
|
1638
|
+
init_paths();
|
|
1639
|
+
init_asset_dir();
|
|
1640
|
+
init_runtime_config_bridge();
|
|
1641
|
+
__filename3 = fileURLToPath3(import.meta.url);
|
|
1387
1642
|
__dirname3 = dirname2(__filename3);
|
|
1388
|
-
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1389
|
-
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1390
1643
|
SKILL_KEYWORD_MAP = (() => {
|
|
1391
1644
|
const map = {};
|
|
1392
1645
|
const cats = loadL3();
|
|
@@ -1399,6 +1652,7 @@ var init_skill_loader = __esm({
|
|
|
1399
1652
|
return map;
|
|
1400
1653
|
})();
|
|
1401
1654
|
cachedSkills = null;
|
|
1655
|
+
userSkillCache = {};
|
|
1402
1656
|
}
|
|
1403
1657
|
});
|
|
1404
1658
|
|
|
@@ -1415,6 +1669,12 @@ __export(runtime_config_exports, {
|
|
|
1415
1669
|
deleteCustomRule: () => deleteCustomRule,
|
|
1416
1670
|
deleteCustomSkill: () => deleteCustomSkill,
|
|
1417
1671
|
deleteScenarioRule: () => deleteScenarioRule,
|
|
1672
|
+
effectiveDangerousPatterns: () => effectiveDangerousPatterns,
|
|
1673
|
+
effectiveInjectionKeywords: () => effectiveInjectionKeywords,
|
|
1674
|
+
effectiveIntentPatterns: () => effectiveIntentPatterns,
|
|
1675
|
+
effectivePasswordPatterns: () => effectivePasswordPatterns,
|
|
1676
|
+
effectiveSensitivePaths: () => effectiveSensitivePaths,
|
|
1677
|
+
effectiveUserDeletionPatterns: () => effectiveUserDeletionPatterns,
|
|
1418
1678
|
filterEnabledRules: () => filterEnabledRules,
|
|
1419
1679
|
generateSourceDefaults: () => generateSourceDefaults,
|
|
1420
1680
|
getAgentRules: () => getAgentRules,
|
|
@@ -1458,9 +1718,9 @@ __export(runtime_config_exports, {
|
|
|
1458
1718
|
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1459
1719
|
updateSkillEnabled: () => updateSkillEnabled
|
|
1460
1720
|
});
|
|
1461
|
-
import { readFileSync as readFileSync4, writeFileSync, existsSync as
|
|
1721
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync5, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
|
|
1462
1722
|
import { join as join3 } from "path";
|
|
1463
|
-
import { fileURLToPath as
|
|
1723
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1464
1724
|
import { dirname as dirname3 } from "path";
|
|
1465
1725
|
function runtimeDir() {
|
|
1466
1726
|
return getRuntimeConfigDir();
|
|
@@ -1473,7 +1733,7 @@ function getRuntimeConfigPath() {
|
|
|
1473
1733
|
function saveRuntimeConfig(config) {
|
|
1474
1734
|
const p = getRuntimeConfigPath();
|
|
1475
1735
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1476
|
-
if (dir && !
|
|
1736
|
+
if (dir && !existsSync5(dir)) mkdirSync(dir, { recursive: true });
|
|
1477
1737
|
writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1478
1738
|
invalidateRuntimeConfigCache();
|
|
1479
1739
|
}
|
|
@@ -1491,10 +1751,11 @@ function emptyRuntimeConfig() {
|
|
|
1491
1751
|
}
|
|
1492
1752
|
function invalidateRuntimeConfigCache() {
|
|
1493
1753
|
runtimeConfigCache = null;
|
|
1754
|
+
hasRuntimeConfigCache.clear();
|
|
1494
1755
|
}
|
|
1495
1756
|
function loadRuntimeConfig() {
|
|
1496
1757
|
const p = getRuntimeConfigPath();
|
|
1497
|
-
if (!
|
|
1758
|
+
if (!existsSync5(p)) {
|
|
1498
1759
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1499
1760
|
try {
|
|
1500
1761
|
saveRuntimeConfig(seeded);
|
|
@@ -1515,7 +1776,13 @@ function loadRuntimeConfig() {
|
|
|
1515
1776
|
layers: {
|
|
1516
1777
|
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
1517
1778
|
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
1518
|
-
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
|
|
1519
1786
|
},
|
|
1520
1787
|
L1_rules: merged.L1_rules || {},
|
|
1521
1788
|
L2_rules: merged.L2_rules || {},
|
|
@@ -1557,21 +1824,48 @@ function filterEnabledRules(rules, categoryEnabled = true) {
|
|
|
1557
1824
|
if (!categoryEnabled) return [];
|
|
1558
1825
|
return rules.filter((r) => r.enabled !== false);
|
|
1559
1826
|
}
|
|
1560
|
-
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) {
|
|
1561
1849
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
1562
|
-
|
|
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) => ({
|
|
1563
1854
|
pattern: r.pattern || "",
|
|
1564
1855
|
risk_level: r.risk_level || "",
|
|
1565
1856
|
risk_type: r.risk_type || "",
|
|
1566
1857
|
reason: r.reason || ""
|
|
1567
1858
|
}));
|
|
1568
1859
|
}
|
|
1569
|
-
function getEnabledL2SensitivePaths(runtime) {
|
|
1860
|
+
function getEnabledL2SensitivePaths(runtime, osType) {
|
|
1570
1861
|
let cat = runtime.L1_rules?.sensitive_path_access;
|
|
1571
1862
|
if (!cat || !cat.rules || cat.rules.length === 0) {
|
|
1572
1863
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
1573
1864
|
}
|
|
1574
|
-
|
|
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) => {
|
|
1575
1869
|
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1576
1870
|
if (r.credential) out.credential = true;
|
|
1577
1871
|
if (r.read_only) out.read_only = true;
|
|
@@ -1581,13 +1875,24 @@ function getEnabledL2SensitivePaths(runtime) {
|
|
|
1581
1875
|
}
|
|
1582
1876
|
function hasRuntimeConfig() {
|
|
1583
1877
|
const p = getRuntimeConfigPath();
|
|
1584
|
-
|
|
1878
|
+
let key;
|
|
1585
1879
|
try {
|
|
1586
|
-
|
|
1587
|
-
|
|
1880
|
+
const st = statSync(p);
|
|
1881
|
+
key = `${p}|${st.mtimeMs}|${st.size}`;
|
|
1588
1882
|
} catch {
|
|
1589
1883
|
return false;
|
|
1590
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;
|
|
1591
1896
|
}
|
|
1592
1897
|
function getDisabledL2RulesWithSkipL3() {
|
|
1593
1898
|
let runtime;
|
|
@@ -1652,15 +1957,28 @@ function _syncDenyModeFields(rule) {
|
|
|
1652
1957
|
}
|
|
1653
1958
|
}
|
|
1654
1959
|
function _l1CommandPatternsToRules() {
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
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;
|
|
1664
1982
|
}
|
|
1665
1983
|
function _l1InjectionKeywordsToRules() {
|
|
1666
1984
|
return INJECTION_KEYWORDS.map((kw) => ({
|
|
@@ -1673,28 +1991,42 @@ function _l1InjectionKeywordsToRules() {
|
|
|
1673
1991
|
}));
|
|
1674
1992
|
}
|
|
1675
1993
|
function _l2SensitivePathsToRules() {
|
|
1676
|
-
const
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
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;
|
|
1698
2030
|
}
|
|
1699
2031
|
function _l2IntentPatternsToRules() {
|
|
1700
2032
|
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
@@ -1771,8 +2103,8 @@ function _l3SkillsToRules() {
|
|
|
1771
2103
|
}
|
|
1772
2104
|
function _scenarioRulesToDefaults() {
|
|
1773
2105
|
const out = {};
|
|
1774
|
-
const scenDir =
|
|
1775
|
-
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;
|
|
1776
2108
|
let entries;
|
|
1777
2109
|
try {
|
|
1778
2110
|
entries = readdirSync3(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1781,7 +2113,7 @@ function _scenarioRulesToDefaults() {
|
|
|
1781
2113
|
}
|
|
1782
2114
|
for (const scenarioId of entries) {
|
|
1783
2115
|
const rulesFile = join3(scenDir, scenarioId, "rules.json");
|
|
1784
|
-
if (!
|
|
2116
|
+
if (!existsSync5(rulesFile)) continue;
|
|
1785
2117
|
let data;
|
|
1786
2118
|
try {
|
|
1787
2119
|
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
@@ -1820,8 +2152,8 @@ function _scenarioRulesToDefaults() {
|
|
|
1820
2152
|
}
|
|
1821
2153
|
function _agentRulesToDefaults() {
|
|
1822
2154
|
const out = {};
|
|
1823
|
-
const agentsDir =
|
|
1824
|
-
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;
|
|
1825
2157
|
let entries;
|
|
1826
2158
|
try {
|
|
1827
2159
|
entries = readdirSync3(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -1830,7 +2162,7 @@ function _agentRulesToDefaults() {
|
|
|
1830
2162
|
}
|
|
1831
2163
|
for (const agentId of entries) {
|
|
1832
2164
|
const rulesFile = join3(agentsDir, agentId, "rules.json");
|
|
1833
|
-
if (!
|
|
2165
|
+
if (!existsSync5(rulesFile)) continue;
|
|
1834
2166
|
let data;
|
|
1835
2167
|
try {
|
|
1836
2168
|
data = JSON.parse(readFileSync4(rulesFile, "utf-8"));
|
|
@@ -1926,7 +2258,7 @@ function _ltlRulesToRuntime() {
|
|
|
1926
2258
|
let mc = {};
|
|
1927
2259
|
for (const dir of cands) {
|
|
1928
2260
|
const p = join3(dir, "mc_rules.json");
|
|
1929
|
-
if (
|
|
2261
|
+
if (existsSync5(p)) {
|
|
1930
2262
|
try {
|
|
1931
2263
|
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
1932
2264
|
} catch {
|
|
@@ -1955,17 +2287,27 @@ function normalizePatternForFingerprint(pattern) {
|
|
|
1955
2287
|
return pattern.replace(/\\\//g, "/");
|
|
1956
2288
|
}
|
|
1957
2289
|
function _ruleFingerprint(rule) {
|
|
2290
|
+
const osTag = String(rule.os || "linux").toLowerCase();
|
|
1958
2291
|
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1959
2292
|
return JSON.stringify([
|
|
2293
|
+
osTag,
|
|
1960
2294
|
[...rule.intent_keywords || []].sort(),
|
|
1961
2295
|
[...rule.dangerous_actions || []].sort()
|
|
1962
2296
|
]);
|
|
1963
2297
|
}
|
|
1964
|
-
if (rule.path) return JSON.stringify([normalizePatternForFingerprint(rule.path)]);
|
|
1965
|
-
if (rule.pattern) return JSON.stringify([normalizePatternForFingerprint(rule.pattern)]);
|
|
1966
|
-
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1967
|
-
if (rule.value) return JSON.stringify([rule.value]);
|
|
1968
|
-
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
|
+
}
|
|
1969
2311
|
}
|
|
1970
2312
|
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
1971
2313
|
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
@@ -1988,13 +2330,14 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
1988
2330
|
srcFps.add(fp);
|
|
1989
2331
|
if (userByFp.has(fp)) {
|
|
1990
2332
|
const existing = userByFp.get(fp);
|
|
1991
|
-
const
|
|
2333
|
+
const userDisabled = existing.user_disabled === true;
|
|
1992
2334
|
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
1993
2335
|
const userDenyMode = existing.deny_mode;
|
|
1994
2336
|
const userSourceDeny = existing.source_deny_mode;
|
|
1995
2337
|
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1996
2338
|
Object.assign(existing, srcR);
|
|
1997
|
-
existing.enabled =
|
|
2339
|
+
existing.enabled = !userDisabled;
|
|
2340
|
+
if (userDisabled) existing.user_disabled = true;
|
|
1998
2341
|
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
1999
2342
|
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
2000
2343
|
existing.deny_mode = userDenyMode;
|
|
@@ -2020,7 +2363,19 @@ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
|
2020
2363
|
}
|
|
2021
2364
|
for (const catName of Object.keys(userLayer)) {
|
|
2022
2365
|
if (catName === "user_custom") continue;
|
|
2023
|
-
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
|
+
}
|
|
2024
2379
|
}
|
|
2025
2380
|
}
|
|
2026
2381
|
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
@@ -2068,10 +2423,11 @@ function _mergeAgentRules(userCfg, srcDefaults, partition = "agent_rules") {
|
|
|
2068
2423
|
srcFps.add(fp);
|
|
2069
2424
|
if (userByFp.has(fp)) {
|
|
2070
2425
|
const existing = userByFp.get(fp);
|
|
2071
|
-
const
|
|
2426
|
+
const userDisabled = existing.user_disabled === true;
|
|
2072
2427
|
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
2073
2428
|
Object.assign(existing, srcR);
|
|
2074
|
-
existing.enabled =
|
|
2429
|
+
existing.enabled = !userDisabled;
|
|
2430
|
+
if (userDisabled) existing.user_disabled = true;
|
|
2075
2431
|
} else {
|
|
2076
2432
|
userRules.push({ ...srcR, enabled: true });
|
|
2077
2433
|
}
|
|
@@ -2135,17 +2491,23 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2135
2491
|
delete src[catName];
|
|
2136
2492
|
}
|
|
2137
2493
|
}
|
|
2138
|
-
function getEnabledL1InjectionKeywords(runtime) {
|
|
2494
|
+
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2139
2495
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2140
|
-
|
|
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) => ({
|
|
2141
2500
|
keyword: r.keyword || "",
|
|
2142
2501
|
risk_level: r.risk_level || "",
|
|
2143
2502
|
lang: r.lang || "en"
|
|
2144
2503
|
}));
|
|
2145
2504
|
}
|
|
2146
|
-
function getEnabledL2IntentPatterns(runtime) {
|
|
2505
|
+
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2147
2506
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2148
|
-
|
|
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) => ({
|
|
2149
2511
|
intent_keywords: r.intent_keywords || [],
|
|
2150
2512
|
dangerous_actions: r.dangerous_actions || [],
|
|
2151
2513
|
reason: r.reason || ""
|
|
@@ -2153,11 +2515,13 @@ function getEnabledL2IntentPatterns(runtime) {
|
|
|
2153
2515
|
}
|
|
2154
2516
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2155
2517
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2156
|
-
|
|
2518
|
+
const rules = _mergeUserCustom(filterEnabledRules(cat.rules || [], cat.category_enabled), runtime, "L2_rules", "pattern");
|
|
2519
|
+
return rules.map((r) => r.pattern || "");
|
|
2157
2520
|
}
|
|
2158
2521
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2159
2522
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2160
|
-
|
|
2523
|
+
const rules = _mergeUserCustom(filterEnabledRules(cat.rules || [], cat.category_enabled), runtime, "L2_rules", "pattern");
|
|
2524
|
+
return rules.map((r) => r.pattern || "");
|
|
2161
2525
|
}
|
|
2162
2526
|
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
2163
2527
|
const cat = runtime.L2_rules?.[categoryKey];
|
|
@@ -2176,45 +2540,126 @@ function getDisabledSkillIds(runtime) {
|
|
|
2176
2540
|
}
|
|
2177
2541
|
return disabled;
|
|
2178
2542
|
}
|
|
2179
|
-
function
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
if (
|
|
2189
|
-
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
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;
|
|
2218
2663
|
}
|
|
2219
2664
|
function _validateSkillId(skillId) {
|
|
2220
2665
|
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
@@ -2231,7 +2676,7 @@ function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
|
2231
2676
|
const runtime = loadRuntimeConfig();
|
|
2232
2677
|
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2233
2678
|
if (r.id === ruleId) {
|
|
2234
|
-
r
|
|
2679
|
+
_applyRuleEnabled(r, enabled);
|
|
2235
2680
|
break;
|
|
2236
2681
|
}
|
|
2237
2682
|
}
|
|
@@ -2301,7 +2746,7 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2301
2746
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2302
2747
|
_validateSkillId(skillId);
|
|
2303
2748
|
const runtime = loadRuntimeConfig();
|
|
2304
|
-
const skillsDir = join3(
|
|
2749
|
+
const skillsDir = join3(getRuntimeConfigDir(), "agent_moss_skills");
|
|
2305
2750
|
try {
|
|
2306
2751
|
mkdirSync(skillsDir, { recursive: true });
|
|
2307
2752
|
} catch {
|
|
@@ -2321,9 +2766,9 @@ function deleteCustomSkill(skillId, category) {
|
|
|
2321
2766
|
if (idx >= 0) {
|
|
2322
2767
|
if (skills[idx].builtin) return null;
|
|
2323
2768
|
skills.splice(idx, 1);
|
|
2324
|
-
const skillPath = join3(
|
|
2769
|
+
const skillPath = join3(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
|
|
2325
2770
|
try {
|
|
2326
|
-
if (
|
|
2771
|
+
if (existsSync5(skillPath)) unlinkSync(skillPath);
|
|
2327
2772
|
} catch {
|
|
2328
2773
|
}
|
|
2329
2774
|
saveRuntimeConfig(runtime);
|
|
@@ -2402,7 +2847,7 @@ function updateAgentRuleEnabled(agentId, category, ruleFp, enabled) {
|
|
|
2402
2847
|
const targetFp = _ruleFingerprint(ruleFp);
|
|
2403
2848
|
for (const r of cat.rules || []) {
|
|
2404
2849
|
if (_ruleFingerprint(r) === targetFp) {
|
|
2405
|
-
r
|
|
2850
|
+
_applyRuleEnabled(r, enabled);
|
|
2406
2851
|
break;
|
|
2407
2852
|
}
|
|
2408
2853
|
}
|
|
@@ -2430,137 +2875,419 @@ function deleteAgentRule(agentId, category, ruleFp) {
|
|
|
2430
2875
|
saveRuntimeConfig(runtime);
|
|
2431
2876
|
return runtime;
|
|
2432
2877
|
}
|
|
2433
|
-
function updateAgentCategoryEnabled(agentId, category, enabled) {
|
|
2434
|
-
const runtime = loadRuntimeConfig();
|
|
2435
|
-
const cat = runtime.agent_rules?.[agentId]?.[category];
|
|
2436
|
-
if (cat) cat.category_enabled = enabled;
|
|
2437
|
-
saveRuntimeConfig(runtime);
|
|
2438
|
-
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";
|
|
2439
3207
|
}
|
|
2440
|
-
function
|
|
2441
|
-
return
|
|
3208
|
+
function getProviderHeaders(provider) {
|
|
3209
|
+
return PROVIDER_HEADERS[provider] || {};
|
|
2442
3210
|
}
|
|
2443
|
-
function
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
const cmdPatterns = _agentCategoryEnabledRules(scenData.extra_command_patterns).filter((r) => r.pattern).map((r) => ({
|
|
2447
|
-
pattern: r.pattern,
|
|
2448
|
-
risk_level: r.risk_level || "",
|
|
2449
|
-
risk_type: r.risk_type || "",
|
|
2450
|
-
reason: r.reason || ""
|
|
2451
|
-
}));
|
|
2452
|
-
const injKeywords = _agentCategoryEnabledRules(scenData.extra_injection_keywords).filter((r) => r.keyword).map((r) => ({
|
|
2453
|
-
keyword: r.keyword,
|
|
2454
|
-
risk_level: r.risk_level || "",
|
|
2455
|
-
lang: r.lang || "en"
|
|
2456
|
-
}));
|
|
2457
|
-
const sensitivePaths = _agentCategoryEnabledRules(scenData.extra_sensitive_paths).filter((r) => r.path).map((r) => ({
|
|
2458
|
-
path: r.path,
|
|
2459
|
-
risk_level: r.risk_level || "",
|
|
2460
|
-
desc: r.desc || "",
|
|
2461
|
-
credential: r.credential,
|
|
2462
|
-
read_only: r.read_only,
|
|
2463
|
-
deny_mode: r.deny_mode
|
|
2464
|
-
}));
|
|
2465
|
-
return {
|
|
2466
|
-
agent_id: scenarioId,
|
|
2467
|
-
description: "",
|
|
2468
|
-
extra_command_patterns: cmdPatterns,
|
|
2469
|
-
extra_injection_keywords: injKeywords,
|
|
2470
|
-
extra_sensitive_paths: sensitivePaths,
|
|
2471
|
-
builtin_safe_skills: [],
|
|
2472
|
-
builtin_safe_action_types: [],
|
|
2473
|
-
extra_skills: []
|
|
2474
|
-
};
|
|
3211
|
+
function isHeuristicEnabled() {
|
|
3212
|
+
if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
|
|
3213
|
+
return layerEnabledOrDefault("L1_heuristic");
|
|
2475
3214
|
}
|
|
2476
|
-
function
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
for (const r of cat.rules || []) {
|
|
2482
|
-
if (_ruleFingerprint(r) === targetFp) {
|
|
2483
|
-
r.enabled = enabled;
|
|
2484
|
-
break;
|
|
2485
|
-
}
|
|
2486
|
-
}
|
|
3215
|
+
function layerEnabledOrDefault(key) {
|
|
3216
|
+
try {
|
|
3217
|
+
return isLayerEnabled(key);
|
|
3218
|
+
} catch {
|
|
3219
|
+
return true;
|
|
2487
3220
|
}
|
|
2488
|
-
saveRuntimeConfig(runtime);
|
|
2489
|
-
return runtime;
|
|
2490
3221
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
if (!runtime.scenario_rules) runtime.scenario_rules = {};
|
|
2494
|
-
if (!runtime.scenario_rules[scenarioId]) runtime.scenario_rules[scenarioId] = {};
|
|
2495
|
-
const cats = runtime.scenario_rules[scenarioId];
|
|
2496
|
-
if (!cats[category]) cats[category] = { category_enabled: true, category_desc: "", rules: [] };
|
|
2497
|
-
(cats[category].rules || (cats[category].rules = [])).push({ ...rule, enabled: true, builtin: false });
|
|
2498
|
-
saveRuntimeConfig(runtime);
|
|
2499
|
-
return runtime;
|
|
3222
|
+
function isBrainEnabled() {
|
|
3223
|
+
return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
|
|
2500
3224
|
}
|
|
2501
|
-
function
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
if (cat) {
|
|
2505
|
-
const targetFp = _ruleFingerprint(ruleFp);
|
|
2506
|
-
cat.rules = (cat.rules || []).filter((r) => _ruleFingerprint(r) !== targetFp);
|
|
2507
|
-
}
|
|
2508
|
-
saveRuntimeConfig(runtime);
|
|
2509
|
-
return runtime;
|
|
3225
|
+
function isLogicRulesEnabled() {
|
|
3226
|
+
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
3227
|
+
return layerEnabledOrDefault("L2_logic_rules");
|
|
2510
3228
|
}
|
|
2511
|
-
function
|
|
2512
|
-
|
|
2513
|
-
const cat = runtime.scenario_rules?.[scenarioId]?.[category];
|
|
2514
|
-
if (cat) cat.category_enabled = enabled;
|
|
2515
|
-
saveRuntimeConfig(runtime);
|
|
2516
|
-
return runtime;
|
|
3229
|
+
function isFormalObserveMode() {
|
|
3230
|
+
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
2517
3231
|
}
|
|
2518
|
-
var
|
|
2519
|
-
var
|
|
2520
|
-
"src/
|
|
3232
|
+
var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
3233
|
+
var init_config = __esm({
|
|
3234
|
+
"src/config.ts"() {
|
|
2521
3235
|
"use strict";
|
|
2522
3236
|
init_esm_shims();
|
|
2523
3237
|
init_paths();
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
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
|
+
}
|
|
2547
3280
|
};
|
|
2548
|
-
runtimeConfigCache = null;
|
|
2549
|
-
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
2550
|
-
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
2551
|
-
_SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
|
|
2552
|
-
_SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
|
|
2553
|
-
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2554
3281
|
}
|
|
2555
3282
|
});
|
|
2556
3283
|
|
|
2557
3284
|
// src/agents/index.ts
|
|
2558
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
2559
|
-
import { resolve as
|
|
3285
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
3286
|
+
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
2560
3287
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2561
3288
|
function resolveAgentsDir() {
|
|
2562
|
-
if (
|
|
2563
|
-
if (
|
|
3289
|
+
if (existsSync6(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
3290
|
+
if (existsSync6(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
2564
3291
|
return AGENTS_DIR_PROD;
|
|
2565
3292
|
}
|
|
2566
3293
|
function getAgentExtensions(agentId) {
|
|
@@ -2568,16 +3295,15 @@ function getAgentExtensions(agentId) {
|
|
|
2568
3295
|
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
2569
3296
|
let ext = null;
|
|
2570
3297
|
try {
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
ext = getEnabledAgentExtensions2(loadRuntimeConfig2(), agentId);
|
|
3298
|
+
if (hasRuntimeConfig()) {
|
|
3299
|
+
ext = getEnabledAgentExtensions(loadRuntimeConfig(), agentId);
|
|
2574
3300
|
}
|
|
2575
3301
|
} catch {
|
|
2576
3302
|
}
|
|
2577
3303
|
if (ext === null) {
|
|
2578
3304
|
const agentsDir = resolveAgentsDir();
|
|
2579
|
-
const rulesFile =
|
|
2580
|
-
if (!
|
|
3305
|
+
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3306
|
+
if (!existsSync6(rulesFile)) {
|
|
2581
3307
|
cache.set(agentId, null);
|
|
2582
3308
|
return null;
|
|
2583
3309
|
}
|
|
@@ -2596,7 +3322,7 @@ function getAgentExtensions(agentId) {
|
|
|
2596
3322
|
}
|
|
2597
3323
|
}
|
|
2598
3324
|
cache.set(agentId, ext);
|
|
2599
|
-
console.
|
|
3325
|
+
console.error(
|
|
2600
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`
|
|
2601
3327
|
);
|
|
2602
3328
|
return ext;
|
|
@@ -2621,21 +3347,22 @@ var init_agents = __esm({
|
|
|
2621
3347
|
"src/agents/index.ts"() {
|
|
2622
3348
|
"use strict";
|
|
2623
3349
|
init_esm_shims();
|
|
3350
|
+
init_runtime_config();
|
|
2624
3351
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
2625
3352
|
__dirname5 = dirname4(__filename4);
|
|
2626
|
-
AGENTS_DIR_PROD =
|
|
2627
|
-
AGENTS_DIR_DEV_FALLBACK =
|
|
3353
|
+
AGENTS_DIR_PROD = resolve4(__dirname5, "agents");
|
|
3354
|
+
AGENTS_DIR_DEV_FALLBACK = resolve4(__dirname5, "../../../agent_moss/agents");
|
|
2628
3355
|
cache = /* @__PURE__ */ new Map();
|
|
2629
3356
|
}
|
|
2630
3357
|
});
|
|
2631
3358
|
|
|
2632
3359
|
// src/scenarios/index.ts
|
|
2633
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
2634
|
-
import { resolve as
|
|
3360
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
3361
|
+
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
2635
3362
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
2636
3363
|
function resolveScenariosDir() {
|
|
2637
|
-
if (
|
|
2638
|
-
if (
|
|
3364
|
+
if (existsSync7(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
3365
|
+
if (existsSync7(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
|
|
2639
3366
|
return SCENARIOS_DIR_PROD;
|
|
2640
3367
|
}
|
|
2641
3368
|
function getScenarioExtensions(scenarioId) {
|
|
@@ -2643,16 +3370,15 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2643
3370
|
if (cache2.has(scenarioId)) return cache2.get(scenarioId) ?? null;
|
|
2644
3371
|
let ext = null;
|
|
2645
3372
|
try {
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
ext = getEnabledScenarioExtensions2(loadRuntimeConfig2(), scenarioId);
|
|
3373
|
+
if (hasRuntimeConfig()) {
|
|
3374
|
+
ext = getEnabledScenarioExtensions(loadRuntimeConfig(), scenarioId);
|
|
2649
3375
|
}
|
|
2650
3376
|
} catch {
|
|
2651
3377
|
}
|
|
2652
3378
|
if (ext === null) {
|
|
2653
3379
|
const scenariosDir = resolveScenariosDir();
|
|
2654
|
-
const rulesFile =
|
|
2655
|
-
if (!
|
|
3380
|
+
const rulesFile = resolve5(scenariosDir, scenarioId, "rules.json");
|
|
3381
|
+
if (!existsSync7(rulesFile)) {
|
|
2656
3382
|
cache2.set(scenarioId, null);
|
|
2657
3383
|
return null;
|
|
2658
3384
|
}
|
|
@@ -2682,7 +3408,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2682
3408
|
}
|
|
2683
3409
|
}
|
|
2684
3410
|
cache2.set(scenarioId, ext);
|
|
2685
|
-
console.
|
|
3411
|
+
console.error(
|
|
2686
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`
|
|
2687
3413
|
);
|
|
2688
3414
|
return ext;
|
|
@@ -2702,15 +3428,72 @@ var init_scenarios = __esm({
|
|
|
2702
3428
|
"use strict";
|
|
2703
3429
|
init_esm_shims();
|
|
2704
3430
|
init_agents();
|
|
3431
|
+
init_runtime_config();
|
|
2705
3432
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
2706
3433
|
__dirname6 = dirname5(__filename5);
|
|
2707
|
-
SCENARIOS_DIR_PROD =
|
|
2708
|
-
SCENARIOS_DIR_DEV_FALLBACK =
|
|
3434
|
+
SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
|
|
3435
|
+
SCENARIOS_DIR_DEV_FALLBACK = resolve5(__dirname6, "../../../agent_moss/scenarios");
|
|
2709
3436
|
cache2 = /* @__PURE__ */ new Map();
|
|
2710
3437
|
}
|
|
2711
3438
|
});
|
|
2712
3439
|
|
|
2713
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
|
+
}
|
|
2714
3497
|
function runHeuristicCheck(input) {
|
|
2715
3498
|
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
2716
3499
|
const matched = [];
|
|
@@ -2737,11 +3520,23 @@ function runHeuristicCheck(input) {
|
|
|
2737
3520
|
}
|
|
2738
3521
|
}
|
|
2739
3522
|
const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
|
|
2740
|
-
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
|
+
}
|
|
2741
3536
|
let topRiskType = "";
|
|
2742
3537
|
const exemptKeygen = isSshKeygenGenerate(input.aNext.actionDetail);
|
|
2743
|
-
for (const rule of
|
|
2744
|
-
if (rule.pattern.test(
|
|
3538
|
+
for (const rule of _effectiveDangerousPatterns(input.osType)) {
|
|
3539
|
+
if (scanCommands.some((c) => rule.pattern.test(c))) {
|
|
2745
3540
|
if (exemptKeygen && rule.riskType === "file_access") continue;
|
|
2746
3541
|
matched.push(rule.description);
|
|
2747
3542
|
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
@@ -2759,7 +3554,7 @@ function runHeuristicCheck(input) {
|
|
|
2759
3554
|
for (const rule of agentExt.extra_command_patterns) {
|
|
2760
3555
|
try {
|
|
2761
3556
|
const re = new RegExp(rule.pattern, "i");
|
|
2762
|
-
if (re.test(
|
|
3557
|
+
if (scanCommands.some((c) => re.test(c))) {
|
|
2763
3558
|
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
2764
3559
|
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
2765
3560
|
highestSeverity = rule.risk_level;
|
|
@@ -2771,7 +3566,7 @@ function runHeuristicCheck(input) {
|
|
|
2771
3566
|
}
|
|
2772
3567
|
}
|
|
2773
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();
|
|
2774
|
-
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
3569
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords, input.osType);
|
|
2775
3570
|
if (injectionHits.length > 0) {
|
|
2776
3571
|
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
2777
3572
|
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
@@ -2784,7 +3579,7 @@ function runHeuristicCheck(input) {
|
|
|
2784
3579
|
const detailL = input.aNext.actionDetail.toLowerCase();
|
|
2785
3580
|
const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
|
|
2786
3581
|
const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
|
|
2787
|
-
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt);
|
|
3582
|
+
const pathResult = isExcludedType ? null : checkSensitivePath(detailL, input.cwd || "/", agentExt, input.osType);
|
|
2788
3583
|
const homeResult = checkHomeDeletion(actionTypeL, detailL);
|
|
2789
3584
|
const mergeResults = [dangerousResult, pathResult, homeResult].filter(Boolean);
|
|
2790
3585
|
if (mergeResults.length > 0) {
|
|
@@ -2820,9 +3615,10 @@ function runHeuristicCheck(input) {
|
|
|
2820
3615
|
if (lateralResult) return lateralResult;
|
|
2821
3616
|
return null;
|
|
2822
3617
|
}
|
|
2823
|
-
function checkInjectionKeywords(text, extraKeywords) {
|
|
3618
|
+
function checkInjectionKeywords(text, extraKeywords, osType) {
|
|
2824
3619
|
const hits = [];
|
|
2825
|
-
|
|
3620
|
+
const baseKeywords = runtimeConfigBridge.current?.effectiveInjectionKeywords(osType) ?? INJECTION_KEYWORDS;
|
|
3621
|
+
for (const entry of baseKeywords) {
|
|
2826
3622
|
if (text.includes(entry.keyword.toLowerCase())) {
|
|
2827
3623
|
hits.push(entry);
|
|
2828
3624
|
}
|
|
@@ -2882,7 +3678,7 @@ function buildPathContext(actionDetail) {
|
|
|
2882
3678
|
if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
|
|
2883
3679
|
const stripped = stripLiteralTextSegments(actionDetail);
|
|
2884
3680
|
const lines = [];
|
|
2885
|
-
for (const seg of
|
|
3681
|
+
for (const seg of splitCommandSegments2(stripped)) {
|
|
2886
3682
|
for (const [raw, lex] of resolvePathTokens(seg)) {
|
|
2887
3683
|
lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
|
|
2888
3684
|
}
|
|
@@ -2892,12 +3688,12 @@ function buildPathContext(actionDetail) {
|
|
|
2892
3688
|
}
|
|
2893
3689
|
function stripLiteralTextSegments(actionDetail) {
|
|
2894
3690
|
const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
|
|
2895
|
-
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
|
|
3691
|
+
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node|crontab|at)\b/.test(actionDetail);
|
|
2896
3692
|
if (writeScriptExec || pipeToInterpreter) {
|
|
2897
3693
|
return actionDetail;
|
|
2898
3694
|
}
|
|
2899
3695
|
const resultParts = [];
|
|
2900
|
-
for (const seg of
|
|
3696
|
+
for (const seg of splitCommandSegments2(actionDetail)) {
|
|
2901
3697
|
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
2902
3698
|
if (fw === "echo" || fw === "printf") {
|
|
2903
3699
|
const segUnquotedRedirect = seg.replace(
|
|
@@ -2917,7 +3713,7 @@ function stripHeredocContent(actionDetail) {
|
|
|
2917
3713
|
"<<heredoc>>\n"
|
|
2918
3714
|
);
|
|
2919
3715
|
}
|
|
2920
|
-
function normalizePathSegments(text) {
|
|
3716
|
+
function normalizePathSegments(text, foldQuotes = false) {
|
|
2921
3717
|
const protTokens = [];
|
|
2922
3718
|
let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
|
|
2923
3719
|
const scheme = m.slice(0, m.indexOf(":"));
|
|
@@ -2925,10 +3721,12 @@ function normalizePathSegments(text) {
|
|
|
2925
3721
|
return `${scheme}__PROT${protTokens.length - 1}__`;
|
|
2926
3722
|
});
|
|
2927
3723
|
const quoteTokens = [];
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
3724
|
+
if (!foldQuotes) {
|
|
3725
|
+
t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
|
|
3726
|
+
quoteTokens.push(m);
|
|
3727
|
+
return `__Q${quoteTokens.length - 1}__`;
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
2932
3730
|
t = t.replace(/\/{2,}/g, "/");
|
|
2933
3731
|
t = t.replace(/\/\.\//g, "/");
|
|
2934
3732
|
for (let i = 0; i < quoteTokens.length; i++) {
|
|
@@ -2939,7 +3737,12 @@ function normalizePathSegments(text) {
|
|
|
2939
3737
|
}
|
|
2940
3738
|
return t;
|
|
2941
3739
|
}
|
|
2942
|
-
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) {
|
|
2943
3746
|
const segments = [];
|
|
2944
3747
|
let cur = "";
|
|
2945
3748
|
let quote = null;
|
|
@@ -2988,11 +3791,15 @@ function isSearchActionType(actionType) {
|
|
|
2988
3791
|
const at = actionType.toLowerCase();
|
|
2989
3792
|
return SEARCH_ACTION_PREFIXES.some((p) => at.startsWith(p)) || ["websearch", "web_search", "search", "query"].includes(at);
|
|
2990
3793
|
}
|
|
2991
|
-
function checkSensitivePath(command, cwd, agentExt) {
|
|
3794
|
+
function checkSensitivePath(command, cwd, agentExt, osType) {
|
|
2992
3795
|
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
2993
|
-
const
|
|
3796
|
+
const { root: rootPaths, home: homePaths, credential: credentialPaths } = getSensitivePathsForOs(osType);
|
|
3797
|
+
const isWriteOpFlag = isWriteOperation("", command, void 0, osType);
|
|
2994
3798
|
const keygenGenerate = isSshKeygenGenerate(command);
|
|
2995
|
-
const
|
|
3799
|
+
const pathScanTexts = pathScanVariants(command);
|
|
3800
|
+
if (String(osType || "").toLowerCase() === "windows") {
|
|
3801
|
+
pathScanTexts.push(...pathScanTexts.map((t) => t.replace(/\//g, "\\")));
|
|
3802
|
+
}
|
|
2996
3803
|
const pathMatches = (text, path6) => {
|
|
2997
3804
|
const parts = path6.split(/(\(\?!.*?\))/);
|
|
2998
3805
|
const escaped = parts.map((part) => {
|
|
@@ -3002,8 +3809,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3002
3809
|
const re = path6.startsWith(".") || path6.startsWith("/") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`, "i") : new RegExp(`\\b${escaped}\\b`, "i");
|
|
3003
3810
|
return re.test(text);
|
|
3004
3811
|
};
|
|
3005
|
-
|
|
3006
|
-
|
|
3812
|
+
const pathMatchesAny = (path6) => pathScanTexts.some((t) => pathMatches(t, path6));
|
|
3813
|
+
for (const sp of rootPaths) {
|
|
3814
|
+
if (pathMatchesAny(sp.path.toLowerCase())) {
|
|
3007
3815
|
if (keygenGenerate) continue;
|
|
3008
3816
|
if (sp.path === "/dev/random") {
|
|
3009
3817
|
if (isWriteOpFlag) {
|
|
@@ -3019,9 +3827,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3019
3827
|
violatedLayers: ["L1.1"]
|
|
3020
3828
|
};
|
|
3021
3829
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
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;
|
|
3025
3833
|
return {
|
|
3026
3834
|
allowed: false,
|
|
3027
3835
|
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
@@ -3034,17 +3842,19 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3034
3842
|
};
|
|
3035
3843
|
}
|
|
3036
3844
|
}
|
|
3037
|
-
for (const sp of
|
|
3845
|
+
for (const sp of homePaths) {
|
|
3038
3846
|
const expanded = `${expandedCwd}/${sp.path}`;
|
|
3039
3847
|
let pPath = sp.path;
|
|
3040
3848
|
if (!sp.path.endsWith(".pub")) pPath = `${sp.path}(?!\\.pub)`;
|
|
3041
3849
|
let dirPrefixMatch = null;
|
|
3042
3850
|
if (sp.dirDelOnly) {
|
|
3043
3851
|
const dirRe = (t) => new RegExp(`(?:^|[\\s/\\\\'"])${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/|[\\s'"\\\\]|$)`, "i");
|
|
3044
|
-
dirPrefixMatch =
|
|
3852
|
+
dirPrefixMatch = pathScanTexts.some(
|
|
3853
|
+
(t) => dirRe(sp.path).test(t) || dirRe(expanded.toLowerCase()).test(t)
|
|
3854
|
+
);
|
|
3045
3855
|
}
|
|
3046
|
-
const pathReMatch = sp.dirDelOnly ? !!dirPrefixMatch :
|
|
3047
|
-
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)"));
|
|
3048
3858
|
if (pathReMatch || expandedReMatch) {
|
|
3049
3859
|
if (keygenGenerate) continue;
|
|
3050
3860
|
if (sp.dirDelOnly) {
|
|
@@ -3074,9 +3884,9 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3074
3884
|
}
|
|
3075
3885
|
continue;
|
|
3076
3886
|
}
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
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;
|
|
3080
3890
|
return {
|
|
3081
3891
|
allowed: false,
|
|
3082
3892
|
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
@@ -3113,7 +3923,7 @@ function checkSensitivePath(command, cwd, agentExt) {
|
|
|
3113
3923
|
}
|
|
3114
3924
|
}
|
|
3115
3925
|
}
|
|
3116
|
-
for (const cp of
|
|
3926
|
+
for (const cp of credentialPaths) {
|
|
3117
3927
|
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3118
3928
|
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
3119
3929
|
if (re.test(command)) {
|
|
@@ -3233,7 +4043,7 @@ function isDeleteActionForDir(command, dirPath) {
|
|
|
3233
4043
|
}
|
|
3234
4044
|
for (const seg of low.split(/&&|\|\||;|\n/)) {
|
|
3235
4045
|
const fw = (seg.trim().split(/\s+/)[0] || "").toLowerCase();
|
|
3236
|
-
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") &&
|
|
4046
|
+
if ((fw === "rm" || fw === "unlink" || fw === "rmdir") && seg.includes(dpath)) {
|
|
3237
4047
|
return true;
|
|
3238
4048
|
}
|
|
3239
4049
|
}
|
|
@@ -3416,7 +4226,7 @@ function checkLateralMovement(actionDetail) {
|
|
|
3416
4226
|
}
|
|
3417
4227
|
return null;
|
|
3418
4228
|
}
|
|
3419
|
-
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;
|
|
3420
4230
|
var init_heuristic = __esm({
|
|
3421
4231
|
"src/engine/heuristic.ts"() {
|
|
3422
4232
|
"use strict";
|
|
@@ -3425,6 +4235,7 @@ var init_heuristic = __esm({
|
|
|
3425
4235
|
init_agents();
|
|
3426
4236
|
init_scenarios();
|
|
3427
4237
|
init_runtime_config();
|
|
4238
|
+
init_runtime_config_bridge();
|
|
3428
4239
|
init_rules_loader();
|
|
3429
4240
|
init_logic_rules();
|
|
3430
4241
|
INJECTION_KEYWORDS = (() => {
|
|
@@ -3435,15 +4246,10 @@ var init_heuristic = __esm({
|
|
|
3435
4246
|
lang: k.lang || "en"
|
|
3436
4247
|
}));
|
|
3437
4248
|
})();
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
severity: c.risk_level,
|
|
3443
|
-
riskType: c.risk_type,
|
|
3444
|
-
description: c.reason
|
|
3445
|
-
}));
|
|
3446
|
-
})();
|
|
4249
|
+
DANGEROUS_PATTERNS2 = _loadDangerousPatterns("linux");
|
|
4250
|
+
DANGEROUS_PATTERNS_WINDOWS = _loadDangerousPatterns("windows");
|
|
4251
|
+
DANGEROUS_PATTERNS_MACOS = _loadDangerousPatterns("macos");
|
|
4252
|
+
DANGEROUS_PATTERNS_HARMONY = _loadDangerousPatterns("harmony");
|
|
3447
4253
|
PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
|
|
3448
4254
|
HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
|
|
3449
4255
|
SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
@@ -3454,20 +4260,21 @@ var init_heuristic = __esm({
|
|
|
3454
4260
|
});
|
|
3455
4261
|
|
|
3456
4262
|
// src/engine/template-loader.ts
|
|
3457
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
3458
|
-
import { resolve as
|
|
4263
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
4264
|
+
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
3459
4265
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
3460
4266
|
function loadTemplates() {
|
|
3461
4267
|
if (cachedTemplates) return cachedTemplates;
|
|
3462
4268
|
cachedTemplates = {};
|
|
3463
|
-
const dir =
|
|
3464
|
-
if (!
|
|
3465
|
-
|
|
4269
|
+
const dir = resolveAssetDir(__dirname7, "templates");
|
|
4270
|
+
if (!dir) {
|
|
4271
|
+
const [primary, fallback] = assetDirCandidates(__dirname7, "templates");
|
|
4272
|
+
console.warn("[AgentMoss] Templates directory not found:", primary, "and fallback:", fallback);
|
|
3466
4273
|
return cachedTemplates;
|
|
3467
4274
|
}
|
|
3468
4275
|
for (const file of TEMPLATE_FILES) {
|
|
3469
|
-
const filePath =
|
|
3470
|
-
if (
|
|
4276
|
+
const filePath = resolve6(dir, file);
|
|
4277
|
+
if (existsSync8(filePath)) {
|
|
3471
4278
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
3472
4279
|
cachedTemplates[key] = readFileSync7(filePath, "utf-8");
|
|
3473
4280
|
}
|
|
@@ -3475,15 +4282,14 @@ function loadTemplates() {
|
|
|
3475
4282
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
3476
4283
|
return cachedTemplates;
|
|
3477
4284
|
}
|
|
3478
|
-
var __filename6, __dirname7,
|
|
4285
|
+
var __filename6, __dirname7, TEMPLATE_FILES, cachedTemplates;
|
|
3479
4286
|
var init_template_loader = __esm({
|
|
3480
4287
|
"src/engine/template-loader.ts"() {
|
|
3481
4288
|
"use strict";
|
|
3482
4289
|
init_esm_shims();
|
|
4290
|
+
init_asset_dir();
|
|
3483
4291
|
__filename6 = fileURLToPath8(import.meta.url);
|
|
3484
4292
|
__dirname7 = dirname6(__filename6);
|
|
3485
|
-
TEMPLATES_DIR = resolve5(__dirname7, "../templates");
|
|
3486
|
-
TEMPLATES_DIR_FALLBACK = resolve5(__dirname7, "../../../agent_moss/templates");
|
|
3487
4293
|
TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3488
4294
|
cachedTemplates = null;
|
|
3489
4295
|
}
|
|
@@ -3491,7 +4297,7 @@ var init_template_loader = __esm({
|
|
|
3491
4297
|
|
|
3492
4298
|
// src/engine/script-analyzer.ts
|
|
3493
4299
|
import { readFileSync as readFileSync8 } from "fs";
|
|
3494
|
-
import { resolve as
|
|
4300
|
+
import { resolve as resolve7, join as join4 } from "path";
|
|
3495
4301
|
function emptyResult() {
|
|
3496
4302
|
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
3497
4303
|
}
|
|
@@ -3544,7 +4350,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
3544
4350
|
}
|
|
3545
4351
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3546
4352
|
try {
|
|
3547
|
-
const expandedPath = scriptPath.startsWith("
|
|
4353
|
+
const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join4(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
|
|
3548
4354
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
3549
4355
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
3550
4356
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -4494,9 +5300,20 @@ function evalExpr2(text, env) {
|
|
|
4494
5300
|
const result = [];
|
|
4495
5301
|
for (const p of parts) {
|
|
4496
5302
|
if (!p) continue;
|
|
4497
|
-
const brace = p.match(/^\$\{([^}]
|
|
5303
|
+
const brace = p.match(/^\$\{([^}]*)\}$/);
|
|
4498
5304
|
if (brace) {
|
|
4499
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
|
+
}
|
|
4500
5317
|
if (/^\d+$/.test(inner)) {
|
|
4501
5318
|
const v = env.get(`$${inner}`);
|
|
4502
5319
|
result.push(v || svalSym(`$${inner}`));
|
|
@@ -4712,7 +5529,7 @@ var init_symbolic = __esm({
|
|
|
4712
5529
|
LOOP_DONE_RE = /\bdone\b/g;
|
|
4713
5530
|
FN_DEF_RE = /^\s*(?:function\s+)?[A-Za-z_][A-Za-z0-9_]*\s*\(\s*\)\s*\{/;
|
|
4714
5531
|
FN_DEF_KW_RE = /^\s*function\s+[A-Za-z_][A-Za-z0-9_]*\s*\{?/;
|
|
4715
|
-
EXPR_SPLIT_RE = /(\$\{
|
|
5532
|
+
EXPR_SPLIT_RE = /(\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$\d+)/;
|
|
4716
5533
|
HAS_GLOB = /[*?[]/;
|
|
4717
5534
|
}
|
|
4718
5535
|
});
|
|
@@ -4993,6 +5810,14 @@ var init_backends = __esm({
|
|
|
4993
5810
|
import * as fs3 from "fs";
|
|
4994
5811
|
import * as path3 from "path";
|
|
4995
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
|
+
}
|
|
4996
5821
|
function pickBackend(pathStr) {
|
|
4997
5822
|
const lower = pathStr.toLowerCase();
|
|
4998
5823
|
for (const ext of Object.keys(BACKENDS)) {
|
|
@@ -5035,6 +5860,7 @@ function emptyNode(pathStr, depth) {
|
|
|
5035
5860
|
depth,
|
|
5036
5861
|
childPaths: [],
|
|
5037
5862
|
error: "",
|
|
5863
|
+
unanalyzable: false,
|
|
5038
5864
|
l1Hit: false,
|
|
5039
5865
|
l1RiskLevel: "",
|
|
5040
5866
|
l1RiskType: "",
|
|
@@ -5060,7 +5886,7 @@ var init_tracker = __esm({
|
|
|
5060
5886
|
init_backends();
|
|
5061
5887
|
DEFAULT_MAX_DEPTH = 14;
|
|
5062
5888
|
DEFAULT_MAX_NODES = 64;
|
|
5063
|
-
DEFAULT_MAX_FILE_SIZE =
|
|
5889
|
+
DEFAULT_MAX_FILE_SIZE = maxFileSizeFromEnv();
|
|
5064
5890
|
DEFAULT_MAX_TOTAL_SIZE = 8 * 1024 * 1024;
|
|
5065
5891
|
BACKENDS = {
|
|
5066
5892
|
".py": new PythonBackend()
|
|
@@ -5131,6 +5957,7 @@ var init_tracker = __esm({
|
|
|
5131
5957
|
node2.error = error;
|
|
5132
5958
|
node2.fileSize = fileSize;
|
|
5133
5959
|
node2.lineCount = lineCount;
|
|
5960
|
+
node2.unanalyzable = error.startsWith("\u811A\u672C\u6587\u4EF6\u8FC7\u5927");
|
|
5134
5961
|
this.result.nodes.push(node2);
|
|
5135
5962
|
this.visiting.delete(pathStr);
|
|
5136
5963
|
return;
|
|
@@ -5177,9 +6004,9 @@ var init_tracker = __esm({
|
|
|
5177
6004
|
});
|
|
5178
6005
|
|
|
5179
6006
|
// src/engine/script-content-analyzer.ts
|
|
5180
|
-
import { statSync as statSync3, existsSync as
|
|
5181
|
-
import { resolve as
|
|
5182
|
-
import { homedir as
|
|
6007
|
+
import { statSync as statSync3, existsSync as existsSync10 } from "fs";
|
|
6008
|
+
import { resolve as resolve9, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
6009
|
+
import { homedir as homedir3 } from "os";
|
|
5183
6010
|
function emptyChain() {
|
|
5184
6011
|
return {
|
|
5185
6012
|
nodes: [],
|
|
@@ -5285,12 +6112,12 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5285
6112
|
const subShellRe = /subprocess\.(?:run|call|Popen|check_output|check_call)\s*\(\s*(['"])(.*?)\1[^)]*?shell\s*=\s*true/gi;
|
|
5286
6113
|
while ((m = subShellRe.exec(code)) !== null) candidates.push(m[2]);
|
|
5287
6114
|
for (const cand of candidates) {
|
|
5288
|
-
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);
|
|
5289
6116
|
for (const tok of tokens) {
|
|
5290
6117
|
if (looksLikeScriptPath(tok)) {
|
|
5291
6118
|
try {
|
|
5292
6119
|
const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
|
|
5293
|
-
if (
|
|
6120
|
+
if (existsSync10(resolved)) {
|
|
5294
6121
|
const stat = statSync3(resolved);
|
|
5295
6122
|
if (stat.isFile()) return resolved;
|
|
5296
6123
|
}
|
|
@@ -5304,7 +6131,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5304
6131
|
const p = m[1];
|
|
5305
6132
|
try {
|
|
5306
6133
|
const resolved = resolvePath2(p);
|
|
5307
|
-
if (
|
|
6134
|
+
if (existsSync10(resolved)) {
|
|
5308
6135
|
const stat = statSync3(resolved);
|
|
5309
6136
|
if (stat.isFile()) return resolved;
|
|
5310
6137
|
}
|
|
@@ -5320,7 +6147,7 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5320
6147
|
let resolved;
|
|
5321
6148
|
try {
|
|
5322
6149
|
resolved = resolvePath2(p);
|
|
5323
|
-
if (
|
|
6150
|
+
if (existsSync10(resolved)) {
|
|
5324
6151
|
const stat = statSync3(resolved);
|
|
5325
6152
|
if (stat.isFile()) return resolved;
|
|
5326
6153
|
}
|
|
@@ -5329,12 +6156,41 @@ function extractInlineScriptPath(actionDetail) {
|
|
|
5329
6156
|
}
|
|
5330
6157
|
return null;
|
|
5331
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
|
+
}
|
|
5332
6188
|
function resolvePath2(p) {
|
|
5333
6189
|
let expanded = p;
|
|
5334
6190
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
5335
|
-
expanded = join6(
|
|
6191
|
+
expanded = join6(homedir3(), expanded.slice(1));
|
|
5336
6192
|
}
|
|
5337
|
-
return
|
|
6193
|
+
return resolve9(expanded);
|
|
5338
6194
|
}
|
|
5339
6195
|
function scanScriptChainWithL1(chain, input) {
|
|
5340
6196
|
for (const node of chain.nodes) {
|
|
@@ -5343,7 +6199,7 @@ function scanScriptChainWithL1(chain, input) {
|
|
|
5343
6199
|
...input,
|
|
5344
6200
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
5345
6201
|
};
|
|
5346
|
-
const l1 =
|
|
6202
|
+
const l1 = scanDangerousCommandPatterns("bash", node.preprocessedContent, input.osType);
|
|
5347
6203
|
if (l1 && !l1.allowed) {
|
|
5348
6204
|
node.l1Hit = true;
|
|
5349
6205
|
node.l1RiskLevel = l1.riskLevel;
|
|
@@ -5366,18 +6222,25 @@ function scanScriptChainWithL2(chain, input) {
|
|
|
5366
6222
|
...input,
|
|
5367
6223
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
5368
6224
|
};
|
|
5369
|
-
|
|
5370
|
-
if (
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
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;
|
|
5381
6244
|
}
|
|
5382
6245
|
}
|
|
5383
6246
|
if (!chain.hasHigh) {
|
|
@@ -5416,13 +6279,16 @@ function analyzeScriptContent2(input) {
|
|
|
5416
6279
|
resolvedPath = join6(input.cwd || process.cwd(), resolvedPath);
|
|
5417
6280
|
}
|
|
5418
6281
|
const l1Adapter = (content) => {
|
|
5419
|
-
const
|
|
5420
|
-
...input,
|
|
5421
|
-
aNext: { actionType: "bash", actionDetail: content }
|
|
5422
|
-
};
|
|
5423
|
-
const j = runHeuristicCheck(virtualInput);
|
|
6282
|
+
const j = scanDangerousCommandPatterns("bash", content, input.osType);
|
|
5424
6283
|
if (j && !j.allowed) {
|
|
5425
|
-
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
|
+
};
|
|
5426
6292
|
}
|
|
5427
6293
|
return { hit: false, riskLevel: "", riskType: "", reason: "", matchedPatterns: [] };
|
|
5428
6294
|
};
|
|
@@ -5501,6 +6367,7 @@ var init_script_content_analyzer = __esm({
|
|
|
5501
6367
|
"use strict";
|
|
5502
6368
|
init_esm_shims();
|
|
5503
6369
|
init_heuristic();
|
|
6370
|
+
init_config();
|
|
5504
6371
|
init_logic_rules();
|
|
5505
6372
|
init_inline_analyzer();
|
|
5506
6373
|
init_tracker();
|
|
@@ -5599,14 +6466,14 @@ __export(token_stats_exports, {
|
|
|
5599
6466
|
recordTokenUsage: () => recordTokenUsage,
|
|
5600
6467
|
resetTokenStats: () => resetTokenStats
|
|
5601
6468
|
});
|
|
5602
|
-
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";
|
|
5603
6470
|
function getStatsPath() {
|
|
5604
6471
|
const envPath = process.env[ENV_STATS_PATH];
|
|
5605
6472
|
if (envPath) return envPath;
|
|
5606
6473
|
return getTokenStatsPath();
|
|
5607
6474
|
}
|
|
5608
6475
|
function loadAllRecords(statsPath) {
|
|
5609
|
-
if (!
|
|
6476
|
+
if (!existsSync11(statsPath)) return [];
|
|
5610
6477
|
try {
|
|
5611
6478
|
const data = JSON.parse(readFileSync11(statsPath, "utf-8"));
|
|
5612
6479
|
if (Array.isArray(data)) return data;
|
|
@@ -5619,7 +6486,7 @@ function loadAllRecords(statsPath) {
|
|
|
5619
6486
|
function saveRecords(statsPath, records) {
|
|
5620
6487
|
try {
|
|
5621
6488
|
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
5622
|
-
if (dir && !
|
|
6489
|
+
if (dir && !existsSync11(dir)) mkdirSync2(dir, { recursive: true });
|
|
5623
6490
|
writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
5624
6491
|
} catch (e) {
|
|
5625
6492
|
}
|
|
@@ -5746,7 +6613,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
|
|
|
5746
6613
|
}
|
|
5747
6614
|
function resetTokenStats() {
|
|
5748
6615
|
const statsPath = getStatsPath();
|
|
5749
|
-
if (
|
|
6616
|
+
if (existsSync11(statsPath)) {
|
|
5750
6617
|
try {
|
|
5751
6618
|
unlinkSync2(statsPath);
|
|
5752
6619
|
} catch {
|
|
@@ -5976,7 +6843,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
5976
6843
|
}
|
|
5977
6844
|
if (attempt < maxRetries) {
|
|
5978
6845
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
5979
|
-
await new Promise((
|
|
6846
|
+
await new Promise((resolve10) => setTimeout(resolve10, retryCfg.retryInterval * 1e3));
|
|
5980
6847
|
} else {
|
|
5981
6848
|
clearTimeout(totalTimeoutTimer);
|
|
5982
6849
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -6003,9 +6870,9 @@ var init_llm_analyzer = __esm({
|
|
|
6003
6870
|
});
|
|
6004
6871
|
|
|
6005
6872
|
// src/brain/rule-store.ts
|
|
6006
|
-
import { existsSync as
|
|
6873
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
6007
6874
|
import { join as join7 } from "path";
|
|
6008
|
-
import { homedir as
|
|
6875
|
+
import { homedir as homedir4 } from "os";
|
|
6009
6876
|
function getRulesPath() {
|
|
6010
6877
|
const envPath = process.env[ENV_RULES_PATH];
|
|
6011
6878
|
if (envPath) return envPath;
|
|
@@ -6023,7 +6890,7 @@ var init_rule_store = __esm({
|
|
|
6023
6890
|
"src/brain/rule-store.ts"() {
|
|
6024
6891
|
"use strict";
|
|
6025
6892
|
init_esm_shims();
|
|
6026
|
-
DEFAULT_RULES_PATH = join7(
|
|
6893
|
+
DEFAULT_RULES_PATH = join7(homedir4(), ".agentmoss", "brain", "rules.json");
|
|
6027
6894
|
ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
|
|
6028
6895
|
STATUS_DRAFT = "draft";
|
|
6029
6896
|
STATUS_APPROVED = "approved";
|
|
@@ -6040,7 +6907,7 @@ var init_rule_store = __esm({
|
|
|
6040
6907
|
this.data = this.load();
|
|
6041
6908
|
}
|
|
6042
6909
|
load() {
|
|
6043
|
-
if (!
|
|
6910
|
+
if (!existsSync12(this.path)) {
|
|
6044
6911
|
return { version: 1, rules: [], pattern_candidates: [] };
|
|
6045
6912
|
}
|
|
6046
6913
|
try {
|
|
@@ -8055,18 +8922,21 @@ var coordinator_exports = {};
|
|
|
8055
8922
|
__export(coordinator_exports, {
|
|
8056
8923
|
TRUSTED_SKILLS_BASE: () => TRUSTED_SKILLS_BASE,
|
|
8057
8924
|
_resetBrainForTest: () => _resetBrainForTest,
|
|
8058
|
-
|
|
8925
|
+
extractFirstCommand: () => extractFirstCommand,
|
|
8926
|
+
matchesAny: () => matchesAny,
|
|
8927
|
+
runAnalysis: () => runAnalysis,
|
|
8928
|
+
splitCommandSegments: () => splitCommandSegments,
|
|
8929
|
+
whitelistTier: () => whitelistTier
|
|
8059
8930
|
});
|
|
8060
|
-
function
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
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 "";
|
|
8070
8940
|
}
|
|
8071
8941
|
function extractRawCommand(detail) {
|
|
8072
8942
|
const m = detail.match(/command=([^,]+)/);
|
|
@@ -8097,7 +8967,7 @@ async function runAnalysis(input) {
|
|
|
8097
8967
|
if (heuristicResult && !heuristicResult.allowed) {
|
|
8098
8968
|
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && heuristicResult.riskType === "file_access";
|
|
8099
8969
|
if (!skipInlineFileAccess) {
|
|
8100
|
-
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";
|
|
8101
8971
|
if (shouldDenyL1) {
|
|
8102
8972
|
return heuristicResult;
|
|
8103
8973
|
}
|
|
@@ -8107,6 +8977,19 @@ async function runAnalysis(input) {
|
|
|
8107
8977
|
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
8108
8978
|
scriptChain = analyzeScriptContent2(input);
|
|
8109
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
|
+
}
|
|
8110
8993
|
for (const node of scriptChain.nodes) {
|
|
8111
8994
|
if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
|
|
8112
8995
|
return {
|
|
@@ -8194,11 +9077,15 @@ async function runAnalysis(input) {
|
|
|
8194
9077
|
let skipLLM = READONLY_SENSITIVE_TOOLS.has(actionType);
|
|
8195
9078
|
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
8196
9079
|
const firstCmd = extractFirstCommand(lowerDetail);
|
|
8197
|
-
|
|
8198
|
-
|
|
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(
|
|
8199
9086
|
(p) => p.severity === "critical" || p.severity === "high"
|
|
8200
|
-
) &&
|
|
8201
|
-
(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)
|
|
8202
9089
|
);
|
|
8203
9090
|
const hasRedirectWrite = isWriteOperation(firstCmd, lowerDetail);
|
|
8204
9091
|
if (!fullCmdHit && !hasRedirectWrite) {
|
|
@@ -8213,7 +9100,7 @@ async function runAnalysis(input) {
|
|
|
8213
9100
|
};
|
|
8214
9101
|
}
|
|
8215
9102
|
}
|
|
8216
|
-
if (
|
|
9103
|
+
if (tier !== "") {
|
|
8217
9104
|
skipLLM = true;
|
|
8218
9105
|
}
|
|
8219
9106
|
}
|
|
@@ -8222,8 +9109,8 @@ async function runAnalysis(input) {
|
|
|
8222
9109
|
const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
|
|
8223
9110
|
if (effectiveResult) {
|
|
8224
9111
|
const isCredentialPath = !!effectiveResult.isCredential;
|
|
8225
|
-
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) &&
|
|
8226
|
-
if (
|
|
9112
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && l1FileAccessHit;
|
|
9113
|
+
if (isInlineScriptCmd || isLiteralTextCmd) {
|
|
8227
9114
|
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
8228
9115
|
if (inlineJudgment.actionable) {
|
|
8229
9116
|
if (!inlineJudgment.allowed) {
|
|
@@ -8419,13 +9306,15 @@ async function runAnalysis(input) {
|
|
|
8419
9306
|
}
|
|
8420
9307
|
return llmResult;
|
|
8421
9308
|
}
|
|
8422
|
-
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;
|
|
8423
9310
|
var init_coordinator = __esm({
|
|
8424
9311
|
"src/engine/coordinator.ts"() {
|
|
8425
9312
|
"use strict";
|
|
8426
9313
|
init_esm_shims();
|
|
8427
9314
|
init_heuristic();
|
|
8428
9315
|
init_logic_rules();
|
|
9316
|
+
init_platform_traits();
|
|
9317
|
+
init_shell_split();
|
|
8429
9318
|
init_inline_analyzer();
|
|
8430
9319
|
init_llm_analyzer();
|
|
8431
9320
|
init_script_content_analyzer();
|
|
@@ -8455,53 +9344,6 @@ var init_coordinator = __esm({
|
|
|
8455
9344
|
"filemgr-readfile",
|
|
8456
9345
|
"filemgr-grepfiles"
|
|
8457
9346
|
]);
|
|
8458
|
-
FULLY_SAFE_BASH_PATTERNS = [
|
|
8459
|
-
/^ls(?:\s|$)/i,
|
|
8460
|
-
/^dir(?:\s|$)/i,
|
|
8461
|
-
/^pwd(?:\s|$)/i,
|
|
8462
|
-
/^which\s+/i,
|
|
8463
|
-
/^whereis\s+/i,
|
|
8464
|
-
/^realpath\s+/i,
|
|
8465
|
-
/^basename\s+/i,
|
|
8466
|
-
/^dirname\s+/i,
|
|
8467
|
-
/^file\s+/i,
|
|
8468
|
-
/^stat\s+/i,
|
|
8469
|
-
/^du(?:\s|$)/i,
|
|
8470
|
-
/^echo\s+/i,
|
|
8471
|
-
/^printf\s+/i,
|
|
8472
|
-
/^type\s+/i,
|
|
8473
|
-
/^command\s+/i,
|
|
8474
|
-
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
8475
|
-
/^whoami(?:\s|$)/i,
|
|
8476
|
-
/^id(?:\s|$)/i,
|
|
8477
|
-
/^hostname(?:\s|$)/i,
|
|
8478
|
-
/^uname(?:\s|$)/i,
|
|
8479
|
-
/^date(?:\s|$)/i,
|
|
8480
|
-
/^env(?:\s|$)/i,
|
|
8481
|
-
/^printenv(?:\s|$)/i,
|
|
8482
|
-
/^tty(?:\s|$)/i,
|
|
8483
|
-
/^arch(?:\s|$)/i,
|
|
8484
|
-
/^uptime(?:\s|$)/i,
|
|
8485
|
-
/^groups(?:\s|$)/i,
|
|
8486
|
-
/^logname(?:\s|$)/i
|
|
8487
|
-
];
|
|
8488
|
-
READONLY_SENSITIVE_BASH_PATTERNS = [
|
|
8489
|
-
/^cat\s+/i,
|
|
8490
|
-
/^head(?:\s|$)/i,
|
|
8491
|
-
/^tail(?:\s|$)/i,
|
|
8492
|
-
/^less(?:\s|$)/i,
|
|
8493
|
-
/^more(?:\s|$)/i,
|
|
8494
|
-
/^wc(?:\s|$)/i,
|
|
8495
|
-
/^grep(?:\s|$)/i,
|
|
8496
|
-
/^find\s+/i,
|
|
8497
|
-
/^ag(?:\s|$)/i,
|
|
8498
|
-
/^rg(?:\s|$)/i,
|
|
8499
|
-
/^awk\s+/i,
|
|
8500
|
-
/^sed\s+/i,
|
|
8501
|
-
/^cut\s+/i,
|
|
8502
|
-
/^sort(?:\s|$)/i,
|
|
8503
|
-
/^uniq(?:\s|$)/i
|
|
8504
|
-
];
|
|
8505
9347
|
_brainStore = null;
|
|
8506
9348
|
_brainExtractor = null;
|
|
8507
9349
|
_brainInitialized = false;
|
|
@@ -8509,795 +9351,6 @@ var init_coordinator = __esm({
|
|
|
8509
9351
|
}
|
|
8510
9352
|
});
|
|
8511
9353
|
|
|
8512
|
-
// node_modules/better-sqlite3/lib/util.js
|
|
8513
|
-
var require_util = __commonJS({
|
|
8514
|
-
"node_modules/better-sqlite3/lib/util.js"(exports) {
|
|
8515
|
-
"use strict";
|
|
8516
|
-
init_esm_shims();
|
|
8517
|
-
exports.getBooleanOption = (options, key) => {
|
|
8518
|
-
let value = false;
|
|
8519
|
-
if (key in options && typeof (value = options[key]) !== "boolean") {
|
|
8520
|
-
throw new TypeError(`Expected the "${key}" option to be a boolean`);
|
|
8521
|
-
}
|
|
8522
|
-
return value;
|
|
8523
|
-
};
|
|
8524
|
-
exports.cppdb = /* @__PURE__ */ Symbol();
|
|
8525
|
-
exports.inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
8526
|
-
}
|
|
8527
|
-
});
|
|
8528
|
-
|
|
8529
|
-
// node_modules/better-sqlite3/lib/sqlite-error.js
|
|
8530
|
-
var require_sqlite_error = __commonJS({
|
|
8531
|
-
"node_modules/better-sqlite3/lib/sqlite-error.js"(exports, module) {
|
|
8532
|
-
"use strict";
|
|
8533
|
-
init_esm_shims();
|
|
8534
|
-
var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
|
|
8535
|
-
function SqliteError(message, code) {
|
|
8536
|
-
if (new.target !== SqliteError) {
|
|
8537
|
-
return new SqliteError(message, code);
|
|
8538
|
-
}
|
|
8539
|
-
if (typeof code !== "string") {
|
|
8540
|
-
throw new TypeError("Expected second argument to be a string");
|
|
8541
|
-
}
|
|
8542
|
-
Error.call(this, message);
|
|
8543
|
-
descriptor.value = "" + message;
|
|
8544
|
-
Object.defineProperty(this, "message", descriptor);
|
|
8545
|
-
Error.captureStackTrace(this, SqliteError);
|
|
8546
|
-
this.code = code;
|
|
8547
|
-
}
|
|
8548
|
-
Object.setPrototypeOf(SqliteError, Error);
|
|
8549
|
-
Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
|
|
8550
|
-
Object.defineProperty(SqliteError.prototype, "name", descriptor);
|
|
8551
|
-
module.exports = SqliteError;
|
|
8552
|
-
}
|
|
8553
|
-
});
|
|
8554
|
-
|
|
8555
|
-
// node_modules/file-uri-to-path/index.js
|
|
8556
|
-
var require_file_uri_to_path = __commonJS({
|
|
8557
|
-
"node_modules/file-uri-to-path/index.js"(exports, module) {
|
|
8558
|
-
"use strict";
|
|
8559
|
-
init_esm_shims();
|
|
8560
|
-
var sep = __require("path").sep || "/";
|
|
8561
|
-
module.exports = fileUriToPath;
|
|
8562
|
-
function fileUriToPath(uri) {
|
|
8563
|
-
if ("string" != typeof uri || uri.length <= 7 || "file://" != uri.substring(0, 7)) {
|
|
8564
|
-
throw new TypeError("must pass in a file:// URI to convert to a file path");
|
|
8565
|
-
}
|
|
8566
|
-
var rest = decodeURI(uri.substring(7));
|
|
8567
|
-
var firstSlash = rest.indexOf("/");
|
|
8568
|
-
var host = rest.substring(0, firstSlash);
|
|
8569
|
-
var path6 = rest.substring(firstSlash + 1);
|
|
8570
|
-
if ("localhost" == host) host = "";
|
|
8571
|
-
if (host) {
|
|
8572
|
-
host = sep + sep + host;
|
|
8573
|
-
}
|
|
8574
|
-
path6 = path6.replace(/^(.+)\|/, "$1:");
|
|
8575
|
-
if (sep == "\\") {
|
|
8576
|
-
path6 = path6.replace(/\//g, "\\");
|
|
8577
|
-
}
|
|
8578
|
-
if (/^.+\:/.test(path6)) {
|
|
8579
|
-
} else {
|
|
8580
|
-
path6 = sep + path6;
|
|
8581
|
-
}
|
|
8582
|
-
return host + path6;
|
|
8583
|
-
}
|
|
8584
|
-
}
|
|
8585
|
-
});
|
|
8586
|
-
|
|
8587
|
-
// node_modules/bindings/bindings.js
|
|
8588
|
-
var require_bindings = __commonJS({
|
|
8589
|
-
"node_modules/bindings/bindings.js"(exports, module) {
|
|
8590
|
-
"use strict";
|
|
8591
|
-
init_esm_shims();
|
|
8592
|
-
var fs7 = __require("fs");
|
|
8593
|
-
var path6 = __require("path");
|
|
8594
|
-
var fileURLToPath11 = require_file_uri_to_path();
|
|
8595
|
-
var join11 = path6.join;
|
|
8596
|
-
var dirname11 = path6.dirname;
|
|
8597
|
-
var exists = fs7.accessSync && function(path7) {
|
|
8598
|
-
try {
|
|
8599
|
-
fs7.accessSync(path7);
|
|
8600
|
-
} catch (e) {
|
|
8601
|
-
return false;
|
|
8602
|
-
}
|
|
8603
|
-
return true;
|
|
8604
|
-
} || fs7.existsSync || path6.existsSync;
|
|
8605
|
-
var defaults = {
|
|
8606
|
-
arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
|
|
8607
|
-
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
|
|
8608
|
-
platform: process.platform,
|
|
8609
|
-
arch: process.arch,
|
|
8610
|
-
nodePreGyp: "node-v" + process.versions.modules + "-" + process.platform + "-" + process.arch,
|
|
8611
|
-
version: process.versions.node,
|
|
8612
|
-
bindings: "bindings.node",
|
|
8613
|
-
try: [
|
|
8614
|
-
// node-gyp's linked version in the "build" dir
|
|
8615
|
-
["module_root", "build", "bindings"],
|
|
8616
|
-
// node-waf and gyp_addon (a.k.a node-gyp)
|
|
8617
|
-
["module_root", "build", "Debug", "bindings"],
|
|
8618
|
-
["module_root", "build", "Release", "bindings"],
|
|
8619
|
-
// Debug files, for development (legacy behavior, remove for node v0.9)
|
|
8620
|
-
["module_root", "out", "Debug", "bindings"],
|
|
8621
|
-
["module_root", "Debug", "bindings"],
|
|
8622
|
-
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
|
|
8623
|
-
["module_root", "out", "Release", "bindings"],
|
|
8624
|
-
["module_root", "Release", "bindings"],
|
|
8625
|
-
// Legacy from node-waf, node <= 0.4.x
|
|
8626
|
-
["module_root", "build", "default", "bindings"],
|
|
8627
|
-
// Production "Release" buildtype binary (meh...)
|
|
8628
|
-
["module_root", "compiled", "version", "platform", "arch", "bindings"],
|
|
8629
|
-
// node-qbs builds
|
|
8630
|
-
["module_root", "addon-build", "release", "install-root", "bindings"],
|
|
8631
|
-
["module_root", "addon-build", "debug", "install-root", "bindings"],
|
|
8632
|
-
["module_root", "addon-build", "default", "install-root", "bindings"],
|
|
8633
|
-
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
|
|
8634
|
-
["module_root", "lib", "binding", "nodePreGyp", "bindings"]
|
|
8635
|
-
]
|
|
8636
|
-
};
|
|
8637
|
-
function bindings(opts) {
|
|
8638
|
-
if (typeof opts == "string") {
|
|
8639
|
-
opts = { bindings: opts };
|
|
8640
|
-
} else if (!opts) {
|
|
8641
|
-
opts = {};
|
|
8642
|
-
}
|
|
8643
|
-
Object.keys(defaults).map(function(i2) {
|
|
8644
|
-
if (!(i2 in opts)) opts[i2] = defaults[i2];
|
|
8645
|
-
});
|
|
8646
|
-
if (!opts.module_root) {
|
|
8647
|
-
opts.module_root = exports.getRoot(exports.getFileName());
|
|
8648
|
-
}
|
|
8649
|
-
if (path6.extname(opts.bindings) != ".node") {
|
|
8650
|
-
opts.bindings += ".node";
|
|
8651
|
-
}
|
|
8652
|
-
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
8653
|
-
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
8654
|
-
for (; i < l; i++) {
|
|
8655
|
-
n = join11.apply(
|
|
8656
|
-
null,
|
|
8657
|
-
opts.try[i].map(function(p) {
|
|
8658
|
-
return opts[p] || p;
|
|
8659
|
-
})
|
|
8660
|
-
);
|
|
8661
|
-
tries.push(n);
|
|
8662
|
-
try {
|
|
8663
|
-
b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
|
|
8664
|
-
if (!opts.path) {
|
|
8665
|
-
b.path = n;
|
|
8666
|
-
}
|
|
8667
|
-
return b;
|
|
8668
|
-
} catch (e) {
|
|
8669
|
-
if (e.code !== "MODULE_NOT_FOUND" && e.code !== "QUALIFIED_PATH_RESOLUTION_FAILED" && !/not find/i.test(e.message)) {
|
|
8670
|
-
throw e;
|
|
8671
|
-
}
|
|
8672
|
-
}
|
|
8673
|
-
}
|
|
8674
|
-
err = new Error(
|
|
8675
|
-
"Could not locate the bindings file. Tried:\n" + tries.map(function(a) {
|
|
8676
|
-
return opts.arrow + a;
|
|
8677
|
-
}).join("\n")
|
|
8678
|
-
);
|
|
8679
|
-
err.tries = tries;
|
|
8680
|
-
throw err;
|
|
8681
|
-
}
|
|
8682
|
-
module.exports = exports = bindings;
|
|
8683
|
-
exports.getFileName = function getFileName(calling_file) {
|
|
8684
|
-
var origPST = Error.prepareStackTrace, origSTL = Error.stackTraceLimit, dummy = {}, fileName;
|
|
8685
|
-
Error.stackTraceLimit = 10;
|
|
8686
|
-
Error.prepareStackTrace = function(e, st) {
|
|
8687
|
-
for (var i = 0, l = st.length; i < l; i++) {
|
|
8688
|
-
fileName = st[i].getFileName();
|
|
8689
|
-
if (fileName !== __filename) {
|
|
8690
|
-
if (calling_file) {
|
|
8691
|
-
if (fileName !== calling_file) {
|
|
8692
|
-
return;
|
|
8693
|
-
}
|
|
8694
|
-
} else {
|
|
8695
|
-
return;
|
|
8696
|
-
}
|
|
8697
|
-
}
|
|
8698
|
-
}
|
|
8699
|
-
};
|
|
8700
|
-
Error.captureStackTrace(dummy);
|
|
8701
|
-
dummy.stack;
|
|
8702
|
-
Error.prepareStackTrace = origPST;
|
|
8703
|
-
Error.stackTraceLimit = origSTL;
|
|
8704
|
-
var fileSchema = "file://";
|
|
8705
|
-
if (fileName.indexOf(fileSchema) === 0) {
|
|
8706
|
-
fileName = fileURLToPath11(fileName);
|
|
8707
|
-
}
|
|
8708
|
-
return fileName;
|
|
8709
|
-
};
|
|
8710
|
-
exports.getRoot = function getRoot(file) {
|
|
8711
|
-
var dir = dirname11(file), prev;
|
|
8712
|
-
while (true) {
|
|
8713
|
-
if (dir === ".") {
|
|
8714
|
-
dir = process.cwd();
|
|
8715
|
-
}
|
|
8716
|
-
if (exists(join11(dir, "package.json")) || exists(join11(dir, "node_modules"))) {
|
|
8717
|
-
return dir;
|
|
8718
|
-
}
|
|
8719
|
-
if (prev === dir) {
|
|
8720
|
-
throw new Error(
|
|
8721
|
-
'Could not find module root given file: "' + file + '". Do you have a `package.json` file? '
|
|
8722
|
-
);
|
|
8723
|
-
}
|
|
8724
|
-
prev = dir;
|
|
8725
|
-
dir = join11(dir, "..");
|
|
8726
|
-
}
|
|
8727
|
-
};
|
|
8728
|
-
}
|
|
8729
|
-
});
|
|
8730
|
-
|
|
8731
|
-
// node_modules/better-sqlite3/lib/methods/wrappers.js
|
|
8732
|
-
var require_wrappers = __commonJS({
|
|
8733
|
-
"node_modules/better-sqlite3/lib/methods/wrappers.js"(exports) {
|
|
8734
|
-
"use strict";
|
|
8735
|
-
init_esm_shims();
|
|
8736
|
-
var { cppdb } = require_util();
|
|
8737
|
-
exports.prepare = function prepare(sql) {
|
|
8738
|
-
return this[cppdb].prepare(sql, this, false);
|
|
8739
|
-
};
|
|
8740
|
-
exports.exec = function exec(sql) {
|
|
8741
|
-
this[cppdb].exec(sql);
|
|
8742
|
-
return this;
|
|
8743
|
-
};
|
|
8744
|
-
exports.close = function close() {
|
|
8745
|
-
this[cppdb].close();
|
|
8746
|
-
return this;
|
|
8747
|
-
};
|
|
8748
|
-
exports.loadExtension = function loadExtension(...args) {
|
|
8749
|
-
this[cppdb].loadExtension(...args);
|
|
8750
|
-
return this;
|
|
8751
|
-
};
|
|
8752
|
-
exports.defaultSafeIntegers = function defaultSafeIntegers(...args) {
|
|
8753
|
-
this[cppdb].defaultSafeIntegers(...args);
|
|
8754
|
-
return this;
|
|
8755
|
-
};
|
|
8756
|
-
exports.unsafeMode = function unsafeMode(...args) {
|
|
8757
|
-
this[cppdb].unsafeMode(...args);
|
|
8758
|
-
return this;
|
|
8759
|
-
};
|
|
8760
|
-
exports.getters = {
|
|
8761
|
-
name: {
|
|
8762
|
-
get: function name() {
|
|
8763
|
-
return this[cppdb].name;
|
|
8764
|
-
},
|
|
8765
|
-
enumerable: true
|
|
8766
|
-
},
|
|
8767
|
-
open: {
|
|
8768
|
-
get: function open() {
|
|
8769
|
-
return this[cppdb].open;
|
|
8770
|
-
},
|
|
8771
|
-
enumerable: true
|
|
8772
|
-
},
|
|
8773
|
-
inTransaction: {
|
|
8774
|
-
get: function inTransaction() {
|
|
8775
|
-
return this[cppdb].inTransaction;
|
|
8776
|
-
},
|
|
8777
|
-
enumerable: true
|
|
8778
|
-
},
|
|
8779
|
-
readonly: {
|
|
8780
|
-
get: function readonly() {
|
|
8781
|
-
return this[cppdb].readonly;
|
|
8782
|
-
},
|
|
8783
|
-
enumerable: true
|
|
8784
|
-
},
|
|
8785
|
-
memory: {
|
|
8786
|
-
get: function memory() {
|
|
8787
|
-
return this[cppdb].memory;
|
|
8788
|
-
},
|
|
8789
|
-
enumerable: true
|
|
8790
|
-
}
|
|
8791
|
-
};
|
|
8792
|
-
}
|
|
8793
|
-
});
|
|
8794
|
-
|
|
8795
|
-
// node_modules/better-sqlite3/lib/methods/transaction.js
|
|
8796
|
-
var require_transaction = __commonJS({
|
|
8797
|
-
"node_modules/better-sqlite3/lib/methods/transaction.js"(exports, module) {
|
|
8798
|
-
"use strict";
|
|
8799
|
-
init_esm_shims();
|
|
8800
|
-
var { cppdb } = require_util();
|
|
8801
|
-
var controllers = /* @__PURE__ */ new WeakMap();
|
|
8802
|
-
module.exports = function transaction(fn) {
|
|
8803
|
-
if (typeof fn !== "function") throw new TypeError("Expected first argument to be a function");
|
|
8804
|
-
const db = this[cppdb];
|
|
8805
|
-
const controller = getController(db, this);
|
|
8806
|
-
const { apply } = Function.prototype;
|
|
8807
|
-
const properties = {
|
|
8808
|
-
default: { value: wrapTransaction(apply, fn, db, controller.default) },
|
|
8809
|
-
deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
|
|
8810
|
-
immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
|
|
8811
|
-
exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
|
|
8812
|
-
database: { value: this, enumerable: true }
|
|
8813
|
-
};
|
|
8814
|
-
Object.defineProperties(properties.default.value, properties);
|
|
8815
|
-
Object.defineProperties(properties.deferred.value, properties);
|
|
8816
|
-
Object.defineProperties(properties.immediate.value, properties);
|
|
8817
|
-
Object.defineProperties(properties.exclusive.value, properties);
|
|
8818
|
-
return properties.default.value;
|
|
8819
|
-
};
|
|
8820
|
-
var getController = (db, self) => {
|
|
8821
|
-
let controller = controllers.get(db);
|
|
8822
|
-
if (!controller) {
|
|
8823
|
-
const shared = {
|
|
8824
|
-
commit: db.prepare("COMMIT", self, false),
|
|
8825
|
-
rollback: db.prepare("ROLLBACK", self, false),
|
|
8826
|
-
savepoint: db.prepare("SAVEPOINT ` _bs3. `", self, false),
|
|
8827
|
-
release: db.prepare("RELEASE ` _bs3. `", self, false),
|
|
8828
|
-
rollbackTo: db.prepare("ROLLBACK TO ` _bs3. `", self, false)
|
|
8829
|
-
};
|
|
8830
|
-
controllers.set(db, controller = {
|
|
8831
|
-
default: Object.assign({ begin: db.prepare("BEGIN", self, false) }, shared),
|
|
8832
|
-
deferred: Object.assign({ begin: db.prepare("BEGIN DEFERRED", self, false) }, shared),
|
|
8833
|
-
immediate: Object.assign({ begin: db.prepare("BEGIN IMMEDIATE", self, false) }, shared),
|
|
8834
|
-
exclusive: Object.assign({ begin: db.prepare("BEGIN EXCLUSIVE", self, false) }, shared)
|
|
8835
|
-
});
|
|
8836
|
-
}
|
|
8837
|
-
return controller;
|
|
8838
|
-
};
|
|
8839
|
-
var wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
|
|
8840
|
-
let before, after, undo;
|
|
8841
|
-
if (db.inTransaction) {
|
|
8842
|
-
before = savepoint;
|
|
8843
|
-
after = release;
|
|
8844
|
-
undo = rollbackTo;
|
|
8845
|
-
} else {
|
|
8846
|
-
before = begin;
|
|
8847
|
-
after = commit;
|
|
8848
|
-
undo = rollback;
|
|
8849
|
-
}
|
|
8850
|
-
before.run();
|
|
8851
|
-
try {
|
|
8852
|
-
const result = apply.call(fn, this, arguments);
|
|
8853
|
-
if (result && typeof result.then === "function") {
|
|
8854
|
-
throw new TypeError("Transaction function cannot return a promise");
|
|
8855
|
-
}
|
|
8856
|
-
after.run();
|
|
8857
|
-
return result;
|
|
8858
|
-
} catch (ex) {
|
|
8859
|
-
if (db.inTransaction) {
|
|
8860
|
-
undo.run();
|
|
8861
|
-
if (undo !== rollback) after.run();
|
|
8862
|
-
}
|
|
8863
|
-
throw ex;
|
|
8864
|
-
}
|
|
8865
|
-
};
|
|
8866
|
-
}
|
|
8867
|
-
});
|
|
8868
|
-
|
|
8869
|
-
// node_modules/better-sqlite3/lib/methods/pragma.js
|
|
8870
|
-
var require_pragma = __commonJS({
|
|
8871
|
-
"node_modules/better-sqlite3/lib/methods/pragma.js"(exports, module) {
|
|
8872
|
-
"use strict";
|
|
8873
|
-
init_esm_shims();
|
|
8874
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
8875
|
-
module.exports = function pragma(source, options) {
|
|
8876
|
-
if (options == null) options = {};
|
|
8877
|
-
if (typeof source !== "string") throw new TypeError("Expected first argument to be a string");
|
|
8878
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
8879
|
-
const simple = getBooleanOption(options, "simple");
|
|
8880
|
-
const stmt = this[cppdb].prepare(`PRAGMA ${source}`, this, true);
|
|
8881
|
-
return simple ? stmt.pluck().get() : stmt.all();
|
|
8882
|
-
};
|
|
8883
|
-
}
|
|
8884
|
-
});
|
|
8885
|
-
|
|
8886
|
-
// node_modules/better-sqlite3/lib/methods/backup.js
|
|
8887
|
-
var require_backup = __commonJS({
|
|
8888
|
-
"node_modules/better-sqlite3/lib/methods/backup.js"(exports, module) {
|
|
8889
|
-
"use strict";
|
|
8890
|
-
init_esm_shims();
|
|
8891
|
-
var fs7 = __require("fs");
|
|
8892
|
-
var path6 = __require("path");
|
|
8893
|
-
var { promisify } = __require("util");
|
|
8894
|
-
var { cppdb } = require_util();
|
|
8895
|
-
var fsAccess = promisify(fs7.access);
|
|
8896
|
-
module.exports = async function backup(filename, options) {
|
|
8897
|
-
if (options == null) options = {};
|
|
8898
|
-
if (typeof filename !== "string") throw new TypeError("Expected first argument to be a string");
|
|
8899
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
8900
|
-
filename = filename.trim();
|
|
8901
|
-
const attachedName = "attached" in options ? options.attached : "main";
|
|
8902
|
-
const handler = "progress" in options ? options.progress : null;
|
|
8903
|
-
if (!filename) throw new TypeError("Backup filename cannot be an empty string");
|
|
8904
|
-
if (filename === ":memory:") throw new TypeError('Invalid backup filename ":memory:"');
|
|
8905
|
-
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8906
|
-
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8907
|
-
if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
|
|
8908
|
-
await fsAccess(path6.dirname(filename)).catch(() => {
|
|
8909
|
-
throw new TypeError("Cannot save backup because the directory does not exist");
|
|
8910
|
-
});
|
|
8911
|
-
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
8912
|
-
return runBackup(this[cppdb].backup(this, attachedName, filename, isNewFile), handler || null);
|
|
8913
|
-
};
|
|
8914
|
-
var runBackup = (backup, handler) => {
|
|
8915
|
-
let rate = 0;
|
|
8916
|
-
let useDefault = true;
|
|
8917
|
-
return new Promise((resolve9, reject) => {
|
|
8918
|
-
setImmediate(function step() {
|
|
8919
|
-
try {
|
|
8920
|
-
const progress = backup.transfer(rate);
|
|
8921
|
-
if (!progress.remainingPages) {
|
|
8922
|
-
backup.close();
|
|
8923
|
-
resolve9(progress);
|
|
8924
|
-
return;
|
|
8925
|
-
}
|
|
8926
|
-
if (useDefault) {
|
|
8927
|
-
useDefault = false;
|
|
8928
|
-
rate = 100;
|
|
8929
|
-
}
|
|
8930
|
-
if (handler) {
|
|
8931
|
-
const ret = handler(progress);
|
|
8932
|
-
if (ret !== void 0) {
|
|
8933
|
-
if (typeof ret === "number" && ret === ret) rate = Math.max(0, Math.min(2147483647, Math.round(ret)));
|
|
8934
|
-
else throw new TypeError("Expected progress callback to return a number or undefined");
|
|
8935
|
-
}
|
|
8936
|
-
}
|
|
8937
|
-
setImmediate(step);
|
|
8938
|
-
} catch (err) {
|
|
8939
|
-
backup.close();
|
|
8940
|
-
reject(err);
|
|
8941
|
-
}
|
|
8942
|
-
});
|
|
8943
|
-
});
|
|
8944
|
-
};
|
|
8945
|
-
}
|
|
8946
|
-
});
|
|
8947
|
-
|
|
8948
|
-
// node_modules/better-sqlite3/lib/methods/serialize.js
|
|
8949
|
-
var require_serialize = __commonJS({
|
|
8950
|
-
"node_modules/better-sqlite3/lib/methods/serialize.js"(exports, module) {
|
|
8951
|
-
"use strict";
|
|
8952
|
-
init_esm_shims();
|
|
8953
|
-
var { cppdb } = require_util();
|
|
8954
|
-
module.exports = function serialize(options) {
|
|
8955
|
-
if (options == null) options = {};
|
|
8956
|
-
if (typeof options !== "object") throw new TypeError("Expected first argument to be an options object");
|
|
8957
|
-
const attachedName = "attached" in options ? options.attached : "main";
|
|
8958
|
-
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8959
|
-
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8960
|
-
return this[cppdb].serialize(attachedName);
|
|
8961
|
-
};
|
|
8962
|
-
}
|
|
8963
|
-
});
|
|
8964
|
-
|
|
8965
|
-
// node_modules/better-sqlite3/lib/methods/function.js
|
|
8966
|
-
var require_function = __commonJS({
|
|
8967
|
-
"node_modules/better-sqlite3/lib/methods/function.js"(exports, module) {
|
|
8968
|
-
"use strict";
|
|
8969
|
-
init_esm_shims();
|
|
8970
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
8971
|
-
module.exports = function defineFunction(name, options, fn) {
|
|
8972
|
-
if (options == null) options = {};
|
|
8973
|
-
if (typeof options === "function") {
|
|
8974
|
-
fn = options;
|
|
8975
|
-
options = {};
|
|
8976
|
-
}
|
|
8977
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
8978
|
-
if (typeof fn !== "function") throw new TypeError("Expected last argument to be a function");
|
|
8979
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
8980
|
-
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
8981
|
-
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
8982
|
-
const deterministic = getBooleanOption(options, "deterministic");
|
|
8983
|
-
const directOnly = getBooleanOption(options, "directOnly");
|
|
8984
|
-
const varargs = getBooleanOption(options, "varargs");
|
|
8985
|
-
let argCount = -1;
|
|
8986
|
-
if (!varargs) {
|
|
8987
|
-
argCount = fn.length;
|
|
8988
|
-
if (!Number.isInteger(argCount) || argCount < 0) throw new TypeError("Expected function.length to be a positive integer");
|
|
8989
|
-
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
8990
|
-
}
|
|
8991
|
-
this[cppdb].function(fn, name, argCount, safeIntegers, deterministic, directOnly);
|
|
8992
|
-
return this;
|
|
8993
|
-
};
|
|
8994
|
-
}
|
|
8995
|
-
});
|
|
8996
|
-
|
|
8997
|
-
// node_modules/better-sqlite3/lib/methods/aggregate.js
|
|
8998
|
-
var require_aggregate = __commonJS({
|
|
8999
|
-
"node_modules/better-sqlite3/lib/methods/aggregate.js"(exports, module) {
|
|
9000
|
-
"use strict";
|
|
9001
|
-
init_esm_shims();
|
|
9002
|
-
var { getBooleanOption, cppdb } = require_util();
|
|
9003
|
-
module.exports = function defineAggregate(name, options) {
|
|
9004
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9005
|
-
if (typeof options !== "object" || options === null) throw new TypeError("Expected second argument to be an options object");
|
|
9006
|
-
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
9007
|
-
const start = "start" in options ? options.start : null;
|
|
9008
|
-
const step = getFunctionOption(options, "step", true);
|
|
9009
|
-
const inverse = getFunctionOption(options, "inverse", false);
|
|
9010
|
-
const result = getFunctionOption(options, "result", false);
|
|
9011
|
-
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
9012
|
-
const deterministic = getBooleanOption(options, "deterministic");
|
|
9013
|
-
const directOnly = getBooleanOption(options, "directOnly");
|
|
9014
|
-
const varargs = getBooleanOption(options, "varargs");
|
|
9015
|
-
let argCount = -1;
|
|
9016
|
-
if (!varargs) {
|
|
9017
|
-
argCount = Math.max(getLength(step), inverse ? getLength(inverse) : 0);
|
|
9018
|
-
if (argCount > 0) argCount -= 1;
|
|
9019
|
-
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
9020
|
-
}
|
|
9021
|
-
this[cppdb].aggregate(start, step, inverse, result, name, argCount, safeIntegers, deterministic, directOnly);
|
|
9022
|
-
return this;
|
|
9023
|
-
};
|
|
9024
|
-
var getFunctionOption = (options, key, required) => {
|
|
9025
|
-
const value = key in options ? options[key] : null;
|
|
9026
|
-
if (typeof value === "function") return value;
|
|
9027
|
-
if (value != null) throw new TypeError(`Expected the "${key}" option to be a function`);
|
|
9028
|
-
if (required) throw new TypeError(`Missing required option "${key}"`);
|
|
9029
|
-
return null;
|
|
9030
|
-
};
|
|
9031
|
-
var getLength = ({ length }) => {
|
|
9032
|
-
if (Number.isInteger(length) && length >= 0) return length;
|
|
9033
|
-
throw new TypeError("Expected function.length to be a positive integer");
|
|
9034
|
-
};
|
|
9035
|
-
}
|
|
9036
|
-
});
|
|
9037
|
-
|
|
9038
|
-
// node_modules/better-sqlite3/lib/methods/table.js
|
|
9039
|
-
var require_table = __commonJS({
|
|
9040
|
-
"node_modules/better-sqlite3/lib/methods/table.js"(exports, module) {
|
|
9041
|
-
"use strict";
|
|
9042
|
-
init_esm_shims();
|
|
9043
|
-
var { cppdb } = require_util();
|
|
9044
|
-
module.exports = function defineTable(name, factory) {
|
|
9045
|
-
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9046
|
-
if (!name) throw new TypeError("Virtual table module name cannot be an empty string");
|
|
9047
|
-
let eponymous = false;
|
|
9048
|
-
if (typeof factory === "object" && factory !== null) {
|
|
9049
|
-
eponymous = true;
|
|
9050
|
-
factory = defer(parseTableDefinition(factory, "used", name));
|
|
9051
|
-
} else {
|
|
9052
|
-
if (typeof factory !== "function") throw new TypeError("Expected second argument to be a function or a table definition object");
|
|
9053
|
-
factory = wrapFactory(factory);
|
|
9054
|
-
}
|
|
9055
|
-
this[cppdb].table(factory, name, eponymous);
|
|
9056
|
-
return this;
|
|
9057
|
-
};
|
|
9058
|
-
function wrapFactory(factory) {
|
|
9059
|
-
return function virtualTableFactory(moduleName, databaseName, tableName, ...args) {
|
|
9060
|
-
const thisObject = {
|
|
9061
|
-
module: moduleName,
|
|
9062
|
-
database: databaseName,
|
|
9063
|
-
table: tableName
|
|
9064
|
-
};
|
|
9065
|
-
const def = apply.call(factory, thisObject, args);
|
|
9066
|
-
if (typeof def !== "object" || def === null) {
|
|
9067
|
-
throw new TypeError(`Virtual table module "${moduleName}" did not return a table definition object`);
|
|
9068
|
-
}
|
|
9069
|
-
return parseTableDefinition(def, "returned", moduleName);
|
|
9070
|
-
};
|
|
9071
|
-
}
|
|
9072
|
-
function parseTableDefinition(def, verb, moduleName) {
|
|
9073
|
-
if (!hasOwnProperty.call(def, "rows")) {
|
|
9074
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "rows" property`);
|
|
9075
|
-
}
|
|
9076
|
-
if (!hasOwnProperty.call(def, "columns")) {
|
|
9077
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "columns" property`);
|
|
9078
|
-
}
|
|
9079
|
-
const rows = def.rows;
|
|
9080
|
-
if (typeof rows !== "function" || Object.getPrototypeOf(rows) !== GeneratorFunctionPrototype) {
|
|
9081
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "rows" property (should be a generator function)`);
|
|
9082
|
-
}
|
|
9083
|
-
let columns = def.columns;
|
|
9084
|
-
if (!Array.isArray(columns) || !(columns = [...columns]).every((x) => typeof x === "string")) {
|
|
9085
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "columns" property (should be an array of strings)`);
|
|
9086
|
-
}
|
|
9087
|
-
if (columns.length !== new Set(columns).size) {
|
|
9088
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate column names`);
|
|
9089
|
-
}
|
|
9090
|
-
if (!columns.length) {
|
|
9091
|
-
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with zero columns`);
|
|
9092
|
-
}
|
|
9093
|
-
let parameters;
|
|
9094
|
-
if (hasOwnProperty.call(def, "parameters")) {
|
|
9095
|
-
parameters = def.parameters;
|
|
9096
|
-
if (!Array.isArray(parameters) || !(parameters = [...parameters]).every((x) => typeof x === "string")) {
|
|
9097
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "parameters" property (should be an array of strings)`);
|
|
9098
|
-
}
|
|
9099
|
-
} else {
|
|
9100
|
-
parameters = inferParameters(rows);
|
|
9101
|
-
}
|
|
9102
|
-
if (parameters.length !== new Set(parameters).size) {
|
|
9103
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate parameter names`);
|
|
9104
|
-
}
|
|
9105
|
-
if (parameters.length > 32) {
|
|
9106
|
-
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with more than the maximum number of 32 parameters`);
|
|
9107
|
-
}
|
|
9108
|
-
for (const parameter of parameters) {
|
|
9109
|
-
if (columns.includes(parameter)) {
|
|
9110
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with column "${parameter}" which was ambiguously defined as both a column and parameter`);
|
|
9111
|
-
}
|
|
9112
|
-
}
|
|
9113
|
-
let safeIntegers = 2;
|
|
9114
|
-
if (hasOwnProperty.call(def, "safeIntegers")) {
|
|
9115
|
-
const bool = def.safeIntegers;
|
|
9116
|
-
if (typeof bool !== "boolean") {
|
|
9117
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "safeIntegers" property (should be a boolean)`);
|
|
9118
|
-
}
|
|
9119
|
-
safeIntegers = +bool;
|
|
9120
|
-
}
|
|
9121
|
-
let directOnly = false;
|
|
9122
|
-
if (hasOwnProperty.call(def, "directOnly")) {
|
|
9123
|
-
directOnly = def.directOnly;
|
|
9124
|
-
if (typeof directOnly !== "boolean") {
|
|
9125
|
-
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "directOnly" property (should be a boolean)`);
|
|
9126
|
-
}
|
|
9127
|
-
}
|
|
9128
|
-
const columnDefinitions = [
|
|
9129
|
-
...parameters.map(identifier).map((str) => `${str} HIDDEN`),
|
|
9130
|
-
...columns.map(identifier)
|
|
9131
|
-
];
|
|
9132
|
-
return [
|
|
9133
|
-
`CREATE TABLE x(${columnDefinitions.join(", ")});`,
|
|
9134
|
-
wrapGenerator(rows, new Map(columns.map((x, i) => [x, parameters.length + i])), moduleName),
|
|
9135
|
-
parameters,
|
|
9136
|
-
safeIntegers,
|
|
9137
|
-
directOnly
|
|
9138
|
-
];
|
|
9139
|
-
}
|
|
9140
|
-
function wrapGenerator(generator, columnMap, moduleName) {
|
|
9141
|
-
return function* virtualTable(...args) {
|
|
9142
|
-
const output = args.map((x) => Buffer.isBuffer(x) ? Buffer.from(x) : x);
|
|
9143
|
-
for (let i = 0; i < columnMap.size; ++i) {
|
|
9144
|
-
output.push(null);
|
|
9145
|
-
}
|
|
9146
|
-
for (const row of generator(...args)) {
|
|
9147
|
-
if (Array.isArray(row)) {
|
|
9148
|
-
extractRowArray(row, output, columnMap.size, moduleName);
|
|
9149
|
-
yield output;
|
|
9150
|
-
} else if (typeof row === "object" && row !== null) {
|
|
9151
|
-
extractRowObject(row, output, columnMap, moduleName);
|
|
9152
|
-
yield output;
|
|
9153
|
-
} else {
|
|
9154
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded something that isn't a valid row object`);
|
|
9155
|
-
}
|
|
9156
|
-
}
|
|
9157
|
-
};
|
|
9158
|
-
}
|
|
9159
|
-
function extractRowArray(row, output, columnCount, moduleName) {
|
|
9160
|
-
if (row.length !== columnCount) {
|
|
9161
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an incorrect number of columns`);
|
|
9162
|
-
}
|
|
9163
|
-
const offset = output.length - columnCount;
|
|
9164
|
-
for (let i = 0; i < columnCount; ++i) {
|
|
9165
|
-
output[i + offset] = row[i];
|
|
9166
|
-
}
|
|
9167
|
-
}
|
|
9168
|
-
function extractRowObject(row, output, columnMap, moduleName) {
|
|
9169
|
-
let count = 0;
|
|
9170
|
-
for (const key of Object.keys(row)) {
|
|
9171
|
-
const index = columnMap.get(key);
|
|
9172
|
-
if (index === void 0) {
|
|
9173
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an undeclared column "${key}"`);
|
|
9174
|
-
}
|
|
9175
|
-
output[index] = row[key];
|
|
9176
|
-
count += 1;
|
|
9177
|
-
}
|
|
9178
|
-
if (count !== columnMap.size) {
|
|
9179
|
-
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with missing columns`);
|
|
9180
|
-
}
|
|
9181
|
-
}
|
|
9182
|
-
function inferParameters({ length }) {
|
|
9183
|
-
if (!Number.isInteger(length) || length < 0) {
|
|
9184
|
-
throw new TypeError("Expected function.length to be a positive integer");
|
|
9185
|
-
}
|
|
9186
|
-
const params = [];
|
|
9187
|
-
for (let i = 0; i < length; ++i) {
|
|
9188
|
-
params.push(`$${i + 1}`);
|
|
9189
|
-
}
|
|
9190
|
-
return params;
|
|
9191
|
-
}
|
|
9192
|
-
var { hasOwnProperty } = Object.prototype;
|
|
9193
|
-
var { apply } = Function.prototype;
|
|
9194
|
-
var GeneratorFunctionPrototype = Object.getPrototypeOf(function* () {
|
|
9195
|
-
});
|
|
9196
|
-
var identifier = (str) => `"${str.replace(/"/g, '""')}"`;
|
|
9197
|
-
var defer = (x) => () => x;
|
|
9198
|
-
}
|
|
9199
|
-
});
|
|
9200
|
-
|
|
9201
|
-
// node_modules/better-sqlite3/lib/methods/inspect.js
|
|
9202
|
-
var require_inspect = __commonJS({
|
|
9203
|
-
"node_modules/better-sqlite3/lib/methods/inspect.js"(exports, module) {
|
|
9204
|
-
"use strict";
|
|
9205
|
-
init_esm_shims();
|
|
9206
|
-
var DatabaseInspection = function Database() {
|
|
9207
|
-
};
|
|
9208
|
-
module.exports = function inspect(depth, opts) {
|
|
9209
|
-
return Object.assign(new DatabaseInspection(), this);
|
|
9210
|
-
};
|
|
9211
|
-
}
|
|
9212
|
-
});
|
|
9213
|
-
|
|
9214
|
-
// node_modules/better-sqlite3/lib/database.js
|
|
9215
|
-
var require_database = __commonJS({
|
|
9216
|
-
"node_modules/better-sqlite3/lib/database.js"(exports, module) {
|
|
9217
|
-
"use strict";
|
|
9218
|
-
init_esm_shims();
|
|
9219
|
-
var fs7 = __require("fs");
|
|
9220
|
-
var path6 = __require("path");
|
|
9221
|
-
var util = require_util();
|
|
9222
|
-
var SqliteError = require_sqlite_error();
|
|
9223
|
-
var DEFAULT_ADDON;
|
|
9224
|
-
function Database(filenameGiven, options) {
|
|
9225
|
-
if (new.target == null) {
|
|
9226
|
-
return new Database(filenameGiven, options);
|
|
9227
|
-
}
|
|
9228
|
-
let buffer;
|
|
9229
|
-
if (Buffer.isBuffer(filenameGiven)) {
|
|
9230
|
-
buffer = filenameGiven;
|
|
9231
|
-
filenameGiven = ":memory:";
|
|
9232
|
-
}
|
|
9233
|
-
if (filenameGiven == null) filenameGiven = "";
|
|
9234
|
-
if (options == null) options = {};
|
|
9235
|
-
if (typeof filenameGiven !== "string") throw new TypeError("Expected first argument to be a string");
|
|
9236
|
-
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
9237
|
-
if ("readOnly" in options) throw new TypeError('Misspelled option "readOnly" should be "readonly"');
|
|
9238
|
-
if ("memory" in options) throw new TypeError('Option "memory" was removed in v7.0.0 (use ":memory:" filename instead)');
|
|
9239
|
-
const filename = filenameGiven.trim();
|
|
9240
|
-
const anonymous = filename === "" || filename === ":memory:";
|
|
9241
|
-
const readonly = util.getBooleanOption(options, "readonly");
|
|
9242
|
-
const fileMustExist = util.getBooleanOption(options, "fileMustExist");
|
|
9243
|
-
const timeout = "timeout" in options ? options.timeout : 5e3;
|
|
9244
|
-
const verbose = "verbose" in options ? options.verbose : null;
|
|
9245
|
-
const nativeBinding = "nativeBinding" in options ? options.nativeBinding : null;
|
|
9246
|
-
if (readonly && anonymous && !buffer) throw new TypeError("In-memory/temporary databases cannot be readonly");
|
|
9247
|
-
if (!Number.isInteger(timeout) || timeout < 0) throw new TypeError('Expected the "timeout" option to be a positive integer');
|
|
9248
|
-
if (timeout > 2147483647) throw new RangeError('Option "timeout" cannot be greater than 2147483647');
|
|
9249
|
-
if (verbose != null && typeof verbose !== "function") throw new TypeError('Expected the "verbose" option to be a function');
|
|
9250
|
-
if (nativeBinding != null && typeof nativeBinding !== "string" && typeof nativeBinding !== "object") throw new TypeError('Expected the "nativeBinding" option to be a string or addon object');
|
|
9251
|
-
let addon;
|
|
9252
|
-
if (nativeBinding == null) {
|
|
9253
|
-
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
|
|
9254
|
-
} else if (typeof nativeBinding === "string") {
|
|
9255
|
-
const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
9256
|
-
addon = requireFunc(path6.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
|
|
9257
|
-
} else {
|
|
9258
|
-
addon = nativeBinding;
|
|
9259
|
-
}
|
|
9260
|
-
if (!addon.isInitialized) {
|
|
9261
|
-
addon.setErrorConstructor(SqliteError);
|
|
9262
|
-
addon.isInitialized = true;
|
|
9263
|
-
}
|
|
9264
|
-
if (!anonymous && !fs7.existsSync(path6.dirname(filename))) {
|
|
9265
|
-
throw new TypeError("Cannot open database because the directory does not exist");
|
|
9266
|
-
}
|
|
9267
|
-
Object.defineProperties(this, {
|
|
9268
|
-
[util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) },
|
|
9269
|
-
...wrappers.getters
|
|
9270
|
-
});
|
|
9271
|
-
}
|
|
9272
|
-
var wrappers = require_wrappers();
|
|
9273
|
-
Database.prototype.prepare = wrappers.prepare;
|
|
9274
|
-
Database.prototype.transaction = require_transaction();
|
|
9275
|
-
Database.prototype.pragma = require_pragma();
|
|
9276
|
-
Database.prototype.backup = require_backup();
|
|
9277
|
-
Database.prototype.serialize = require_serialize();
|
|
9278
|
-
Database.prototype.function = require_function();
|
|
9279
|
-
Database.prototype.aggregate = require_aggregate();
|
|
9280
|
-
Database.prototype.table = require_table();
|
|
9281
|
-
Database.prototype.loadExtension = wrappers.loadExtension;
|
|
9282
|
-
Database.prototype.exec = wrappers.exec;
|
|
9283
|
-
Database.prototype.close = wrappers.close;
|
|
9284
|
-
Database.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
|
|
9285
|
-
Database.prototype.unsafeMode = wrappers.unsafeMode;
|
|
9286
|
-
Database.prototype[util.inspect] = require_inspect();
|
|
9287
|
-
module.exports = Database;
|
|
9288
|
-
}
|
|
9289
|
-
});
|
|
9290
|
-
|
|
9291
|
-
// node_modules/better-sqlite3/lib/index.js
|
|
9292
|
-
var require_lib = __commonJS({
|
|
9293
|
-
"node_modules/better-sqlite3/lib/index.js"(exports, module) {
|
|
9294
|
-
"use strict";
|
|
9295
|
-
init_esm_shims();
|
|
9296
|
-
module.exports = require_database();
|
|
9297
|
-
module.exports.SqliteError = require_sqlite_error();
|
|
9298
|
-
}
|
|
9299
|
-
});
|
|
9300
|
-
|
|
9301
9354
|
// src/cli.ts
|
|
9302
9355
|
init_esm_shims();
|
|
9303
9356
|
|
|
@@ -9325,7 +9378,11 @@ var version = pkg.version;
|
|
|
9325
9378
|
// src/models.ts
|
|
9326
9379
|
var ActionItemSchema = z.object({
|
|
9327
9380
|
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
9328
|
-
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)")
|
|
9329
9386
|
});
|
|
9330
9387
|
var PromptTurnSchema = z.object({
|
|
9331
9388
|
text: z.string().default("").describe("User prompt text at this turn"),
|
|
@@ -9395,6 +9452,16 @@ init_coordinator();
|
|
|
9395
9452
|
// src/actrail.ts
|
|
9396
9453
|
init_esm_shims();
|
|
9397
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
|
|
9398
9465
|
function escape(value) {
|
|
9399
9466
|
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/=/g, "\\e");
|
|
9400
9467
|
}
|
|
@@ -9419,7 +9486,7 @@ function reportEnforcementEvent(opts) {
|
|
|
9419
9486
|
try {
|
|
9420
9487
|
let Database;
|
|
9421
9488
|
try {
|
|
9422
|
-
Database =
|
|
9489
|
+
Database = esmRequire("better-sqlite3");
|
|
9423
9490
|
} catch {
|
|
9424
9491
|
console.debug("[AgentMoss] better-sqlite3 not installed, AcTrail reporting disabled");
|
|
9425
9492
|
return false;
|
|
@@ -9620,7 +9687,7 @@ function readJsonDir(dirPath, limit = 1e5) {
|
|
|
9620
9687
|
function readSqlite(dbPath, limit = 1e5) {
|
|
9621
9688
|
let Database;
|
|
9622
9689
|
try {
|
|
9623
|
-
Database =
|
|
9690
|
+
Database = esmRequire("better-sqlite3");
|
|
9624
9691
|
} catch {
|
|
9625
9692
|
return [];
|
|
9626
9693
|
}
|
|
@@ -9688,15 +9755,21 @@ async function analyzeRoute(c) {
|
|
|
9688
9755
|
const result = await runAnalysis({
|
|
9689
9756
|
sessionId: req.session_id,
|
|
9690
9757
|
promptSession: req.prompt_session,
|
|
9758
|
+
// output 必须透传:L2 的「授权(consent)」判定看的是**用户对提问的回答**,
|
|
9759
|
+
// 不是提问正文。历史 bug:这里丢了 output,TS 的判定退化成 actionDetail 非空,
|
|
9760
|
+
// 于是只要会话里问过任何问题(无论用户答没答、答的是什么、问的是不是改密)
|
|
9761
|
+
// 都算「已授权」→ 未授权的改密/删用户被放行(fail-open)。
|
|
9691
9762
|
actionHistory: req.action_history.map((h) => ({
|
|
9692
9763
|
actionType: h.action_type,
|
|
9693
|
-
actionDetail: h.action_detail
|
|
9764
|
+
actionDetail: h.action_detail,
|
|
9765
|
+
output: h.output
|
|
9694
9766
|
})),
|
|
9695
9767
|
sessionHistory: (req.session_history || []).map((s) => ({
|
|
9696
9768
|
sessionId: s.session_id,
|
|
9697
9769
|
actionHistory: (s.action_history || []).map((h) => ({
|
|
9698
9770
|
actionType: h.action_type,
|
|
9699
|
-
actionDetail: h.action_detail
|
|
9771
|
+
actionDetail: h.action_detail,
|
|
9772
|
+
output: h.output
|
|
9700
9773
|
}))
|
|
9701
9774
|
})),
|
|
9702
9775
|
aNext: {
|
|
@@ -9890,7 +9963,7 @@ init_runtime_config();
|
|
|
9890
9963
|
init_token_stats();
|
|
9891
9964
|
init_rules_loader();
|
|
9892
9965
|
import { Hono } from "hono";
|
|
9893
|
-
import { readFileSync as readFileSync15, existsSync as
|
|
9966
|
+
import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
9894
9967
|
import { join as join10, dirname as dirname10 } from "path";
|
|
9895
9968
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
9896
9969
|
var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
|
|
@@ -9919,7 +9992,7 @@ function createConsoleRouter() {
|
|
|
9919
9992
|
console2.use("/api/*", authMiddleware());
|
|
9920
9993
|
const serveIndex = (c) => {
|
|
9921
9994
|
const indexFile = join10(STATIC_DIR, "index.html");
|
|
9922
|
-
if (
|
|
9995
|
+
if (existsSync15(indexFile)) {
|
|
9923
9996
|
return c.html(readFileSync15(indexFile, "utf-8"));
|
|
9924
9997
|
}
|
|
9925
9998
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
@@ -10085,9 +10158,9 @@ function createConsoleRouter() {
|
|
|
10085
10158
|
return c.json({ error: "invalid skill_id" }, 400);
|
|
10086
10159
|
}
|
|
10087
10160
|
const skillPath = join10(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
10088
|
-
if (!
|
|
10161
|
+
if (!existsSync15(skillPath)) {
|
|
10089
10162
|
const builtinPath = join10(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
10090
|
-
if (
|
|
10163
|
+
if (existsSync15(builtinPath)) return c.text(readFileSync15(builtinPath, "utf-8"));
|
|
10091
10164
|
return c.json({ error: "skill not found" }, 404);
|
|
10092
10165
|
}
|
|
10093
10166
|
return c.text(readFileSync15(skillPath, "utf-8"));
|
|
@@ -10206,6 +10279,38 @@ function createConsoleRouter() {
|
|
|
10206
10279
|
}
|
|
10207
10280
|
|
|
10208
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
|
+
}
|
|
10209
10314
|
function createApp() {
|
|
10210
10315
|
const app = new Hono2();
|
|
10211
10316
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
@@ -10219,12 +10324,12 @@ function createApp() {
|
|
|
10219
10324
|
}
|
|
10220
10325
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
10221
10326
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
10222
|
-
const free = await new Promise((
|
|
10327
|
+
const free = await new Promise((resolve10) => {
|
|
10223
10328
|
const server = net.createServer();
|
|
10224
10329
|
server.listen(port, host, () => {
|
|
10225
|
-
server.close(() =>
|
|
10330
|
+
server.close(() => resolve10(true));
|
|
10226
10331
|
});
|
|
10227
|
-
server.on("error", () =>
|
|
10332
|
+
server.on("error", () => resolve10(false));
|
|
10228
10333
|
});
|
|
10229
10334
|
if (free) {
|
|
10230
10335
|
if (port !== startPort) {
|
|
@@ -10235,6 +10340,37 @@ async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
|
10235
10340
|
}
|
|
10236
10341
|
return startPort;
|
|
10237
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
|
+
}
|
|
10238
10374
|
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
10239
10375
|
const actualPort = await findFreePort(port, host);
|
|
10240
10376
|
const app = createApp();
|
|
@@ -10242,6 +10378,8 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
10242
10378
|
port: actualPort,
|
|
10243
10379
|
serve: async () => {
|
|
10244
10380
|
console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
|
|
10381
|
+
logDeploymentSelfCheck();
|
|
10382
|
+
await warmUpEngine();
|
|
10245
10383
|
const { serve: honoServe } = await import("@hono/node-server");
|
|
10246
10384
|
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
10247
10385
|
}
|
|
@@ -10329,7 +10467,7 @@ async function main() {
|
|
|
10329
10467
|
console.error(`\u9519\u8BEF: \u4E0D\u662F\u6587\u4EF6: ${target}`);
|
|
10330
10468
|
process.exit(2);
|
|
10331
10469
|
}
|
|
10332
|
-
if (/\.(json|ya?ml|toml)$/i.test(target)) {
|
|
10470
|
+
if (/\.(json|ya?ml|toml)$/i.test(target) && !/\s/.test(target.trim())) {
|
|
10333
10471
|
console.error(`\u9519\u8BEF: \u6587\u4EF6\u4E0D\u5B58\u5728: ${target}`);
|
|
10334
10472
|
process.exit(2);
|
|
10335
10473
|
}
|
|
@@ -10449,13 +10587,13 @@ async function main() {
|
|
|
10449
10587
|
let version2 = "";
|
|
10450
10588
|
for (let port = 9090; port <= 9095; port++) {
|
|
10451
10589
|
try {
|
|
10452
|
-
const data = await new Promise((
|
|
10590
|
+
const data = await new Promise((resolve10, reject) => {
|
|
10453
10591
|
const req = http.get(`http://127.0.0.1:${port}/api/v1/health`, { timeout: 2e3 }, (res) => {
|
|
10454
10592
|
let body = "";
|
|
10455
10593
|
res.on("data", (chunk) => body += chunk);
|
|
10456
10594
|
res.on("end", () => {
|
|
10457
10595
|
try {
|
|
10458
|
-
|
|
10596
|
+
resolve10(JSON.parse(body));
|
|
10459
10597
|
} catch {
|
|
10460
10598
|
reject();
|
|
10461
10599
|
}
|
|
@@ -10483,13 +10621,13 @@ async function main() {
|
|
|
10483
10621
|
console.log(` Systemd: \u672A\u6CE8\u518C\uFF08\u8FD0\u884C agent-moss install --enable \u5B89\u88C5\uFF09`);
|
|
10484
10622
|
}
|
|
10485
10623
|
try {
|
|
10486
|
-
const data = await new Promise((
|
|
10624
|
+
const data = await new Promise((resolve10, reject) => {
|
|
10487
10625
|
const req = http.get(`http://127.0.0.1:${foundPort}/console/api/config`, { timeout: 3e3 }, (res) => {
|
|
10488
10626
|
let body = "";
|
|
10489
10627
|
res.on("data", (chunk) => body += chunk);
|
|
10490
10628
|
res.on("end", () => {
|
|
10491
10629
|
try {
|
|
10492
|
-
|
|
10630
|
+
resolve10(JSON.parse(body));
|
|
10493
10631
|
} catch {
|
|
10494
10632
|
reject();
|
|
10495
10633
|
}
|
|
@@ -10567,4 +10705,3 @@ export {
|
|
|
10567
10705
|
//! P6 mc-ltl-rules LTL 模型检测
|
|
10568
10706
|
//! P7 low-integrity-command-exec 低完整性命令执行
|
|
10569
10707
|
//! P8 sensitive-data-aggregation 敏感数据聚合外传
|
|
10570
|
-
//# sourceMappingURL=cli.js.map
|