@f2a/openclaw-adapter 0.1.0 → 0.1.4

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.
Files changed (61) hide show
  1. package/dist/announcement-queue.d.ts +72 -1
  2. package/dist/announcement-queue.d.ts.map +1 -1
  3. package/dist/announcement-queue.js +145 -20
  4. package/dist/announcement-queue.js.map +1 -1
  5. package/dist/claim-handlers.d.ts +75 -0
  6. package/dist/claim-handlers.d.ts.map +1 -0
  7. package/dist/claim-handlers.js +368 -0
  8. package/dist/claim-handlers.js.map +1 -0
  9. package/dist/connector.d.ts +45 -18
  10. package/dist/connector.d.ts.map +1 -1
  11. package/dist/connector.js +219 -583
  12. package/dist/connector.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/logger.d.ts +28 -0
  18. package/dist/logger.d.ts.map +1 -0
  19. package/dist/logger.js +44 -0
  20. package/dist/logger.js.map +1 -0
  21. package/dist/network-client.d.ts +17 -1
  22. package/dist/network-client.d.ts.map +1 -1
  23. package/dist/network-client.js +119 -23
  24. package/dist/network-client.js.map +1 -1
  25. package/dist/node-manager.d.ts +20 -0
  26. package/dist/node-manager.d.ts.map +1 -1
  27. package/dist/node-manager.js +194 -18
  28. package/dist/node-manager.js.map +1 -1
  29. package/dist/plugin.d.ts +1 -1
  30. package/dist/plugin.d.ts.map +1 -1
  31. package/dist/plugin.js +4 -5
  32. package/dist/plugin.js.map +1 -1
  33. package/dist/reputation.d.ts +22 -1
  34. package/dist/reputation.d.ts.map +1 -1
  35. package/dist/reputation.js +102 -5
  36. package/dist/reputation.js.map +1 -1
  37. package/dist/task-guard.d.ts +82 -0
  38. package/dist/task-guard.d.ts.map +1 -1
  39. package/dist/task-guard.js +392 -15
  40. package/dist/task-guard.js.map +1 -1
  41. package/dist/task-queue.d.ts +50 -7
  42. package/dist/task-queue.d.ts.map +1 -1
  43. package/dist/task-queue.js +445 -12
  44. package/dist/task-queue.js.map +1 -1
  45. package/dist/tool-handlers.d.ts +96 -0
  46. package/dist/tool-handlers.d.ts.map +1 -0
  47. package/dist/tool-handlers.js +431 -0
  48. package/dist/tool-handlers.js.map +1 -0
  49. package/dist/types.d.ts +98 -10
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/types.js +10 -0
  52. package/dist/types.js.map +1 -1
  53. package/dist/webhook-pusher.d.ts +71 -0
  54. package/dist/webhook-pusher.d.ts.map +1 -0
  55. package/dist/webhook-pusher.js +174 -0
  56. package/dist/webhook-pusher.js.map +1 -0
  57. package/dist/webhook-server.d.ts +8 -1
  58. package/dist/webhook-server.d.ts.map +1 -1
  59. package/dist/webhook-server.js +42 -7
  60. package/dist/webhook-server.js.map +1 -1
  61. package/package.json +21 -8
@@ -11,11 +11,45 @@ export interface TaskGuardRule {
11
11
  severity: 'info' | 'warn' | 'block';
12
12
  check: (task: TaskRequest | TaskAnnouncement, context: TaskGuardContext) => TaskGuardResult;
13
13
  }
