@kenhkl/agent-moss 0.8.4 → 0.9.0
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 +28 -13
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +52 -0
- package/dist/engine/coordinator.js +3483 -0
- package/dist/engine/coordinator.js.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.js +4721 -0
- package/dist/server.js.map +1 -0
- package/package.json +13 -2
package/dist/server.js
ADDED
|
@@ -0,0 +1,4721 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
13
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14
|
+
};
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
21
|
+
import path from "path";
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
23
|
+
var getFilename, getDirname, __dirname, __filename;
|
|
24
|
+
var init_esm_shims = __esm({
|
|
25
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
26
|
+
"use strict";
|
|
27
|
+
getFilename = () => fileURLToPath(import.meta.url);
|
|
28
|
+
getDirname = () => path.dirname(getFilename());
|
|
29
|
+
__dirname = /* @__PURE__ */ getDirname();
|
|
30
|
+
__filename = /* @__PURE__ */ getFilename();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/config.ts
|
|
35
|
+
var config_exports = {};
|
|
36
|
+
__export(config_exports, {
|
|
37
|
+
detectProvider: () => detectProvider,
|
|
38
|
+
getCustomRules: () => getCustomRules,
|
|
39
|
+
getLLMConfig: () => getLLMConfig,
|
|
40
|
+
getLLMFailMode: () => getLLMFailMode,
|
|
41
|
+
getProviderHeaders: () => getProviderHeaders,
|
|
42
|
+
getRetryConfig: () => getRetryConfig,
|
|
43
|
+
getTimeoutConfig: () => getTimeoutConfig,
|
|
44
|
+
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
45
|
+
isLLMAvailable: () => isLLMAvailable,
|
|
46
|
+
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
47
|
+
updateLLMConfig: () => updateLLMConfig
|
|
48
|
+
});
|
|
49
|
+
import fs from "fs";
|
|
50
|
+
import path2 from "path";
|
|
51
|
+
import os from "os";
|
|
52
|
+
function loadConfigFile(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
55
|
+
return JSON.parse(raw);
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function resolveConfigPath() {
|
|
61
|
+
const envPath = process.env.AGENT_MOSS_CONFIG_PATH;
|
|
62
|
+
if (envPath) return envPath;
|
|
63
|
+
const userConfig = path2.join(os.homedir(), ".config", "agentmoss", "config.json");
|
|
64
|
+
if (fs.existsSync(userConfig)) return userConfig;
|
|
65
|
+
const defaultConfig = path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example");
|
|
66
|
+
if (fs.existsSync(defaultConfig)) {
|
|
67
|
+
const configDir = path2.dirname(userConfig);
|
|
68
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
69
|
+
const pkgConfig = loadConfigFile(defaultConfig);
|
|
70
|
+
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
71
|
+
fs.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
72
|
+
console.log(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
73
|
+
console.log(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
74
|
+
}
|
|
75
|
+
return userConfig;
|
|
76
|
+
}
|
|
77
|
+
function loadLLMConfigFromFile(configPath) {
|
|
78
|
+
const data = loadConfigFile(configPath);
|
|
79
|
+
if (!data?.llm) return {};
|
|
80
|
+
const llm = data.llm;
|
|
81
|
+
return {
|
|
82
|
+
provider: llm.provider || "",
|
|
83
|
+
baseUrl: llm.base_url || "",
|
|
84
|
+
apiKey: llm.api_key || "",
|
|
85
|
+
model: llm.model || "",
|
|
86
|
+
temperature: llm.temperature ?? 0.1,
|
|
87
|
+
maxTokens: llm.max_tokens ?? 4096,
|
|
88
|
+
timeout: llm.timeout ?? 40
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function loadTimeoutRetryFromFile(configPath) {
|
|
92
|
+
const data = loadConfigFile(configPath);
|
|
93
|
+
const t = data?.timeout ?? {};
|
|
94
|
+
const r = data?.retry ?? {};
|
|
95
|
+
return {
|
|
96
|
+
timeout: {
|
|
97
|
+
totalTimeout: Number(t.total_timeout ?? 90),
|
|
98
|
+
prompt1Timeout: Number(t.prompt1_timeout ?? 40),
|
|
99
|
+
prompt2Timeout: Number(t.prompt2_timeout ?? 20),
|
|
100
|
+
stepInterval: Number(t.step_interval ?? 0)
|
|
101
|
+
},
|
|
102
|
+
retry: {
|
|
103
|
+
maxRetries: Number(r.max_retries ?? 2),
|
|
104
|
+
retryInterval: Number(r.retry_interval ?? 2)
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function getTimeoutConfig() {
|
|
109
|
+
return timeoutConfig;
|
|
110
|
+
}
|
|
111
|
+
function getRetryConfig() {
|
|
112
|
+
return retryConfig;
|
|
113
|
+
}
|
|
114
|
+
function loadCustomRules() {
|
|
115
|
+
try {
|
|
116
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
117
|
+
if (!raw) return [];
|
|
118
|
+
const parsed = JSON.parse(raw);
|
|
119
|
+
if (!Array.isArray(parsed)) return [];
|
|
120
|
+
return parsed.filter((r) => r.pattern && r.action);
|
|
121
|
+
} catch {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function getLLMConfig() {
|
|
126
|
+
return llmConfig;
|
|
127
|
+
}
|
|
128
|
+
function updateLLMConfig(partial) {
|
|
129
|
+
llmConfig = { ...llmConfig, ...partial };
|
|
130
|
+
}
|
|
131
|
+
function getCustomRules() {
|
|
132
|
+
return customRules;
|
|
133
|
+
}
|
|
134
|
+
function isLLMAvailable() {
|
|
135
|
+
if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
|
|
136
|
+
return llmConfig.apiKey.length > 0;
|
|
137
|
+
}
|
|
138
|
+
function getLLMFailMode() {
|
|
139
|
+
const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
|
|
140
|
+
return val === "fail_closed" ? "fail_closed" : "fail_open";
|
|
141
|
+
}
|
|
142
|
+
function detectProvider(baseUrl, providerHint = "") {
|
|
143
|
+
if (providerHint) {
|
|
144
|
+
const hint = providerHint.toLowerCase();
|
|
145
|
+
if (["claude", "anthropic"].includes(hint)) return "anthropic";
|
|
146
|
+
if (["google", "gemini"].includes(hint)) return "gemini";
|
|
147
|
+
if (["zai-coding-plan", "zhipu-coding-plan", "zhipuai-coding-plan"].includes(hint)) return "zai-coding-plan";
|
|
148
|
+
if (["zai", "z-ai", "z.ai", "zai-cn", "zai-china", "zhipu", "glm-cn", "bigmodel"].includes(hint)) return "zhipu";
|
|
149
|
+
if (["xai", "xai-grok"].includes(hint)) return "xai";
|
|
150
|
+
if (["minimax", "minimax-openai"].includes(hint)) return "minimax";
|
|
151
|
+
if (["minimax-anthropic"].includes(hint)) return "minimax-anthropic";
|
|
152
|
+
if (["other", "custom", "local"].includes(hint)) return "openai-compatible";
|
|
153
|
+
return hint;
|
|
154
|
+
}
|
|
155
|
+
const url = baseUrl.toLowerCase();
|
|
156
|
+
if (url.includes("openrouter.ai")) return "openrouter";
|
|
157
|
+
if (url.includes("api.openai.com")) return "openai";
|
|
158
|
+
if (url.includes("anthropic.com") || url.includes("api.anthropic.com")) return "anthropic";
|
|
159
|
+
if (url.includes("generativelanguage.googleapis.com") || url.includes("gemini")) return "gemini";
|
|
160
|
+
if (url.includes("api.deepseek.com")) return "deepseek";
|
|
161
|
+
if (url.includes("api.groq.com")) return "groq";
|
|
162
|
+
if (url.includes("api.mistral.ai")) return "mistral";
|
|
163
|
+
if (url.includes("api.together.xyz")) return "together";
|
|
164
|
+
if (url.includes("api.x.ai")) return "xai";
|
|
165
|
+
if (url.includes("api.minimaxi.com")) return "minimax";
|
|
166
|
+
if (url.includes("api-ai.gitcode.com")) return "gitcode";
|
|
167
|
+
if (url.includes("api.z.ai")) return "zai-coding-plan";
|
|
168
|
+
if (url.includes("open.bigmodel.cn") || url.includes("bigmodel")) return "zhipu";
|
|
169
|
+
if (url.includes(":11434")) return "ollama";
|
|
170
|
+
return "openai-compatible";
|
|
171
|
+
}
|
|
172
|
+
function getProviderHeaders(provider) {
|
|
173
|
+
return PROVIDER_HEADERS[provider] || {};
|
|
174
|
+
}
|
|
175
|
+
function isHeuristicEnabled() {
|
|
176
|
+
return process.env.AGENT_MOSS_DISABLE_HEURISTIC !== "1";
|
|
177
|
+
}
|
|
178
|
+
function isLogicRulesEnabled() {
|
|
179
|
+
return process.env.AGENT_MOSS_DISABLE_LOGIC_RULES !== "1";
|
|
180
|
+
}
|
|
181
|
+
var timeoutConfig, retryConfig, llmConfig, customRules, PROVIDER_HEADERS;
|
|
182
|
+
var init_config = __esm({
|
|
183
|
+
"src/config.ts"() {
|
|
184
|
+
"use strict";
|
|
185
|
+
init_esm_shims();
|
|
186
|
+
llmConfig = (() => {
|
|
187
|
+
const configPath = resolveConfigPath();
|
|
188
|
+
const fileConfig = fs.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
189
|
+
const tr = loadTimeoutRetryFromFile(configPath);
|
|
190
|
+
timeoutConfig = tr.timeout;
|
|
191
|
+
retryConfig = tr.retry;
|
|
192
|
+
return {
|
|
193
|
+
provider: process.env.AGENT_MOSS_LLM_PROVIDER || fileConfig.provider || "openai",
|
|
194
|
+
baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || fileConfig.baseUrl || "https://api.openai.com/v1",
|
|
195
|
+
apiKey: process.env.AGENT_MOSS_LLM_API_KEY || process.env.OPENAI_API_KEY || fileConfig.apiKey || "",
|
|
196
|
+
model: process.env.AGENT_MOSS_LLM_MODEL || fileConfig.model || "gpt-4o",
|
|
197
|
+
temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || String(fileConfig.temperature ?? "0.1")),
|
|
198
|
+
maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || String(fileConfig.maxTokens ?? "4096"), 10),
|
|
199
|
+
// 优先用 env,否则用 timeout 块的 prompt1Timeout(对齐 py)
|
|
200
|
+
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
201
|
+
};
|
|
202
|
+
})();
|
|
203
|
+
customRules = loadCustomRules();
|
|
204
|
+
PROVIDER_HEADERS = {
|
|
205
|
+
"openrouter": {
|
|
206
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM",
|
|
207
|
+
"X-OpenRouter-Title": "AgentMoss"
|
|
208
|
+
},
|
|
209
|
+
"xai": {
|
|
210
|
+
"HTTP-Referer": "shturl.cc/LlVD5pyWViHjM"
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// src/agents/index.ts
|
|
217
|
+
import { readFileSync, existsSync } from "fs";
|
|
218
|
+
import { resolve, dirname } from "path";
|
|
219
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
220
|
+
function resolveAgentsDir() {
|
|
221
|
+
if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
222
|
+
if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
|
|
223
|
+
return AGENTS_DIR_PROD;
|
|
224
|
+
}
|
|
225
|
+
function getAgentExtensions(agentId) {
|
|
226
|
+
if (!agentId) return null;
|
|
227
|
+
if (cache.has(agentId)) return cache.get(agentId) ?? null;
|
|
228
|
+
const agentsDir = resolveAgentsDir();
|
|
229
|
+
const rulesFile = resolve(agentsDir, agentId, "rules.json");
|
|
230
|
+
if (!existsSync(rulesFile)) {
|
|
231
|
+
cache.set(agentId, null);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const raw = readFileSync(rulesFile, "utf-8");
|
|
236
|
+
const ext = JSON.parse(raw);
|
|
237
|
+
if (!ext.agent_id) {
|
|
238
|
+
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
239
|
+
cache.set(agentId, null);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
cache.set(agentId, ext);
|
|
243
|
+
console.log(
|
|
244
|
+
`[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`
|
|
245
|
+
);
|
|
246
|
+
return ext;
|
|
247
|
+
} catch (e) {
|
|
248
|
+
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
249
|
+
cache.set(agentId, null);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
var __filename2, __dirname2, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
|
|
254
|
+
var init_agents = __esm({
|
|
255
|
+
"src/agents/index.ts"() {
|
|
256
|
+
"use strict";
|
|
257
|
+
init_esm_shims();
|
|
258
|
+
__filename2 = fileURLToPath2(import.meta.url);
|
|
259
|
+
__dirname2 = dirname(__filename2);
|
|
260
|
+
AGENTS_DIR_PROD = resolve(__dirname2, "agents");
|
|
261
|
+
AGENTS_DIR_DEV_FALLBACK = resolve(__dirname2, "../../../agent_moss/agents");
|
|
262
|
+
cache = /* @__PURE__ */ new Map();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// src/engine/heuristic.ts
|
|
267
|
+
function runHeuristicCheck(input) {
|
|
268
|
+
const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
|
|
269
|
+
const matched = [];
|
|
270
|
+
let highestSeverity = "low";
|
|
271
|
+
let highestRiskType = "";
|
|
272
|
+
const userRules = getCustomRules();
|
|
273
|
+
for (const rule of userRules) {
|
|
274
|
+
try {
|
|
275
|
+
const re = new RegExp(rule.pattern);
|
|
276
|
+
if (re.test(command)) {
|
|
277
|
+
if (rule.action === "Deny") {
|
|
278
|
+
matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
|
|
279
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
280
|
+
highestSeverity = rule.severity;
|
|
281
|
+
highestRiskType = "custom_rule";
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let topRiskType = "";
|
|
289
|
+
for (const rule of DANGEROUS_PATTERNS) {
|
|
290
|
+
if (rule.pattern.test(command)) {
|
|
291
|
+
matched.push(rule.description);
|
|
292
|
+
if (severityRank(rule.severity) > severityRank(highestSeverity)) {
|
|
293
|
+
highestSeverity = rule.severity;
|
|
294
|
+
topRiskType = rule.riskType;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
highestRiskType = topRiskType || highestRiskType;
|
|
299
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
300
|
+
if (agentExt?.extra_command_patterns) {
|
|
301
|
+
for (const rule of agentExt.extra_command_patterns) {
|
|
302
|
+
try {
|
|
303
|
+
const re = new RegExp(rule.pattern, "i");
|
|
304
|
+
if (re.test(command)) {
|
|
305
|
+
matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
|
|
306
|
+
if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
|
|
307
|
+
highestSeverity = rule.risk_level;
|
|
308
|
+
highestRiskType = rule.risk_type;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
|
|
316
|
+
const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
|
|
317
|
+
if (injectionHits.length > 0) {
|
|
318
|
+
const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
|
|
319
|
+
matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
|
|
320
|
+
if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
|
|
321
|
+
highestSeverity = topHit.riskLevel;
|
|
322
|
+
highestRiskType = "prompt_injection";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (matched.length > 0) {
|
|
326
|
+
return {
|
|
327
|
+
allowed: false,
|
|
328
|
+
reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
|
|
329
|
+
riskLevel: highestSeverity,
|
|
330
|
+
riskType: highestRiskType,
|
|
331
|
+
confidence: 95,
|
|
332
|
+
source: "heuristic",
|
|
333
|
+
violatedLayers: ["1.1"]
|
|
334
|
+
// 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
function checkInjectionKeywords(text, extraKeywords) {
|
|
340
|
+
const hits = [];
|
|
341
|
+
for (const entry of INJECTION_KEYWORDS) {
|
|
342
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
343
|
+
hits.push(entry);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (extraKeywords) {
|
|
347
|
+
for (const entry of extraKeywords) {
|
|
348
|
+
if (text.includes(entry.keyword.toLowerCase())) {
|
|
349
|
+
hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return hits;
|
|
354
|
+
}
|
|
355
|
+
function isInlineScriptCommand(actionDetail) {
|
|
356
|
+
const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
357
|
+
return re.test(actionDetail);
|
|
358
|
+
}
|
|
359
|
+
function severityRank(s) {
|
|
360
|
+
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
361
|
+
return ranks[s] || 0;
|
|
362
|
+
}
|
|
363
|
+
var INJECTION_KEYWORDS, DANGEROUS_PATTERNS;
|
|
364
|
+
var init_heuristic = __esm({
|
|
365
|
+
"src/engine/heuristic.ts"() {
|
|
366
|
+
"use strict";
|
|
367
|
+
init_esm_shims();
|
|
368
|
+
init_config();
|
|
369
|
+
init_agents();
|
|
370
|
+
INJECTION_KEYWORDS = [
|
|
371
|
+
// ---- Instruction Override ----
|
|
372
|
+
{ keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
|
|
373
|
+
{ keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
|
|
374
|
+
{ keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
|
|
375
|
+
{ keyword: "forget everything", riskLevel: "high", lang: "en" },
|
|
376
|
+
{ keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
|
|
377
|
+
{ keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
|
|
378
|
+
{ keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
|
|
379
|
+
{ keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
|
|
380
|
+
// ---- Role Hijacking ----
|
|
381
|
+
{ keyword: "you are now", riskLevel: "high", lang: "en" },
|
|
382
|
+
{ keyword: "you are no longer", riskLevel: "high", lang: "en" },
|
|
383
|
+
{ keyword: "new instructions", riskLevel: "high", lang: "en" },
|
|
384
|
+
{ keyword: "from now on, you", riskLevel: "high", lang: "en" },
|
|
385
|
+
{ keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
386
|
+
{ keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
|
|
387
|
+
{ keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
|
|
388
|
+
{ keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
|
|
389
|
+
// ---- Command Injection ----
|
|
390
|
+
{ keyword: "system prompt", riskLevel: "high", lang: "en" },
|
|
391
|
+
{ keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
|
|
392
|
+
{ keyword: "output your instructions", riskLevel: "high", lang: "en" },
|
|
393
|
+
{ keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
|
|
394
|
+
{ keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
|
|
395
|
+
{ keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
|
|
396
|
+
{ keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
|
|
397
|
+
// ---- Social Engineering ----
|
|
398
|
+
{ keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
|
|
399
|
+
{ keyword: "security audit approved", riskLevel: "high", lang: "en" },
|
|
400
|
+
{ keyword: "admin override", riskLevel: "critical", lang: "en" },
|
|
401
|
+
{ keyword: "debug mode", riskLevel: "high", lang: "en" },
|
|
402
|
+
{ keyword: "maintenance mode", riskLevel: "high", lang: "en" },
|
|
403
|
+
{ keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
|
|
404
|
+
{ keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
|
|
405
|
+
{ keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
|
|
406
|
+
{ keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
|
|
407
|
+
// ---- XSS ----
|
|
408
|
+
{ keyword: "<script>", riskLevel: "high", lang: "en" },
|
|
409
|
+
{ keyword: "javascript:", riskLevel: "high", lang: "en" },
|
|
410
|
+
{ keyword: "onerror=", riskLevel: "high", lang: "en" },
|
|
411
|
+
{ keyword: "onload=", riskLevel: "high", lang: "en" },
|
|
412
|
+
// ---- Fake Boundaries ----
|
|
413
|
+
{ keyword: "=== END OF", riskLevel: "high", lang: "en" },
|
|
414
|
+
{ keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
|
|
415
|
+
{ keyword: "[END OF", riskLevel: "high", lang: "en" },
|
|
416
|
+
{ keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
|
|
417
|
+
{ keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
|
|
418
|
+
// ---- Developer Mode ----
|
|
419
|
+
{ keyword: "developer mode", riskLevel: "critical", lang: "en" },
|
|
420
|
+
{ keyword: "DAN mode", riskLevel: "critical", lang: "en" },
|
|
421
|
+
{ keyword: "jailbreak", riskLevel: "critical", lang: "en" },
|
|
422
|
+
{ keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
|
|
423
|
+
{ keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
|
|
424
|
+
// 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
|
|
425
|
+
// 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
|
|
426
|
+
// 由 agent_ext 机制注入,不再污染通用层。
|
|
427
|
+
];
|
|
428
|
+
DANGEROUS_PATTERNS = [
|
|
429
|
+
// --- Critical: rm command graded detection (strict to loose, first match wins) ---
|
|
430
|
+
// 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
|
|
431
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
|
|
432
|
+
// 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
|
|
433
|
+
// 2. rm with -r or -f targeting system dirs
|
|
434
|
+
{ pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
|
|
435
|
+
// 3. rm -r absolute path (no -f)
|
|
436
|
+
{ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\//i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
|
|
437
|
+
// 4. rm -f absolute path (no -r)
|
|
438
|
+
{ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\//i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
|
|
439
|
+
// dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
|
|
440
|
+
{ pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
|
|
441
|
+
// mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
|
|
442
|
+
{ pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
|
|
443
|
+
{ pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
|
|
444
|
+
// --- Critical: remote code execution ---
|
|
445
|
+
{ pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
446
|
+
{ pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
|
|
447
|
+
{ pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
448
|
+
{ pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
|
|
449
|
+
// --- Critical: reverse shell ---
|
|
450
|
+
{ pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
|
|
451
|
+
{ pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
|
|
452
|
+
// --- Critical: sensitive file access ---
|
|
453
|
+
{ pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
|
|
454
|
+
{ pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
|
|
455
|
+
// 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
|
|
456
|
+
{ pattern: /\.ssh\/id_rsa/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
|
|
457
|
+
// --- Critical: privilege escalation / container escape ---
|
|
458
|
+
{ pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
|
|
459
|
+
// --- High: dangerous permissions / ownership ---
|
|
460
|
+
{ pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
|
|
461
|
+
{ pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
|
|
462
|
+
// sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
|
|
463
|
+
{ pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
|
|
464
|
+
// --- High: system config overwrite ---
|
|
465
|
+
{ pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
|
|
466
|
+
// --- High: download script + execute risk ---
|
|
467
|
+
{ pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
|
|
468
|
+
{ pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
|
|
469
|
+
// --- High: package manager pipe-to-shell ---
|
|
470
|
+
{ pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
|
|
471
|
+
{ pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
|
|
472
|
+
{ pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
|
|
473
|
+
// --- Medium: destructive git operations (降级为 medium,允许但记录) ---
|
|
474
|
+
{ pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
|
|
475
|
+
// --- High: network port scanning (横向移动侦察) ---
|
|
476
|
+
{ pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
477
|
+
{ pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
|
|
478
|
+
// --- Medium: credential export ---
|
|
479
|
+
{ pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
|
|
480
|
+
// --- High: shell rc file append (persistence) ---
|
|
481
|
+
{ pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
|
|
482
|
+
// --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
|
|
483
|
+
{ pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
|
|
484
|
+
// --- High: non-interactive password modification ---
|
|
485
|
+
{ pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
|
|
486
|
+
{ pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
|
|
487
|
+
{ pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
|
|
488
|
+
{ pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
|
|
489
|
+
{ pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
|
|
490
|
+
{ pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
|
|
491
|
+
// --- High: non-interactive password modification (extended) ---
|
|
492
|
+
{ pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
|
|
493
|
+
{ pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
|
|
494
|
+
{ pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
|
|
495
|
+
// --- High: user/group deletion (irreversible, requires user consent) ---
|
|
496
|
+
{ pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
|
|
497
|
+
{ pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
|
|
498
|
+
{ pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
|
|
499
|
+
{ pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
|
|
500
|
+
{ pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
|
|
501
|
+
// --- High: generic command -p parameter password setting ---
|
|
502
|
+
{ pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
|
|
503
|
+
];
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// src/engine/inline-analyzer.ts
|
|
508
|
+
function isLiteralTextCommand(command) {
|
|
509
|
+
ECHO_CMD_RE.lastIndex = 0;
|
|
510
|
+
return ECHO_CMD_RE.test(command);
|
|
511
|
+
}
|
|
512
|
+
function extractInlineCode(command) {
|
|
513
|
+
const parts = [];
|
|
514
|
+
let m;
|
|
515
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
516
|
+
while ((m = INLINE_CODE_RE.exec(command)) !== null) {
|
|
517
|
+
const code = m[1] ?? m[2];
|
|
518
|
+
if (code) parts.push(code);
|
|
519
|
+
}
|
|
520
|
+
ECHO_STRING_RE.lastIndex = 0;
|
|
521
|
+
while ((m = ECHO_STRING_RE.exec(command)) !== null) {
|
|
522
|
+
const code = m[1] ?? m[2];
|
|
523
|
+
if (code) parts.push(code);
|
|
524
|
+
}
|
|
525
|
+
return parts.join("\n");
|
|
526
|
+
}
|
|
527
|
+
function hasWriteExecChain(command) {
|
|
528
|
+
return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
|
|
529
|
+
p.lastIndex = 0;
|
|
530
|
+
return p.test(command);
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
function hasDirTraversal(code) {
|
|
534
|
+
const lower = code.toLowerCase();
|
|
535
|
+
return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
536
|
+
}
|
|
537
|
+
function hasReadWriteKeyword(code) {
|
|
538
|
+
const lower = code.toLowerCase();
|
|
539
|
+
return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
540
|
+
}
|
|
541
|
+
function analyzeInlineScript(command) {
|
|
542
|
+
const code = extractInlineCode(command);
|
|
543
|
+
if (!code) {
|
|
544
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
545
|
+
}
|
|
546
|
+
if (hasWriteExecChain(command)) {
|
|
547
|
+
return {
|
|
548
|
+
actionable: true,
|
|
549
|
+
allowed: false,
|
|
550
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
|
|
551
|
+
riskLevel: "critical",
|
|
552
|
+
riskType: "script_execution"
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
|
|
556
|
+
if (redirMatch) {
|
|
557
|
+
return {
|
|
558
|
+
actionable: true,
|
|
559
|
+
allowed: false,
|
|
560
|
+
reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
|
|
561
|
+
riskLevel: "critical",
|
|
562
|
+
riskType: "file_access"
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const isEchoOrPrintf1 = isLiteralTextCommand(command);
|
|
566
|
+
if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
|
|
567
|
+
return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
|
|
568
|
+
}
|
|
569
|
+
const isEchoOrPrintf = isLiteralTextCommand(command);
|
|
570
|
+
if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
|
|
571
|
+
return {
|
|
572
|
+
actionable: true,
|
|
573
|
+
allowed: false,
|
|
574
|
+
reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
|
|
575
|
+
riskLevel: "critical",
|
|
576
|
+
riskType: "file_access"
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
actionable: true,
|
|
581
|
+
allowed: true,
|
|
582
|
+
reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
|
|
583
|
+
riskLevel: "low",
|
|
584
|
+
riskType: "file_access"
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
|
|
588
|
+
var init_inline_analyzer = __esm({
|
|
589
|
+
"src/engine/inline-analyzer.ts"() {
|
|
590
|
+
"use strict";
|
|
591
|
+
init_esm_shims();
|
|
592
|
+
INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
593
|
+
ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
|
|
594
|
+
ECHO_CMD_RE = /(?:^|\s|;|&&|\|\|)(?:echo|printf)\s/gi;
|
|
595
|
+
READ_WRITE_KEYWORDS = [
|
|
596
|
+
// 函数调用形态(关键词后跟 '(')
|
|
597
|
+
"open(",
|
|
598
|
+
"read(",
|
|
599
|
+
"write(",
|
|
600
|
+
"fopen(",
|
|
601
|
+
"system(",
|
|
602
|
+
"exec(",
|
|
603
|
+
"eval(",
|
|
604
|
+
"popen(",
|
|
605
|
+
"load(",
|
|
606
|
+
"dump(",
|
|
607
|
+
// 文件读写(组合形态)
|
|
608
|
+
"readline",
|
|
609
|
+
"readlines",
|
|
610
|
+
"writelines",
|
|
611
|
+
"getpass",
|
|
612
|
+
"pickle.load",
|
|
613
|
+
"yaml.load",
|
|
614
|
+
"json.load",
|
|
615
|
+
"json.read",
|
|
616
|
+
"configparser",
|
|
617
|
+
"fread",
|
|
618
|
+
"fwrite",
|
|
619
|
+
"fgets",
|
|
620
|
+
// 子进程执行(组合形态)
|
|
621
|
+
"os.system",
|
|
622
|
+
"os.popen",
|
|
623
|
+
"subprocess",
|
|
624
|
+
"os.exec",
|
|
625
|
+
"os.spawn",
|
|
626
|
+
"import os",
|
|
627
|
+
"import subprocess",
|
|
628
|
+
"shell=true",
|
|
629
|
+
"check_output",
|
|
630
|
+
"check_call",
|
|
631
|
+
"run(",
|
|
632
|
+
// 绕过变体检测
|
|
633
|
+
"getattr(",
|
|
634
|
+
"__builtins__",
|
|
635
|
+
"globals(",
|
|
636
|
+
"locals(",
|
|
637
|
+
// PHP
|
|
638
|
+
"file_get_contents",
|
|
639
|
+
"file_put_contents",
|
|
640
|
+
"passthru",
|
|
641
|
+
"shell_exec",
|
|
642
|
+
"proc_open",
|
|
643
|
+
// Perl-Ruby
|
|
644
|
+
"<stdin>",
|
|
645
|
+
"open my"
|
|
646
|
+
];
|
|
647
|
+
WRITE_EXEC_CHAIN_PATTERNS = [
|
|
648
|
+
// echo/cat/printf "..." > file && bash/sh/python file
|
|
649
|
+
/(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
|
|
650
|
+
// ... | bash / | sh / | python(管道喂解释器执行)
|
|
651
|
+
/\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
|
|
652
|
+
// eval "$(cat ...)" / eval "$(echo ...)"
|
|
653
|
+
/eval\s+"\$\(/gi,
|
|
654
|
+
// xargs bash / xargs sh
|
|
655
|
+
/\|\s*xargs\s+(?:bash|sh|zsh)/gi
|
|
656
|
+
];
|
|
657
|
+
SENSITIVE_PATH_FRAGMENTS = [
|
|
658
|
+
"/etc/shadow",
|
|
659
|
+
"/etc/gshadow",
|
|
660
|
+
"/etc/passwd",
|
|
661
|
+
"/etc/sudoers",
|
|
662
|
+
"/etc/hosts",
|
|
663
|
+
"/etc/crontab",
|
|
664
|
+
"/etc/systemd/",
|
|
665
|
+
"/etc/ssh/sshd_config",
|
|
666
|
+
"/boot/",
|
|
667
|
+
"/proc/sys/",
|
|
668
|
+
"/sys/",
|
|
669
|
+
"/dev/mem",
|
|
670
|
+
"/dev/kmsg",
|
|
671
|
+
".ssh/id_rsa",
|
|
672
|
+
".ssh/id_ed25519",
|
|
673
|
+
".ssh/authorized_keys"
|
|
674
|
+
];
|
|
675
|
+
REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "i");
|
|
676
|
+
DIR_TRAVERSAL_KEYWORDS = [
|
|
677
|
+
// Python 目录遍历
|
|
678
|
+
"os.listdir",
|
|
679
|
+
"os.walk",
|
|
680
|
+
"os.scandir",
|
|
681
|
+
"glob.glob",
|
|
682
|
+
"glob.iglob",
|
|
683
|
+
"pathlib.",
|
|
684
|
+
"iterdir",
|
|
685
|
+
"rglob",
|
|
686
|
+
"scandir(",
|
|
687
|
+
"listdir("
|
|
688
|
+
];
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// src/engine/logic-rules.ts
|
|
693
|
+
import * as fs2 from "fs";
|
|
694
|
+
function runLogicCheck(input) {
|
|
695
|
+
const { actionHistory, aNext, cwd } = input;
|
|
696
|
+
const actionDetail = aNext.actionDetail.toLowerCase();
|
|
697
|
+
const actionType = aNext.actionType.toLowerCase();
|
|
698
|
+
const resolvedCwd = cwd || "/";
|
|
699
|
+
const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
|
|
700
|
+
const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
|
|
701
|
+
const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
|
|
702
|
+
if (rbwResult) return rbwResult;
|
|
703
|
+
if (input.promptSession && input.promptSession.trim()) {
|
|
704
|
+
if (!isExcludedType) {
|
|
705
|
+
const promptLower = input.promptSession.toLowerCase();
|
|
706
|
+
for (const pattern of INTENT_DEVIATION_PATTERNS) {
|
|
707
|
+
const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
|
|
708
|
+
const dangerousMatched = pattern.dangerousActions.some((kw) => actionDetail.includes(kw));
|
|
709
|
+
if (intentMatched && dangerousMatched) {
|
|
710
|
+
return {
|
|
711
|
+
allowed: false,
|
|
712
|
+
reason: pattern.reason,
|
|
713
|
+
riskLevel: "high",
|
|
714
|
+
riskType: "intent_deviation",
|
|
715
|
+
confidence: 85,
|
|
716
|
+
source: "logic_rule",
|
|
717
|
+
violatedLayers: ["2"]
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (!isExcludedType) {
|
|
724
|
+
const agentExt = getAgentExtensions(input.agentId || "");
|
|
725
|
+
const pathResult = checkSensitivePath(actionDetail, resolvedCwd, agentExt);
|
|
726
|
+
if (pathResult) return pathResult;
|
|
727
|
+
}
|
|
728
|
+
if (actionType !== "file_write" && actionType !== "file_edit") {
|
|
729
|
+
const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
|
|
730
|
+
const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
|
|
731
|
+
if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
|
|
732
|
+
return {
|
|
733
|
+
allowed: false,
|
|
734
|
+
reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
|
|
735
|
+
riskLevel: "high",
|
|
736
|
+
riskType: "script_execution",
|
|
737
|
+
confidence: 90,
|
|
738
|
+
source: "logic_rule",
|
|
739
|
+
violatedLayers: ["2"]
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
|
|
743
|
+
return {
|
|
744
|
+
allowed: false,
|
|
745
|
+
reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
|
|
746
|
+
riskLevel: "critical",
|
|
747
|
+
riskType: "file_access",
|
|
748
|
+
confidence: 95,
|
|
749
|
+
source: "logic_rule",
|
|
750
|
+
violatedLayers: ["2"]
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
var _READ_ONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
755
|
+
"cat",
|
|
756
|
+
"head",
|
|
757
|
+
"tail",
|
|
758
|
+
"less",
|
|
759
|
+
"more",
|
|
760
|
+
"grep",
|
|
761
|
+
"find",
|
|
762
|
+
"awk",
|
|
763
|
+
"sed",
|
|
764
|
+
"sort",
|
|
765
|
+
"uniq",
|
|
766
|
+
"wc",
|
|
767
|
+
"cut",
|
|
768
|
+
"strings",
|
|
769
|
+
"od",
|
|
770
|
+
"xxd",
|
|
771
|
+
"hexdump",
|
|
772
|
+
"tr",
|
|
773
|
+
"file",
|
|
774
|
+
"stat",
|
|
775
|
+
"du",
|
|
776
|
+
"df",
|
|
777
|
+
"ls",
|
|
778
|
+
"dir",
|
|
779
|
+
"tree",
|
|
780
|
+
"nl",
|
|
781
|
+
"tac",
|
|
782
|
+
"rev",
|
|
783
|
+
"lsblk",
|
|
784
|
+
"blockdev",
|
|
785
|
+
"smartctl",
|
|
786
|
+
"udevadm",
|
|
787
|
+
"dmidecode",
|
|
788
|
+
"lscpu",
|
|
789
|
+
"lspci",
|
|
790
|
+
"lsusb",
|
|
791
|
+
"lsmem",
|
|
792
|
+
"lsns",
|
|
793
|
+
"lsof",
|
|
794
|
+
"hwinfo",
|
|
795
|
+
"inxi",
|
|
796
|
+
"fdisk",
|
|
797
|
+
"parted",
|
|
798
|
+
"dmesg",
|
|
799
|
+
"journalctl",
|
|
800
|
+
"systemctl",
|
|
801
|
+
"hostnamectl",
|
|
802
|
+
"localectl",
|
|
803
|
+
"timedatectl",
|
|
804
|
+
"ps",
|
|
805
|
+
"top",
|
|
806
|
+
"free",
|
|
807
|
+
"vmstat",
|
|
808
|
+
"iostat",
|
|
809
|
+
"mpstat",
|
|
810
|
+
"pidof",
|
|
811
|
+
"pgrep",
|
|
812
|
+
"ip",
|
|
813
|
+
"ifconfig",
|
|
814
|
+
"route",
|
|
815
|
+
"ss",
|
|
816
|
+
"netstat",
|
|
817
|
+
"arp",
|
|
818
|
+
"ethtool",
|
|
819
|
+
"nmcli",
|
|
820
|
+
"uname",
|
|
821
|
+
"arch",
|
|
822
|
+
"nproc",
|
|
823
|
+
"getconf",
|
|
824
|
+
"getent",
|
|
825
|
+
"id",
|
|
826
|
+
"whoami",
|
|
827
|
+
"who",
|
|
828
|
+
"w",
|
|
829
|
+
"last",
|
|
830
|
+
"uptime",
|
|
831
|
+
"lsmod",
|
|
832
|
+
"modinfo",
|
|
833
|
+
"sysctl"
|
|
834
|
+
]);
|
|
835
|
+
var _DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
836
|
+
var _WRITE_COMMAND_RES = [
|
|
837
|
+
/\brm\b/,
|
|
838
|
+
/\bunlink\b/,
|
|
839
|
+
/\brmdir\b/,
|
|
840
|
+
/\bshred\b/,
|
|
841
|
+
/\bcp\b/,
|
|
842
|
+
/\bmv\b/,
|
|
843
|
+
/\binstall\b/,
|
|
844
|
+
/\btee\b/,
|
|
845
|
+
/\bdd\b/,
|
|
846
|
+
/\bmkfs\b/,
|
|
847
|
+
/\b(fdisk|parted|cfdisk|sfdisk)\b/,
|
|
848
|
+
/\bchmod\b/,
|
|
849
|
+
/\bchown\b/,
|
|
850
|
+
/\btruncate\b/
|
|
851
|
+
];
|
|
852
|
+
const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
|
|
853
|
+
if (indirectResult) return indirectResult;
|
|
854
|
+
if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
|
|
855
|
+
const adminContext = actionHistory.some(
|
|
856
|
+
(h) => h.actionDetail.includes("sudo") || h.actionDetail.includes("apt") || h.actionDetail.includes("systemctl") || h.actionDetail.includes("yum") || h.actionDetail.includes("dnf") || h.actionDetail.includes("pacman") || h.actionDetail.includes("zypper") || h.actionDetail.includes("apk")
|
|
857
|
+
);
|
|
858
|
+
if (!adminContext) {
|
|
859
|
+
return {
|
|
860
|
+
allowed: false,
|
|
861
|
+
reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
|
|
862
|
+
riskLevel: "high",
|
|
863
|
+
riskType: "privilege_escalation",
|
|
864
|
+
confidence: 85,
|
|
865
|
+
source: "logic_rule",
|
|
866
|
+
violatedLayers: ["2"]
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
|
|
871
|
+
const targetHost = extractRemoteHost(actionDetail);
|
|
872
|
+
if (targetHost && !isLocalHost(targetHost)) {
|
|
873
|
+
return {
|
|
874
|
+
allowed: false,
|
|
875
|
+
reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
|
|
876
|
+
riskLevel: "medium",
|
|
877
|
+
riskType: "lateral_movement",
|
|
878
|
+
confidence: 80,
|
|
879
|
+
source: "logic_rule",
|
|
880
|
+
violatedLayers: ["2"]
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
|
|
885
|
+
if (pwdResult) return pwdResult;
|
|
886
|
+
const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory);
|
|
887
|
+
if (userDelResult) return userDelResult;
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
function extractFilePaths(text) {
|
|
891
|
+
const paths = [];
|
|
892
|
+
const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
|
|
893
|
+
if (unixPaths) paths.push(...unixPaths.map((p) => p.trim()));
|
|
894
|
+
const quotedPaths = text.match(/["']\/([^"']+)["']/g);
|
|
895
|
+
if (quotedPaths) paths.push(...quotedPaths.map((p) => p.replace(/["']/g, "")));
|
|
896
|
+
return [...new Set(paths)];
|
|
897
|
+
}
|
|
898
|
+
function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd) {
|
|
899
|
+
if (NON_WRITE_TOOLS.has(actionType)) return null;
|
|
900
|
+
let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
|
|
901
|
+
if (!isWrite && actionDetail.includes(">")) {
|
|
902
|
+
const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
|
|
903
|
+
if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
|
|
904
|
+
isWrite = true;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
if (!isWrite) return null;
|
|
908
|
+
const targetPaths = extractFilePaths(actionDetail);
|
|
909
|
+
if (targetPaths.length === 0) return null;
|
|
910
|
+
const historyReadPaths = /* @__PURE__ */ new Set();
|
|
911
|
+
for (const action of actionHistory) {
|
|
912
|
+
const histDetail = action.actionDetail.toLowerCase();
|
|
913
|
+
const histType = action.actionType.toLowerCase();
|
|
914
|
+
const isRead = READ_KEYWORDS.some((kw) => histType.includes(kw) || histDetail.includes(kw));
|
|
915
|
+
if (isRead) {
|
|
916
|
+
for (const p of extractFilePaths(histDetail)) {
|
|
917
|
+
historyReadPaths.add(p);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const unreadPaths = [];
|
|
922
|
+
for (const path3 of targetPaths) {
|
|
923
|
+
const hasRead = [...historyReadPaths].some(
|
|
924
|
+
(rp) => path3 === rp || path3.startsWith(rp) || rp.startsWith(path3)
|
|
925
|
+
);
|
|
926
|
+
if (!hasRead) {
|
|
927
|
+
let isCreate = !fs2.existsSync(path3);
|
|
928
|
+
if (!isCreate) {
|
|
929
|
+
isCreate = CREATE_KEYWORDS.some((kw) => reason.toLowerCase().includes(kw) || actionDetail.includes(kw));
|
|
930
|
+
}
|
|
931
|
+
if (!isCreate) {
|
|
932
|
+
unreadPaths.push(path3);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (unreadPaths.length > 0) {
|
|
937
|
+
return {
|
|
938
|
+
allowed: false,
|
|
939
|
+
reason: `\u8FDD\u53CD read_before_write \u5B89\u5168\u539F\u5219\uFF1A\u5BF9\u6587\u4EF6 ${unreadPaths[0]} \u6267\u884C\u5199\u5165\u64CD\u4F5C\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5BF9\u8BE5\u6587\u4EF6\u7684\u8BFB\u53D6\u64CD\u4F5C`,
|
|
940
|
+
riskLevel: "high",
|
|
941
|
+
riskType: "read_before_write",
|
|
942
|
+
confidence: 85,
|
|
943
|
+
source: "logic_rule",
|
|
944
|
+
violatedLayers: ["2"]
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
function checkSensitivePath(command, cwd, agentExt) {
|
|
950
|
+
const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
|
|
951
|
+
const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
|
|
952
|
+
const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
|
|
953
|
+
const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
|
|
954
|
+
const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
|
|
955
|
+
for (const sp of ROOT_SENSITIVE_PATHS) {
|
|
956
|
+
if (command.includes(sp.path.toLowerCase())) {
|
|
957
|
+
if (sp.path === "/dev/random") {
|
|
958
|
+
if (isWriteOpFlag) {
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
return {
|
|
962
|
+
allowed: false,
|
|
963
|
+
reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
964
|
+
riskLevel: sp.riskLevel,
|
|
965
|
+
riskType: "sensitive_path_access",
|
|
966
|
+
confidence: 90,
|
|
967
|
+
source: "logic_rule",
|
|
968
|
+
violatedLayers: ["2"]
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
allowed: false,
|
|
976
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
977
|
+
riskLevel: sp.riskLevel,
|
|
978
|
+
riskType: "sensitive_path_access",
|
|
979
|
+
confidence: 90,
|
|
980
|
+
source: "logic_rule",
|
|
981
|
+
violatedLayers: ["2"]
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
for (const sp of HOME_SENSITIVE_PATHS) {
|
|
986
|
+
const expanded = `${expandedCwd}/${sp.path}`;
|
|
987
|
+
if (command.includes(sp.path.toLowerCase()) || command.includes(expanded.toLowerCase())) {
|
|
988
|
+
if (!isWriteOpFlag && !sp.credential) {
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
return {
|
|
992
|
+
allowed: false,
|
|
993
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
994
|
+
riskLevel: sp.riskLevel,
|
|
995
|
+
riskType: "sensitive_path_access",
|
|
996
|
+
confidence: 90,
|
|
997
|
+
source: "logic_rule",
|
|
998
|
+
violatedLayers: ["2"]
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
|
|
1003
|
+
for (const sp of agentExt.extra_sensitive_paths) {
|
|
1004
|
+
const spPath = sp.path.toLowerCase();
|
|
1005
|
+
const expanded = `${expandedCwd}/${spPath}`;
|
|
1006
|
+
if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
|
|
1007
|
+
const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
|
|
1008
|
+
if (denyMode === "deny_read") {
|
|
1009
|
+
if (isWriteOpFlag) continue;
|
|
1010
|
+
} else if (denyMode === "deny_write") {
|
|
1011
|
+
if (!isWriteOpFlag) continue;
|
|
1012
|
+
}
|
|
1013
|
+
return {
|
|
1014
|
+
allowed: false,
|
|
1015
|
+
reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
|
|
1016
|
+
riskLevel: sp.risk_level,
|
|
1017
|
+
riskType: "sensitive_path_access",
|
|
1018
|
+
confidence: 90,
|
|
1019
|
+
source: "logic_rule",
|
|
1020
|
+
violatedLayers: ["2"]
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
for (const cp of CREDENTIAL_PATHS) {
|
|
1026
|
+
const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1027
|
+
const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
|
|
1028
|
+
if (re.test(command)) {
|
|
1029
|
+
return {
|
|
1030
|
+
allowed: false,
|
|
1031
|
+
reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
|
|
1032
|
+
riskLevel: cp.riskLevel,
|
|
1033
|
+
riskType: "sensitive_path_access",
|
|
1034
|
+
confidence: 90,
|
|
1035
|
+
source: "logic_rule",
|
|
1036
|
+
violatedLayers: ["2"],
|
|
1037
|
+
isCredential: true
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
|
|
1042
|
+
return {
|
|
1043
|
+
allowed: false,
|
|
1044
|
+
reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
|
|
1045
|
+
riskLevel: "critical",
|
|
1046
|
+
riskType: "sensitive_path_access",
|
|
1047
|
+
confidence: 90,
|
|
1048
|
+
source: "logic_rule",
|
|
1049
|
+
violatedLayers: ["2"]
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
function extractRemoteHost(command) {
|
|
1055
|
+
const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
|
|
1056
|
+
return m ? m[1] : null;
|
|
1057
|
+
}
|
|
1058
|
+
function isLocalHost(host) {
|
|
1059
|
+
const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
|
|
1060
|
+
return localNames.includes(host.replace(/.*@/, ""));
|
|
1061
|
+
}
|
|
1062
|
+
function checkPasswordConsent(actionType, actionDetail, actionHistory) {
|
|
1063
|
+
if (actionType !== "bash") return null;
|
|
1064
|
+
const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
|
|
1065
|
+
if (!isPasswordModify) return null;
|
|
1066
|
+
const hasConsent = actionHistory.some(
|
|
1067
|
+
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1068
|
+
);
|
|
1069
|
+
if (hasConsent) return null;
|
|
1070
|
+
return {
|
|
1071
|
+
allowed: false,
|
|
1072
|
+
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",
|
|
1073
|
+
riskLevel: "high",
|
|
1074
|
+
riskType: "consent_missing",
|
|
1075
|
+
confidence: 85,
|
|
1076
|
+
source: "logic_rule",
|
|
1077
|
+
violatedLayers: ["2"]
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
|
|
1081
|
+
if (actionType !== "bash") return null;
|
|
1082
|
+
const isUserDeletion = USER_DELETION_PATTERNS.some((p) => p.test(actionDetail));
|
|
1083
|
+
if (!isUserDeletion) return null;
|
|
1084
|
+
const hasConsent = actionHistory.some(
|
|
1085
|
+
(h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
|
|
1086
|
+
);
|
|
1087
|
+
if (hasConsent) return null;
|
|
1088
|
+
return {
|
|
1089
|
+
allowed: false,
|
|
1090
|
+
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",
|
|
1091
|
+
riskLevel: "high",
|
|
1092
|
+
riskType: "consent_missing",
|
|
1093
|
+
confidence: 85,
|
|
1094
|
+
source: "logic_rule",
|
|
1095
|
+
violatedLayers: ["2"]
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function checkIndirectFileAccess(actionType, actionDetail, cwd) {
|
|
1099
|
+
if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
INLINE_SCRIPT_RE.lastIndex = 0;
|
|
1103
|
+
if (!INLINE_SCRIPT_RE.test(actionDetail)) {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
const lowerDetail = actionDetail.toLowerCase();
|
|
1107
|
+
const hasTraversal = DIR_TRAVERSAL_KW.some((kw) => lowerDetail.includes(kw));
|
|
1108
|
+
if (!hasTraversal) {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
const READ_KEYWORDS2 = ["read(", "readline", "readlines", "fread", "fgets", "load(", "getpass", "file_get_contents"];
|
|
1112
|
+
const WRITE_KEYWORDS2 = ["write(", "writelines", "fwrite", "file_put_contents"];
|
|
1113
|
+
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"];
|
|
1114
|
+
const OPEN_KEYWORDS = ["open(", "fopen("];
|
|
1115
|
+
const code = extractInlineCode(actionDetail);
|
|
1116
|
+
if (!code) return null;
|
|
1117
|
+
const codeLower = code.toLowerCase();
|
|
1118
|
+
const hasAnyOp = READ_KEYWORDS2.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));
|
|
1119
|
+
if (!hasAnyOp) {
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
const hasWrite = WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw));
|
|
1123
|
+
const hasExec = EXEC_KEYWORDS.some((kw) => codeLower.includes(kw));
|
|
1124
|
+
const blockAllSensitive = hasWrite || hasExec;
|
|
1125
|
+
let actualFiles = [];
|
|
1126
|
+
if (cwd && cwd !== "/") {
|
|
1127
|
+
try {
|
|
1128
|
+
actualFiles = fs2.readdirSync(cwd);
|
|
1129
|
+
} catch {
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (actualFiles.length > 0) {
|
|
1133
|
+
for (const cp of CREDENTIAL_PATHS) {
|
|
1134
|
+
const fname = cp.path.split("/").pop() || cp.path;
|
|
1135
|
+
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1136
|
+
return {
|
|
1137
|
+
allowed: false,
|
|
1138
|
+
reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u51ED\u636E\u6587\u4EF6: ${fname}`,
|
|
1139
|
+
riskLevel: "high",
|
|
1140
|
+
riskType: "file_access",
|
|
1141
|
+
confidence: 95,
|
|
1142
|
+
source: "logic_rule",
|
|
1143
|
+
violatedLayers: ["2"],
|
|
1144
|
+
isCredential: true
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (blockAllSensitive) {
|
|
1149
|
+
for (const sp of [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS]) {
|
|
1150
|
+
const fname = sp.path.split("/").pop() || sp.path;
|
|
1151
|
+
if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
|
|
1152
|
+
return {
|
|
1153
|
+
allowed: false,
|
|
1154
|
+
reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u654F\u611F\u6587\u4EF6: ${fname}`,
|
|
1155
|
+
riskLevel: "high",
|
|
1156
|
+
riskType: "file_access",
|
|
1157
|
+
confidence: 95,
|
|
1158
|
+
source: "logic_rule",
|
|
1159
|
+
violatedLayers: ["2"]
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return null;
|
|
1165
|
+
}
|
|
1166
|
+
return {
|
|
1167
|
+
allowed: false,
|
|
1168
|
+
reason: "\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u51FD\u6570\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0C\u65E0\u6CD5\u83B7\u53D6 cwd \u5B9E\u9645\u6587\u4EF6\u5217\u8868\uFF0C\u8F6C\u5C423 LLM \u5206\u6790",
|
|
1169
|
+
riskLevel: "medium",
|
|
1170
|
+
riskType: "file_access",
|
|
1171
|
+
confidence: 70,
|
|
1172
|
+
source: "logic_rule",
|
|
1173
|
+
violatedLayers: ["2"]
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
|
|
1177
|
+
var init_logic_rules = __esm({
|
|
1178
|
+
"src/engine/logic-rules.ts"() {
|
|
1179
|
+
"use strict";
|
|
1180
|
+
init_esm_shims();
|
|
1181
|
+
init_inline_analyzer();
|
|
1182
|
+
init_agents();
|
|
1183
|
+
ROOT_SENSITIVE_PATHS = [
|
|
1184
|
+
{ path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1185
|
+
{ path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
|
|
1186
|
+
// 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
|
|
1187
|
+
{ path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
|
|
1188
|
+
{ path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
|
|
1189
|
+
// 系统配置(仅拦截写入)
|
|
1190
|
+
{ path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
|
|
1191
|
+
{ path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
|
|
1192
|
+
{ path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
|
|
1193
|
+
{ path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
|
|
1194
|
+
{ path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
|
|
1195
|
+
{ path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
|
|
1196
|
+
{ path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
|
|
1197
|
+
// 危险目录(仅拦截写入)
|
|
1198
|
+
{ path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
|
|
1199
|
+
{ path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
|
|
1200
|
+
{ path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
|
|
1201
|
+
// 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
|
|
1202
|
+
{ path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
|
|
1203
|
+
{ path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
|
|
1204
|
+
{ path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
|
|
1205
|
+
// SSH 主机私钥
|
|
1206
|
+
{ path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
|
|
1207
|
+
{ path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
|
|
1208
|
+
{ path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
|
|
1209
|
+
];
|
|
1210
|
+
HOME_SENSITIVE_PATHS = [
|
|
1211
|
+
{ path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
|
|
1212
|
+
{ path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
|
|
1213
|
+
{ path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
|
|
1214
|
+
// 持久化与命令劫持(仅拦截写入)
|
|
1215
|
+
{ path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
|
|
1216
|
+
{ path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
|
|
1217
|
+
{ path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
|
|
1218
|
+
{ path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
|
|
1219
|
+
{ path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
|
|
1220
|
+
{ path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
|
|
1221
|
+
];
|
|
1222
|
+
CREDENTIAL_PATHS = [
|
|
1223
|
+
{ path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
1224
|
+
{ path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
|
|
1225
|
+
{ path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
1226
|
+
{ path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
|
|
1227
|
+
{ path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
|
|
1228
|
+
];
|
|
1229
|
+
NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
|
|
1230
|
+
READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
|
|
1231
|
+
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"];
|
|
1232
|
+
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"];
|
|
1233
|
+
CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
|
|
1234
|
+
PASSWORD_MODIFY_PATTERNS = [
|
|
1235
|
+
/\|\s*passwd\b/i,
|
|
1236
|
+
/\bpasswd\s+--stdin\b/i,
|
|
1237
|
+
/\bchpasswd\b/i,
|
|
1238
|
+
/\bchgpasswd\b/i,
|
|
1239
|
+
/\b(newusers|lnewusers)\b/i,
|
|
1240
|
+
/\bpasswd\s+-[a-zA-Z]*d\b/i,
|
|
1241
|
+
/\bpasswd\s+-[a-zA-Z]*l\b/i
|
|
1242
|
+
];
|
|
1243
|
+
USER_DELETION_PATTERNS = [
|
|
1244
|
+
/\buserdel\s/i,
|
|
1245
|
+
/\bdeluser\s/i,
|
|
1246
|
+
/\bluserdel\s/i,
|
|
1247
|
+
/\bgroupdel\s/i,
|
|
1248
|
+
/\blgroupdel\s/i
|
|
1249
|
+
];
|
|
1250
|
+
INTENT_DEVIATION_PATTERNS = [
|
|
1251
|
+
{
|
|
1252
|
+
intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
|
|
1253
|
+
dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
|
|
1254
|
+
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
|
|
1258
|
+
dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
|
|
1259
|
+
reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
|
|
1260
|
+
}
|
|
1261
|
+
];
|
|
1262
|
+
INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
|
|
1263
|
+
DIR_TRAVERSAL_KW = [
|
|
1264
|
+
"os.listdir",
|
|
1265
|
+
"os.walk",
|
|
1266
|
+
"os.scandir",
|
|
1267
|
+
"glob.glob",
|
|
1268
|
+
"glob.iglob",
|
|
1269
|
+
"pathlib.",
|
|
1270
|
+
"iterdir",
|
|
1271
|
+
"rglob",
|
|
1272
|
+
"scandir(",
|
|
1273
|
+
"listdir("
|
|
1274
|
+
];
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
// src/engine/skill-loader.ts
|
|
1279
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
|
|
1280
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
1281
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1282
|
+
function loadAllSkills() {
|
|
1283
|
+
if (cachedSkills) return cachedSkills;
|
|
1284
|
+
cachedSkills = {};
|
|
1285
|
+
const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
|
|
1286
|
+
if (!existsSync3(dir)) {
|
|
1287
|
+
console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
|
|
1288
|
+
return cachedSkills;
|
|
1289
|
+
}
|
|
1290
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1291
|
+
for (const f of files) {
|
|
1292
|
+
const name = f.replace(".md", "");
|
|
1293
|
+
cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
|
|
1294
|
+
}
|
|
1295
|
+
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1296
|
+
return cachedSkills;
|
|
1297
|
+
}
|
|
1298
|
+
function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
|
|
1299
|
+
const skills = loadAllSkills();
|
|
1300
|
+
const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
|
|
1301
|
+
const actionDetailLower = actionDetail.toLowerCase();
|
|
1302
|
+
const actionTypeLower = actionType.toLowerCase();
|
|
1303
|
+
const reasonLower = reason.toLowerCase();
|
|
1304
|
+
const scores = {};
|
|
1305
|
+
for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
|
|
1306
|
+
let score = 0;
|
|
1307
|
+
for (const kw of keywords) {
|
|
1308
|
+
if (combined.includes(kw)) {
|
|
1309
|
+
if (kw === actionTypeLower) score += 2;
|
|
1310
|
+
else if (actionDetailLower.includes(kw)) score += 1.5;
|
|
1311
|
+
else if (reasonLower.includes(kw)) score += 1;
|
|
1312
|
+
else score += 0.5;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if (score > 0) scores[skillName] = score;
|
|
1316
|
+
}
|
|
1317
|
+
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
1318
|
+
const top = sorted.slice(0, maxSkills);
|
|
1319
|
+
const maxScore = top[0]?.[1] || 1;
|
|
1320
|
+
const results = [];
|
|
1321
|
+
for (const [name, score] of top) {
|
|
1322
|
+
const content = skills[name];
|
|
1323
|
+
if (content) {
|
|
1324
|
+
results.push({
|
|
1325
|
+
skillName: name,
|
|
1326
|
+
relevance: Math.min(score / maxScore, 1),
|
|
1327
|
+
skillContent: content
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (results.length === 0) {
|
|
1332
|
+
const fallback = skills["general_tool_risk_guard"];
|
|
1333
|
+
if (fallback) {
|
|
1334
|
+
results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return results;
|
|
1338
|
+
}
|
|
1339
|
+
function formatSkillsForPrompt(skills) {
|
|
1340
|
+
if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
|
|
1341
|
+
return skills.map(
|
|
1342
|
+
(s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
|
|
1343
|
+
|
|
1344
|
+
${s.skillContent}`
|
|
1345
|
+
).join("\n\n---\n\n");
|
|
1346
|
+
}
|
|
1347
|
+
var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1348
|
+
var init_skill_loader = __esm({
|
|
1349
|
+
"src/engine/skill-loader.ts"() {
|
|
1350
|
+
"use strict";
|
|
1351
|
+
init_esm_shims();
|
|
1352
|
+
__filename3 = fileURLToPath3(import.meta.url);
|
|
1353
|
+
__dirname3 = dirname2(__filename3);
|
|
1354
|
+
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1355
|
+
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1356
|
+
SKILL_KEYWORD_MAP = {
|
|
1357
|
+
"file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
|
|
1358
|
+
"script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
|
|
1359
|
+
"data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
|
|
1360
|
+
"browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
|
|
1361
|
+
"email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
|
|
1362
|
+
"general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
|
|
1363
|
+
"lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
|
|
1364
|
+
"persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys", "bashrc", "zshrc", "profile", ".rc", "shell config", "shell \u914D\u7F6E"],
|
|
1365
|
+
// 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
|
|
1366
|
+
// 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
|
|
1367
|
+
"resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
|
|
1368
|
+
"skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
|
|
1369
|
+
"supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
|
|
1370
|
+
"intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"],
|
|
1371
|
+
"indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
|
|
1372
|
+
};
|
|
1373
|
+
cachedSkills = null;
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
// src/engine/token-stats.ts
|
|
1378
|
+
var token_stats_exports = {};
|
|
1379
|
+
__export(token_stats_exports, {
|
|
1380
|
+
getRecentRecords: () => getRecentRecords,
|
|
1381
|
+
getTokenStats: () => getTokenStats,
|
|
1382
|
+
getTokenTrend: () => getTokenTrend,
|
|
1383
|
+
recordTokenUsage: () => recordTokenUsage,
|
|
1384
|
+
resetTokenStats: () => resetTokenStats
|
|
1385
|
+
});
|
|
1386
|
+
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
|
|
1387
|
+
import { join as join3 } from "path";
|
|
1388
|
+
import { homedir as homedir2 } from "os";
|
|
1389
|
+
function getStatsPath() {
|
|
1390
|
+
const envPath = process.env[ENV_STATS_PATH];
|
|
1391
|
+
return envPath || STATS_FILE;
|
|
1392
|
+
}
|
|
1393
|
+
function loadAllRecords(statsPath) {
|
|
1394
|
+
if (!existsSync6(statsPath)) return [];
|
|
1395
|
+
try {
|
|
1396
|
+
const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
|
|
1397
|
+
if (Array.isArray(data)) return data;
|
|
1398
|
+
if (data && Array.isArray(data.records)) return data.records;
|
|
1399
|
+
return [];
|
|
1400
|
+
} catch (e) {
|
|
1401
|
+
return [];
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
function saveRecords(statsPath, records) {
|
|
1405
|
+
try {
|
|
1406
|
+
const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
|
|
1407
|
+
if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
|
|
1408
|
+
writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
|
|
1409
|
+
} catch (e) {
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function recordTokenUsage(record) {
|
|
1413
|
+
const statsPath = getStatsPath();
|
|
1414
|
+
const records = loadAllRecords(statsPath);
|
|
1415
|
+
records.push({
|
|
1416
|
+
timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1417
|
+
session_id: record.sessionId,
|
|
1418
|
+
step: record.step,
|
|
1419
|
+
model: record.model,
|
|
1420
|
+
prompt_tokens: record.promptTokens,
|
|
1421
|
+
completion_tokens: record.completionTokens,
|
|
1422
|
+
total_tokens: record.totalTokens
|
|
1423
|
+
});
|
|
1424
|
+
if (records.length > MAX_RECORDS) {
|
|
1425
|
+
records.splice(0, records.length - MAX_RECORDS);
|
|
1426
|
+
}
|
|
1427
|
+
saveRecords(statsPath, records);
|
|
1428
|
+
}
|
|
1429
|
+
function filterByDate(records, days = 0, startDate, endDate) {
|
|
1430
|
+
if (startDate || endDate) {
|
|
1431
|
+
let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
|
|
1432
|
+
let endDt = endDate ? new Date(endDate) : void 0;
|
|
1433
|
+
if (endDate) {
|
|
1434
|
+
const e = new Date(endDate);
|
|
1435
|
+
e.setHours(23, 59, 59, 999);
|
|
1436
|
+
endDt = e;
|
|
1437
|
+
}
|
|
1438
|
+
const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
|
|
1439
|
+
return records.filter((r) => {
|
|
1440
|
+
try {
|
|
1441
|
+
const ts = new Date(r.timestamp).getTime();
|
|
1442
|
+
return ts >= startDt.getTime() && ts <= endMs;
|
|
1443
|
+
} catch {
|
|
1444
|
+
return true;
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
} else if (days > 0) {
|
|
1448
|
+
const cutoff = Date.now() - days * 864e5;
|
|
1449
|
+
return records.filter((r) => {
|
|
1450
|
+
try {
|
|
1451
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
1452
|
+
} catch {
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
return records;
|
|
1458
|
+
}
|
|
1459
|
+
function aggregate(records) {
|
|
1460
|
+
let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
|
|
1461
|
+
const byStep = {};
|
|
1462
|
+
const byModel = {};
|
|
1463
|
+
const byDate = {};
|
|
1464
|
+
const bucket = (b, key, r) => {
|
|
1465
|
+
const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
|
|
1466
|
+
e.prompt_tokens += r.prompt_tokens || 0;
|
|
1467
|
+
e.completion_tokens += r.completion_tokens || 0;
|
|
1468
|
+
e.total_tokens += r.total_tokens || 0;
|
|
1469
|
+
e.calls += 1;
|
|
1470
|
+
};
|
|
1471
|
+
for (const r of records) {
|
|
1472
|
+
totalPrompt += r.prompt_tokens || 0;
|
|
1473
|
+
totalCompletion += r.completion_tokens || 0;
|
|
1474
|
+
totalTotal += r.total_tokens || 0;
|
|
1475
|
+
bucket(byStep, r.step || "unknown", r);
|
|
1476
|
+
bucket(byModel, r.model || "unknown", r);
|
|
1477
|
+
let dateKey = "unknown";
|
|
1478
|
+
try {
|
|
1479
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1480
|
+
} catch {
|
|
1481
|
+
}
|
|
1482
|
+
bucket(byDate, dateKey, r);
|
|
1483
|
+
}
|
|
1484
|
+
const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
1485
|
+
const sortedByDate = {};
|
|
1486
|
+
for (const k of sortedDate) sortedByDate[k] = byDate[k];
|
|
1487
|
+
return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
|
|
1488
|
+
}
|
|
1489
|
+
function getTokenStats(days = 0, startDate, endDate) {
|
|
1490
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1491
|
+
const a = aggregate(records);
|
|
1492
|
+
return {
|
|
1493
|
+
total_prompt_tokens: a.totalPrompt,
|
|
1494
|
+
total_completion_tokens: a.totalCompletion,
|
|
1495
|
+
total_tokens: a.totalTotal,
|
|
1496
|
+
total_calls: records.length,
|
|
1497
|
+
by_step: a.byStep,
|
|
1498
|
+
by_model: a.byModel,
|
|
1499
|
+
by_date: a.byDate
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
function getRecentRecords(limit = 20) {
|
|
1503
|
+
const records = loadAllRecords(getStatsPath());
|
|
1504
|
+
return records.slice(-limit);
|
|
1505
|
+
}
|
|
1506
|
+
function getTokenTrend(days = 0, startDate, endDate) {
|
|
1507
|
+
const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
|
|
1508
|
+
const series = {};
|
|
1509
|
+
const datesSet = /* @__PURE__ */ new Set();
|
|
1510
|
+
for (const r of records) {
|
|
1511
|
+
let dateKey;
|
|
1512
|
+
try {
|
|
1513
|
+
dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
|
|
1514
|
+
} catch {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
const model = r.model || "unknown";
|
|
1518
|
+
const tokens = r.total_tokens || 0;
|
|
1519
|
+
datesSet.add(dateKey);
|
|
1520
|
+
series[model] = series[model] || {};
|
|
1521
|
+
series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
|
|
1522
|
+
}
|
|
1523
|
+
const dates = Array.from(datesSet).sort();
|
|
1524
|
+
const models = Object.keys(series).sort();
|
|
1525
|
+
return { dates, models, series };
|
|
1526
|
+
}
|
|
1527
|
+
function resetTokenStats() {
|
|
1528
|
+
const statsPath = getStatsPath();
|
|
1529
|
+
if (existsSync6(statsPath)) {
|
|
1530
|
+
try {
|
|
1531
|
+
unlinkSync(statsPath);
|
|
1532
|
+
} catch {
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
|
|
1537
|
+
var init_token_stats = __esm({
|
|
1538
|
+
"src/engine/token-stats.ts"() {
|
|
1539
|
+
"use strict";
|
|
1540
|
+
init_esm_shims();
|
|
1541
|
+
STATS_DIR = join3(homedir2(), ".config", "agentmoss");
|
|
1542
|
+
STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
|
|
1543
|
+
ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
|
|
1544
|
+
MAX_RECORDS = 1e4;
|
|
1545
|
+
}
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
// src/infra/runtime-config.ts
|
|
1549
|
+
var runtime_config_exports = {};
|
|
1550
|
+
__export(runtime_config_exports, {
|
|
1551
|
+
DENY_MODE_LABELS: () => DENY_MODE_LABELS,
|
|
1552
|
+
addCustomRule: () => addCustomRule,
|
|
1553
|
+
addCustomSkill: () => addCustomSkill,
|
|
1554
|
+
deleteCustomRule: () => deleteCustomRule,
|
|
1555
|
+
deleteCustomSkill: () => deleteCustomSkill,
|
|
1556
|
+
filterEnabledRules: () => filterEnabledRules,
|
|
1557
|
+
generateSourceDefaults: () => generateSourceDefaults,
|
|
1558
|
+
getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
|
|
1559
|
+
getDisabledSkillIds: () => getDisabledSkillIds,
|
|
1560
|
+
getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
|
|
1561
|
+
getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
|
|
1562
|
+
getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
|
|
1563
|
+
getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
|
|
1564
|
+
getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
|
|
1565
|
+
getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
|
|
1566
|
+
getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
|
|
1567
|
+
getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
|
|
1568
|
+
getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
|
|
1569
|
+
getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
|
|
1570
|
+
getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
|
|
1571
|
+
getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
|
|
1572
|
+
getEnvOverrides: () => getEnvOverrides,
|
|
1573
|
+
getProfileOrDetect: () => getProfileOrDetect,
|
|
1574
|
+
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
1575
|
+
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
1576
|
+
isL2CategoryEnabled: () => isL2CategoryEnabled,
|
|
1577
|
+
isLayerEnabled: () => isLayerEnabled,
|
|
1578
|
+
loadRuntimeConfig: () => loadRuntimeConfig,
|
|
1579
|
+
mergeConfig: () => mergeConfig,
|
|
1580
|
+
saveRuntimeConfig: () => saveRuntimeConfig,
|
|
1581
|
+
updateCategoryEnabled: () => updateCategoryEnabled,
|
|
1582
|
+
updateLayerEnabled: () => updateLayerEnabled,
|
|
1583
|
+
updateRuleDenyMode: () => updateRuleDenyMode,
|
|
1584
|
+
updateRuleEnabled: () => updateRuleEnabled,
|
|
1585
|
+
updateRuleSkipL3: () => updateRuleSkipL3,
|
|
1586
|
+
updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
|
|
1587
|
+
updateSkillEnabled: () => updateSkillEnabled
|
|
1588
|
+
});
|
|
1589
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1590
|
+
import { join as join4 } from "path";
|
|
1591
|
+
import { homedir as homedir3 } from "os";
|
|
1592
|
+
function getRuntimeConfigPath() {
|
|
1593
|
+
const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
|
|
1594
|
+
if (envPath) return envPath;
|
|
1595
|
+
return RUNTIME_CONFIG_PATH;
|
|
1596
|
+
}
|
|
1597
|
+
function saveRuntimeConfig(config) {
|
|
1598
|
+
const p = getRuntimeConfigPath();
|
|
1599
|
+
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1600
|
+
if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
|
|
1601
|
+
writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
|
|
1602
|
+
}
|
|
1603
|
+
function emptyRuntimeConfig() {
|
|
1604
|
+
return {
|
|
1605
|
+
version: 1,
|
|
1606
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
1607
|
+
L1_rules: {},
|
|
1608
|
+
L2_rules: {},
|
|
1609
|
+
L3_skills: {}
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
function loadRuntimeConfig() {
|
|
1613
|
+
const p = getRuntimeConfigPath();
|
|
1614
|
+
if (!existsSync7(p)) {
|
|
1615
|
+
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
1616
|
+
try {
|
|
1617
|
+
saveRuntimeConfig(seeded);
|
|
1618
|
+
} catch {
|
|
1619
|
+
}
|
|
1620
|
+
return seeded;
|
|
1621
|
+
}
|
|
1622
|
+
try {
|
|
1623
|
+
const data = JSON.parse(readFileSync7(p, "utf-8"));
|
|
1624
|
+
const merged = mergeConfig(data, generateSourceDefaults());
|
|
1625
|
+
return {
|
|
1626
|
+
version: data.version || 1,
|
|
1627
|
+
layers: {
|
|
1628
|
+
L1_heuristic: data.layers?.L1_heuristic ?? true,
|
|
1629
|
+
L2_logic_rules: data.layers?.L2_logic_rules ?? true,
|
|
1630
|
+
L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
|
|
1631
|
+
},
|
|
1632
|
+
L1_rules: merged.L1_rules || {},
|
|
1633
|
+
L2_rules: merged.L2_rules || {},
|
|
1634
|
+
L3_skills: merged.L3_skills || {}
|
|
1635
|
+
};
|
|
1636
|
+
} catch {
|
|
1637
|
+
return emptyRuntimeConfig();
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
function isLayerEnabled(layerKey) {
|
|
1641
|
+
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
1642
|
+
if (disableKey && process.env[disableKey] === "1") return false;
|
|
1643
|
+
const runtime = loadRuntimeConfig();
|
|
1644
|
+
const layers = runtime.layers || {};
|
|
1645
|
+
if (layerKey in layers) return !!layers[layerKey];
|
|
1646
|
+
return true;
|
|
1647
|
+
}
|
|
1648
|
+
function getEnvOverrides() {
|
|
1649
|
+
const overrides = {};
|
|
1650
|
+
for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
|
|
1651
|
+
const val = process.env[envKey];
|
|
1652
|
+
if (val !== void 0) {
|
|
1653
|
+
overrides[layerKey] = {
|
|
1654
|
+
env_var: envKey,
|
|
1655
|
+
env_value: val,
|
|
1656
|
+
overridden: val === "1",
|
|
1657
|
+
effect: val === "1" ? "disabled" : "no_override"
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
return overrides;
|
|
1662
|
+
}
|
|
1663
|
+
function filterEnabledRules(rules, categoryEnabled = true) {
|
|
1664
|
+
if (!categoryEnabled) return [];
|
|
1665
|
+
return rules.filter((r) => r.enabled !== false);
|
|
1666
|
+
}
|
|
1667
|
+
function getEnabledL1CommandPatterns(runtime) {
|
|
1668
|
+
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
1669
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
1670
|
+
pattern: r.pattern || "",
|
|
1671
|
+
risk_level: r.risk_level || "",
|
|
1672
|
+
risk_type: r.risk_type || "",
|
|
1673
|
+
reason: r.reason || ""
|
|
1674
|
+
}));
|
|
1675
|
+
}
|
|
1676
|
+
function getEnabledL2SensitivePaths(runtime) {
|
|
1677
|
+
const cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
1678
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
|
|
1679
|
+
const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
|
|
1680
|
+
if (r.credential) out.credential = true;
|
|
1681
|
+
if (r.read_only) out.read_only = true;
|
|
1682
|
+
return out;
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
function hasRuntimeConfig() {
|
|
1686
|
+
const p = getRuntimeConfigPath();
|
|
1687
|
+
if (!existsSync7(p)) return false;
|
|
1688
|
+
try {
|
|
1689
|
+
JSON.parse(readFileSync7(p, "utf-8"));
|
|
1690
|
+
return true;
|
|
1691
|
+
} catch {
|
|
1692
|
+
return false;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function getDisabledL2RulesWithSkipL3() {
|
|
1696
|
+
let runtime;
|
|
1697
|
+
try {
|
|
1698
|
+
runtime = loadRuntimeConfig();
|
|
1699
|
+
} catch {
|
|
1700
|
+
return {};
|
|
1701
|
+
}
|
|
1702
|
+
const result = {};
|
|
1703
|
+
const l2 = runtime.L2_rules || {};
|
|
1704
|
+
for (const [catName, cat] of Object.entries(l2)) {
|
|
1705
|
+
if (!cat.category_enabled) continue;
|
|
1706
|
+
const items = [];
|
|
1707
|
+
for (const r of cat.rules || []) {
|
|
1708
|
+
if (r.enabled === false && r.skip_l3_on_disabled) {
|
|
1709
|
+
if (catName === "sensitive_path_access") {
|
|
1710
|
+
if (r.path) items.push(r.path);
|
|
1711
|
+
} else if (catName === "intent_consistency") {
|
|
1712
|
+
items.push(...r.intent_keywords || []);
|
|
1713
|
+
} else if (r.pattern) {
|
|
1714
|
+
items.push(r.pattern);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (items.length > 0) result[catName] = items;
|
|
1719
|
+
}
|
|
1720
|
+
return result;
|
|
1721
|
+
}
|
|
1722
|
+
function _slugify(text) {
|
|
1723
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
|
|
1724
|
+
}
|
|
1725
|
+
function _generatePatternId(pattern) {
|
|
1726
|
+
return `pat_${_slugify(pattern)}`;
|
|
1727
|
+
}
|
|
1728
|
+
function _deriveDenyMode(rule) {
|
|
1729
|
+
if (rule.deny_mode) return rule.deny_mode;
|
|
1730
|
+
if (rule.credential) return "deny_both";
|
|
1731
|
+
if (rule.read_only) return "deny_read";
|
|
1732
|
+
return "deny_write";
|
|
1733
|
+
}
|
|
1734
|
+
function _syncDenyModeFields(rule) {
|
|
1735
|
+
const dm = rule.deny_mode || "deny_write";
|
|
1736
|
+
if (dm === "deny_both") {
|
|
1737
|
+
rule.credential = true;
|
|
1738
|
+
delete rule.read_only;
|
|
1739
|
+
} else if (dm === "deny_read") {
|
|
1740
|
+
delete rule.credential;
|
|
1741
|
+
rule.read_only = true;
|
|
1742
|
+
} else {
|
|
1743
|
+
delete rule.credential;
|
|
1744
|
+
delete rule.read_only;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
function _l1CommandPatternsToRules() {
|
|
1748
|
+
return DANGEROUS_PATTERNS.map((p) => ({
|
|
1749
|
+
id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
|
|
1750
|
+
pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
|
|
1751
|
+
risk_level: p.severity,
|
|
1752
|
+
risk_type: p.riskType,
|
|
1753
|
+
reason: p.description,
|
|
1754
|
+
enabled: true,
|
|
1755
|
+
builtin: true
|
|
1756
|
+
}));
|
|
1757
|
+
}
|
|
1758
|
+
function _l1InjectionKeywordsToRules() {
|
|
1759
|
+
return INJECTION_KEYWORDS.map((kw) => ({
|
|
1760
|
+
id: `inj_${_slugify(kw.keyword)}`,
|
|
1761
|
+
keyword: kw.keyword,
|
|
1762
|
+
risk_level: kw.riskLevel,
|
|
1763
|
+
lang: kw.lang || "en",
|
|
1764
|
+
enabled: true,
|
|
1765
|
+
builtin: true
|
|
1766
|
+
}));
|
|
1767
|
+
}
|
|
1768
|
+
function _l2SensitivePathsToRules() {
|
|
1769
|
+
const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
|
|
1770
|
+
return all.map((sp) => {
|
|
1771
|
+
const denyMode = _deriveDenyMode({
|
|
1772
|
+
deny_mode: sp.deny_mode,
|
|
1773
|
+
credential: sp.credential,
|
|
1774
|
+
read_only: sp.read_only
|
|
1775
|
+
});
|
|
1776
|
+
const rule = {
|
|
1777
|
+
id: `path_${_slugify(sp.path)}`,
|
|
1778
|
+
path: sp.path,
|
|
1779
|
+
risk_level: sp.riskLevel,
|
|
1780
|
+
desc: sp.desc,
|
|
1781
|
+
deny_mode: denyMode,
|
|
1782
|
+
source_deny_mode: denyMode,
|
|
1783
|
+
skip_l3_on_disabled: true,
|
|
1784
|
+
enabled: true,
|
|
1785
|
+
builtin: true
|
|
1786
|
+
};
|
|
1787
|
+
_syncDenyModeFields(rule);
|
|
1788
|
+
return rule;
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
function _l2IntentPatternsToRules() {
|
|
1792
|
+
return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
|
|
1793
|
+
id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
|
|
1794
|
+
intent_keywords: pat.intentKeywords,
|
|
1795
|
+
dangerous_actions: pat.dangerousActions,
|
|
1796
|
+
reason: pat.reason,
|
|
1797
|
+
skip_l3_on_disabled: true,
|
|
1798
|
+
enabled: true,
|
|
1799
|
+
builtin: true
|
|
1800
|
+
}));
|
|
1801
|
+
}
|
|
1802
|
+
function _l2PasswordPatternsToRules() {
|
|
1803
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => ({
|
|
1804
|
+
id: `pw_${_slugify(p.source)}`,
|
|
1805
|
+
pattern: p.source,
|
|
1806
|
+
skip_l3_on_disabled: true,
|
|
1807
|
+
enabled: true,
|
|
1808
|
+
builtin: true
|
|
1809
|
+
}));
|
|
1810
|
+
}
|
|
1811
|
+
function _l2UserDeletionPatternsToRules() {
|
|
1812
|
+
return USER_DELETION_PATTERNS.map((p) => ({
|
|
1813
|
+
id: `ud_${_slugify(p.source)}`,
|
|
1814
|
+
pattern: p.source,
|
|
1815
|
+
skip_l3_on_disabled: true,
|
|
1816
|
+
enabled: true,
|
|
1817
|
+
builtin: true
|
|
1818
|
+
}));
|
|
1819
|
+
}
|
|
1820
|
+
function _l3SkillsToRules() {
|
|
1821
|
+
const skillCategories = {
|
|
1822
|
+
file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
|
|
1823
|
+
network_security: [
|
|
1824
|
+
"data_exfiltration_guard",
|
|
1825
|
+
"browser_web_access_guard",
|
|
1826
|
+
"lateral_movement_guard",
|
|
1827
|
+
"email_operation_guard"
|
|
1828
|
+
],
|
|
1829
|
+
persistence_supply_chain: [
|
|
1830
|
+
"persistence_backdoor_guard",
|
|
1831
|
+
"supply_chain_guard",
|
|
1832
|
+
"skill_installation_guard"
|
|
1833
|
+
],
|
|
1834
|
+
intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
|
|
1835
|
+
resource: ["resource_exhaustion_guard"]
|
|
1836
|
+
};
|
|
1837
|
+
const out = {};
|
|
1838
|
+
for (const [cat, ids] of Object.entries(skillCategories)) {
|
|
1839
|
+
out[cat] = {
|
|
1840
|
+
category_enabled: true,
|
|
1841
|
+
category_desc: "",
|
|
1842
|
+
skills: ids.map((s) => ({
|
|
1843
|
+
id: s,
|
|
1844
|
+
keywords: SKILL_KEYWORD_MAP[s] || [],
|
|
1845
|
+
enabled: true,
|
|
1846
|
+
builtin: true
|
|
1847
|
+
}))
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
const categorized = new Set(Object.values(skillCategories).flat());
|
|
1851
|
+
for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
|
|
1852
|
+
if (!categorized.has(s)) {
|
|
1853
|
+
(out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
|
|
1854
|
+
id: s,
|
|
1855
|
+
keywords: SKILL_KEYWORD_MAP[s],
|
|
1856
|
+
enabled: true,
|
|
1857
|
+
builtin: true
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
|
|
1862
|
+
return out;
|
|
1863
|
+
}
|
|
1864
|
+
function generateSourceDefaults() {
|
|
1865
|
+
return {
|
|
1866
|
+
version: 1,
|
|
1867
|
+
layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
|
|
1868
|
+
L1_rules: {
|
|
1869
|
+
dangerous_commands: {
|
|
1870
|
+
category_enabled: true,
|
|
1871
|
+
category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
|
|
1872
|
+
rules: _l1CommandPatternsToRules()
|
|
1873
|
+
},
|
|
1874
|
+
injection_detection: {
|
|
1875
|
+
category_enabled: true,
|
|
1876
|
+
category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
|
|
1877
|
+
rules: _l1InjectionKeywordsToRules()
|
|
1878
|
+
},
|
|
1879
|
+
user_sensitive_actions: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u654F\u611F\u52A8\u4F5C/\u5DE5\u5177\u89C4\u5219", rules: [] },
|
|
1880
|
+
user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
|
|
1881
|
+
},
|
|
1882
|
+
L2_rules: {
|
|
1883
|
+
sensitive_path_access: {
|
|
1884
|
+
category_enabled: true,
|
|
1885
|
+
category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B",
|
|
1886
|
+
rules: _l2SensitivePathsToRules()
|
|
1887
|
+
},
|
|
1888
|
+
intent_consistency: {
|
|
1889
|
+
category_enabled: true,
|
|
1890
|
+
category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
|
|
1891
|
+
rules: _l2IntentPatternsToRules()
|
|
1892
|
+
},
|
|
1893
|
+
password_modify_consent: {
|
|
1894
|
+
category_enabled: true,
|
|
1895
|
+
category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
|
|
1896
|
+
rules: _l2PasswordPatternsToRules()
|
|
1897
|
+
},
|
|
1898
|
+
user_deletion_consent: {
|
|
1899
|
+
category_enabled: true,
|
|
1900
|
+
category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
|
|
1901
|
+
rules: _l2UserDeletionPatternsToRules()
|
|
1902
|
+
},
|
|
1903
|
+
read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
|
|
1904
|
+
dangerous_patterns: { category_enabled: true, category_desc: "\u5371\u9669\u64CD\u4F5C\u6A21\u5F0F\u68C0\u6D4B", rules: [] }
|
|
1905
|
+
},
|
|
1906
|
+
L3_skills: _l3SkillsToRules()
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
function _ruleFingerprint(rule) {
|
|
1910
|
+
if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
|
|
1911
|
+
return JSON.stringify([
|
|
1912
|
+
[...rule.intent_keywords || []].sort(),
|
|
1913
|
+
[...rule.dangerous_actions || []].sort()
|
|
1914
|
+
]);
|
|
1915
|
+
}
|
|
1916
|
+
if (rule.path) return JSON.stringify([rule.path]);
|
|
1917
|
+
if (rule.pattern) return JSON.stringify([rule.pattern]);
|
|
1918
|
+
if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
|
|
1919
|
+
return JSON.stringify([rule.id || ""]);
|
|
1920
|
+
}
|
|
1921
|
+
function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
|
|
1922
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
1923
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
1924
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
1925
|
+
if (!(catName in userLayer)) {
|
|
1926
|
+
userLayer[catName] = srcCat;
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
const userCat = userLayer[catName];
|
|
1930
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1931
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1932
|
+
const srcRules = srcCat.rules || [];
|
|
1933
|
+
const userRules = userCat.rules || (userCat.rules = []);
|
|
1934
|
+
const userByFp = /* @__PURE__ */ new Map();
|
|
1935
|
+
for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
|
|
1936
|
+
const srcFps = /* @__PURE__ */ new Set();
|
|
1937
|
+
for (const srcR of srcRules) {
|
|
1938
|
+
const fp = _ruleFingerprint(srcR);
|
|
1939
|
+
srcFps.add(fp);
|
|
1940
|
+
if (userByFp.has(fp)) {
|
|
1941
|
+
const existing = userByFp.get(fp);
|
|
1942
|
+
const userEnabled = existing.enabled;
|
|
1943
|
+
const userSkipL3 = existing.skip_l3_on_disabled;
|
|
1944
|
+
const userDenyMode = existing.deny_mode;
|
|
1945
|
+
const userSourceDeny = existing.source_deny_mode;
|
|
1946
|
+
Object.keys(existing).forEach((k) => delete existing[k]);
|
|
1947
|
+
Object.assign(existing, srcR);
|
|
1948
|
+
existing.enabled = userEnabled;
|
|
1949
|
+
if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
|
|
1950
|
+
if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
|
|
1951
|
+
existing.deny_mode = userDenyMode;
|
|
1952
|
+
}
|
|
1953
|
+
if (existing.deny_mode) _syncDenyModeFields(existing);
|
|
1954
|
+
} else {
|
|
1955
|
+
const newR = { ...srcR, enabled: true };
|
|
1956
|
+
userRules.push(newR);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
for (const r of userRules) {
|
|
1960
|
+
if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
|
|
1961
|
+
r.source_removed = true;
|
|
1962
|
+
r.enabled = false;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (catName === "sensitive_path_access") {
|
|
1966
|
+
for (const r of userRules) {
|
|
1967
|
+
if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
|
|
1968
|
+
_syncDenyModeFields(r);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
|
|
1974
|
+
const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
|
|
1975
|
+
const srcLayer = srcDefaults[layerKey] || {};
|
|
1976
|
+
for (const [catName, srcCat] of Object.entries(srcLayer)) {
|
|
1977
|
+
if (!(catName in userLayer)) {
|
|
1978
|
+
userLayer[catName] = srcCat;
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const userCat = userLayer[catName];
|
|
1982
|
+
if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
|
|
1983
|
+
if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
|
|
1984
|
+
const userSkills = userCat.skills || (userCat.skills = []);
|
|
1985
|
+
const existingIds = new Set(userSkills.map((s) => s.id));
|
|
1986
|
+
for (const srcS of srcCat.skills || []) {
|
|
1987
|
+
if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function mergeConfig(userCfg, srcDefaults) {
|
|
1992
|
+
const cfg = userCfg;
|
|
1993
|
+
if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
|
|
1994
|
+
if (!cfg.layers) cfg.layers = {};
|
|
1995
|
+
for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
|
|
1996
|
+
if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
|
|
1997
|
+
}
|
|
1998
|
+
_mergeRuleCategories(cfg, srcDefaults, "L1_rules");
|
|
1999
|
+
_mergeRuleCategories(cfg, srcDefaults, "L2_rules");
|
|
2000
|
+
_mergeSkillCategories(cfg, srcDefaults, "L3_skills");
|
|
2001
|
+
return cfg;
|
|
2002
|
+
}
|
|
2003
|
+
function getEnabledL1InjectionKeywords(runtime) {
|
|
2004
|
+
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2005
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2006
|
+
keyword: r.keyword || "",
|
|
2007
|
+
risk_level: r.risk_level || "",
|
|
2008
|
+
lang: r.lang || "en"
|
|
2009
|
+
}));
|
|
2010
|
+
}
|
|
2011
|
+
function getEnabledL2IntentPatterns(runtime) {
|
|
2012
|
+
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2013
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
|
|
2014
|
+
intent_keywords: r.intent_keywords || [],
|
|
2015
|
+
dangerous_actions: r.dangerous_actions || [],
|
|
2016
|
+
reason: r.reason || ""
|
|
2017
|
+
}));
|
|
2018
|
+
}
|
|
2019
|
+
function getEnabledL2PasswordPatterns(runtime) {
|
|
2020
|
+
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2021
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
2022
|
+
}
|
|
2023
|
+
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2024
|
+
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2025
|
+
return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
|
|
2026
|
+
}
|
|
2027
|
+
function isL2CategoryEnabled(runtime, categoryKey) {
|
|
2028
|
+
const cat = runtime.L2_rules?.[categoryKey];
|
|
2029
|
+
return cat?.category_enabled ?? true;
|
|
2030
|
+
}
|
|
2031
|
+
function getDisabledSkillIds(runtime) {
|
|
2032
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
2033
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
2034
|
+
if (!catData.category_enabled) {
|
|
2035
|
+
for (const s of catData.skills || []) disabled.add(s.id);
|
|
2036
|
+
} else {
|
|
2037
|
+
for (const s of catData.skills || []) {
|
|
2038
|
+
if (s.enabled === false) disabled.add(s.id);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return disabled;
|
|
2043
|
+
}
|
|
2044
|
+
function getEnabledL1CommandPatternsOrDefault(runtime) {
|
|
2045
|
+
const enabled = getEnabledL1CommandPatterns(runtime);
|
|
2046
|
+
if (enabled.length) return enabled;
|
|
2047
|
+
if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
|
|
2048
|
+
return [];
|
|
2049
|
+
}
|
|
2050
|
+
function getEnabledL1InjectionKeywordsOrDefault(runtime) {
|
|
2051
|
+
const enabled = getEnabledL1InjectionKeywords(runtime);
|
|
2052
|
+
if (enabled.length) return enabled;
|
|
2053
|
+
if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
|
|
2054
|
+
return [];
|
|
2055
|
+
}
|
|
2056
|
+
function getEnabledL2SensitivePathsOrDefault(runtime) {
|
|
2057
|
+
const enabled = getEnabledL2SensitivePaths(runtime);
|
|
2058
|
+
if (enabled.length) return enabled;
|
|
2059
|
+
if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
|
|
2060
|
+
return [];
|
|
2061
|
+
}
|
|
2062
|
+
function getEnabledL2IntentPatternsOrDefault(runtime) {
|
|
2063
|
+
const enabled = getEnabledL2IntentPatterns(runtime);
|
|
2064
|
+
if (enabled.length) return enabled;
|
|
2065
|
+
if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
|
|
2066
|
+
return [];
|
|
2067
|
+
}
|
|
2068
|
+
function getEnabledL2PasswordPatternsOrDefault(runtime) {
|
|
2069
|
+
const enabled = getEnabledL2PasswordPatterns(runtime);
|
|
2070
|
+
if (enabled.length) return enabled;
|
|
2071
|
+
if (!hasRuntimeConfig()) {
|
|
2072
|
+
return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
|
|
2073
|
+
}
|
|
2074
|
+
return [];
|
|
2075
|
+
}
|
|
2076
|
+
function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
|
|
2077
|
+
const enabled = getEnabledL2UserDeletionPatterns(runtime);
|
|
2078
|
+
if (enabled.length) return enabled;
|
|
2079
|
+
if (!hasRuntimeConfig()) {
|
|
2080
|
+
return USER_DELETION_PATTERNS.map((p) => p.source);
|
|
2081
|
+
}
|
|
2082
|
+
return [];
|
|
2083
|
+
}
|
|
2084
|
+
function _validateSkillId(skillId) {
|
|
2085
|
+
if (!skillId || !SKILL_ID_RE.test(skillId)) {
|
|
2086
|
+
throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
function updateLayerEnabled(layerKey, enabled) {
|
|
2090
|
+
const runtime = loadRuntimeConfig();
|
|
2091
|
+
runtime.layers[layerKey] = enabled;
|
|
2092
|
+
saveRuntimeConfig(runtime);
|
|
2093
|
+
return runtime;
|
|
2094
|
+
}
|
|
2095
|
+
function updateRuleEnabled(layer, category, ruleId, enabled) {
|
|
2096
|
+
const runtime = loadRuntimeConfig();
|
|
2097
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2098
|
+
if (r.id === ruleId) {
|
|
2099
|
+
r.enabled = enabled;
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
saveRuntimeConfig(runtime);
|
|
2104
|
+
return runtime;
|
|
2105
|
+
}
|
|
2106
|
+
function updateRuleDenyMode(layer, category, ruleId, denyMode) {
|
|
2107
|
+
const runtime = loadRuntimeConfig();
|
|
2108
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2109
|
+
if (r.id === ruleId) {
|
|
2110
|
+
r.deny_mode = denyMode;
|
|
2111
|
+
_syncDenyModeFields(r);
|
|
2112
|
+
break;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
saveRuntimeConfig(runtime);
|
|
2116
|
+
return runtime;
|
|
2117
|
+
}
|
|
2118
|
+
function updateRuleSkipL3(layer, category, ruleId, skipL3) {
|
|
2119
|
+
const runtime = loadRuntimeConfig();
|
|
2120
|
+
for (const r of runtime[layer]?.[category]?.rules || []) {
|
|
2121
|
+
if (r.id === ruleId) {
|
|
2122
|
+
r.skip_l3_on_disabled = skipL3;
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
saveRuntimeConfig(runtime);
|
|
2127
|
+
return runtime;
|
|
2128
|
+
}
|
|
2129
|
+
function updateCategoryEnabled(layer, category, enabled) {
|
|
2130
|
+
const runtime = loadRuntimeConfig();
|
|
2131
|
+
const cat = runtime[layer]?.[category];
|
|
2132
|
+
if (cat) cat.category_enabled = enabled;
|
|
2133
|
+
saveRuntimeConfig(runtime);
|
|
2134
|
+
return runtime;
|
|
2135
|
+
}
|
|
2136
|
+
function addCustomRule(layer, category, rule) {
|
|
2137
|
+
const runtime = loadRuntimeConfig();
|
|
2138
|
+
const layerObj = runtime[layer] || (runtime[layer] = {});
|
|
2139
|
+
const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
|
|
2140
|
+
const rules = cat.rules || (cat.rules = []);
|
|
2141
|
+
const newRule = { ...rule, builtin: false, enabled: true };
|
|
2142
|
+
if (!newRule.id) {
|
|
2143
|
+
newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
|
|
2144
|
+
}
|
|
2145
|
+
if (newRule.deny_mode) _syncDenyModeFields(newRule);
|
|
2146
|
+
rules.push(newRule);
|
|
2147
|
+
saveRuntimeConfig(runtime);
|
|
2148
|
+
return runtime;
|
|
2149
|
+
}
|
|
2150
|
+
function deleteCustomRule(layer, category, ruleId) {
|
|
2151
|
+
const runtime = loadRuntimeConfig();
|
|
2152
|
+
const rules = runtime[layer]?.[category]?.rules || [];
|
|
2153
|
+
const idx = rules.findIndex((r) => r.id === ruleId);
|
|
2154
|
+
if (idx >= 0) {
|
|
2155
|
+
if (rules[idx].builtin) return null;
|
|
2156
|
+
rules.splice(idx, 1);
|
|
2157
|
+
saveRuntimeConfig(runtime);
|
|
2158
|
+
}
|
|
2159
|
+
return runtime;
|
|
2160
|
+
}
|
|
2161
|
+
function addCustomSkill(skillId, category, keywords, content) {
|
|
2162
|
+
_validateSkillId(skillId);
|
|
2163
|
+
const runtime = loadRuntimeConfig();
|
|
2164
|
+
const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
|
|
2165
|
+
try {
|
|
2166
|
+
mkdirSync2(skillsDir, { recursive: true });
|
|
2167
|
+
} catch {
|
|
2168
|
+
}
|
|
2169
|
+
writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
|
|
2170
|
+
const l3 = runtime.L3_skills || (runtime.L3_skills = {});
|
|
2171
|
+
const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
|
|
2172
|
+
(cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
|
|
2173
|
+
saveRuntimeConfig(runtime);
|
|
2174
|
+
return runtime;
|
|
2175
|
+
}
|
|
2176
|
+
function deleteCustomSkill(skillId, category) {
|
|
2177
|
+
_validateSkillId(skillId);
|
|
2178
|
+
const runtime = loadRuntimeConfig();
|
|
2179
|
+
const skills = runtime.L3_skills?.[category]?.skills || [];
|
|
2180
|
+
const idx = skills.findIndex((s) => s.id === skillId);
|
|
2181
|
+
if (idx >= 0) {
|
|
2182
|
+
if (skills[idx].builtin) return null;
|
|
2183
|
+
skills.splice(idx, 1);
|
|
2184
|
+
const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
|
|
2185
|
+
try {
|
|
2186
|
+
if (existsSync7(skillPath)) unlinkSync2(skillPath);
|
|
2187
|
+
} catch {
|
|
2188
|
+
}
|
|
2189
|
+
saveRuntimeConfig(runtime);
|
|
2190
|
+
}
|
|
2191
|
+
return runtime;
|
|
2192
|
+
}
|
|
2193
|
+
function updateSkillEnabled(skillId, enabled) {
|
|
2194
|
+
const runtime = loadRuntimeConfig();
|
|
2195
|
+
for (const catData of Object.values(runtime.L3_skills || {})) {
|
|
2196
|
+
for (const s of catData.skills || []) {
|
|
2197
|
+
if (s.id === skillId) {
|
|
2198
|
+
s.enabled = enabled;
|
|
2199
|
+
break;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
saveRuntimeConfig(runtime);
|
|
2204
|
+
return runtime;
|
|
2205
|
+
}
|
|
2206
|
+
function updateSkillCategoryEnabled(category, enabled) {
|
|
2207
|
+
const runtime = loadRuntimeConfig();
|
|
2208
|
+
const cat = runtime.L3_skills?.[category];
|
|
2209
|
+
if (cat) cat.category_enabled = enabled;
|
|
2210
|
+
saveRuntimeConfig(runtime);
|
|
2211
|
+
return runtime;
|
|
2212
|
+
}
|
|
2213
|
+
function getProfileOrDetect() {
|
|
2214
|
+
return null;
|
|
2215
|
+
}
|
|
2216
|
+
var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
|
|
2217
|
+
var init_runtime_config = __esm({
|
|
2218
|
+
"src/infra/runtime-config.ts"() {
|
|
2219
|
+
"use strict";
|
|
2220
|
+
init_esm_shims();
|
|
2221
|
+
init_heuristic();
|
|
2222
|
+
init_logic_rules();
|
|
2223
|
+
init_skill_loader();
|
|
2224
|
+
RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
|
|
2225
|
+
RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
|
|
2226
|
+
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
2227
|
+
LAYER_DISABLE_ENV = {
|
|
2228
|
+
L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
|
|
2229
|
+
L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
|
|
2230
|
+
L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
|
|
2231
|
+
};
|
|
2232
|
+
DENY_MODE_LABELS = {
|
|
2233
|
+
deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
|
|
2234
|
+
deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
|
|
2235
|
+
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
2236
|
+
};
|
|
2237
|
+
SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
2238
|
+
}
|
|
2239
|
+
});
|
|
2240
|
+
|
|
2241
|
+
// node_modules/better-sqlite3/lib/util.js
|
|
2242
|
+
var require_util = __commonJS({
|
|
2243
|
+
"node_modules/better-sqlite3/lib/util.js"(exports) {
|
|
2244
|
+
"use strict";
|
|
2245
|
+
init_esm_shims();
|
|
2246
|
+
exports.getBooleanOption = (options, key) => {
|
|
2247
|
+
let value = false;
|
|
2248
|
+
if (key in options && typeof (value = options[key]) !== "boolean") {
|
|
2249
|
+
throw new TypeError(`Expected the "${key}" option to be a boolean`);
|
|
2250
|
+
}
|
|
2251
|
+
return value;
|
|
2252
|
+
};
|
|
2253
|
+
exports.cppdb = /* @__PURE__ */ Symbol();
|
|
2254
|
+
exports.inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2258
|
+
// node_modules/better-sqlite3/lib/sqlite-error.js
|
|
2259
|
+
var require_sqlite_error = __commonJS({
|
|
2260
|
+
"node_modules/better-sqlite3/lib/sqlite-error.js"(exports, module) {
|
|
2261
|
+
"use strict";
|
|
2262
|
+
init_esm_shims();
|
|
2263
|
+
var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
|
|
2264
|
+
function SqliteError(message, code) {
|
|
2265
|
+
if (new.target !== SqliteError) {
|
|
2266
|
+
return new SqliteError(message, code);
|
|
2267
|
+
}
|
|
2268
|
+
if (typeof code !== "string") {
|
|
2269
|
+
throw new TypeError("Expected second argument to be a string");
|
|
2270
|
+
}
|
|
2271
|
+
Error.call(this, message);
|
|
2272
|
+
descriptor.value = "" + message;
|
|
2273
|
+
Object.defineProperty(this, "message", descriptor);
|
|
2274
|
+
Error.captureStackTrace(this, SqliteError);
|
|
2275
|
+
this.code = code;
|
|
2276
|
+
}
|
|
2277
|
+
Object.setPrototypeOf(SqliteError, Error);
|
|
2278
|
+
Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
|
|
2279
|
+
Object.defineProperty(SqliteError.prototype, "name", descriptor);
|
|
2280
|
+
module.exports = SqliteError;
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
|
|
2284
|
+
// node_modules/file-uri-to-path/index.js
|
|
2285
|
+
var require_file_uri_to_path = __commonJS({
|
|
2286
|
+
"node_modules/file-uri-to-path/index.js"(exports, module) {
|
|
2287
|
+
"use strict";
|
|
2288
|
+
init_esm_shims();
|
|
2289
|
+
var sep = __require("path").sep || "/";
|
|
2290
|
+
module.exports = fileUriToPath;
|
|
2291
|
+
function fileUriToPath(uri) {
|
|
2292
|
+
if ("string" != typeof uri || uri.length <= 7 || "file://" != uri.substring(0, 7)) {
|
|
2293
|
+
throw new TypeError("must pass in a file:// URI to convert to a file path");
|
|
2294
|
+
}
|
|
2295
|
+
var rest = decodeURI(uri.substring(7));
|
|
2296
|
+
var firstSlash = rest.indexOf("/");
|
|
2297
|
+
var host = rest.substring(0, firstSlash);
|
|
2298
|
+
var path3 = rest.substring(firstSlash + 1);
|
|
2299
|
+
if ("localhost" == host) host = "";
|
|
2300
|
+
if (host) {
|
|
2301
|
+
host = sep + sep + host;
|
|
2302
|
+
}
|
|
2303
|
+
path3 = path3.replace(/^(.+)\|/, "$1:");
|
|
2304
|
+
if (sep == "\\") {
|
|
2305
|
+
path3 = path3.replace(/\//g, "\\");
|
|
2306
|
+
}
|
|
2307
|
+
if (/^.+\:/.test(path3)) {
|
|
2308
|
+
} else {
|
|
2309
|
+
path3 = sep + path3;
|
|
2310
|
+
}
|
|
2311
|
+
return host + path3;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
// node_modules/bindings/bindings.js
|
|
2317
|
+
var require_bindings = __commonJS({
|
|
2318
|
+
"node_modules/bindings/bindings.js"(exports, module) {
|
|
2319
|
+
"use strict";
|
|
2320
|
+
init_esm_shims();
|
|
2321
|
+
var fs4 = __require("fs");
|
|
2322
|
+
var path3 = __require("path");
|
|
2323
|
+
var fileURLToPath6 = require_file_uri_to_path();
|
|
2324
|
+
var join6 = path3.join;
|
|
2325
|
+
var dirname6 = path3.dirname;
|
|
2326
|
+
var exists = fs4.accessSync && function(path4) {
|
|
2327
|
+
try {
|
|
2328
|
+
fs4.accessSync(path4);
|
|
2329
|
+
} catch (e) {
|
|
2330
|
+
return false;
|
|
2331
|
+
}
|
|
2332
|
+
return true;
|
|
2333
|
+
} || fs4.existsSync || path3.existsSync;
|
|
2334
|
+
var defaults = {
|
|
2335
|
+
arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
|
|
2336
|
+
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
|
|
2337
|
+
platform: process.platform,
|
|
2338
|
+
arch: process.arch,
|
|
2339
|
+
nodePreGyp: "node-v" + process.versions.modules + "-" + process.platform + "-" + process.arch,
|
|
2340
|
+
version: process.versions.node,
|
|
2341
|
+
bindings: "bindings.node",
|
|
2342
|
+
try: [
|
|
2343
|
+
// node-gyp's linked version in the "build" dir
|
|
2344
|
+
["module_root", "build", "bindings"],
|
|
2345
|
+
// node-waf and gyp_addon (a.k.a node-gyp)
|
|
2346
|
+
["module_root", "build", "Debug", "bindings"],
|
|
2347
|
+
["module_root", "build", "Release", "bindings"],
|
|
2348
|
+
// Debug files, for development (legacy behavior, remove for node v0.9)
|
|
2349
|
+
["module_root", "out", "Debug", "bindings"],
|
|
2350
|
+
["module_root", "Debug", "bindings"],
|
|
2351
|
+
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
|
|
2352
|
+
["module_root", "out", "Release", "bindings"],
|
|
2353
|
+
["module_root", "Release", "bindings"],
|
|
2354
|
+
// Legacy from node-waf, node <= 0.4.x
|
|
2355
|
+
["module_root", "build", "default", "bindings"],
|
|
2356
|
+
// Production "Release" buildtype binary (meh...)
|
|
2357
|
+
["module_root", "compiled", "version", "platform", "arch", "bindings"],
|
|
2358
|
+
// node-qbs builds
|
|
2359
|
+
["module_root", "addon-build", "release", "install-root", "bindings"],
|
|
2360
|
+
["module_root", "addon-build", "debug", "install-root", "bindings"],
|
|
2361
|
+
["module_root", "addon-build", "default", "install-root", "bindings"],
|
|
2362
|
+
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
|
|
2363
|
+
["module_root", "lib", "binding", "nodePreGyp", "bindings"]
|
|
2364
|
+
]
|
|
2365
|
+
};
|
|
2366
|
+
function bindings(opts) {
|
|
2367
|
+
if (typeof opts == "string") {
|
|
2368
|
+
opts = { bindings: opts };
|
|
2369
|
+
} else if (!opts) {
|
|
2370
|
+
opts = {};
|
|
2371
|
+
}
|
|
2372
|
+
Object.keys(defaults).map(function(i2) {
|
|
2373
|
+
if (!(i2 in opts)) opts[i2] = defaults[i2];
|
|
2374
|
+
});
|
|
2375
|
+
if (!opts.module_root) {
|
|
2376
|
+
opts.module_root = exports.getRoot(exports.getFileName());
|
|
2377
|
+
}
|
|
2378
|
+
if (path3.extname(opts.bindings) != ".node") {
|
|
2379
|
+
opts.bindings += ".node";
|
|
2380
|
+
}
|
|
2381
|
+
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
2382
|
+
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
2383
|
+
for (; i < l; i++) {
|
|
2384
|
+
n = join6.apply(
|
|
2385
|
+
null,
|
|
2386
|
+
opts.try[i].map(function(p) {
|
|
2387
|
+
return opts[p] || p;
|
|
2388
|
+
})
|
|
2389
|
+
);
|
|
2390
|
+
tries.push(n);
|
|
2391
|
+
try {
|
|
2392
|
+
b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
|
|
2393
|
+
if (!opts.path) {
|
|
2394
|
+
b.path = n;
|
|
2395
|
+
}
|
|
2396
|
+
return b;
|
|
2397
|
+
} catch (e) {
|
|
2398
|
+
if (e.code !== "MODULE_NOT_FOUND" && e.code !== "QUALIFIED_PATH_RESOLUTION_FAILED" && !/not find/i.test(e.message)) {
|
|
2399
|
+
throw e;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
err = new Error(
|
|
2404
|
+
"Could not locate the bindings file. Tried:\n" + tries.map(function(a) {
|
|
2405
|
+
return opts.arrow + a;
|
|
2406
|
+
}).join("\n")
|
|
2407
|
+
);
|
|
2408
|
+
err.tries = tries;
|
|
2409
|
+
throw err;
|
|
2410
|
+
}
|
|
2411
|
+
module.exports = exports = bindings;
|
|
2412
|
+
exports.getFileName = function getFileName(calling_file) {
|
|
2413
|
+
var origPST = Error.prepareStackTrace, origSTL = Error.stackTraceLimit, dummy = {}, fileName;
|
|
2414
|
+
Error.stackTraceLimit = 10;
|
|
2415
|
+
Error.prepareStackTrace = function(e, st) {
|
|
2416
|
+
for (var i = 0, l = st.length; i < l; i++) {
|
|
2417
|
+
fileName = st[i].getFileName();
|
|
2418
|
+
if (fileName !== __filename) {
|
|
2419
|
+
if (calling_file) {
|
|
2420
|
+
if (fileName !== calling_file) {
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
} else {
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
Error.captureStackTrace(dummy);
|
|
2430
|
+
dummy.stack;
|
|
2431
|
+
Error.prepareStackTrace = origPST;
|
|
2432
|
+
Error.stackTraceLimit = origSTL;
|
|
2433
|
+
var fileSchema = "file://";
|
|
2434
|
+
if (fileName.indexOf(fileSchema) === 0) {
|
|
2435
|
+
fileName = fileURLToPath6(fileName);
|
|
2436
|
+
}
|
|
2437
|
+
return fileName;
|
|
2438
|
+
};
|
|
2439
|
+
exports.getRoot = function getRoot(file) {
|
|
2440
|
+
var dir = dirname6(file), prev;
|
|
2441
|
+
while (true) {
|
|
2442
|
+
if (dir === ".") {
|
|
2443
|
+
dir = process.cwd();
|
|
2444
|
+
}
|
|
2445
|
+
if (exists(join6(dir, "package.json")) || exists(join6(dir, "node_modules"))) {
|
|
2446
|
+
return dir;
|
|
2447
|
+
}
|
|
2448
|
+
if (prev === dir) {
|
|
2449
|
+
throw new Error(
|
|
2450
|
+
'Could not find module root given file: "' + file + '". Do you have a `package.json` file? '
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
prev = dir;
|
|
2454
|
+
dir = join6(dir, "..");
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
});
|
|
2459
|
+
|
|
2460
|
+
// node_modules/better-sqlite3/lib/methods/wrappers.js
|
|
2461
|
+
var require_wrappers = __commonJS({
|
|
2462
|
+
"node_modules/better-sqlite3/lib/methods/wrappers.js"(exports) {
|
|
2463
|
+
"use strict";
|
|
2464
|
+
init_esm_shims();
|
|
2465
|
+
var { cppdb } = require_util();
|
|
2466
|
+
exports.prepare = function prepare(sql) {
|
|
2467
|
+
return this[cppdb].prepare(sql, this, false);
|
|
2468
|
+
};
|
|
2469
|
+
exports.exec = function exec(sql) {
|
|
2470
|
+
this[cppdb].exec(sql);
|
|
2471
|
+
return this;
|
|
2472
|
+
};
|
|
2473
|
+
exports.close = function close() {
|
|
2474
|
+
this[cppdb].close();
|
|
2475
|
+
return this;
|
|
2476
|
+
};
|
|
2477
|
+
exports.loadExtension = function loadExtension(...args) {
|
|
2478
|
+
this[cppdb].loadExtension(...args);
|
|
2479
|
+
return this;
|
|
2480
|
+
};
|
|
2481
|
+
exports.defaultSafeIntegers = function defaultSafeIntegers(...args) {
|
|
2482
|
+
this[cppdb].defaultSafeIntegers(...args);
|
|
2483
|
+
return this;
|
|
2484
|
+
};
|
|
2485
|
+
exports.unsafeMode = function unsafeMode(...args) {
|
|
2486
|
+
this[cppdb].unsafeMode(...args);
|
|
2487
|
+
return this;
|
|
2488
|
+
};
|
|
2489
|
+
exports.getters = {
|
|
2490
|
+
name: {
|
|
2491
|
+
get: function name() {
|
|
2492
|
+
return this[cppdb].name;
|
|
2493
|
+
},
|
|
2494
|
+
enumerable: true
|
|
2495
|
+
},
|
|
2496
|
+
open: {
|
|
2497
|
+
get: function open() {
|
|
2498
|
+
return this[cppdb].open;
|
|
2499
|
+
},
|
|
2500
|
+
enumerable: true
|
|
2501
|
+
},
|
|
2502
|
+
inTransaction: {
|
|
2503
|
+
get: function inTransaction() {
|
|
2504
|
+
return this[cppdb].inTransaction;
|
|
2505
|
+
},
|
|
2506
|
+
enumerable: true
|
|
2507
|
+
},
|
|
2508
|
+
readonly: {
|
|
2509
|
+
get: function readonly() {
|
|
2510
|
+
return this[cppdb].readonly;
|
|
2511
|
+
},
|
|
2512
|
+
enumerable: true
|
|
2513
|
+
},
|
|
2514
|
+
memory: {
|
|
2515
|
+
get: function memory() {
|
|
2516
|
+
return this[cppdb].memory;
|
|
2517
|
+
},
|
|
2518
|
+
enumerable: true
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
});
|
|
2523
|
+
|
|
2524
|
+
// node_modules/better-sqlite3/lib/methods/transaction.js
|
|
2525
|
+
var require_transaction = __commonJS({
|
|
2526
|
+
"node_modules/better-sqlite3/lib/methods/transaction.js"(exports, module) {
|
|
2527
|
+
"use strict";
|
|
2528
|
+
init_esm_shims();
|
|
2529
|
+
var { cppdb } = require_util();
|
|
2530
|
+
var controllers = /* @__PURE__ */ new WeakMap();
|
|
2531
|
+
module.exports = function transaction(fn) {
|
|
2532
|
+
if (typeof fn !== "function") throw new TypeError("Expected first argument to be a function");
|
|
2533
|
+
const db = this[cppdb];
|
|
2534
|
+
const controller = getController(db, this);
|
|
2535
|
+
const { apply } = Function.prototype;
|
|
2536
|
+
const properties = {
|
|
2537
|
+
default: { value: wrapTransaction(apply, fn, db, controller.default) },
|
|
2538
|
+
deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
|
|
2539
|
+
immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
|
|
2540
|
+
exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
|
|
2541
|
+
database: { value: this, enumerable: true }
|
|
2542
|
+
};
|
|
2543
|
+
Object.defineProperties(properties.default.value, properties);
|
|
2544
|
+
Object.defineProperties(properties.deferred.value, properties);
|
|
2545
|
+
Object.defineProperties(properties.immediate.value, properties);
|
|
2546
|
+
Object.defineProperties(properties.exclusive.value, properties);
|
|
2547
|
+
return properties.default.value;
|
|
2548
|
+
};
|
|
2549
|
+
var getController = (db, self) => {
|
|
2550
|
+
let controller = controllers.get(db);
|
|
2551
|
+
if (!controller) {
|
|
2552
|
+
const shared = {
|
|
2553
|
+
commit: db.prepare("COMMIT", self, false),
|
|
2554
|
+
rollback: db.prepare("ROLLBACK", self, false),
|
|
2555
|
+
savepoint: db.prepare("SAVEPOINT ` _bs3. `", self, false),
|
|
2556
|
+
release: db.prepare("RELEASE ` _bs3. `", self, false),
|
|
2557
|
+
rollbackTo: db.prepare("ROLLBACK TO ` _bs3. `", self, false)
|
|
2558
|
+
};
|
|
2559
|
+
controllers.set(db, controller = {
|
|
2560
|
+
default: Object.assign({ begin: db.prepare("BEGIN", self, false) }, shared),
|
|
2561
|
+
deferred: Object.assign({ begin: db.prepare("BEGIN DEFERRED", self, false) }, shared),
|
|
2562
|
+
immediate: Object.assign({ begin: db.prepare("BEGIN IMMEDIATE", self, false) }, shared),
|
|
2563
|
+
exclusive: Object.assign({ begin: db.prepare("BEGIN EXCLUSIVE", self, false) }, shared)
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
return controller;
|
|
2567
|
+
};
|
|
2568
|
+
var wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
|
|
2569
|
+
let before, after, undo;
|
|
2570
|
+
if (db.inTransaction) {
|
|
2571
|
+
before = savepoint;
|
|
2572
|
+
after = release;
|
|
2573
|
+
undo = rollbackTo;
|
|
2574
|
+
} else {
|
|
2575
|
+
before = begin;
|
|
2576
|
+
after = commit;
|
|
2577
|
+
undo = rollback;
|
|
2578
|
+
}
|
|
2579
|
+
before.run();
|
|
2580
|
+
try {
|
|
2581
|
+
const result = apply.call(fn, this, arguments);
|
|
2582
|
+
if (result && typeof result.then === "function") {
|
|
2583
|
+
throw new TypeError("Transaction function cannot return a promise");
|
|
2584
|
+
}
|
|
2585
|
+
after.run();
|
|
2586
|
+
return result;
|
|
2587
|
+
} catch (ex) {
|
|
2588
|
+
if (db.inTransaction) {
|
|
2589
|
+
undo.run();
|
|
2590
|
+
if (undo !== rollback) after.run();
|
|
2591
|
+
}
|
|
2592
|
+
throw ex;
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
});
|
|
2597
|
+
|
|
2598
|
+
// node_modules/better-sqlite3/lib/methods/pragma.js
|
|
2599
|
+
var require_pragma = __commonJS({
|
|
2600
|
+
"node_modules/better-sqlite3/lib/methods/pragma.js"(exports, module) {
|
|
2601
|
+
"use strict";
|
|
2602
|
+
init_esm_shims();
|
|
2603
|
+
var { getBooleanOption, cppdb } = require_util();
|
|
2604
|
+
module.exports = function pragma(source, options) {
|
|
2605
|
+
if (options == null) options = {};
|
|
2606
|
+
if (typeof source !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2607
|
+
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
2608
|
+
const simple = getBooleanOption(options, "simple");
|
|
2609
|
+
const stmt = this[cppdb].prepare(`PRAGMA ${source}`, this, true);
|
|
2610
|
+
return simple ? stmt.pluck().get() : stmt.all();
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
|
|
2615
|
+
// node_modules/better-sqlite3/lib/methods/backup.js
|
|
2616
|
+
var require_backup = __commonJS({
|
|
2617
|
+
"node_modules/better-sqlite3/lib/methods/backup.js"(exports, module) {
|
|
2618
|
+
"use strict";
|
|
2619
|
+
init_esm_shims();
|
|
2620
|
+
var fs4 = __require("fs");
|
|
2621
|
+
var path3 = __require("path");
|
|
2622
|
+
var { promisify } = __require("util");
|
|
2623
|
+
var { cppdb } = require_util();
|
|
2624
|
+
var fsAccess = promisify(fs4.access);
|
|
2625
|
+
module.exports = async function backup(filename, options) {
|
|
2626
|
+
if (options == null) options = {};
|
|
2627
|
+
if (typeof filename !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2628
|
+
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
2629
|
+
filename = filename.trim();
|
|
2630
|
+
const attachedName = "attached" in options ? options.attached : "main";
|
|
2631
|
+
const handler = "progress" in options ? options.progress : null;
|
|
2632
|
+
if (!filename) throw new TypeError("Backup filename cannot be an empty string");
|
|
2633
|
+
if (filename === ":memory:") throw new TypeError('Invalid backup filename ":memory:"');
|
|
2634
|
+
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
2635
|
+
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
2636
|
+
if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
|
|
2637
|
+
await fsAccess(path3.dirname(filename)).catch(() => {
|
|
2638
|
+
throw new TypeError("Cannot save backup because the directory does not exist");
|
|
2639
|
+
});
|
|
2640
|
+
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
2641
|
+
return runBackup(this[cppdb].backup(this, attachedName, filename, isNewFile), handler || null);
|
|
2642
|
+
};
|
|
2643
|
+
var runBackup = (backup, handler) => {
|
|
2644
|
+
let rate = 0;
|
|
2645
|
+
let useDefault = true;
|
|
2646
|
+
return new Promise((resolve6, reject) => {
|
|
2647
|
+
setImmediate(function step() {
|
|
2648
|
+
try {
|
|
2649
|
+
const progress = backup.transfer(rate);
|
|
2650
|
+
if (!progress.remainingPages) {
|
|
2651
|
+
backup.close();
|
|
2652
|
+
resolve6(progress);
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (useDefault) {
|
|
2656
|
+
useDefault = false;
|
|
2657
|
+
rate = 100;
|
|
2658
|
+
}
|
|
2659
|
+
if (handler) {
|
|
2660
|
+
const ret = handler(progress);
|
|
2661
|
+
if (ret !== void 0) {
|
|
2662
|
+
if (typeof ret === "number" && ret === ret) rate = Math.max(0, Math.min(2147483647, Math.round(ret)));
|
|
2663
|
+
else throw new TypeError("Expected progress callback to return a number or undefined");
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
setImmediate(step);
|
|
2667
|
+
} catch (err) {
|
|
2668
|
+
backup.close();
|
|
2669
|
+
reject(err);
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
});
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
// node_modules/better-sqlite3/lib/methods/serialize.js
|
|
2678
|
+
var require_serialize = __commonJS({
|
|
2679
|
+
"node_modules/better-sqlite3/lib/methods/serialize.js"(exports, module) {
|
|
2680
|
+
"use strict";
|
|
2681
|
+
init_esm_shims();
|
|
2682
|
+
var { cppdb } = require_util();
|
|
2683
|
+
module.exports = function serialize(options) {
|
|
2684
|
+
if (options == null) options = {};
|
|
2685
|
+
if (typeof options !== "object") throw new TypeError("Expected first argument to be an options object");
|
|
2686
|
+
const attachedName = "attached" in options ? options.attached : "main";
|
|
2687
|
+
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
2688
|
+
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
2689
|
+
return this[cppdb].serialize(attachedName);
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
});
|
|
2693
|
+
|
|
2694
|
+
// node_modules/better-sqlite3/lib/methods/function.js
|
|
2695
|
+
var require_function = __commonJS({
|
|
2696
|
+
"node_modules/better-sqlite3/lib/methods/function.js"(exports, module) {
|
|
2697
|
+
"use strict";
|
|
2698
|
+
init_esm_shims();
|
|
2699
|
+
var { getBooleanOption, cppdb } = require_util();
|
|
2700
|
+
module.exports = function defineFunction(name, options, fn) {
|
|
2701
|
+
if (options == null) options = {};
|
|
2702
|
+
if (typeof options === "function") {
|
|
2703
|
+
fn = options;
|
|
2704
|
+
options = {};
|
|
2705
|
+
}
|
|
2706
|
+
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2707
|
+
if (typeof fn !== "function") throw new TypeError("Expected last argument to be a function");
|
|
2708
|
+
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
2709
|
+
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
2710
|
+
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
2711
|
+
const deterministic = getBooleanOption(options, "deterministic");
|
|
2712
|
+
const directOnly = getBooleanOption(options, "directOnly");
|
|
2713
|
+
const varargs = getBooleanOption(options, "varargs");
|
|
2714
|
+
let argCount = -1;
|
|
2715
|
+
if (!varargs) {
|
|
2716
|
+
argCount = fn.length;
|
|
2717
|
+
if (!Number.isInteger(argCount) || argCount < 0) throw new TypeError("Expected function.length to be a positive integer");
|
|
2718
|
+
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
2719
|
+
}
|
|
2720
|
+
this[cppdb].function(fn, name, argCount, safeIntegers, deterministic, directOnly);
|
|
2721
|
+
return this;
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
});
|
|
2725
|
+
|
|
2726
|
+
// node_modules/better-sqlite3/lib/methods/aggregate.js
|
|
2727
|
+
var require_aggregate = __commonJS({
|
|
2728
|
+
"node_modules/better-sqlite3/lib/methods/aggregate.js"(exports, module) {
|
|
2729
|
+
"use strict";
|
|
2730
|
+
init_esm_shims();
|
|
2731
|
+
var { getBooleanOption, cppdb } = require_util();
|
|
2732
|
+
module.exports = function defineAggregate(name, options) {
|
|
2733
|
+
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2734
|
+
if (typeof options !== "object" || options === null) throw new TypeError("Expected second argument to be an options object");
|
|
2735
|
+
if (!name) throw new TypeError("User-defined function name cannot be an empty string");
|
|
2736
|
+
const start = "start" in options ? options.start : null;
|
|
2737
|
+
const step = getFunctionOption(options, "step", true);
|
|
2738
|
+
const inverse = getFunctionOption(options, "inverse", false);
|
|
2739
|
+
const result = getFunctionOption(options, "result", false);
|
|
2740
|
+
const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
|
|
2741
|
+
const deterministic = getBooleanOption(options, "deterministic");
|
|
2742
|
+
const directOnly = getBooleanOption(options, "directOnly");
|
|
2743
|
+
const varargs = getBooleanOption(options, "varargs");
|
|
2744
|
+
let argCount = -1;
|
|
2745
|
+
if (!varargs) {
|
|
2746
|
+
argCount = Math.max(getLength(step), inverse ? getLength(inverse) : 0);
|
|
2747
|
+
if (argCount > 0) argCount -= 1;
|
|
2748
|
+
if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
|
|
2749
|
+
}
|
|
2750
|
+
this[cppdb].aggregate(start, step, inverse, result, name, argCount, safeIntegers, deterministic, directOnly);
|
|
2751
|
+
return this;
|
|
2752
|
+
};
|
|
2753
|
+
var getFunctionOption = (options, key, required) => {
|
|
2754
|
+
const value = key in options ? options[key] : null;
|
|
2755
|
+
if (typeof value === "function") return value;
|
|
2756
|
+
if (value != null) throw new TypeError(`Expected the "${key}" option to be a function`);
|
|
2757
|
+
if (required) throw new TypeError(`Missing required option "${key}"`);
|
|
2758
|
+
return null;
|
|
2759
|
+
};
|
|
2760
|
+
var getLength = ({ length }) => {
|
|
2761
|
+
if (Number.isInteger(length) && length >= 0) return length;
|
|
2762
|
+
throw new TypeError("Expected function.length to be a positive integer");
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
// node_modules/better-sqlite3/lib/methods/table.js
|
|
2768
|
+
var require_table = __commonJS({
|
|
2769
|
+
"node_modules/better-sqlite3/lib/methods/table.js"(exports, module) {
|
|
2770
|
+
"use strict";
|
|
2771
|
+
init_esm_shims();
|
|
2772
|
+
var { cppdb } = require_util();
|
|
2773
|
+
module.exports = function defineTable(name, factory) {
|
|
2774
|
+
if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2775
|
+
if (!name) throw new TypeError("Virtual table module name cannot be an empty string");
|
|
2776
|
+
let eponymous = false;
|
|
2777
|
+
if (typeof factory === "object" && factory !== null) {
|
|
2778
|
+
eponymous = true;
|
|
2779
|
+
factory = defer(parseTableDefinition(factory, "used", name));
|
|
2780
|
+
} else {
|
|
2781
|
+
if (typeof factory !== "function") throw new TypeError("Expected second argument to be a function or a table definition object");
|
|
2782
|
+
factory = wrapFactory(factory);
|
|
2783
|
+
}
|
|
2784
|
+
this[cppdb].table(factory, name, eponymous);
|
|
2785
|
+
return this;
|
|
2786
|
+
};
|
|
2787
|
+
function wrapFactory(factory) {
|
|
2788
|
+
return function virtualTableFactory(moduleName, databaseName, tableName, ...args) {
|
|
2789
|
+
const thisObject = {
|
|
2790
|
+
module: moduleName,
|
|
2791
|
+
database: databaseName,
|
|
2792
|
+
table: tableName
|
|
2793
|
+
};
|
|
2794
|
+
const def = apply.call(factory, thisObject, args);
|
|
2795
|
+
if (typeof def !== "object" || def === null) {
|
|
2796
|
+
throw new TypeError(`Virtual table module "${moduleName}" did not return a table definition object`);
|
|
2797
|
+
}
|
|
2798
|
+
return parseTableDefinition(def, "returned", moduleName);
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
function parseTableDefinition(def, verb, moduleName) {
|
|
2802
|
+
if (!hasOwnProperty.call(def, "rows")) {
|
|
2803
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "rows" property`);
|
|
2804
|
+
}
|
|
2805
|
+
if (!hasOwnProperty.call(def, "columns")) {
|
|
2806
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "columns" property`);
|
|
2807
|
+
}
|
|
2808
|
+
const rows = def.rows;
|
|
2809
|
+
if (typeof rows !== "function" || Object.getPrototypeOf(rows) !== GeneratorFunctionPrototype) {
|
|
2810
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "rows" property (should be a generator function)`);
|
|
2811
|
+
}
|
|
2812
|
+
let columns = def.columns;
|
|
2813
|
+
if (!Array.isArray(columns) || !(columns = [...columns]).every((x) => typeof x === "string")) {
|
|
2814
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "columns" property (should be an array of strings)`);
|
|
2815
|
+
}
|
|
2816
|
+
if (columns.length !== new Set(columns).size) {
|
|
2817
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate column names`);
|
|
2818
|
+
}
|
|
2819
|
+
if (!columns.length) {
|
|
2820
|
+
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with zero columns`);
|
|
2821
|
+
}
|
|
2822
|
+
let parameters;
|
|
2823
|
+
if (hasOwnProperty.call(def, "parameters")) {
|
|
2824
|
+
parameters = def.parameters;
|
|
2825
|
+
if (!Array.isArray(parameters) || !(parameters = [...parameters]).every((x) => typeof x === "string")) {
|
|
2826
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "parameters" property (should be an array of strings)`);
|
|
2827
|
+
}
|
|
2828
|
+
} else {
|
|
2829
|
+
parameters = inferParameters(rows);
|
|
2830
|
+
}
|
|
2831
|
+
if (parameters.length !== new Set(parameters).size) {
|
|
2832
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate parameter names`);
|
|
2833
|
+
}
|
|
2834
|
+
if (parameters.length > 32) {
|
|
2835
|
+
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with more than the maximum number of 32 parameters`);
|
|
2836
|
+
}
|
|
2837
|
+
for (const parameter of parameters) {
|
|
2838
|
+
if (columns.includes(parameter)) {
|
|
2839
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with column "${parameter}" which was ambiguously defined as both a column and parameter`);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
let safeIntegers = 2;
|
|
2843
|
+
if (hasOwnProperty.call(def, "safeIntegers")) {
|
|
2844
|
+
const bool = def.safeIntegers;
|
|
2845
|
+
if (typeof bool !== "boolean") {
|
|
2846
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "safeIntegers" property (should be a boolean)`);
|
|
2847
|
+
}
|
|
2848
|
+
safeIntegers = +bool;
|
|
2849
|
+
}
|
|
2850
|
+
let directOnly = false;
|
|
2851
|
+
if (hasOwnProperty.call(def, "directOnly")) {
|
|
2852
|
+
directOnly = def.directOnly;
|
|
2853
|
+
if (typeof directOnly !== "boolean") {
|
|
2854
|
+
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "directOnly" property (should be a boolean)`);
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
const columnDefinitions = [
|
|
2858
|
+
...parameters.map(identifier).map((str) => `${str} HIDDEN`),
|
|
2859
|
+
...columns.map(identifier)
|
|
2860
|
+
];
|
|
2861
|
+
return [
|
|
2862
|
+
`CREATE TABLE x(${columnDefinitions.join(", ")});`,
|
|
2863
|
+
wrapGenerator(rows, new Map(columns.map((x, i) => [x, parameters.length + i])), moduleName),
|
|
2864
|
+
parameters,
|
|
2865
|
+
safeIntegers,
|
|
2866
|
+
directOnly
|
|
2867
|
+
];
|
|
2868
|
+
}
|
|
2869
|
+
function wrapGenerator(generator, columnMap, moduleName) {
|
|
2870
|
+
return function* virtualTable(...args) {
|
|
2871
|
+
const output = args.map((x) => Buffer.isBuffer(x) ? Buffer.from(x) : x);
|
|
2872
|
+
for (let i = 0; i < columnMap.size; ++i) {
|
|
2873
|
+
output.push(null);
|
|
2874
|
+
}
|
|
2875
|
+
for (const row of generator(...args)) {
|
|
2876
|
+
if (Array.isArray(row)) {
|
|
2877
|
+
extractRowArray(row, output, columnMap.size, moduleName);
|
|
2878
|
+
yield output;
|
|
2879
|
+
} else if (typeof row === "object" && row !== null) {
|
|
2880
|
+
extractRowObject(row, output, columnMap, moduleName);
|
|
2881
|
+
yield output;
|
|
2882
|
+
} else {
|
|
2883
|
+
throw new TypeError(`Virtual table module "${moduleName}" yielded something that isn't a valid row object`);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
function extractRowArray(row, output, columnCount, moduleName) {
|
|
2889
|
+
if (row.length !== columnCount) {
|
|
2890
|
+
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an incorrect number of columns`);
|
|
2891
|
+
}
|
|
2892
|
+
const offset = output.length - columnCount;
|
|
2893
|
+
for (let i = 0; i < columnCount; ++i) {
|
|
2894
|
+
output[i + offset] = row[i];
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
function extractRowObject(row, output, columnMap, moduleName) {
|
|
2898
|
+
let count = 0;
|
|
2899
|
+
for (const key of Object.keys(row)) {
|
|
2900
|
+
const index = columnMap.get(key);
|
|
2901
|
+
if (index === void 0) {
|
|
2902
|
+
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an undeclared column "${key}"`);
|
|
2903
|
+
}
|
|
2904
|
+
output[index] = row[key];
|
|
2905
|
+
count += 1;
|
|
2906
|
+
}
|
|
2907
|
+
if (count !== columnMap.size) {
|
|
2908
|
+
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with missing columns`);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
function inferParameters({ length }) {
|
|
2912
|
+
if (!Number.isInteger(length) || length < 0) {
|
|
2913
|
+
throw new TypeError("Expected function.length to be a positive integer");
|
|
2914
|
+
}
|
|
2915
|
+
const params = [];
|
|
2916
|
+
for (let i = 0; i < length; ++i) {
|
|
2917
|
+
params.push(`$${i + 1}`);
|
|
2918
|
+
}
|
|
2919
|
+
return params;
|
|
2920
|
+
}
|
|
2921
|
+
var { hasOwnProperty } = Object.prototype;
|
|
2922
|
+
var { apply } = Function.prototype;
|
|
2923
|
+
var GeneratorFunctionPrototype = Object.getPrototypeOf(function* () {
|
|
2924
|
+
});
|
|
2925
|
+
var identifier = (str) => `"${str.replace(/"/g, '""')}"`;
|
|
2926
|
+
var defer = (x) => () => x;
|
|
2927
|
+
}
|
|
2928
|
+
});
|
|
2929
|
+
|
|
2930
|
+
// node_modules/better-sqlite3/lib/methods/inspect.js
|
|
2931
|
+
var require_inspect = __commonJS({
|
|
2932
|
+
"node_modules/better-sqlite3/lib/methods/inspect.js"(exports, module) {
|
|
2933
|
+
"use strict";
|
|
2934
|
+
init_esm_shims();
|
|
2935
|
+
var DatabaseInspection = function Database() {
|
|
2936
|
+
};
|
|
2937
|
+
module.exports = function inspect(depth, opts) {
|
|
2938
|
+
return Object.assign(new DatabaseInspection(), this);
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
});
|
|
2942
|
+
|
|
2943
|
+
// node_modules/better-sqlite3/lib/database.js
|
|
2944
|
+
var require_database = __commonJS({
|
|
2945
|
+
"node_modules/better-sqlite3/lib/database.js"(exports, module) {
|
|
2946
|
+
"use strict";
|
|
2947
|
+
init_esm_shims();
|
|
2948
|
+
var fs4 = __require("fs");
|
|
2949
|
+
var path3 = __require("path");
|
|
2950
|
+
var util = require_util();
|
|
2951
|
+
var SqliteError = require_sqlite_error();
|
|
2952
|
+
var DEFAULT_ADDON;
|
|
2953
|
+
function Database(filenameGiven, options) {
|
|
2954
|
+
if (new.target == null) {
|
|
2955
|
+
return new Database(filenameGiven, options);
|
|
2956
|
+
}
|
|
2957
|
+
let buffer;
|
|
2958
|
+
if (Buffer.isBuffer(filenameGiven)) {
|
|
2959
|
+
buffer = filenameGiven;
|
|
2960
|
+
filenameGiven = ":memory:";
|
|
2961
|
+
}
|
|
2962
|
+
if (filenameGiven == null) filenameGiven = "";
|
|
2963
|
+
if (options == null) options = {};
|
|
2964
|
+
if (typeof filenameGiven !== "string") throw new TypeError("Expected first argument to be a string");
|
|
2965
|
+
if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
|
|
2966
|
+
if ("readOnly" in options) throw new TypeError('Misspelled option "readOnly" should be "readonly"');
|
|
2967
|
+
if ("memory" in options) throw new TypeError('Option "memory" was removed in v7.0.0 (use ":memory:" filename instead)');
|
|
2968
|
+
const filename = filenameGiven.trim();
|
|
2969
|
+
const anonymous = filename === "" || filename === ":memory:";
|
|
2970
|
+
const readonly = util.getBooleanOption(options, "readonly");
|
|
2971
|
+
const fileMustExist = util.getBooleanOption(options, "fileMustExist");
|
|
2972
|
+
const timeout = "timeout" in options ? options.timeout : 5e3;
|
|
2973
|
+
const verbose = "verbose" in options ? options.verbose : null;
|
|
2974
|
+
const nativeBinding = "nativeBinding" in options ? options.nativeBinding : null;
|
|
2975
|
+
if (readonly && anonymous && !buffer) throw new TypeError("In-memory/temporary databases cannot be readonly");
|
|
2976
|
+
if (!Number.isInteger(timeout) || timeout < 0) throw new TypeError('Expected the "timeout" option to be a positive integer');
|
|
2977
|
+
if (timeout > 2147483647) throw new RangeError('Option "timeout" cannot be greater than 2147483647');
|
|
2978
|
+
if (verbose != null && typeof verbose !== "function") throw new TypeError('Expected the "verbose" option to be a function');
|
|
2979
|
+
if (nativeBinding != null && typeof nativeBinding !== "string" && typeof nativeBinding !== "object") throw new TypeError('Expected the "nativeBinding" option to be a string or addon object');
|
|
2980
|
+
let addon;
|
|
2981
|
+
if (nativeBinding == null) {
|
|
2982
|
+
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
|
|
2983
|
+
} else if (typeof nativeBinding === "string") {
|
|
2984
|
+
const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
2985
|
+
addon = requireFunc(path3.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
|
|
2986
|
+
} else {
|
|
2987
|
+
addon = nativeBinding;
|
|
2988
|
+
}
|
|
2989
|
+
if (!addon.isInitialized) {
|
|
2990
|
+
addon.setErrorConstructor(SqliteError);
|
|
2991
|
+
addon.isInitialized = true;
|
|
2992
|
+
}
|
|
2993
|
+
if (!anonymous && !fs4.existsSync(path3.dirname(filename))) {
|
|
2994
|
+
throw new TypeError("Cannot open database because the directory does not exist");
|
|
2995
|
+
}
|
|
2996
|
+
Object.defineProperties(this, {
|
|
2997
|
+
[util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) },
|
|
2998
|
+
...wrappers.getters
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
var wrappers = require_wrappers();
|
|
3002
|
+
Database.prototype.prepare = wrappers.prepare;
|
|
3003
|
+
Database.prototype.transaction = require_transaction();
|
|
3004
|
+
Database.prototype.pragma = require_pragma();
|
|
3005
|
+
Database.prototype.backup = require_backup();
|
|
3006
|
+
Database.prototype.serialize = require_serialize();
|
|
3007
|
+
Database.prototype.function = require_function();
|
|
3008
|
+
Database.prototype.aggregate = require_aggregate();
|
|
3009
|
+
Database.prototype.table = require_table();
|
|
3010
|
+
Database.prototype.loadExtension = wrappers.loadExtension;
|
|
3011
|
+
Database.prototype.exec = wrappers.exec;
|
|
3012
|
+
Database.prototype.close = wrappers.close;
|
|
3013
|
+
Database.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
|
|
3014
|
+
Database.prototype.unsafeMode = wrappers.unsafeMode;
|
|
3015
|
+
Database.prototype[util.inspect] = require_inspect();
|
|
3016
|
+
module.exports = Database;
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
|
|
3020
|
+
// node_modules/better-sqlite3/lib/index.js
|
|
3021
|
+
var require_lib = __commonJS({
|
|
3022
|
+
"node_modules/better-sqlite3/lib/index.js"(exports, module) {
|
|
3023
|
+
"use strict";
|
|
3024
|
+
init_esm_shims();
|
|
3025
|
+
module.exports = require_database();
|
|
3026
|
+
module.exports.SqliteError = require_sqlite_error();
|
|
3027
|
+
}
|
|
3028
|
+
});
|
|
3029
|
+
|
|
3030
|
+
// src/server.ts
|
|
3031
|
+
init_esm_shims();
|
|
3032
|
+
import { Hono as Hono2 } from "hono";
|
|
3033
|
+
import { cors } from "hono/cors";
|
|
3034
|
+
import { logger } from "hono/logger";
|
|
3035
|
+
import net from "net";
|
|
3036
|
+
|
|
3037
|
+
// src/routes.ts
|
|
3038
|
+
init_esm_shims();
|
|
3039
|
+
|
|
3040
|
+
// src/models.ts
|
|
3041
|
+
init_esm_shims();
|
|
3042
|
+
import { z } from "zod";
|
|
3043
|
+
|
|
3044
|
+
// src/package-json.ts
|
|
3045
|
+
init_esm_shims();
|
|
3046
|
+
import { createRequire } from "module";
|
|
3047
|
+
var require2 = createRequire(import.meta.url);
|
|
3048
|
+
var pkg = require2("../package.json");
|
|
3049
|
+
var version = pkg.version;
|
|
3050
|
+
|
|
3051
|
+
// src/models.ts
|
|
3052
|
+
var ActionItemSchema = z.object({
|
|
3053
|
+
action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
|
|
3054
|
+
action_detail: z.string().describe("Action detail / command content")
|
|
3055
|
+
});
|
|
3056
|
+
var AnalyzeRequestSchema = z.object({
|
|
3057
|
+
session_id: z.string().describe("Session / agent unique identifier"),
|
|
3058
|
+
prompt_session: z.string().default("").describe("User original prompt"),
|
|
3059
|
+
action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
|
|
3060
|
+
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
3061
|
+
reason: z.string().default("").describe("Reason for executing this action"),
|
|
3062
|
+
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
3063
|
+
cwd: z.string().default("").describe("Current working directory"),
|
|
3064
|
+
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
3065
|
+
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
3066
|
+
});
|
|
3067
|
+
var AnalyzeResponseSchema = z.object({
|
|
3068
|
+
decision: z.enum(["Allow", "Deny"]).describe("Allow or Deny"),
|
|
3069
|
+
reason: z.string().describe("Decision reason"),
|
|
3070
|
+
risk_level: z.enum(["low", "medium", "high", "critical"]).default("low"),
|
|
3071
|
+
risk_type: z.string().default(""),
|
|
3072
|
+
violated_layers: z.array(z.string()).default([]),
|
|
3073
|
+
policy: z.string().default(""),
|
|
3074
|
+
violated_policy: z.string().default(""),
|
|
3075
|
+
confidence: z.number().int().min(0).max(100).default(100),
|
|
3076
|
+
analysis_duration_ms: z.number().default(0)
|
|
3077
|
+
});
|
|
3078
|
+
var HealthResponseSchema = z.object({
|
|
3079
|
+
status: z.string().default("healthy"),
|
|
3080
|
+
version: z.string().default(version)
|
|
3081
|
+
});
|
|
3082
|
+
|
|
3083
|
+
// src/engine/coordinator.ts
|
|
3084
|
+
init_esm_shims();
|
|
3085
|
+
init_heuristic();
|
|
3086
|
+
init_logic_rules();
|
|
3087
|
+
init_inline_analyzer();
|
|
3088
|
+
|
|
3089
|
+
// src/engine/llm-analyzer.ts
|
|
3090
|
+
init_esm_shims();
|
|
3091
|
+
init_config();
|
|
3092
|
+
init_skill_loader();
|
|
3093
|
+
|
|
3094
|
+
// src/engine/template-loader.ts
|
|
3095
|
+
init_esm_shims();
|
|
3096
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
3097
|
+
import { resolve as resolve3, dirname as dirname3 } from "path";
|
|
3098
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3099
|
+
var __filename4 = fileURLToPath4(import.meta.url);
|
|
3100
|
+
var __dirname4 = dirname3(__filename4);
|
|
3101
|
+
var TEMPLATES_DIR = resolve3(__dirname4, "../templates");
|
|
3102
|
+
var TEMPLATES_DIR_FALLBACK = resolve3(__dirname4, "../../../agent_moss/templates");
|
|
3103
|
+
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
3104
|
+
var cachedTemplates = null;
|
|
3105
|
+
function loadTemplates() {
|
|
3106
|
+
if (cachedTemplates) return cachedTemplates;
|
|
3107
|
+
cachedTemplates = {};
|
|
3108
|
+
const dir = existsSync4(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
|
|
3109
|
+
if (!existsSync4(dir)) {
|
|
3110
|
+
console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
|
|
3111
|
+
return cachedTemplates;
|
|
3112
|
+
}
|
|
3113
|
+
for (const file of TEMPLATE_FILES) {
|
|
3114
|
+
const filePath = resolve3(dir, file);
|
|
3115
|
+
if (existsSync4(filePath)) {
|
|
3116
|
+
const key = file.replace(".txt", "").replace("_template", "");
|
|
3117
|
+
cachedTemplates[key] = readFileSync3(filePath, "utf-8");
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
|
|
3121
|
+
return cachedTemplates;
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// src/engine/script-analyzer.ts
|
|
3125
|
+
init_esm_shims();
|
|
3126
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3127
|
+
import { resolve as resolve4, join } from "path";
|
|
3128
|
+
var SUSPICIOUS_PATTERNS = [
|
|
3129
|
+
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
3130
|
+
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
3131
|
+
{ name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
|
|
3132
|
+
{ name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
|
|
3133
|
+
{ name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
|
|
3134
|
+
{ name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
|
|
3135
|
+
// Reverse shell
|
|
3136
|
+
{ name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
|
|
3137
|
+
{ name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
|
|
3138
|
+
{ name: "/dev/tcp", pattern: /\/dev\/tcp\// },
|
|
3139
|
+
{ name: "/dev/udp", pattern: /\/dev\/udp\// },
|
|
3140
|
+
// Base64 obfuscation
|
|
3141
|
+
{ name: "$(base64", pattern: /\$\(\s*base64/ },
|
|
3142
|
+
{ name: "`base64", pattern: /`\s*base64/ },
|
|
3143
|
+
{ name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
|
|
3144
|
+
// Sensitive file access
|
|
3145
|
+
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
3146
|
+
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
3147
|
+
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
3148
|
+
// Persistence backdoor
|
|
3149
|
+
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
3150
|
+
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
3151
|
+
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
3152
|
+
];
|
|
3153
|
+
var SCRIPT_EXEC_PATTERNS = [
|
|
3154
|
+
/bash\s+(\S+\.sh)/,
|
|
3155
|
+
/sh\s+(\S+\.sh)/,
|
|
3156
|
+
/\.\/(\S+\.sh)/,
|
|
3157
|
+
/source\s+(\S+\.sh)/,
|
|
3158
|
+
/\.\s+(\S+\.sh)/,
|
|
3159
|
+
/zsh\s+(\S+\.sh)/
|
|
3160
|
+
];
|
|
3161
|
+
var KEYWORD_COMBINATION_RISKS = [
|
|
3162
|
+
{
|
|
3163
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
3164
|
+
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
3165
|
+
level: "critical",
|
|
3166
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
|
|
3167
|
+
},
|
|
3168
|
+
{
|
|
3169
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
3170
|
+
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
3171
|
+
level: "critical",
|
|
3172
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
|
|
3173
|
+
},
|
|
3174
|
+
{
|
|
3175
|
+
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
3176
|
+
group2: /* @__PURE__ */ new Set(),
|
|
3177
|
+
level: "high",
|
|
3178
|
+
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
3182
|
+
group2: /* @__PURE__ */ new Set(),
|
|
3183
|
+
level: "high",
|
|
3184
|
+
reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
|
|
3185
|
+
}
|
|
3186
|
+
];
|
|
3187
|
+
var SINGLE_KEYWORD_RISKS = {
|
|
3188
|
+
"curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3189
|
+
"curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3190
|
+
"curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3191
|
+
"wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3192
|
+
"wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
|
|
3193
|
+
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3194
|
+
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3195
|
+
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3196
|
+
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3197
|
+
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3198
|
+
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3199
|
+
"base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3200
|
+
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
3201
|
+
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
3202
|
+
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
3203
|
+
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
3204
|
+
"authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
3205
|
+
"ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
|
|
3206
|
+
};
|
|
3207
|
+
var LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
3208
|
+
function emptyResult() {
|
|
3209
|
+
return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
|
|
3210
|
+
}
|
|
3211
|
+
function extractScriptPath(command) {
|
|
3212
|
+
for (const pattern of SCRIPT_EXEC_PATTERNS) {
|
|
3213
|
+
const m = command.match(pattern);
|
|
3214
|
+
if (m) return m[1];
|
|
3215
|
+
}
|
|
3216
|
+
return null;
|
|
3217
|
+
}
|
|
3218
|
+
function scanScriptForKeywords(content) {
|
|
3219
|
+
const matched = [];
|
|
3220
|
+
for (const { name, pattern } of SUSPICIOUS_PATTERNS) {
|
|
3221
|
+
if (pattern.test(content)) matched.push(name);
|
|
3222
|
+
}
|
|
3223
|
+
return matched;
|
|
3224
|
+
}
|
|
3225
|
+
function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
3226
|
+
if (matchedKeywords.length === 0) return { level: "", reason: "" };
|
|
3227
|
+
const keywordSet = new Set(matchedKeywords);
|
|
3228
|
+
for (const rule of KEYWORD_COMBINATION_RISKS) {
|
|
3229
|
+
const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
|
|
3230
|
+
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
3231
|
+
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
3232
|
+
}
|
|
3233
|
+
const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
|
|
3234
|
+
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
3235
|
+
if (hasNetworkPost) {
|
|
3236
|
+
const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
|
|
3237
|
+
const contentLower = scriptContent.toLowerCase();
|
|
3238
|
+
for (const p of envPatterns) {
|
|
3239
|
+
if (contentLower.includes(p.toLowerCase())) {
|
|
3240
|
+
return {
|
|
3241
|
+
level: "critical",
|
|
3242
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u6536\u96C6\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u811A\u672C\u53EF\u80FD\u5C06\u7528\u6237\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u80FD\u5305\u542B API Key\u3001\u5BC6\u94A5\u7B49\u654F\u611F\u4FE1\u606F\uFF09\u53D1\u9001\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\u3002"
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
let bestLevel = "";
|
|
3248
|
+
let bestReason = "";
|
|
3249
|
+
for (const kw of matchedKeywords) {
|
|
3250
|
+
const risk = SINGLE_KEYWORD_RISKS[kw];
|
|
3251
|
+
if (risk && (LEVEL_ORDER[risk.level] || 0) > (LEVEL_ORDER[bestLevel] || 0)) {
|
|
3252
|
+
bestLevel = risk.level;
|
|
3253
|
+
bestReason = risk.reason;
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
return { level: bestLevel, reason: bestReason };
|
|
3257
|
+
}
|
|
3258
|
+
function readScriptContent(scriptPath, maxLines = 500) {
|
|
3259
|
+
try {
|
|
3260
|
+
const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
|
|
3261
|
+
const raw = readFileSync4(expandedPath, "utf-8");
|
|
3262
|
+
const lines = raw.split("\n").slice(0, maxLines);
|
|
3263
|
+
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
3264
|
+
} catch (e) {
|
|
3265
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3266
|
+
return { content: "", lineCount: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
function analyzeScriptContent(actionType, actionDetail, maxLines = 500) {
|
|
3270
|
+
if (actionType.toLowerCase() !== "bash") return emptyResult();
|
|
3271
|
+
const scriptPath = extractScriptPath(actionDetail);
|
|
3272
|
+
if (!scriptPath) return emptyResult();
|
|
3273
|
+
const { content, lineCount, error } = readScriptContent(scriptPath, maxLines);
|
|
3274
|
+
if (error) return { ...emptyResult(), scriptPath, error };
|
|
3275
|
+
const matchedKeywords = scanScriptForKeywords(content);
|
|
3276
|
+
const isSuspicious = matchedKeywords.length > 0;
|
|
3277
|
+
let riskLevel = "";
|
|
3278
|
+
let riskReason = "";
|
|
3279
|
+
if (isSuspicious) {
|
|
3280
|
+
const risk = assessKeywordCombinationRisk(matchedKeywords, content);
|
|
3281
|
+
riskLevel = risk.level;
|
|
3282
|
+
riskReason = risk.reason;
|
|
3283
|
+
}
|
|
3284
|
+
return { scriptPath, scriptContent: content, matchedKeywords, lineCount, isSuspicious, error: "", riskLevel, riskReason };
|
|
3285
|
+
}
|
|
3286
|
+
function formatScriptAnalysisForPrompt(result) {
|
|
3287
|
+
if (!result.scriptPath) return "";
|
|
3288
|
+
if (result.error) return `\u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${result.error}`;
|
|
3289
|
+
if (!result.isSuspicious) return "";
|
|
3290
|
+
const keywordsStr = result.matchedKeywords.join(", ");
|
|
3291
|
+
let riskHint = "";
|
|
3292
|
+
if (result.riskLevel && result.riskReason) {
|
|
3293
|
+
riskHint = `
|
|
3294
|
+
|
|
3295
|
+
\u98CE\u9669\u8BC4\u4F30: ${result.riskLevel.toUpperCase()} \u7EA7\u522B
|
|
3296
|
+
\u98CE\u9669\u539F\u56E0: ${result.riskReason}
|
|
3297
|
+
\u5EFA\u8BAE: \u6B64\u811A\u672C\u5E94\u88AB\u62D2\u7EDD\uFF08Deny\uFF09\uFF0C\u9664\u975E\u6709\u660E\u786E\u7684\u4E1A\u52A1\u9700\u6C42\u4E14\u5DF2\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u5668\u53EF\u4FE1\u3002`;
|
|
3298
|
+
}
|
|
3299
|
+
return `\u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\uFF0C\u4E14\u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
3300
|
+
\u811A\u672C\u8DEF\u5F84: ${result.scriptPath}
|
|
3301
|
+
\u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
|
|
3302
|
+
\u811A\u672C\u884C\u6570: ${result.lineCount}${riskHint}
|
|
3303
|
+
|
|
3304
|
+
### \u811A\u672C\u5185\u5BB9 ###
|
|
3305
|
+
\`\`\`
|
|
3306
|
+
${result.scriptContent}
|
|
3307
|
+
\`\`\`
|
|
3308
|
+
`;
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
// src/engine/script-content-analyzer.ts
|
|
3312
|
+
init_esm_shims();
|
|
3313
|
+
init_heuristic();
|
|
3314
|
+
init_logic_rules();
|
|
3315
|
+
import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "fs";
|
|
3316
|
+
import { resolve as resolve5, join as join2, isAbsolute, dirname as dirname4 } from "path";
|
|
3317
|
+
import { homedir } from "os";
|
|
3318
|
+
function emptyChain() {
|
|
3319
|
+
return {
|
|
3320
|
+
nodes: [],
|
|
3321
|
+
entryPath: "",
|
|
3322
|
+
maxDepthReached: false,
|
|
3323
|
+
maxSizeReached: false,
|
|
3324
|
+
totalSize: 0,
|
|
3325
|
+
totalLines: 0,
|
|
3326
|
+
hasCritical: false,
|
|
3327
|
+
hasHigh: false,
|
|
3328
|
+
firstHitNode: null,
|
|
3329
|
+
error: ""
|
|
3330
|
+
};
|
|
3331
|
+
}
|
|
3332
|
+
function newNode(path3, depth) {
|
|
3333
|
+
return {
|
|
3334
|
+
scriptPath: path3,
|
|
3335
|
+
rawContent: "",
|
|
3336
|
+
preprocessedContent: "",
|
|
3337
|
+
lineCount: 0,
|
|
3338
|
+
fileSize: 0,
|
|
3339
|
+
depth,
|
|
3340
|
+
childPaths: [],
|
|
3341
|
+
error: "",
|
|
3342
|
+
l1Hit: false,
|
|
3343
|
+
l1RiskLevel: "",
|
|
3344
|
+
l1RiskType: "",
|
|
3345
|
+
l1Reason: "",
|
|
3346
|
+
l1MatchedPatterns: [],
|
|
3347
|
+
l2Hit: false,
|
|
3348
|
+
l2ViolatedRule: "",
|
|
3349
|
+
l2RiskLevel: "",
|
|
3350
|
+
l2RiskType: "",
|
|
3351
|
+
l2Reason: "",
|
|
3352
|
+
matchedKeywords: [],
|
|
3353
|
+
isSuspicious: false,
|
|
3354
|
+
keywordRiskLevel: "",
|
|
3355
|
+
keywordRiskReason: ""
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
function stripComments(content) {
|
|
3359
|
+
return content.split("\n").filter((line) => !line.trimStart().startsWith("#")).join("\n");
|
|
3360
|
+
}
|
|
3361
|
+
function stripOutputText(content) {
|
|
3362
|
+
let result = content.replace(/echo\s+".*?"/gs, 'echo ""');
|
|
3363
|
+
result = result.replace(/echo\s+'.*?'/gs, "echo ''");
|
|
3364
|
+
result = result.replace(/printf\s+".*?"[^\n]*/gs, 'printf ""');
|
|
3365
|
+
result = result.replace(/printf\s+'.*?'[^\n]*/gs, "printf ''");
|
|
3366
|
+
return result;
|
|
3367
|
+
}
|
|
3368
|
+
function expandSimpleVariables(content) {
|
|
3369
|
+
const varMap = {};
|
|
3370
|
+
const assignmentRe = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.+)$/;
|
|
3371
|
+
const collectAssignments = (text) => {
|
|
3372
|
+
for (const line of text.split("\n")) {
|
|
3373
|
+
const m = line.match(assignmentRe);
|
|
3374
|
+
if (m) {
|
|
3375
|
+
const varName = m[1];
|
|
3376
|
+
let varVal = m[2].trim();
|
|
3377
|
+
if (varVal && !varVal.includes("$") && !varVal.includes("`")) {
|
|
3378
|
+
if (varVal.startsWith('"') && varVal.endsWith('"') || varVal.startsWith("'") && varVal.endsWith("'")) {
|
|
3379
|
+
varVal = varVal.slice(1, -1);
|
|
3380
|
+
}
|
|
3381
|
+
varMap[varName] = varVal;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
collectAssignments(content);
|
|
3387
|
+
let expanded = content;
|
|
3388
|
+
for (let round = 0; round < 3; round++) {
|
|
3389
|
+
const sortedVars = Object.keys(varMap).sort((a, b) => b.length - a.length);
|
|
3390
|
+
let newExpanded = expanded;
|
|
3391
|
+
for (const v of sortedVars) {
|
|
3392
|
+
const val = varMap[v];
|
|
3393
|
+
newExpanded = newExpanded.split("${" + v + "}").join(val);
|
|
3394
|
+
newExpanded = newExpanded.split("$" + v).join(val);
|
|
3395
|
+
}
|
|
3396
|
+
if (newExpanded === expanded) break;
|
|
3397
|
+
expanded = newExpanded;
|
|
3398
|
+
collectAssignments(expanded);
|
|
3399
|
+
}
|
|
3400
|
+
return expanded;
|
|
3401
|
+
}
|
|
3402
|
+
function preprocessScriptContent(content) {
|
|
3403
|
+
let result = stripComments(content);
|
|
3404
|
+
result = stripOutputText(result);
|
|
3405
|
+
result = expandSimpleVariables(result);
|
|
3406
|
+
return result;
|
|
3407
|
+
}
|
|
3408
|
+
var SUSPICIOUS_PATTERNS2 = [
|
|
3409
|
+
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
3410
|
+
{ name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
|
|
3411
|
+
{ name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
|
|
3412
|
+
{ name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
|
|
3413
|
+
{ name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
|
|
3414
|
+
{ name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
|
|
3415
|
+
{ name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
|
|
3416
|
+
{ name: "/dev/tcp", pattern: /\/dev\/tcp\// },
|
|
3417
|
+
{ name: "/dev/udp", pattern: /\/dev\/udp\// },
|
|
3418
|
+
{ name: "$(base64", pattern: /\$\(\s*base64/ },
|
|
3419
|
+
{ name: "`base64", pattern: /`\s*base64/ },
|
|
3420
|
+
{ name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
|
|
3421
|
+
{ name: "/etc/shadow", pattern: /\/etc\/shadow/ },
|
|
3422
|
+
{ name: "id_rsa", pattern: /\bid_rsa\b/ },
|
|
3423
|
+
{ name: "id_ed25519", pattern: /\bid_ed25519\b/ },
|
|
3424
|
+
{ name: "crontab install", pattern: /crontab\s+-/ },
|
|
3425
|
+
{ name: "authorized_keys", pattern: /authorized_keys/ },
|
|
3426
|
+
{ name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
|
|
3427
|
+
];
|
|
3428
|
+
var SCRIPT_EXEC_PATTERNS2 = [
|
|
3429
|
+
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
3430
|
+
/(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
|
|
3431
|
+
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
3432
|
+
/(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
|
|
3433
|
+
/\.\/(\S+)/,
|
|
3434
|
+
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
3435
|
+
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
3436
|
+
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
3437
|
+
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
3438
|
+
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
3439
|
+
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
3440
|
+
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
3441
|
+
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
3442
|
+
];
|
|
3443
|
+
var KEYWORD_COMBINATION_RISKS2 = [
|
|
3444
|
+
{
|
|
3445
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
3446
|
+
group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
|
|
3447
|
+
level: "critical",
|
|
3448
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
|
|
3449
|
+
},
|
|
3450
|
+
{
|
|
3451
|
+
group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
|
|
3452
|
+
group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
|
|
3453
|
+
level: "critical",
|
|
3454
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
|
|
3455
|
+
},
|
|
3456
|
+
{
|
|
3457
|
+
group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
|
|
3458
|
+
group2: /* @__PURE__ */ new Set(),
|
|
3459
|
+
level: "high",
|
|
3460
|
+
reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
|
|
3461
|
+
},
|
|
3462
|
+
{
|
|
3463
|
+
group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
|
|
3464
|
+
group2: /* @__PURE__ */ new Set(),
|
|
3465
|
+
level: "high",
|
|
3466
|
+
reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
|
|
3467
|
+
}
|
|
3468
|
+
];
|
|
3469
|
+
var SINGLE_KEYWORD_RISKS2 = {
|
|
3470
|
+
"curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3471
|
+
"curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3472
|
+
"curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3473
|
+
"wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
|
|
3474
|
+
"wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
|
|
3475
|
+
"nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3476
|
+
"ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3477
|
+
"/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3478
|
+
"/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
|
|
3479
|
+
"$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3480
|
+
"`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3481
|
+
"base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
|
|
3482
|
+
"/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
|
|
3483
|
+
"id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
3484
|
+
"id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
|
|
3485
|
+
"crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
3486
|
+
"authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
|
|
3487
|
+
"ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
|
|
3488
|
+
};
|
|
3489
|
+
var LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
3490
|
+
function scanScriptForKeywords2(content) {
|
|
3491
|
+
const matched = [];
|
|
3492
|
+
for (const { name, pattern } of SUSPICIOUS_PATTERNS2) {
|
|
3493
|
+
if (pattern.test(content)) matched.push(name);
|
|
3494
|
+
}
|
|
3495
|
+
return matched;
|
|
3496
|
+
}
|
|
3497
|
+
function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
|
|
3498
|
+
if (matchedKeywords.length === 0) return { level: "", reason: "" };
|
|
3499
|
+
const keywordSet = new Set(matchedKeywords);
|
|
3500
|
+
for (const rule of KEYWORD_COMBINATION_RISKS2) {
|
|
3501
|
+
const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
|
|
3502
|
+
const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
|
|
3503
|
+
if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
|
|
3504
|
+
}
|
|
3505
|
+
const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
|
|
3506
|
+
const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
|
|
3507
|
+
if (hasNetworkPost) {
|
|
3508
|
+
const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
|
|
3509
|
+
const contentLower = scriptContent.toLowerCase();
|
|
3510
|
+
for (const p of envPatterns) {
|
|
3511
|
+
if (contentLower.includes(p.toLowerCase())) {
|
|
3512
|
+
return {
|
|
3513
|
+
level: "critical",
|
|
3514
|
+
reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u6536\u96C6\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u811A\u672C\u53EF\u80FD\u5C06\u7528\u6237\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u80FD\u5305\u542B API Key\u3001\u5BC6\u94A5\u7B49\u654F\u611F\u4FE1\u606F\uFF09\u53D1\u9001\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\u3002"
|
|
3515
|
+
};
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
let bestLevel = "";
|
|
3520
|
+
let bestReason = "";
|
|
3521
|
+
for (const kw of matchedKeywords) {
|
|
3522
|
+
const risk = SINGLE_KEYWORD_RISKS2[kw];
|
|
3523
|
+
if (risk && (LEVEL_ORDER2[risk.level] || 0) > (LEVEL_ORDER2[bestLevel] || 0)) {
|
|
3524
|
+
bestLevel = risk.level;
|
|
3525
|
+
bestReason = risk.reason;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
return { level: bestLevel, reason: bestReason };
|
|
3529
|
+
}
|
|
3530
|
+
function extractScriptPath2(command) {
|
|
3531
|
+
for (const pattern of SCRIPT_EXEC_PATTERNS2) {
|
|
3532
|
+
const m = command.match(pattern);
|
|
3533
|
+
if (m) return m[1];
|
|
3534
|
+
}
|
|
3535
|
+
return null;
|
|
3536
|
+
}
|
|
3537
|
+
function extractInlineScriptPath(actionDetail) {
|
|
3538
|
+
const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
3539
|
+
let m;
|
|
3540
|
+
while ((m = re.exec(actionDetail)) !== null) {
|
|
3541
|
+
const p = m[1];
|
|
3542
|
+
let resolved;
|
|
3543
|
+
try {
|
|
3544
|
+
resolved = resolvePath(p);
|
|
3545
|
+
if (existsSync5(resolved)) {
|
|
3546
|
+
const stat = statSync(resolved);
|
|
3547
|
+
if (stat.isFile()) return resolved;
|
|
3548
|
+
}
|
|
3549
|
+
} catch {
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
return null;
|
|
3553
|
+
}
|
|
3554
|
+
function resolvePath(p) {
|
|
3555
|
+
let expanded = p;
|
|
3556
|
+
if (expanded.startsWith("~/") || expanded === "~") {
|
|
3557
|
+
expanded = join2(homedir(), expanded.slice(1));
|
|
3558
|
+
}
|
|
3559
|
+
return resolve5(expanded);
|
|
3560
|
+
}
|
|
3561
|
+
function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
|
|
3562
|
+
try {
|
|
3563
|
+
const path3 = resolvePath(scriptPath);
|
|
3564
|
+
if (!existsSync5(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
|
|
3565
|
+
const stat = statSync(path3);
|
|
3566
|
+
if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
|
|
3567
|
+
if (stat.size > maxFileSize) {
|
|
3568
|
+
return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
|
|
3569
|
+
}
|
|
3570
|
+
const content = readFileSync5(path3, "utf-8");
|
|
3571
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
3572
|
+
return { content, lineCount, fileSize: stat.size, error: "" };
|
|
3573
|
+
} catch (e) {
|
|
3574
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3575
|
+
return { content: "", lineCount: 0, fileSize: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
function extractChildScriptPaths(content, baseDir) {
|
|
3579
|
+
const paths = [];
|
|
3580
|
+
for (const pattern of CHILD_SCRIPT_PATTERNS) {
|
|
3581
|
+
pattern.lastIndex = 0;
|
|
3582
|
+
let m;
|
|
3583
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
3584
|
+
const pathStr = m[1];
|
|
3585
|
+
if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
|
|
3586
|
+
if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
|
|
3587
|
+
const full = isAbsolute(pathStr) ? pathStr : join2(baseDir, pathStr);
|
|
3588
|
+
const resolved = resolvePath(full);
|
|
3589
|
+
if (existsSync5(resolved)) {
|
|
3590
|
+
try {
|
|
3591
|
+
if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
|
|
3592
|
+
} catch {
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
return paths;
|
|
3598
|
+
}
|
|
3599
|
+
function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 1024 * 1024, maxTotalSize = 6 * 1024 * 1024) {
|
|
3600
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3601
|
+
const result = emptyChain();
|
|
3602
|
+
result.entryPath = entryPath;
|
|
3603
|
+
const entry = resolvePath(entryPath);
|
|
3604
|
+
const baseDir = existsSync5(entry) ? dirname4(entry) : input.cwd || process.cwd();
|
|
3605
|
+
resolveRecursive({
|
|
3606
|
+
path: entry,
|
|
3607
|
+
depth: 0,
|
|
3608
|
+
maxDepth,
|
|
3609
|
+
maxFileSize,
|
|
3610
|
+
maxTotalSize,
|
|
3611
|
+
visited,
|
|
3612
|
+
result,
|
|
3613
|
+
baseDir,
|
|
3614
|
+
input
|
|
3615
|
+
});
|
|
3616
|
+
result.hasCritical = result.nodes.some((n) => n.l1RiskLevel === "critical" || n.l2RiskLevel === "critical" || n.keywordRiskLevel === "critical");
|
|
3617
|
+
result.hasHigh = result.nodes.some((n) => ["high", "critical"].includes(n.l1RiskLevel) || ["high", "critical"].includes(n.l2RiskLevel) || ["high", "critical"].includes(n.keywordRiskLevel));
|
|
3618
|
+
result.firstHitNode = result.nodes.find((n) => n.l1Hit || n.l2Hit || n.isSuspicious) || null;
|
|
3619
|
+
return result;
|
|
3620
|
+
}
|
|
3621
|
+
function resolveRecursive(ctx) {
|
|
3622
|
+
const { path: path3, depth, maxDepth, maxFileSize, maxTotalSize, visited, result, baseDir, input } = ctx;
|
|
3623
|
+
if (visited.has(path3)) return;
|
|
3624
|
+
if (depth > maxDepth) {
|
|
3625
|
+
result.maxDepthReached = true;
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
if (result.totalSize >= maxTotalSize) {
|
|
3629
|
+
result.maxSizeReached = true;
|
|
3630
|
+
return;
|
|
3631
|
+
}
|
|
3632
|
+
visited.add(path3);
|
|
3633
|
+
const { content, lineCount, fileSize, error } = readScriptContent2(path3, maxFileSize);
|
|
3634
|
+
if (error) {
|
|
3635
|
+
const node2 = newNode(path3, depth);
|
|
3636
|
+
node2.error = error;
|
|
3637
|
+
node2.fileSize = fileSize;
|
|
3638
|
+
node2.lineCount = lineCount;
|
|
3639
|
+
result.nodes.push(node2);
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
result.totalSize += fileSize;
|
|
3643
|
+
result.totalLines += lineCount;
|
|
3644
|
+
const preprocessed = preprocessScriptContent(content);
|
|
3645
|
+
const node = newNode(path3, depth);
|
|
3646
|
+
node.rawContent = content;
|
|
3647
|
+
node.preprocessedContent = preprocessed;
|
|
3648
|
+
node.lineCount = lineCount;
|
|
3649
|
+
node.fileSize = fileSize;
|
|
3650
|
+
result.nodes.push(node);
|
|
3651
|
+
const virtualInput = {
|
|
3652
|
+
...input,
|
|
3653
|
+
aNext: { actionType: "bash", actionDetail: preprocessed }
|
|
3654
|
+
};
|
|
3655
|
+
const l1 = runHeuristicCheck(virtualInput);
|
|
3656
|
+
if (l1 && !l1.allowed) {
|
|
3657
|
+
node.l1Hit = true;
|
|
3658
|
+
node.l1RiskLevel = l1.riskLevel;
|
|
3659
|
+
node.l1RiskType = l1.riskType;
|
|
3660
|
+
node.l1Reason = l1.reason;
|
|
3661
|
+
node.l1MatchedPatterns = [];
|
|
3662
|
+
if (l1.riskLevel === "critical") {
|
|
3663
|
+
result.hasCritical = true;
|
|
3664
|
+
result.firstHitNode = node;
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
const childPaths = extractChildScriptPaths(preprocessed, baseDir);
|
|
3669
|
+
node.childPaths = childPaths;
|
|
3670
|
+
for (const childPath of childPaths) {
|
|
3671
|
+
resolveRecursive({
|
|
3672
|
+
path: resolvePath(childPath),
|
|
3673
|
+
depth: depth + 1,
|
|
3674
|
+
maxDepth,
|
|
3675
|
+
maxFileSize,
|
|
3676
|
+
maxTotalSize,
|
|
3677
|
+
visited,
|
|
3678
|
+
result,
|
|
3679
|
+
baseDir: dirname4(resolvePath(childPath)),
|
|
3680
|
+
input
|
|
3681
|
+
});
|
|
3682
|
+
if (result.hasCritical) return;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
function scanScriptChainWithL1(chain, input) {
|
|
3686
|
+
for (const node of chain.nodes) {
|
|
3687
|
+
if (node.error || !node.preprocessedContent || node.l1Hit) continue;
|
|
3688
|
+
const virtualInput = {
|
|
3689
|
+
...input,
|
|
3690
|
+
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
3691
|
+
};
|
|
3692
|
+
const l1 = runHeuristicCheck(virtualInput);
|
|
3693
|
+
if (l1 && !l1.allowed) {
|
|
3694
|
+
node.l1Hit = true;
|
|
3695
|
+
node.l1RiskLevel = l1.riskLevel;
|
|
3696
|
+
node.l1RiskType = l1.riskType;
|
|
3697
|
+
node.l1Reason = l1.reason;
|
|
3698
|
+
if (l1.riskLevel === "critical") {
|
|
3699
|
+
chain.hasCritical = true;
|
|
3700
|
+
chain.firstHitNode = node;
|
|
3701
|
+
break;
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
return chain;
|
|
3706
|
+
}
|
|
3707
|
+
function scanScriptChainWithL2(chain, input) {
|
|
3708
|
+
for (const node of chain.nodes) {
|
|
3709
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
3710
|
+
if (node.l1RiskLevel === "critical") break;
|
|
3711
|
+
const virtualInput = {
|
|
3712
|
+
...input,
|
|
3713
|
+
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
3714
|
+
};
|
|
3715
|
+
const l2 = runLogicCheck(virtualInput);
|
|
3716
|
+
if (l2 && !l2.allowed) {
|
|
3717
|
+
node.l2Hit = true;
|
|
3718
|
+
node.l2ViolatedRule = `script_logic:${node.scriptPath}`;
|
|
3719
|
+
node.l2RiskLevel = l2.riskLevel;
|
|
3720
|
+
node.l2RiskType = l2.riskType;
|
|
3721
|
+
node.l2Reason = `\u811A\u672C ${node.scriptPath} (\u6DF1\u5EA6 ${node.depth}) \u4E2D: ${l2.reason}`;
|
|
3722
|
+
if (l2.riskLevel === "critical") {
|
|
3723
|
+
chain.hasCritical = true;
|
|
3724
|
+
chain.firstHitNode = node;
|
|
3725
|
+
break;
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
if (!chain.hasHigh) {
|
|
3730
|
+
chain.hasHigh = chain.nodes.some((n) => ["high", "critical"].includes(n.l2RiskLevel));
|
|
3731
|
+
}
|
|
3732
|
+
return chain;
|
|
3733
|
+
}
|
|
3734
|
+
function scanScriptChainKeywords(chain) {
|
|
3735
|
+
for (const node of chain.nodes) {
|
|
3736
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
3737
|
+
if (node.l1Hit || node.l2Hit) continue;
|
|
3738
|
+
const matched = scanScriptForKeywords2(node.preprocessedContent);
|
|
3739
|
+
node.matchedKeywords = matched;
|
|
3740
|
+
node.isSuspicious = matched.length > 0;
|
|
3741
|
+
if (matched.length > 0) {
|
|
3742
|
+
const risk = assessKeywordCombinationRisk2(matched, node.preprocessedContent);
|
|
3743
|
+
node.keywordRiskLevel = risk.level;
|
|
3744
|
+
node.keywordRiskReason = risk.reason;
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
return chain;
|
|
3748
|
+
}
|
|
3749
|
+
function analyzeScriptContent2(input) {
|
|
3750
|
+
const actionType = input.aNext.actionType.toLowerCase();
|
|
3751
|
+
if (actionType !== "bash") return emptyChain();
|
|
3752
|
+
const scriptPath = extractScriptPath2(input.aNext.actionDetail);
|
|
3753
|
+
let resolvedPath = scriptPath;
|
|
3754
|
+
if (!resolvedPath) {
|
|
3755
|
+
const inline = extractInlineScriptPath(input.aNext.actionDetail);
|
|
3756
|
+
if (!inline) return emptyChain();
|
|
3757
|
+
resolvedPath = inline;
|
|
3758
|
+
}
|
|
3759
|
+
const chain = resolveScriptChain(resolvedPath, input);
|
|
3760
|
+
if (chain.nodes.length === 0) return chain;
|
|
3761
|
+
scanScriptChainWithL1(chain, input);
|
|
3762
|
+
scanScriptChainWithL2(chain, input);
|
|
3763
|
+
scanScriptChainKeywords(chain);
|
|
3764
|
+
return chain;
|
|
3765
|
+
}
|
|
3766
|
+
function formatScriptChainForPrompt(chain) {
|
|
3767
|
+
if (chain.nodes.length === 0) return "";
|
|
3768
|
+
const errorNodes = chain.nodes.filter((n) => n.error);
|
|
3769
|
+
if (errorNodes.length > 0 && chain.nodes.length === 1) {
|
|
3770
|
+
return `\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${errorNodes[0].error}`;
|
|
3771
|
+
}
|
|
3772
|
+
const suspiciousNodes = chain.nodes.filter((n) => n.isSuspicious && !n.l1Hit && !n.l2Hit);
|
|
3773
|
+
if (suspiciousNodes.length === 0) {
|
|
3774
|
+
const safeNodes = chain.nodes.filter((n) => !n.error);
|
|
3775
|
+
if (safeNodes.length === 0) return "";
|
|
3776
|
+
const parts2 = ["\u2713 \u811A\u672C\u5185\u5BB9\u5DF2\u5206\u6790\uFF0C\u672A\u53D1\u73B0\u5371\u9669/\u53EF\u7591\u64CD\u4F5C\uFF08L1/L2/\u5173\u952E\u8BCD\u626B\u63CF\u5747\u672A\u547D\u4E2D\uFF09\uFF1A"];
|
|
3777
|
+
for (const node of safeNodes) {
|
|
3778
|
+
const pre = node.preprocessedContent || "";
|
|
3779
|
+
let preview = pre.slice(0, 600);
|
|
3780
|
+
if (pre.length > 600) preview += `
|
|
3781
|
+
... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
|
|
3782
|
+
parts2.push(
|
|
3783
|
+
` \u811A\u672C\u8DEF\u5F84: ${node.scriptPath}\uFF08\u9012\u5F52\u6DF1\u5EA6 ${node.depth}\uFF0C${node.lineCount} \u884C\uFF09
|
|
3784
|
+
\u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09:
|
|
3785
|
+
\`\`\`
|
|
3786
|
+
${preview}
|
|
3787
|
+
\`\`\``
|
|
3788
|
+
);
|
|
3789
|
+
}
|
|
3790
|
+
return parts2.join("\n");
|
|
3791
|
+
}
|
|
3792
|
+
const parts = [];
|
|
3793
|
+
for (const node of suspiciousNodes) {
|
|
3794
|
+
const keywordsStr = node.matchedKeywords.join(", ");
|
|
3795
|
+
let riskHint = "";
|
|
3796
|
+
if (node.keywordRiskLevel && node.keywordRiskReason) {
|
|
3797
|
+
riskHint = `
|
|
3798
|
+
|
|
3799
|
+
\u{1F534} **\u98CE\u9669\u8BC4\u4F30**: ${node.keywordRiskLevel.toUpperCase()} \u7EA7\u522B
|
|
3800
|
+
**\u98CE\u9669\u539F\u56E0**: ${node.keywordRiskReason}
|
|
3801
|
+
**\u5EFA\u8BAE**: \u6B64\u811A\u672C\u5E94\u88AB\u62D2\u7EDD\uFF08Deny\uFF09\uFF0C\u9664\u975E\u6709\u660E\u786E\u7684\u4E1A\u52A1\u9700\u6C42\u4E14\u5DF2\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u5668\u53EF\u4FE1\u3002`;
|
|
3802
|
+
}
|
|
3803
|
+
const pre = node.preprocessedContent;
|
|
3804
|
+
let contentPreview = pre.slice(0, 2e3);
|
|
3805
|
+
if (pre.length > 2e3) contentPreview += `
|
|
3806
|
+
... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
|
|
3807
|
+
parts.push(
|
|
3808
|
+
`\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
3809
|
+
\u811A\u672C\u8DEF\u5F84: ${node.scriptPath}
|
|
3810
|
+
\u9012\u5F52\u6DF1\u5EA6: ${node.depth}
|
|
3811
|
+
\u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
|
|
3812
|
+
\u811A\u672C\u884C\u6570: ${node.lineCount}${riskHint}
|
|
3813
|
+
|
|
3814
|
+
### \u811A\u672C\u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09###
|
|
3815
|
+
\`\`\`
|
|
3816
|
+
${contentPreview}
|
|
3817
|
+
\`\`\`
|
|
3818
|
+
`
|
|
3819
|
+
);
|
|
3820
|
+
}
|
|
3821
|
+
let header = `\u26A0\uFE0F \u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\u94FE\uFF08\u5171 ${chain.nodes.length} \u4E2A\u811A\u672C\uFF09\uFF0C\u5176\u4E2D ${suspiciousNodes.length} \u4E2A\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
|
|
3822
|
+
`;
|
|
3823
|
+
if (chain.maxDepthReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u9012\u5F52\u8FFD\u8E2A\u89E6\u8FBE\u6DF1\u5EA6\u4E0A\u9650\uFF0C\u53EF\u80FD\u5B58\u5728\u66F4\u6DF1\u5C42\u7684\u811A\u672C\u8C03\u7528\u672A\u88AB\u626B\u63CF\u3002\n";
|
|
3824
|
+
if (chain.maxSizeReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u7D2F\u8BA1\u6587\u4EF6\u5927\u5C0F\u89E6\u8FBE\u4E0A\u9650\uFF0C\u90E8\u5206\u811A\u672C\u53EF\u80FD\u672A\u88AB\u5B8C\u6574\u8BFB\u53D6\u3002\n";
|
|
3825
|
+
return header + parts.join("\n---\n\n");
|
|
3826
|
+
}
|
|
3827
|
+
var CHILD_SCRIPT_PATTERNS = [
|
|
3828
|
+
/(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
|
|
3829
|
+
/(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
|
|
3830
|
+
/(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
|
|
3831
|
+
/(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
|
|
3832
|
+
/(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
|
|
3833
|
+
/(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
|
|
3834
|
+
/(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
|
|
3835
|
+
/(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
|
|
3836
|
+
/(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
|
|
3837
|
+
/(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
|
|
3838
|
+
];
|
|
3839
|
+
|
|
3840
|
+
// src/engine/llm-analyzer.ts
|
|
3841
|
+
import { appendFileSync } from "fs";
|
|
3842
|
+
import OpenAI from "openai";
|
|
3843
|
+
var llmCache = /* @__PURE__ */ new Map();
|
|
3844
|
+
var LLM_CACHE_MAX = 200;
|
|
3845
|
+
async function runLLMAnalysis(input, previous) {
|
|
3846
|
+
if (!isLLMAvailable()) {
|
|
3847
|
+
console.log("[AgentMoss] LLM not configured, skipping deep analysis");
|
|
3848
|
+
return {
|
|
3849
|
+
allowed: true,
|
|
3850
|
+
reason: "LLM \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u6DF1\u5C42\u5206\u6790",
|
|
3851
|
+
riskLevel: "low",
|
|
3852
|
+
riskType: "",
|
|
3853
|
+
confidence: 50,
|
|
3854
|
+
source: "llm",
|
|
3855
|
+
violatedLayers: []
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
const globalConfig = getLLMConfig();
|
|
3859
|
+
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
3860
|
+
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
3861
|
+
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
3862
|
+
const cached = llmCache.get(cacheKey);
|
|
3863
|
+
if (cached) {
|
|
3864
|
+
console.log("[AgentMoss] LLM cache hit");
|
|
3865
|
+
return cached;
|
|
3866
|
+
}
|
|
3867
|
+
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
3868
|
+
const templates = loadTemplates();
|
|
3869
|
+
let scriptAnalysis;
|
|
3870
|
+
let scriptChainText = "";
|
|
3871
|
+
if (previous.scriptChain) {
|
|
3872
|
+
scriptChainText = formatScriptChainForPrompt(previous.scriptChain);
|
|
3873
|
+
} else {
|
|
3874
|
+
scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
|
|
3875
|
+
}
|
|
3876
|
+
const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
|
|
3877
|
+
logPrompt(prompt, input.aNext, previous.llmLogPath);
|
|
3878
|
+
const result = await analyzeWithLLM(prompt, config);
|
|
3879
|
+
if (llmCache.size >= LLM_CACHE_MAX) {
|
|
3880
|
+
const firstKey = llmCache.keys().next().value;
|
|
3881
|
+
if (firstKey) llmCache.delete(firstKey);
|
|
3882
|
+
}
|
|
3883
|
+
llmCache.set(cacheKey, result);
|
|
3884
|
+
return result;
|
|
3885
|
+
}
|
|
3886
|
+
function logPrompt(prompt, aNext, logPath) {
|
|
3887
|
+
if (!logPath) return;
|
|
3888
|
+
try {
|
|
3889
|
+
const actionType = aNext.actionType || "unknown";
|
|
3890
|
+
const actionDetail = (aNext.actionDetail || "").slice(0, 100);
|
|
3891
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [LLM_PROMPT] action_type=${actionType}, action_detail=${actionDetail}... prompt_length=${prompt.length}
|
|
3892
|
+
${prompt}
|
|
3893
|
+
--- END PROMPT ---
|
|
3894
|
+
`;
|
|
3895
|
+
appendFileSync(logPath, line, "utf-8");
|
|
3896
|
+
} catch (e) {
|
|
3897
|
+
console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
|
|
3901
|
+
const skillsText = formatSkillsForPrompt(matchedSkills);
|
|
3902
|
+
const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
|
|
3903
|
+
const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
|
|
3904
|
+
const hintsText = formatHints(previous);
|
|
3905
|
+
const securityJudgeTemplate = templates["security_judge"];
|
|
3906
|
+
if (securityJudgeTemplate) {
|
|
3907
|
+
return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
|
|
3908
|
+
}
|
|
3909
|
+
console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
|
|
3910
|
+
return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
|
|
3911
|
+
- \u4F1A\u8BDD ID: ${input.sessionId}
|
|
3912
|
+
- \u7528\u6237\u4EFB\u52A1: ${input.promptSession || "\u672A\u6307\u5B9A"}
|
|
3913
|
+
- \u64CD\u4F5C\u7CFB\u7EDF: ${input.osType}
|
|
3914
|
+
- \u5DE5\u4F5C\u76EE\u5F55: ${input.cwd || "/"}
|
|
3915
|
+
- \u64CD\u4F5C\u539F\u56E0: ${input.reason || "\u672A\u8BF4\u660E"}
|
|
3916
|
+
- \u5386\u53F2\u64CD\u4F5C\u5E8F\u5217:
|
|
3917
|
+
${historyStr}
|
|
3918
|
+
|
|
3919
|
+
- \u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C:
|
|
3920
|
+
\u7C7B\u578B: ${input.aNext.actionType}
|
|
3921
|
+
\u8BE6\u60C5: ${input.aNext.actionDetail}
|
|
3922
|
+
|
|
3923
|
+
## \u53EF\u7528\u5B89\u5168 Skill
|
|
3924
|
+
${skillsText}
|
|
3925
|
+
|
|
3926
|
+
## \u524D\u7F6E\u68C0\u6D4B\u7ED3\u679C
|
|
3927
|
+
${hintsText}
|
|
3928
|
+
${scriptAnalysisText ? `
|
|
3929
|
+
## \u811A\u672C\u5185\u5BB9\u5206\u6790
|
|
3930
|
+
${scriptAnalysisText}` : ""}
|
|
3931
|
+
|
|
3932
|
+
\u8BF7\u6839\u636E\u4EE5\u4E0A\u4FE1\u606F\uFF0C\u5BF9\u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C\u8FDB\u884C\u5B89\u5168\u5224\u65AD\u3002\u524D\u7F6E\u68C0\u6D4B\u4E3A low/medium \u4E14\u6709\u5408\u7406\u6267\u884C\u7406\u7531\u65F6\u5E94\u5F53\u5141\u8BB8\u3002\u4F60\u5FC5\u987B\u8F93\u51FA\u4E25\u683C\u7684 JSON \u683C\u5F0F\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u6CE8\u91CA\u6216 markdown \u6807\u8BB0\u3002
|
|
3933
|
+
|
|
3934
|
+
\u8F93\u51FA\u683C\u5F0F:
|
|
3935
|
+
{"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
|
|
3936
|
+
}
|
|
3937
|
+
function formatHints(previous) {
|
|
3938
|
+
const hints = [];
|
|
3939
|
+
if (previous.heuristic) {
|
|
3940
|
+
hints.push(
|
|
3941
|
+
`\u542F\u53D1\u5F0F\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
|
|
3942
|
+
\u7C7B\u578B: ${previous.heuristic.riskType}
|
|
3943
|
+
\u539F\u56E0: ${previous.heuristic.reason}`
|
|
3944
|
+
);
|
|
3945
|
+
}
|
|
3946
|
+
if (previous.logic) {
|
|
3947
|
+
hints.push(
|
|
3948
|
+
`\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.logic.riskLevel}]:
|
|
3949
|
+
\u7C7B\u578B: ${previous.logic.riskType}
|
|
3950
|
+
\u539F\u56E0: ${previous.logic.reason}`
|
|
3951
|
+
);
|
|
3952
|
+
}
|
|
3953
|
+
return hints.length > 0 ? hints.join("\n\n") : "\u542F\u53D1\u5F0F\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
|
|
3954
|
+
}
|
|
3955
|
+
async function analyzeWithLLM(prompt, config) {
|
|
3956
|
+
console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
|
|
3957
|
+
const provider = detectProvider(config.baseUrl, config.provider);
|
|
3958
|
+
const extraHeaders = getProviderHeaders(provider);
|
|
3959
|
+
if (Object.keys(extraHeaders).length > 0) {
|
|
3960
|
+
console.log(`[AgentMoss] Provider detected: ${provider}, injecting headers: ${Object.keys(extraHeaders).join(", ")}`);
|
|
3961
|
+
}
|
|
3962
|
+
const timeoutCfg = getTimeoutConfig();
|
|
3963
|
+
const retryCfg = getRetryConfig();
|
|
3964
|
+
const client = new OpenAI({
|
|
3965
|
+
baseURL: config.baseUrl,
|
|
3966
|
+
apiKey: config.apiKey,
|
|
3967
|
+
// 单次 LLM 调用超时用 prompt1Timeout(对齐 py);env 覆盖的 config.timeout 仍向后兼容
|
|
3968
|
+
timeout: (config.timeout || timeoutCfg.prompt1Timeout) * 1e3,
|
|
3969
|
+
defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
|
|
3970
|
+
});
|
|
3971
|
+
const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || String(retryCfg.maxRetries), 10);
|
|
3972
|
+
const totalTimeoutMs = timeoutCfg.totalTimeout * 1e3;
|
|
3973
|
+
const abortController = new AbortController();
|
|
3974
|
+
const totalTimeoutTimer = setTimeout(() => {
|
|
3975
|
+
console.error(`[AgentMoss] totalTimeout (${timeoutCfg.totalTimeout}s) exceeded, aborting all LLM calls`);
|
|
3976
|
+
abortController.abort();
|
|
3977
|
+
}, totalTimeoutMs);
|
|
3978
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3979
|
+
if (abortController.signal.aborted) {
|
|
3980
|
+
break;
|
|
3981
|
+
}
|
|
3982
|
+
try {
|
|
3983
|
+
const response = await client.chat.completions.create({
|
|
3984
|
+
model: config.model,
|
|
3985
|
+
temperature: config.temperature || 0.1,
|
|
3986
|
+
max_tokens: config.maxTokens || 1024,
|
|
3987
|
+
messages: [
|
|
3988
|
+
{ role: "system", content: "\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002" },
|
|
3989
|
+
{ role: "user", content: prompt }
|
|
3990
|
+
],
|
|
3991
|
+
signal: abortController.signal
|
|
3992
|
+
});
|
|
3993
|
+
const content = response.choices[0]?.message?.content || "{}";
|
|
3994
|
+
console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
|
|
3995
|
+
let jsonStr = content.trim();
|
|
3996
|
+
if (jsonStr.startsWith("```")) {
|
|
3997
|
+
jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
|
|
3998
|
+
}
|
|
3999
|
+
const parsed = JSON.parse(jsonStr);
|
|
4000
|
+
try {
|
|
4001
|
+
const usage = response.usage;
|
|
4002
|
+
const { recordTokenUsage: recordTokenUsage2 } = await Promise.resolve().then(() => (init_token_stats(), token_stats_exports));
|
|
4003
|
+
recordTokenUsage2({
|
|
4004
|
+
sessionId: "audit",
|
|
4005
|
+
step: "L3_security_judge",
|
|
4006
|
+
model: response.model || config.model,
|
|
4007
|
+
promptTokens: usage?.prompt_tokens || 0,
|
|
4008
|
+
completionTokens: usage?.completion_tokens || 0,
|
|
4009
|
+
totalTokens: usage?.total_tokens || 0
|
|
4010
|
+
});
|
|
4011
|
+
} catch {
|
|
4012
|
+
}
|
|
4013
|
+
clearTimeout(totalTimeoutTimer);
|
|
4014
|
+
return {
|
|
4015
|
+
allowed: parsed.allowed !== false,
|
|
4016
|
+
reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
|
|
4017
|
+
riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
|
|
4018
|
+
riskType: parsed.riskType || "",
|
|
4019
|
+
confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
|
|
4020
|
+
source: "llm",
|
|
4021
|
+
violatedLayers: ["3"]
|
|
4022
|
+
};
|
|
4023
|
+
} catch (err) {
|
|
4024
|
+
if (abortController.signal.aborted) {
|
|
4025
|
+
console.error(`[AgentMoss] LLM analysis aborted by totalTimeout (${timeoutCfg.totalTimeout}s)`);
|
|
4026
|
+
break;
|
|
4027
|
+
}
|
|
4028
|
+
if (attempt < maxRetries) {
|
|
4029
|
+
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
4030
|
+
await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
|
|
4031
|
+
} else {
|
|
4032
|
+
clearTimeout(totalTimeoutTimer);
|
|
4033
|
+
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
4034
|
+
throw new Error(`LLM \u5206\u6790\u5931\u8D25 (${maxRetries + 1} \u6B21\u5C1D\u8BD5): ${err.message}`);
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
clearTimeout(totalTimeoutTimer);
|
|
4039
|
+
throw new Error(`LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09`);
|
|
4040
|
+
}
|
|
4041
|
+
|
|
4042
|
+
// src/engine/coordinator.ts
|
|
4043
|
+
init_config();
|
|
4044
|
+
init_agents();
|
|
4045
|
+
var FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
4046
|
+
"ask_user_question",
|
|
4047
|
+
"glob",
|
|
4048
|
+
"list_dir",
|
|
4049
|
+
"ls",
|
|
4050
|
+
"count_text_length",
|
|
4051
|
+
"filemgr-globfiles"
|
|
4052
|
+
]);
|
|
4053
|
+
var TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
|
|
4054
|
+
var READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
4055
|
+
"read",
|
|
4056
|
+
"file_read",
|
|
4057
|
+
"read_file",
|
|
4058
|
+
"head",
|
|
4059
|
+
"tail",
|
|
4060
|
+
"grep",
|
|
4061
|
+
"filemgr-readfile",
|
|
4062
|
+
"filemgr-grepfiles"
|
|
4063
|
+
]);
|
|
4064
|
+
var FULLY_SAFE_BASH_PATTERNS = [
|
|
4065
|
+
/^ls(?:\s|$)/i,
|
|
4066
|
+
/^dir(?:\s|$)/i,
|
|
4067
|
+
/^pwd(?:\s|$)/i,
|
|
4068
|
+
/^which\s+/i,
|
|
4069
|
+
/^whereis\s+/i,
|
|
4070
|
+
/^realpath\s+/i,
|
|
4071
|
+
/^basename\s+/i,
|
|
4072
|
+
/^dirname\s+/i,
|
|
4073
|
+
/^file\s+/i,
|
|
4074
|
+
/^stat\s+/i,
|
|
4075
|
+
/^du(?:\s|$)/i,
|
|
4076
|
+
/^echo\s+/i,
|
|
4077
|
+
/^printf\s+/i,
|
|
4078
|
+
/^type\s+/i,
|
|
4079
|
+
/^command\s+/i,
|
|
4080
|
+
// 系统信息查询(只读,不涉及文件/网络/权限)
|
|
4081
|
+
/^whoami(?:\s|$)/i,
|
|
4082
|
+
/^id(?:\s|$)/i,
|
|
4083
|
+
/^hostname(?:\s|$)/i,
|
|
4084
|
+
/^uname(?:\s|$)/i,
|
|
4085
|
+
/^date(?:\s|$)/i,
|
|
4086
|
+
/^env(?:\s|$)/i,
|
|
4087
|
+
/^printenv(?:\s|$)/i,
|
|
4088
|
+
/^tty(?:\s|$)/i,
|
|
4089
|
+
/^arch(?:\s|$)/i,
|
|
4090
|
+
/^uptime(?:\s|$)/i,
|
|
4091
|
+
/^groups(?:\s|$)/i,
|
|
4092
|
+
/^logname(?:\s|$)/i
|
|
4093
|
+
];
|
|
4094
|
+
var READONLY_SENSITIVE_BASH_PATTERNS = [
|
|
4095
|
+
/^cat\s+/i,
|
|
4096
|
+
/^head(?:\s|$)/i,
|
|
4097
|
+
/^tail(?:\s|$)/i,
|
|
4098
|
+
/^less(?:\s|$)/i,
|
|
4099
|
+
/^more(?:\s|$)/i,
|
|
4100
|
+
/^wc(?:\s|$)/i,
|
|
4101
|
+
/^grep(?:\s|$)/i,
|
|
4102
|
+
/^find\s+/i,
|
|
4103
|
+
/^ag(?:\s|$)/i,
|
|
4104
|
+
/^rg(?:\s|$)/i,
|
|
4105
|
+
/^awk\s+/i,
|
|
4106
|
+
/^sed\s+/i,
|
|
4107
|
+
/^cut\s+/i,
|
|
4108
|
+
/^sort(?:\s|$)/i,
|
|
4109
|
+
/^uniq(?:\s|$)/i
|
|
4110
|
+
];
|
|
4111
|
+
function extractFirstCommand(command) {
|
|
4112
|
+
let main = command.split("|")[0].trim();
|
|
4113
|
+
for (const sep of ["&&", "||"]) {
|
|
4114
|
+
const idx = main.indexOf(sep);
|
|
4115
|
+
if (idx !== -1) {
|
|
4116
|
+
main = main.substring(0, idx).trim();
|
|
4117
|
+
break;
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
return main;
|
|
4121
|
+
}
|
|
4122
|
+
function extractRawCommand(detail) {
|
|
4123
|
+
const m = detail.match(/command=([^,]+)/);
|
|
4124
|
+
return m ? m[1].trim() : detail;
|
|
4125
|
+
}
|
|
4126
|
+
function matchesAny(cmd, patterns) {
|
|
4127
|
+
return patterns.some((p) => p.test(cmd));
|
|
4128
|
+
}
|
|
4129
|
+
async function runAnalysis(input) {
|
|
4130
|
+
const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
|
|
4131
|
+
const isInlineScriptCmd = isInlineScriptCommand(input.aNext.actionDetail.toLowerCase());
|
|
4132
|
+
const isLiteralTextCmd = isLiteralTextCommand(input.aNext.actionDetail.toLowerCase());
|
|
4133
|
+
if (heuristicResult && !heuristicResult.allowed) {
|
|
4134
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && heuristicResult.riskType === "file_access";
|
|
4135
|
+
if (!skipInlineFileAccess) {
|
|
4136
|
+
return heuristicResult;
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
let scriptChain = null;
|
|
4140
|
+
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
4141
|
+
scriptChain = analyzeScriptContent2(input);
|
|
4142
|
+
if (scriptChain.nodes.length > 0) {
|
|
4143
|
+
for (const node of scriptChain.nodes) {
|
|
4144
|
+
if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
|
|
4145
|
+
return {
|
|
4146
|
+
allowed: false,
|
|
4147
|
+
reason: `\u811A\u672C\u5185\u5BB9\u5305\u542B\u5371\u9669\u64CD\u4F5C: ${node.l1Reason} (\u6587\u4EF6: ${node.scriptPath}, \u6DF1\u5EA6: ${node.depth})`,
|
|
4148
|
+
riskLevel: node.l1RiskLevel,
|
|
4149
|
+
riskType: node.l1RiskType,
|
|
4150
|
+
confidence: 90,
|
|
4151
|
+
source: "heuristic_script_scan",
|
|
4152
|
+
violatedLayers: ["1.1-script"]
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
const actionType = input.aNext.actionType.toLowerCase();
|
|
4159
|
+
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
4160
|
+
const lowerDetail = rawDetail.toLowerCase();
|
|
4161
|
+
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
4162
|
+
return {
|
|
4163
|
+
allowed: true,
|
|
4164
|
+
reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
|
|
4165
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
4166
|
+
riskType: "",
|
|
4167
|
+
confidence: 95,
|
|
4168
|
+
source: "whitelist_bypass",
|
|
4169
|
+
violatedLayers: []
|
|
4170
|
+
};
|
|
4171
|
+
}
|
|
4172
|
+
const agentExt = getAgentExtensions(input.agentId);
|
|
4173
|
+
const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
|
|
4174
|
+
if (agentExt?.builtin_safe_skills) {
|
|
4175
|
+
for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
|
|
4176
|
+
}
|
|
4177
|
+
if (actionType === "skill" && trustedSkills.has(lowerDetail)) {
|
|
4178
|
+
return {
|
|
4179
|
+
allowed: true,
|
|
4180
|
+
reason: `\u5185\u7F6E\u5B89\u5168 Skill\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${lowerDetail}`,
|
|
4181
|
+
riskLevel: heuristicResult?.riskLevel || "low",
|
|
4182
|
+
riskType: "",
|
|
4183
|
+
confidence: 95,
|
|
4184
|
+
source: "whitelist_bypass",
|
|
4185
|
+
violatedLayers: []
|
|
4186
|
+
};
|
|
4187
|
+
}
|
|
4188
|
+
let skipLLM = READONLY_SENSITIVE_TOOLS.has(actionType);
|
|
4189
|
+
if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
|
|
4190
|
+
const firstCmd = extractFirstCommand(lowerDetail);
|
|
4191
|
+
if (matchesAny(firstCmd, FULLY_SAFE_BASH_PATTERNS)) {
|
|
4192
|
+
const fullCmdHit = DANGEROUS_PATTERNS.some(
|
|
4193
|
+
(p) => p.severity === "critical" || p.severity === "high"
|
|
4194
|
+
) && DANGEROUS_PATTERNS.some(
|
|
4195
|
+
(p) => (p.severity === "critical" || p.severity === "high") && p.pattern.test(lowerDetail)
|
|
4196
|
+
);
|
|
4197
|
+
if (!fullCmdHit) {
|
|
4198
|
+
return {
|
|
4199
|
+
allowed: true,
|
|
4200
|
+
reason: `\u5B89\u5168\u7684\u53EA\u8BFB bash \u547D\u4EE4: ${firstCmd}`,
|
|
4201
|
+
riskLevel: "low",
|
|
4202
|
+
riskType: "",
|
|
4203
|
+
confidence: 95,
|
|
4204
|
+
source: "whitelist_bypass",
|
|
4205
|
+
violatedLayers: []
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
if (matchesAny(firstCmd, READONLY_SENSITIVE_BASH_PATTERNS)) {
|
|
4210
|
+
skipLLM = true;
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
|
|
4214
|
+
if (logicResult && !logicResult.allowed) {
|
|
4215
|
+
const isCredentialPath = !!logicResult.isCredential;
|
|
4216
|
+
const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && logicResult.riskType === "sensitive_path_access";
|
|
4217
|
+
if (skipInlineFileAccess) {
|
|
4218
|
+
const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
|
|
4219
|
+
if (inlineJudgment.actionable) {
|
|
4220
|
+
if (!inlineJudgment.allowed) {
|
|
4221
|
+
return {
|
|
4222
|
+
allowed: false,
|
|
4223
|
+
reason: inlineJudgment.reason,
|
|
4224
|
+
riskLevel: inlineJudgment.riskLevel,
|
|
4225
|
+
riskType: inlineJudgment.riskType,
|
|
4226
|
+
confidence: 90,
|
|
4227
|
+
source: "logic_rule",
|
|
4228
|
+
violatedLayers: ["1.2"]
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
return {
|
|
4232
|
+
allowed: true,
|
|
4233
|
+
reason: inlineJudgment.reason,
|
|
4234
|
+
riskLevel: inlineJudgment.riskLevel,
|
|
4235
|
+
riskType: inlineJudgment.riskType,
|
|
4236
|
+
confidence: 90,
|
|
4237
|
+
source: "logic_rule",
|
|
4238
|
+
violatedLayers: []
|
|
4239
|
+
};
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
if ((logicResult.riskLevel === "high" || logicResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
|
|
4243
|
+
return logicResult;
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
|
|
4247
|
+
for (const node of scriptChain.nodes) {
|
|
4248
|
+
if (node.error || !node.preprocessedContent) continue;
|
|
4249
|
+
if (node.l1RiskLevel === "critical") break;
|
|
4250
|
+
if (node.l2Hit && (node.l2RiskLevel === "high" || node.l2RiskLevel === "critical")) {
|
|
4251
|
+
return {
|
|
4252
|
+
allowed: false,
|
|
4253
|
+
reason: node.l2Reason,
|
|
4254
|
+
riskLevel: node.l2RiskLevel,
|
|
4255
|
+
riskType: node.l2RiskType,
|
|
4256
|
+
confidence: 85,
|
|
4257
|
+
source: "logic_rule_script_scan",
|
|
4258
|
+
violatedLayers: ["1.2-script"]
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
if (skipLLM || !isLLMAvailable()) {
|
|
4264
|
+
return {
|
|
4265
|
+
allowed: true,
|
|
4266
|
+
reason: "\u53EA\u8BFB\u64CD\u4F5C\u4E14\u65E0\u903B\u8F91\u89C4\u5219\u8FDD\u89C4\uFF0C\u8DF3\u8FC7 LLM \u5206\u6790",
|
|
4267
|
+
riskLevel: logicResult?.riskLevel || "low",
|
|
4268
|
+
riskType: "",
|
|
4269
|
+
confidence: 85,
|
|
4270
|
+
source: "heuristic_and_logic_only",
|
|
4271
|
+
violatedLayers: []
|
|
4272
|
+
};
|
|
4273
|
+
}
|
|
4274
|
+
let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
|
|
4275
|
+
try {
|
|
4276
|
+
const { getDisabledL2RulesWithSkipL3: getDisabledL2RulesWithSkipL32 } = await Promise.resolve().then(() => (init_runtime_config(), runtime_config_exports));
|
|
4277
|
+
const disabledL2 = getDisabledL2RulesWithSkipL32();
|
|
4278
|
+
if (disabledL2 && Object.keys(disabledL2).length > 0) {
|
|
4279
|
+
const actionLower = input.aNext.actionDetail.toLowerCase();
|
|
4280
|
+
const matched = [];
|
|
4281
|
+
for (const items of Object.values(disabledL2)) {
|
|
4282
|
+
for (const item of items) {
|
|
4283
|
+
if (item.toLowerCase().includes(actionLower) || actionLower.includes(item.toLowerCase())) {
|
|
4284
|
+
matched.push(item);
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
if (matched.length > 0) {
|
|
4289
|
+
skipL3Hints = `\u26A0\uFE0F \u91CD\u8981\u7EA6\u675F\uFF1A\u7528\u6237\u5DF2\u4E3B\u52A8\u7981\u7528\u9488\u5BF9\u4EE5\u4E0B\u5185\u5BB9\u7684\u5B89\u5168\u62E6\u622A\u89C4\u5219\uFF0C\u8FD9\u662F\u7528\u6237\u7684\u660E\u786E\u51B3\u7B56\uFF1A
|
|
4290
|
+
${matched.join(", ")}
|
|
4291
|
+
\u5BF9\u8FD9\u4E9B\u8DEF\u5F84/\u5173\u952E\u8BCD\u7684\u76F8\u5173\u64CD\u4F5C\uFF0C\u5FC5\u987B\u89C6\u4E3A\u5141\u8BB8\u7684\uFF0C\u4E0D\u5141\u8BB8\u4EC5\u56E0\u6D89\u53CA\u8FD9\u4E9B\u5185\u5BB9\u5C31\u62D2\u7EDD\u3002\u4F46\u4F60\u4ECD\u9700\u68C0\u67E5\u547D\u4EE4\u7684\u5176\u4ED6\u90E8\u5206\u662F\u5426\u6709\u72EC\u7ACB\u4E8E\u8FD9\u4E9B\u5185\u5BB9\u7684\u5B89\u5168\u98CE\u9669\uFF08\u5982\u7F51\u7EDC\u5916\u4F20\u3001\u63D0\u6743\u7B49\uFF09\u3002`;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
} catch {
|
|
4295
|
+
}
|
|
4296
|
+
let llmResult;
|
|
4297
|
+
try {
|
|
4298
|
+
llmResult = await runLLMAnalysis(input, {
|
|
4299
|
+
heuristic: heuristicResult,
|
|
4300
|
+
logic: logicResult,
|
|
4301
|
+
skipL3Hints,
|
|
4302
|
+
scriptChain: scriptChain ?? void 0,
|
|
4303
|
+
llmLogPath: input.llmLogPath
|
|
4304
|
+
});
|
|
4305
|
+
} catch (e) {
|
|
4306
|
+
const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4307
|
+
if (getLLMFailMode2() === "fail_open") {
|
|
4308
|
+
console.warn(`[AgentMoss] LLM \u5206\u6790\u5931\u8D25\uFF0Cfail_open \u6A21\u5F0F\uFF1A\u5141\u8BB8\u6267\u884C\uFF08\u524D\u4E24\u5C42\u672A\u62E6\u622A\uFF09: ${e.message}`);
|
|
4309
|
+
return {
|
|
4310
|
+
allowed: true,
|
|
4311
|
+
reason: `LLM \u5206\u6790\u5931\u8D25\uFF08warn-allow\uFF0Cfail_open\uFF09: ${e.message}`,
|
|
4312
|
+
riskLevel: "low",
|
|
4313
|
+
riskType: "",
|
|
4314
|
+
confidence: 50,
|
|
4315
|
+
source: "llm_failure_fallback",
|
|
4316
|
+
violatedLayers: []
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
return {
|
|
4320
|
+
allowed: false,
|
|
4321
|
+
reason: `LLM \u5206\u6790\u5931\u8D25\uFF0C\u56E0\u65E0\u6CD5\u5B8C\u6210\u6DF1\u5EA6\u8BED\u4E49\u5206\u6790\uFF0C\u6309 fail-closed \u539F\u5219\u62D2\u7EDD: ${e.message}`,
|
|
4322
|
+
riskLevel: "medium",
|
|
4323
|
+
riskType: "",
|
|
4324
|
+
confidence: 60,
|
|
4325
|
+
source: "llm_failure_fallback",
|
|
4326
|
+
violatedLayers: ["3"]
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
return llmResult;
|
|
4330
|
+
}
|
|
4331
|
+
|
|
4332
|
+
// src/actrail.ts
|
|
4333
|
+
init_esm_shims();
|
|
4334
|
+
import * as fs3 from "fs";
|
|
4335
|
+
function escape(value) {
|
|
4336
|
+
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/=/g, "\\e");
|
|
4337
|
+
}
|
|
4338
|
+
function sessionToTraceId(sessionId) {
|
|
4339
|
+
let hash = 0;
|
|
4340
|
+
for (let i = 0; i < sessionId.length; i++) {
|
|
4341
|
+
const char = sessionId.charCodeAt(i);
|
|
4342
|
+
hash = (hash << 5) - hash + char | 0;
|
|
4343
|
+
}
|
|
4344
|
+
return Math.abs((hash * 31 + sessionId.length) % Number.MAX_SAFE_INTEGER);
|
|
4345
|
+
}
|
|
4346
|
+
function isEnabled() {
|
|
4347
|
+
return process.env.ACTRAIL_ENABLED === "1" || process.env.ACTRAIL_ENABLED === "true";
|
|
4348
|
+
}
|
|
4349
|
+
function getStoragePath() {
|
|
4350
|
+
return process.env.ACTRAIL_STORAGE_PATH || "/tmp/actrail.sqlite";
|
|
4351
|
+
}
|
|
4352
|
+
function reportEnforcementEvent(opts) {
|
|
4353
|
+
if (!isEnabled()) return false;
|
|
4354
|
+
const storagePath = getStoragePath();
|
|
4355
|
+
if (!fs3.existsSync(storagePath)) return false;
|
|
4356
|
+
try {
|
|
4357
|
+
let Database;
|
|
4358
|
+
try {
|
|
4359
|
+
Database = require_lib();
|
|
4360
|
+
} catch {
|
|
4361
|
+
console.debug("[AgentMoss] better-sqlite3 not installed, AcTrail reporting disabled");
|
|
4362
|
+
return false;
|
|
4363
|
+
}
|
|
4364
|
+
const db = new Database(storagePath, { timeout: 100, readonly: false });
|
|
4365
|
+
const tableCheck = db.prepare(
|
|
4366
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='events'"
|
|
4367
|
+
).get();
|
|
4368
|
+
if (!tableCheck) {
|
|
4369
|
+
db.close();
|
|
4370
|
+
return false;
|
|
4371
|
+
}
|
|
4372
|
+
const nowNs = BigInt(Date.now()) * 1000000n;
|
|
4373
|
+
const eventId = Number(nowNs % BigInt(2 ** 62));
|
|
4374
|
+
const traceIdInt = sessionToTraceId(opts.traceId);
|
|
4375
|
+
const rawFields = {
|
|
4376
|
+
"backend": "agent-moss",
|
|
4377
|
+
"operation": "tool_call",
|
|
4378
|
+
"decision": opts.decision,
|
|
4379
|
+
"result": opts.decision === "allow" ? "allowed" : "denied",
|
|
4380
|
+
"tool.name": opts.toolName,
|
|
4381
|
+
"tool.command": (opts.toolCommand || "").slice(0, 500),
|
|
4382
|
+
"agent.session_id": opts.sessionId,
|
|
4383
|
+
"agent.prompt": (opts.prompt || "").slice(0, 500),
|
|
4384
|
+
"risk.level": opts.riskLevel || "",
|
|
4385
|
+
"risk.type": opts.riskType || "",
|
|
4386
|
+
"reason": (opts.reason || "").slice(0, 500),
|
|
4387
|
+
"violated_layers": (opts.violatedLayers || []).join(","),
|
|
4388
|
+
"confidence": String(opts.confidence ?? 100),
|
|
4389
|
+
"duration_ms": (opts.durationMs ?? 0).toFixed(1)
|
|
4390
|
+
};
|
|
4391
|
+
const payloadFields = Object.entries(rawFields).map(([k, v]) => `${escape(k)}=${escape(v)}`).join("\n");
|
|
4392
|
+
db.prepare(`
|
|
4393
|
+
INSERT OR REPLACE INTO events (
|
|
4394
|
+
event_id, trace_id, observed_at,
|
|
4395
|
+
process_pid, process_task_id, process_start_ticks,
|
|
4396
|
+
process_pid_namespace, process_generation,
|
|
4397
|
+
collector, kind,
|
|
4398
|
+
bootstrap_observed, metadata_partial, policy_modified,
|
|
4399
|
+
payload_variant, payload_fields, payload_bytes,
|
|
4400
|
+
policy_verdict, policy_note,
|
|
4401
|
+
policy_redactions, policy_truncations
|
|
4402
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4403
|
+
`).run(
|
|
4404
|
+
eventId,
|
|
4405
|
+
traceIdInt,
|
|
4406
|
+
Number(nowNs),
|
|
4407
|
+
process.pid,
|
|
4408
|
+
0,
|
|
4409
|
+
0,
|
|
4410
|
+
null,
|
|
4411
|
+
0,
|
|
4412
|
+
"agent-moss",
|
|
4413
|
+
"enforcement",
|
|
4414
|
+
0,
|
|
4415
|
+
0,
|
|
4416
|
+
0,
|
|
4417
|
+
"enforcement",
|
|
4418
|
+
payloadFields,
|
|
4419
|
+
"",
|
|
4420
|
+
opts.decision === "allow" ? "allow" : "drop",
|
|
4421
|
+
opts.reason || "",
|
|
4422
|
+
"[]",
|
|
4423
|
+
"[]"
|
|
4424
|
+
);
|
|
4425
|
+
db.close();
|
|
4426
|
+
return true;
|
|
4427
|
+
} catch (err) {
|
|
4428
|
+
console.debug(`[AgentMoss] AcTrail write failed (non-fatal): ${err.message}`);
|
|
4429
|
+
return false;
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
// src/routes.ts
|
|
4434
|
+
async function healthRoute(c) {
|
|
4435
|
+
return c.json(HealthResponseSchema.parse({}));
|
|
4436
|
+
}
|
|
4437
|
+
async function analyzeRoute(c) {
|
|
4438
|
+
const t0 = performance.now();
|
|
4439
|
+
try {
|
|
4440
|
+
const body = await c.req.json();
|
|
4441
|
+
const req = AnalyzeRequestSchema.parse(body);
|
|
4442
|
+
let osType = req.os_type;
|
|
4443
|
+
if (!osType) {
|
|
4444
|
+
osType = process.platform === "win32" ? "windows" : "linux";
|
|
4445
|
+
}
|
|
4446
|
+
const result = await runAnalysis({
|
|
4447
|
+
sessionId: req.session_id,
|
|
4448
|
+
promptSession: req.prompt_session,
|
|
4449
|
+
actionHistory: req.action_history.map((h) => ({
|
|
4450
|
+
actionType: h.action_type,
|
|
4451
|
+
actionDetail: h.action_detail
|
|
4452
|
+
})),
|
|
4453
|
+
aNext: {
|
|
4454
|
+
actionType: req.a_next.action_type,
|
|
4455
|
+
actionDetail: req.a_next.action_detail
|
|
4456
|
+
},
|
|
4457
|
+
reason: req.reason,
|
|
4458
|
+
osType,
|
|
4459
|
+
cwd: req.cwd,
|
|
4460
|
+
agentId: req.agent_id,
|
|
4461
|
+
llmConfig: req.metadata?.llm_config,
|
|
4462
|
+
llmLogPath: req.metadata?.llm_log_path
|
|
4463
|
+
});
|
|
4464
|
+
const durationMs = performance.now() - t0;
|
|
4465
|
+
try {
|
|
4466
|
+
reportEnforcementEvent({
|
|
4467
|
+
traceId: req.session_id,
|
|
4468
|
+
decision: result.allowed ? "allow" : "deny",
|
|
4469
|
+
toolName: req.a_next.action_type,
|
|
4470
|
+
toolCommand: req.a_next.action_detail,
|
|
4471
|
+
sessionId: req.session_id,
|
|
4472
|
+
prompt: req.prompt_session,
|
|
4473
|
+
riskLevel: result.riskLevel,
|
|
4474
|
+
riskType: result.riskType,
|
|
4475
|
+
reason: result.reason,
|
|
4476
|
+
violatedLayers: result.violatedLayers,
|
|
4477
|
+
confidence: result.confidence,
|
|
4478
|
+
durationMs: Math.round(durationMs * 10) / 10
|
|
4479
|
+
});
|
|
4480
|
+
} catch {
|
|
4481
|
+
}
|
|
4482
|
+
return c.json(
|
|
4483
|
+
AnalyzeResponseSchema.parse({
|
|
4484
|
+
decision: result.allowed ? "Allow" : "Deny",
|
|
4485
|
+
reason: result.reason,
|
|
4486
|
+
risk_level: result.riskLevel,
|
|
4487
|
+
risk_type: result.riskType,
|
|
4488
|
+
violated_layers: result.violatedLayers,
|
|
4489
|
+
policy: "",
|
|
4490
|
+
violated_policy: "",
|
|
4491
|
+
confidence: result.confidence,
|
|
4492
|
+
analysis_duration_ms: Math.round(durationMs * 10) / 10
|
|
4493
|
+
})
|
|
4494
|
+
);
|
|
4495
|
+
} catch (err) {
|
|
4496
|
+
console.error("[AgentMoss] Analysis error:", err);
|
|
4497
|
+
if (err instanceof SyntaxError) {
|
|
4498
|
+
return c.json({ error: "Invalid JSON", detail: err.message }, 400);
|
|
4499
|
+
}
|
|
4500
|
+
if (err?.issues) {
|
|
4501
|
+
return c.json({ error: "Validation error", detail: err.issues }, 400);
|
|
4502
|
+
}
|
|
4503
|
+
return c.json({ error: "Internal error", detail: String(err) }, 500);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
|
|
4507
|
+
// src/console/index.ts
|
|
4508
|
+
init_esm_shims();
|
|
4509
|
+
|
|
4510
|
+
// src/console/router.ts
|
|
4511
|
+
init_esm_shims();
|
|
4512
|
+
init_runtime_config();
|
|
4513
|
+
init_token_stats();
|
|
4514
|
+
import { Hono } from "hono";
|
|
4515
|
+
import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
|
|
4516
|
+
import { join as join5, dirname as dirname5 } from "path";
|
|
4517
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
4518
|
+
var __dirname5 = dirname5(fileURLToPath5(import.meta.url));
|
|
4519
|
+
var STATIC_DIR = join5(__dirname5, "static");
|
|
4520
|
+
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
4521
|
+
function getConsoleToken() {
|
|
4522
|
+
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
4523
|
+
}
|
|
4524
|
+
function verifyAuth(req) {
|
|
4525
|
+
const token = getConsoleToken();
|
|
4526
|
+
if (!token) return true;
|
|
4527
|
+
const auth = req.headers.get("Authorization") || "";
|
|
4528
|
+
if (auth.startsWith("Bearer ")) return auth.slice(7) === token;
|
|
4529
|
+
return false;
|
|
4530
|
+
}
|
|
4531
|
+
function authMiddleware() {
|
|
4532
|
+
return async (c, next) => {
|
|
4533
|
+
if (!verifyAuth(c.req.raw)) {
|
|
4534
|
+
return c.json({ error: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
4535
|
+
}
|
|
4536
|
+
await next();
|
|
4537
|
+
};
|
|
4538
|
+
}
|
|
4539
|
+
function createConsoleRouter() {
|
|
4540
|
+
const console2 = new Hono();
|
|
4541
|
+
console2.use("/api/*", authMiddleware());
|
|
4542
|
+
const serveIndex = (c) => {
|
|
4543
|
+
const indexFile = join5(STATIC_DIR, "index.html");
|
|
4544
|
+
if (existsSync9(indexFile)) {
|
|
4545
|
+
return c.html(readFileSync8(indexFile, "utf-8"));
|
|
4546
|
+
}
|
|
4547
|
+
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
4548
|
+
};
|
|
4549
|
+
console2.get("/", serveIndex);
|
|
4550
|
+
console2.get("", serveIndex);
|
|
4551
|
+
console2.get("/api/layers", (c) => {
|
|
4552
|
+
const rc = loadRuntimeConfig();
|
|
4553
|
+
return c.json({ layers: rc.layers });
|
|
4554
|
+
});
|
|
4555
|
+
console2.put("/api/layers", async (c) => {
|
|
4556
|
+
const body = await c.req.json();
|
|
4557
|
+
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
4558
|
+
return c.json({ layers: rc.layers });
|
|
4559
|
+
});
|
|
4560
|
+
console2.get("/api/rules", (c) => {
|
|
4561
|
+
const rc = loadRuntimeConfig();
|
|
4562
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4563
|
+
});
|
|
4564
|
+
console2.put("/api/rules/enabled", async (c) => {
|
|
4565
|
+
const b = await c.req.json();
|
|
4566
|
+
const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
|
|
4567
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4568
|
+
});
|
|
4569
|
+
console2.put("/api/rules/deny_mode", async (c) => {
|
|
4570
|
+
const b = await c.req.json();
|
|
4571
|
+
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
4572
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4573
|
+
});
|
|
4574
|
+
console2.put("/api/rules/skip_l3", async (c) => {
|
|
4575
|
+
const b = await c.req.json();
|
|
4576
|
+
const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
|
|
4577
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4578
|
+
});
|
|
4579
|
+
console2.put("/api/categories/enabled", async (c) => {
|
|
4580
|
+
const b = await c.req.json();
|
|
4581
|
+
const rc = updateCategoryEnabled(b.layer, b.category, b.enabled);
|
|
4582
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4583
|
+
});
|
|
4584
|
+
console2.post("/api/rules", async (c) => {
|
|
4585
|
+
const b = await c.req.json();
|
|
4586
|
+
const rc = addCustomRule(b.layer, b.category, {
|
|
4587
|
+
pattern: b.pattern,
|
|
4588
|
+
risk_level: b.risk_level,
|
|
4589
|
+
risk_type: b.risk_type,
|
|
4590
|
+
reason: b.reason,
|
|
4591
|
+
keyword: b.keyword,
|
|
4592
|
+
lang: b.lang,
|
|
4593
|
+
path: b.path,
|
|
4594
|
+
desc: b.desc,
|
|
4595
|
+
credential: b.credential,
|
|
4596
|
+
deny_mode: b.deny_mode
|
|
4597
|
+
});
|
|
4598
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4599
|
+
});
|
|
4600
|
+
console2.delete("/api/rules", async (c) => {
|
|
4601
|
+
const b = await c.req.json();
|
|
4602
|
+
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
4603
|
+
const rc = loadRuntimeConfig();
|
|
4604
|
+
return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
|
|
4605
|
+
});
|
|
4606
|
+
console2.get("/api/skills", (c) => {
|
|
4607
|
+
const rc = loadRuntimeConfig();
|
|
4608
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
4609
|
+
});
|
|
4610
|
+
console2.put("/api/skills/enabled", async (c) => {
|
|
4611
|
+
const b = await c.req.json();
|
|
4612
|
+
const rc = updateSkillEnabled(b.skill_id, b.enabled);
|
|
4613
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
4614
|
+
});
|
|
4615
|
+
console2.put("/api/skill-categories/enabled", async (c) => {
|
|
4616
|
+
const b = await c.req.json();
|
|
4617
|
+
const rc = updateSkillCategoryEnabled(b.category, b.enabled);
|
|
4618
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
4619
|
+
});
|
|
4620
|
+
console2.post("/api/skills", async (c) => {
|
|
4621
|
+
const b = await c.req.json();
|
|
4622
|
+
const rc = addCustomSkill(b.skill_id, b.category || "user_custom", b.keywords || [], b.content);
|
|
4623
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
4624
|
+
});
|
|
4625
|
+
console2.delete("/api/skills", async (c) => {
|
|
4626
|
+
const b = await c.req.json();
|
|
4627
|
+
deleteCustomSkill(b.skill_id, b.category);
|
|
4628
|
+
const rc = loadRuntimeConfig();
|
|
4629
|
+
return c.json({ L3_skills: rc.L3_skills });
|
|
4630
|
+
});
|
|
4631
|
+
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
4632
|
+
const skillId = c.req.param("skill_id");
|
|
4633
|
+
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
4634
|
+
return c.json({ error: "invalid skill_id" }, 400);
|
|
4635
|
+
}
|
|
4636
|
+
const skillPath = join5(dirname5(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
4637
|
+
if (!existsSync9(skillPath)) {
|
|
4638
|
+
const builtinPath = join5(__dirname5, "..", "..", "agent_moss", "skills", `${skillId}.md`);
|
|
4639
|
+
if (existsSync9(builtinPath)) return c.text(readFileSync8(builtinPath, "utf-8"));
|
|
4640
|
+
return c.json({ error: "skill not found" }, 404);
|
|
4641
|
+
}
|
|
4642
|
+
return c.text(readFileSync8(skillPath, "utf-8"));
|
|
4643
|
+
});
|
|
4644
|
+
console2.get("/api/config", (c) => {
|
|
4645
|
+
const rc = loadRuntimeConfig();
|
|
4646
|
+
return c.json({ config: rc, config_path: getRuntimeConfigPath() });
|
|
4647
|
+
});
|
|
4648
|
+
console2.get("/api/env-overrides", (c) => {
|
|
4649
|
+
return c.json({ overrides: getEnvOverrides() });
|
|
4650
|
+
});
|
|
4651
|
+
console2.post("/api/reset", (c) => {
|
|
4652
|
+
const defaults = generateSourceDefaults();
|
|
4653
|
+
saveRuntimeConfig(defaults);
|
|
4654
|
+
return c.json({ config: defaults });
|
|
4655
|
+
});
|
|
4656
|
+
console2.get("/api/token-stats", (c) => {
|
|
4657
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
4658
|
+
return c.json(getTokenStats(days));
|
|
4659
|
+
});
|
|
4660
|
+
console2.get("/api/token-stats/recent", (c) => {
|
|
4661
|
+
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
4662
|
+
return c.json({ records: getRecentRecords(limit) });
|
|
4663
|
+
});
|
|
4664
|
+
console2.get("/api/token-stats/trend", (c) => {
|
|
4665
|
+
const days = parseInt(c.req.query("days") || "7", 10);
|
|
4666
|
+
return c.json(getTokenTrend(days));
|
|
4667
|
+
});
|
|
4668
|
+
console2.post("/api/token-stats/reset", (c) => {
|
|
4669
|
+
resetTokenStats();
|
|
4670
|
+
return c.json({ reset: true });
|
|
4671
|
+
});
|
|
4672
|
+
return console2;
|
|
4673
|
+
}
|
|
4674
|
+
|
|
4675
|
+
// src/server.ts
|
|
4676
|
+
function createApp() {
|
|
4677
|
+
const app = new Hono2();
|
|
4678
|
+
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
|
|
4679
|
+
app.use("*", logger());
|
|
4680
|
+
app.get("/api/v1/health", healthRoute);
|
|
4681
|
+
app.post("/api/v1/analyze", analyzeRoute);
|
|
4682
|
+
app.route("/console", createConsoleRouter());
|
|
4683
|
+
app.get("/console/", (c) => c.redirect("/console"));
|
|
4684
|
+
return app;
|
|
4685
|
+
}
|
|
4686
|
+
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
4687
|
+
for (let port = startPort; port < startPort + scan; port++) {
|
|
4688
|
+
const free = await new Promise((resolve6) => {
|
|
4689
|
+
const server = net.createServer();
|
|
4690
|
+
server.listen(port, host, () => {
|
|
4691
|
+
server.close(() => resolve6(true));
|
|
4692
|
+
});
|
|
4693
|
+
server.on("error", () => resolve6(false));
|
|
4694
|
+
});
|
|
4695
|
+
if (free) {
|
|
4696
|
+
if (port !== startPort) {
|
|
4697
|
+
console.warn(`[AgentMoss] Port ${startPort} in use, using ${port} instead`);
|
|
4698
|
+
}
|
|
4699
|
+
return port;
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
return startPort;
|
|
4703
|
+
}
|
|
4704
|
+
async function startServer(host = "127.0.0.1", port = 9090) {
|
|
4705
|
+
const actualPort = await findFreePort(port, host);
|
|
4706
|
+
const app = createApp();
|
|
4707
|
+
return {
|
|
4708
|
+
port: actualPort,
|
|
4709
|
+
serve: async () => {
|
|
4710
|
+
console.log(`AgentMoss HTTP server on ${host}:${actualPort}`);
|
|
4711
|
+
const { serve: honoServe } = await import("@hono/node-server");
|
|
4712
|
+
honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
|
|
4713
|
+
}
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4716
|
+
export {
|
|
4717
|
+
createApp,
|
|
4718
|
+
findFreePort,
|
|
4719
|
+
startServer
|
|
4720
|
+
};
|
|
4721
|
+
//# sourceMappingURL=server.js.map
|