14
+ /**
15
+ * 任务安全检查上下文
16
+ *
17
+ * 提供任务检查所需的上下文信息,包括请求者信誉、黑白名单状态、
18
+ * 近期请求频率等,用于安全规则判断任务是否应该被接受或拒绝。
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const context: TaskGuardContext = {
23
+ * requesterReputation: {
24
+ * peerId: 'f2a-peer-xxx',
25
+ * score: 85,
26
+ * successfulTasks: 42,
27
+ * failedTasks: 2,
28
+ * // ...其他字段
29
+ * },
30
+ * isWhitelisted: true,
31
+ * isBlacklisted: false,
32
+ * recentTaskCount: 3,
33
+ * config: DEFAULT_TASK_GUARD_CONFIG
34
+ * };
35
+ *
36
+ * // 使用上下文进行任务检查
37
+ * const report = taskGuard.check(task, context);
38
+ * if (!report.passed) {
39
+ * console.warn('任务被安全规则拒绝:', report.blocks);
40
+ * }
41
+ * ```
42
+ */
14
43
  export interface TaskGuardContext {
44
+ /** 请求者的信誉信息(可选,新 peer 可能没有) */
15
45
  requesterReputation?: ReputationEntry;
46
+ /** 请求者是否在白名单中 */
16
47
  isWhitelisted: boolean;
48
+ /** 请求者是否在黑名单中 */
17
49
  isBlacklisted: boolean;
50
+ /** 近期(1分钟内)的任务请求数量 */
18
51
  recentTaskCount: number;
52
+ /** 任务守卫配置 */
19
53
  config: TaskGuardConfig;
20
54
  }
21
55
  export interface TaskGuardConfig {
@@ -25,6 +59,10 @@ export interface TaskGuardConfig {
25
59
  blockedKeywords: string[];
26
60
  dangerousPatterns: RegExp[];
27
61
  minReputationForDangerous: number;
62
+ /** 持久化目录路径,用于存储 rate limiting 状态。不设置则不持久化 */
63
+ persistDir?: string;
64
+ /** 持久化保存间隔(毫秒),默认 30000 (30秒) */
65
+ persistIntervalMs?: number;
28
66
  }
29
67
  export interface TaskGuardResult {
30
68
  passed: boolean;
@@ -47,7 +85,43 @@ export declare class TaskGuard {
47
85
  private config;
48
86
  private rules;
49
87
  private recentTasks;
88
+ /** 清理阈值:当条目数超过此值时触发清理 */
89
+ private cleanupThreshold;
90
+ /** 上次清理时间戳 */
91
+ private lastCleanupTime;
92
+ /** 定时清理间隔(毫秒) */
93
+ private cleanupIntervalMs;
94
+ /** 持久化文件路径 */
95
+ private persistFilePath;
96
+ /** 持久化定时器 */
97
+ private persistTimer;
98
+ /** 是否有未保存的更改 */
99
+ private hasUnsavedChanges;
50
100
  constructor(config?: Partial<TaskGuardConfig>);
101
+ /**
102
+ * 初始化持久化
103
+ */
104
+ private initPersistence;
105
+ /**
106
+ * 加载已保存的状态
107
+ */
108
+ private loadPersistedState;
109
+ /**
110
+ * 保存状态到文件
111
+ */
112
+ private saveState;
113
+ /**
114
+ * 仅在有未保存更改时保存
115
+ */
116
+ private saveStateIfNeeded;
117
+ /**
118
+ * 手动保存当前状态
119
+ */
120
+ forceSave(): void;
121
+ /**
122
+ * 关闭持久化(停止定时器并保存最后状态)
123
+ */
124
+ shutdown(): void;
51
125
  /**
52
126
  * 检查任务
53
127
  */
@@ -72,6 +146,14 @@ export declare class TaskGuard {
72
146
  private isDangerousTask;
73
147
  private getRecentTaskCount;
74
148
  private recordTask;
149
+ /**
150
+ * 条件触发清理:当条目数超过阈值或距上次清理超过间隔时执行
151
+ */
152
+ private maybeCleanup;
153
+ /**
154
+ * 清理过期的 recentTasks 条目,防止内存泄漏
155
+ */
156
+ private cleanupRecentTasks;
75
157
  }
76
158
  export declare const taskGuard: TaskGuard;
77
159
  //# sourceMappingURL=task-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-guard.d.ts","sourceRoot":"","sources":["../src/task-guard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEjF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,gBAAgB,EAAE,OAAO,EAAE,gBAAgB,KAAK,eAAe,CAAC;CAC7F;AAED,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B,EAAE,OAAO,CAAC;IACzC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,eAAO,MAAM,yBAAyB,EAAE,eAoBvC,CAAC;AAEF,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,WAAW,CAAoC;gBAE3C,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM;IAKjD;;OAEG;IACH,KAAK,CACH,IAAI,EAAE,WAAW,GAAG,gBAAgB,EACpC,OAAO,GAAE,OAAO,CAAC,gBAAgB,CAAM,GACtC,eAAe;IAkDlB;;OAEG;IACH,UAAU,CACR,IAAI,EAAE,WAAW,GAAG,gBAAgB,EACpC,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAClC,OAAO;IAKV;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAIlC;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAOtD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAMpD,OAAO,CAAC,kBAAkB;IA+M1B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,UAAU;CAKnB;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAC"}
1
+ {"version":3,"file":"task-guard.d.ts","sourceRoot":"","sources":["../src/task-guard.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,44 @@
3
3
  * F2A Task Guard
4
4
  * 轻量级任务安全检查和评审
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
6
39
  Object.defineProperty(exports, "__esModule", { value: true });
7
40
  exports.taskGuard = exports.TaskGuard = exports.DEFAULT_TASK_GUARD_CONFIG = void 0;
41
+ const logger_js_1 = require("./logger.js");
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
8
44
  // 默认配置
9
45
  exports.DEFAULT_TASK_GUARD_CONFIG = {
10
46
  enabled: true,
@@ -25,15 +61,252 @@ exports.DEFAULT_TASK_GUARD_CONFIG = {
25
61
  /drop\s+database/i,
26
62
  /shutdown\s+-h/i
27
63
  ],
28
- minReputationForDangerous: 70
64
+ minReputationForDangerous: 70,
65
+ persistDir: undefined,
66
+ persistIntervalMs: 30000 // 30秒
29
67
  };
68
+ /**
69
+ * 路径规范化 - 移除 .. 和多余的斜杠
70
+ * 用于检测路径遍历绕过
71
+ */
72
+ function normalizePath(path) {
73
+ // 解码 URL 编码
74
+ let normalized = path;
75
+ try {
76
+ normalized = decodeURIComponent(normalized);
77
+ }
78
+ catch { /* ignore */ }
79
+ // 替换多个斜杠为单个
80
+ normalized = normalized.replace(/\/+/g, '/');
81
+ // 解析 .. 和 .
82
+ const parts = normalized.split('/');
83
+ const result = [];
84
+ for (const part of parts) {
85
+ if (part === '..') {
86
+ result.pop();
87
+ }
88
+ else if (part !== '.' && part !== '') {
89
+ result.push(part);
90
+ }
91
+ }
92
+ return '/' + result.join('/');
93
+ }
94
+ /**
95
+ * 检测变量替换绕过
96
+ */
97
+ function detectVariableSubstitution(text) {
98
+ const detected = [];
99
+ // 环境变量模式: $VAR, ${VAR}, %VAR%
100
+ const envPatterns = [
101
+ /\$([A-Za-z_][A-Za-z0-9_]*)/g, // $VAR
102
+ /\$\{([^}]+)\}/g, // ${VAR}
103
+ /%([A-Za-z_][A-Za-z0-9_]*)%/g, // %VAR%
104
+ ];
105
+ for (const pattern of envPatterns) {
106
+ let match;
107
+ while ((match = pattern.exec(text)) !== null) {
108
+ detected.push(`变量替换: ${match[0]}`);
109
+ }
110
+ }
111
+ return detected;
112
+ }
113
+ /**
114
+ * 检测编码绕过
115
+ */
116
+ function detectEncodingBypass(text) {
117
+ const detected = [];
118
+ // 八进制编码: \177, \027
119
+ if (/\\[0-7]{1,3}/.test(text)) {
120
+ detected.push('八进制编码');
121
+ }
122
+ // 十六进制编码: \x7f, \x1b
123
+ if (/\\x[0-9a-fA-F]{2}/.test(text)) {
124
+ detected.push('十六进制编码');
125
+ }
126
+ // Unicode 编码: \u007f, \u007F, \u{7f} (ES6)
127
+ if (/\\u[0-9a-fA-F]{4}/.test(text) || /\\u\{[0-9a-fA-F]+\}/.test(text)) {
128
+ detected.push('Unicode编码');
129
+ }
130
+ // HTML 实体编码: &#x20;, &#32;, &lt;, &gt;, &amp;
131
+ // 十六进制格式: &#xHH;
132
+ if (/&#x[0-9a-fA-F]+;?/i.test(text)) {
133
+ detected.push('HTML实体编码(十六进制)');
134
+ }
135
+ // 十进制格式: &#DDD;
136
+ if (/&#\d+;?/.test(text)) {
137
+ detected.push('HTML实体编码(十进制)');
138
+ }
139
+ // 命名实体: &lt; &gt; &amp; &quot; &apos;
140
+ if (/&(lt|gt|amp|quot|apos|#x?\d+);/i.test(text)) {
141
+ detected.push('HTML实体编码(命名)');
142
+ }
143
+ // URL 编码: %20, %2f
144
+ if (/%[0-9a-fA-F]{2}/.test(text)) {
145
+ detected.push('URL编码');
146
+ }
147
+ return detected;
148
+ }
149
+ /**
150
+ * 检测命令注入绕过
151
+ */
152
+ function detectCommandInjectionBypass(text) {
153
+ const detected = [];
154
+ const lowerText = text.toLowerCase();
155
+ // 反引号命令替换
156
+ if (/`[^`]+`/.test(text)) {
157
+ detected.push('反引号命令替换');
158
+ }
159
+ // $() 命令替换
160
+ if (/\$\([^)]+\)/.test(text)) {
161
+ detected.push('$()命令替换');
162
+ }
163
+ // 分号命令链接
164
+ if (/;\s*(rm|dd|mkfs|shutdown|reboot|halt|init)\b/i.test(text)) {
165
+ detected.push('分号命令链接');
166
+ }
167
+ // 管道命令注入
168
+ if (/\|\s*(rm|dd|mkfs|shutdown|reboot|halt)\b/i.test(text)) {
169
+ detected.push('管道命令注入');
170
+ }
171
+ // &&/|| 命令链接
172
+ if (/(&&|\|\|)\s*(rm|dd|mkfs|shutdown|reboot|halt)\b/i.test(text)) {
173
+ detected.push('逻辑运算符命令链接');
174
+ }
175
+ return detected;
176
+ }
30
177
  class TaskGuard {
31
178
  config;
32
179
  rules;
33
180
  recentTasks = new Map();
181
+ /** 清理阈值:当条目数超过此值时触发清理 */
182
+ cleanupThreshold = 100;
183
+ /** 上次清理时间戳 */
184
+ lastCleanupTime = 0;
185
+ /** 定时清理间隔(毫秒) */
186
+ cleanupIntervalMs = 60000; // 1分钟
187
+ /** 持久化文件路径 */
188
+ persistFilePath = null;
189
+ /** 持久化定时器 */
190
+ persistTimer = null;
191
+ /** 是否有未保存的更改 */
192
+ hasUnsavedChanges = false;
34
193
  constructor(config = {}) {
35
194
  this.config = { ...exports.DEFAULT_TASK_GUARD_CONFIG, ...config };
36
195
  this.rules = this.createDefaultRules();
196
+ // 初始化持久化
197
+ if (this.config.persistDir) {
198
+ this.initPersistence(this.config.persistDir, this.config.persistIntervalMs);
199
+ }
200
+ }
201
+ /**
202
+ * 初始化持久化
203
+ */
204
+ initPersistence(persistDir, persistIntervalMs) {
205
+ try {
206
+ // 确保目录存在
207
+ if (!fs.existsSync(persistDir)) {
208
+ fs.mkdirSync(persistDir, { recursive: true });
209
+ }
210
+ this.persistFilePath = path.join(persistDir, 'task-guard-state.json');
211
+ // 加载已保存的状态
212
+ this.loadPersistedState();
213
+ // 设置定期保存
214
+ const interval = persistIntervalMs ?? exports.DEFAULT_TASK_GUARD_CONFIG.persistIntervalMs ?? 30000;
215
+ this.persistTimer = setInterval(() => {
216
+ this.saveStateIfNeeded();
217
+ }, interval);
218
+ // 防止定时器阻止进程退出
219
+ if (this.persistTimer.unref) {
220
+ this.persistTimer.unref();
221
+ }
222
+ logger_js_1.taskGuardLogger.info('persistence-initialized: persistDir=%s, intervalMs=%d', persistDir, interval);
223
+ }
224
+ catch (error) {
225
+ logger_js_1.taskGuardLogger.error('persistence-init-failed: error=%s', error);
226
+ this.persistFilePath = null;
227
+ }
228
+ }
229
+ /**
230
+ * 加载已保存的状态
231
+ */
232
+ loadPersistedState() {
233
+ if (!this.persistFilePath || !fs.existsSync(this.persistFilePath)) {
234
+ return;
235
+ }
236
+ try {
237
+ const data = fs.readFileSync(this.persistFilePath, 'utf-8');
238
+ const state = JSON.parse(data);
239
+ if (state.recentTasks && typeof state.recentTasks === 'object') {
240
+ const now = Date.now();
241
+ const windowMs = 60000; // 1分钟窗口
242
+ // 过滤掉过期的时间戳,只保留有效的
243
+ let loadedCount = 0;
244
+ for (const [peerId, timestamps] of Object.entries(state.recentTasks)) {
245
+ if (Array.isArray(timestamps)) {
246
+ const validTimestamps = timestamps.filter(t => typeof t === 'number' && now - t < windowMs);
247
+ if (validTimestamps.length > 0) {
248
+ this.recentTasks.set(peerId, validTimestamps);
249
+ loadedCount += validTimestamps.length;
250
+ }
251
+ }
252
+ }
253
+ logger_js_1.taskGuardLogger.info('persistence-loaded: entries=%d, timestamps=%d, savedAt=%s', this.recentTasks.size, loadedCount, new Date(state.savedAt).toISOString());
254
+ }
255
+ }
256
+ catch (error) {
257
+ logger_js_1.taskGuardLogger.warn('persistence-load-failed: error=%s', error);
258
+ }
259
+ }
260
+ /**
261
+ * 保存状态到文件
262
+ */
263
+ saveState() {
264
+ if (!this.persistFilePath) {
265
+ return;
266
+ }
267
+ try {
268
+ const state = {
269
+ recentTasks: Object.fromEntries(this.recentTasks),
270
+ savedAt: Date.now()
271
+ };
272
+ // 写入临时文件,然后原子性重命名
273
+ const tempPath = this.persistFilePath + '.tmp';
274
+ fs.writeFileSync(tempPath, JSON.stringify(state), 'utf-8');
275
+ fs.renameSync(tempPath, this.persistFilePath);
276
+ this.hasUnsavedChanges = false;
277
+ logger_js_1.taskGuardLogger.debug('persistence-saved: entries=%d', this.recentTasks.size);
278
+ }
279
+ catch (error) {
280
+ logger_js_1.taskGuardLogger.error('persistence-save-failed: error=%s', error);
281
+ }
282
+ }
283
+ /**
284
+ * 仅在有未保存更改时保存
285
+ */
286
+ saveStateIfNeeded() {
287
+ if (this.hasUnsavedChanges) {
288
+ this.saveState();
289
+ }
290
+ }
291
+ /**
292
+ * 手动保存当前状态
293
+ */
294
+ forceSave() {
295
+ this.saveState();
296
+ }
297
+ /**
298
+ * 关闭持久化(停止定时器并保存最后状态)
299
+ */
300
+ shutdown() {
301
+ if (this.persistTimer) {
302
+ clearInterval(this.persistTimer);
303
+ this.persistTimer = null;
304
+ }
305
+ // 保存最终状态
306
+ if (this.hasUnsavedChanges) {
307
+ this.saveState();
308
+ }
309
+ logger_js_1.taskGuardLogger.info('task-guard-shutdown: persisted=%s', !!this.persistFilePath);
37
310
  }
38
311
  /**
39
312
  * 检查任务
@@ -46,6 +319,8 @@ class TaskGuard {
46
319
  recentTaskCount: this.getRecentTaskCount(task.from),
47
320
  config: this.config
48
321
  };
322
+ const taskId = 'taskId' in task ? task.taskId : task.announcementId;
323
+ logger_js_1.taskGuardLogger.debug('check: taskId=%s, from=%s, rules=%d', taskId, task.from, this.rules.filter(r => r.enabled).length);
49
324
  const results = [];
50
325
  // 运行所有规则
51
326
  for (const rule of this.rules) {
@@ -54,8 +329,18 @@ class TaskGuard {
54
329
  try {
55
330
  const result = rule.check(task, fullContext);
56
331
  results.push(result);
332
+ // 记录规则执行结果
333
+ if (!result.passed) {
334
+ if (result.severity === 'block') {
335
+ logger_js_1.taskGuardLogger.warn('rule-blocked: taskId=%s, ruleId=%s, message=%s', taskId, rule.id, result.message);
336
+ }
337
+ else if (result.severity === 'warn') {
338
+ logger_js_1.taskGuardLogger.info('rule-warning: taskId=%s, ruleId=%s, message=%s', taskId, rule.id, result.message);
339
+ }
340
+ }
57
341
  }
58
342
  catch (error) {
343
+ logger_js_1.taskGuardLogger.error('rule-error: ruleId=%s, taskId=%s, error=%s', rule.id, taskId, error);
59
344
  results.push({
60
345
  passed: false,
61
346
  severity: 'warn',
@@ -71,9 +356,11 @@ class TaskGuard {
71
356
  const requiresConfirmation = results.some(r => r.severity === 'warn' &&
72
357
  !r.passed &&
73
358
  this.config.requireConfirmationForDangerous);
359
+ const passed = blocks.length === 0;
360
+ logger_js_1.taskGuardLogger.debug('check-result: taskId=%s, passed=%s, blocks=%d, warnings=%d, requiresConfirmation=%s', taskId, passed, blocks.length, warnings.length, requiresConfirmation);
74
361
  return {
75
- taskId: 'taskId' in task ? task.taskId : task.announcementId,
76
- passed: blocks.length === 0,
362
+ taskId,
363
+ passed,
77
364
  results,
78
365
  warnings,
79
366
  blocks,
@@ -86,6 +373,8 @@ class TaskGuard {
86
373
  */
87
374
  quickCheck(task, context) {
88
375
  const report = this.check(task, context);
376
+ const taskId = 'taskId' in task ? task.taskId : task.announcementId;
377
+ logger_js_1.taskGuardLogger.debug('quickCheck: taskId=%s, passed=%s', taskId, report.passed);
89
378
  return report.passed;
90
379
  }
91
380
  /**
@@ -93,6 +382,7 @@ class TaskGuard {
93
382
  */
94
383
  addRule(rule) {
95
384
  this.rules.push(rule);
385
+ logger_js_1.taskGuardLogger.info('addRule: ruleId=%s, name=%s, severity=%s, enabled=%s', rule.id, rule.name, rule.severity, rule.enabled);
96
386
  }
97
387
  /**
98
388
  * 启用/禁用规则
@@ -101,6 +391,10 @@ class TaskGuard {
101
391
  const rule = this.rules.find(r => r.id === ruleId);
102
392
  if (rule) {
103
393
  rule.enabled = enabled;
394
+ logger_js_1.taskGuardLogger.info('setRuleEnabled: ruleId=%s, enabled=%s', ruleId, enabled);
395
+ }
396
+ else {
397
+ logger_js_1.taskGuardLogger.warn('setRuleEnabled: rule not found, ruleId=%s', ruleId);
104
398
  }
105
399
  }
106
400
  /**
@@ -204,11 +498,13 @@ class TaskGuard {
204
498
  severity: 'warn',
205
499
  check: (task, context) => {
206
500
  if (!context.requesterReputation) {
501
+ // 无信誉记录时返回 warn,而非直接通过
502
+ // 新 peer 首次任务需要谨慎处理
207
503
  return {
208
504
  passed: true,
209
- severity: 'info',
505
+ severity: 'warn',
210
506
  ruleId: 'reputation',
211
- message: '无信誉记录,使用默认处理'
507
+ message: '无信誉记录,首次任务建议谨慎处理'
212
508
  };
213
509
  }
214
510
  const rep = context.requesterReputation.score;
@@ -243,9 +539,17 @@ class TaskGuard {
243
539
  const isFileOp = /\b(read|write|edit|delete|remove)\b/.test(description);
244
540
  const hasPath = /[\/~]\w+/.test(description);
245
541
  if (isFileOp && hasPath) {
246
- // 检查是否是系统路径
247
- const systemPaths = ['/etc/', '/sys/', '/proc/', '/dev/', 'c:\\windows'];
248
- const hasSystemPath = systemPaths.some(p => description.includes(p));
542
+ // 检查是否是系统路径(包括 macOS 路径)
543
+ const systemPaths = [
544
+ // Linux/Unix 系统路径
545
+ '/etc/', '/sys/', '/proc/', '/dev/', '/root/', '/boot/',
546
+ // macOS 系统路径
547
+ '/System/', '/Library/', '/Applications/', '/usr/', '/bin/', '/sbin/',
548
+ // Windows 系统路径
549
+ 'c:\\windows', 'c:\\program files', 'c:\\program files (x86)',
550
+ 'c:\\users\\public', 'c:\\users\\default'
551
+ ];
552
+ const hasSystemPath = systemPaths.some(p => description.includes(p.toLowerCase()));
249
553
  if (hasSystemPath) {
250
554
  return {
251
555
  passed: false,
@@ -275,14 +579,19 @@ class TaskGuard {
275
579
  const description = task.description.toLowerCase();
276
580
  const isNetworkOp = /\b(fetch|download|curl|wget|http|api)\b/.test(description);
277
581
  if (isNetworkOp) {
278
- const suspicious = ['exe', 'dll', 'sh', 'bash', 'python', 'script'];
279
- const hasSuspicious = suspicious.some(s => description.includes(s));
582
+ // 更精确的可疑文件扩展名检测(移除 python/script 等宽泛词)
583
+ const suspiciousExtensions = ['exe', 'dll', 'app', 'deb', 'rpm', 'dmg', 'msi', 'bat', 'ps1'];
584
+ const hasSuspicious = suspiciousExtensions.some(ext => {
585
+ // 匹配 .ext 后跟空格、引号、斜杠、大于号,或字符串结尾
586
+ const pattern = new RegExp(`\\.${ext}([\\s"'\\/>]|$)`, 'i');
587
+ return pattern.test(description);
588
+ });
280
589
  if (hasSuspicious) {
281
590
  return {
282
591
  passed: false,
283
592
  severity: 'warn',
284
593
  ruleId: 'network-operation',
285
- message: '检测到可疑的网络下载操作',
594
+ message: '检测到可疑的网络下载操作(可执行文件)',
286
595
  details: { suspicious: true }
287
596
  };
288
597
  }
@@ -311,9 +620,10 @@ class TaskGuard {
311
620
  }
312
621
  getRecentTaskCount(peerId) {
313
622
  const now = Date.now();
314
- const oneMinuteAgo = now - 60000;
623
+ const windowMs = this.config.maxTasksPerMinute ? 60000 : 60000;
624
+ const windowStart = now - windowMs;
315
625
  const timestamps = this.recentTasks.get(peerId) || [];
316
- const recent = timestamps.filter(t => t > oneMinuteAgo);
626
+ const recent = timestamps.filter(t => t > windowStart);
317
627
  // 更新存储
318
628
  this.recentTasks.set(peerId, recent);
319
629
  return recent.length;
@@ -322,9 +632,76 @@ class TaskGuard {
322
632
  const timestamps = this.recentTasks.get(peerId) || [];
323
633
  timestamps.push(Date.now());
324
634
  this.recentTasks.set(peerId, timestamps);
635
+ // 标记有未保存的更改
636
+ this.hasUnsavedChanges = true;
637
+ // 优化:仅在超过阈值或定时触发时清理,而非每次都清理
638
+ this.maybeCleanup();
639
+ }
640
+ /**
641
+ * 条件触发清理:当条目数超过阈值或距上次清理超过间隔时执行
642
+ */
643
+ maybeCleanup() {
644
+ const now = Date.now();
645
+ const shouldCleanup = this.recentTasks.size > this.cleanupThreshold ||
646
+ (now - this.lastCleanupTime) > this.cleanupIntervalMs;
647
+ if (shouldCleanup) {
648
+ this.cleanupRecentTasks();
649
+ this.lastCleanupTime = now;
650
+ }
651
+ }
652
+ /**
653
+ * 清理过期的 recentTasks 条目,防止内存泄漏
654
+ */
655
+ cleanupRecentTasks() {
656
+ const now = Date.now();
657
+ const windowMs = 60000; // 1分钟窗口
658
+ let hadChanges = false;
659
+ for (const [key, timestamps] of this.recentTasks.entries()) {
660
+ // 过滤掉过期的时间戳
661
+ const validTimestamps = timestamps.filter(t => now - t < windowMs);
662
+ if (validTimestamps.length === 0) {
663
+ // 没有有效时间戳,删除整个条目
664
+ this.recentTasks.delete(key);
665
+ hadChanges = true;
666
+ }
667
+ else if (validTimestamps.length !== timestamps.length) {
668
+ // 更新为有效的时间戳
669
+ this.recentTasks.set(key, validTimestamps);
670
+ hadChanges = true;
671
+ }
672
+ }
673
+ if (hadChanges) {
674
+ this.hasUnsavedChanges = true;
675
+ }
325
676
  }
326
677
  }
327
678
  exports.TaskGuard = TaskGuard;
328
- // 导出单例
329
- exports.taskGuard = new TaskGuard();
679
+ // 导出单例(带进程退出时自动保存)
680
+ const globalTaskGuard = new TaskGuard();
681
+ // 注册进程退出处理,确保状态持久化
682
+ let shutdownHandlersRegistered = false;
683
+ const registerShutdownHandlers = () => {
684
+ if (shutdownHandlersRegistered)
685
+ return;
686
+ shutdownHandlersRegistered = true;
687
+ // 处理正常退出
688
+ process.on('beforeExit', () => {
689
+ globalTaskGuard.shutdown();
690
+ });
691
+ // 处理 SIGINT (Ctrl+C)
692
+ process.on('SIGINT', () => {
693
+ globalTaskGuard.shutdown();
694
+ process.exit(0);
695
+ });
696
+ // 处理 SIGTERM
697
+ process.on('SIGTERM', () => {
698
+ globalTaskGuard.shutdown();
699
+ process.exit(0);
700
+ });
701
+ };
702
+ // 仅在非测试环境注册
703
+ if (process.env.NODE_ENV !== 'test') {
704
+ registerShutdownHandlers();
705
+ }
706
+ exports.taskGuard = globalTaskGuard;
330
707
  //# sourceMappingURL=task-guard.js.map