@nest-omni/core 4.1.3-10 → 4.1.3-12

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 (183) hide show
  1. package/audit/audit.module.js +42 -2
  2. package/audit/controllers/audit.controller.d.ts +64 -0
  3. package/audit/controllers/audit.controller.js +50 -0
  4. package/audit/decorators/audit-action.decorator.d.ts +74 -0
  5. package/audit/decorators/audit-action.decorator.js +42 -0
  6. package/audit/decorators/audit-controller.decorator.d.ts +1 -1
  7. package/audit/decorators/audit-controller.decorator.js +2 -2
  8. package/audit/decorators/entity-audit.decorator.d.ts +78 -2
  9. package/audit/decorators/entity-audit.decorator.js +145 -4
  10. package/audit/decorators/index.d.ts +2 -0
  11. package/audit/decorators/index.js +2 -0
  12. package/audit/entities/audit-action-summary.entity.d.ts +23 -0
  13. package/audit/entities/audit-action-summary.entity.js +101 -0
  14. package/audit/entities/entity-audit-log.entity.d.ts +8 -0
  15. package/audit/entities/entity-audit-log.entity.js +54 -2
  16. package/audit/entities/entity-transaction.entity.d.ts +8 -2
  17. package/audit/entities/entity-transaction.entity.js +39 -3
  18. package/audit/entities/index.d.ts +3 -0
  19. package/audit/entities/index.js +3 -0
  20. package/audit/entities/manual-operation-log.entity.js +8 -1
  21. package/audit/enums/audit.enums.d.ts +22 -6
  22. package/audit/enums/audit.enums.js +27 -9
  23. package/audit/index.d.ts +4 -1
  24. package/audit/index.js +25 -2
  25. package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
  26. package/audit/interceptors/audit-action.interceptor.js +215 -0
  27. package/audit/interceptors/index.d.ts +1 -0
  28. package/audit/interceptors/index.js +1 -0
  29. package/audit/interfaces/audit.interfaces.d.ts +145 -2
  30. package/audit/services/audit-action.service.d.ts +141 -0
  31. package/audit/services/audit-action.service.js +244 -0
  32. package/audit/services/audit-context.service.d.ts +82 -0
  33. package/audit/services/audit-context.service.js +170 -0
  34. package/audit/services/entity-audit.service.d.ts +174 -4
  35. package/audit/services/entity-audit.service.js +515 -14
  36. package/audit/services/index.d.ts +3 -0
  37. package/audit/services/index.js +3 -0
  38. package/audit/services/manual-audit-log.service.d.ts +24 -23
  39. package/audit/services/manual-audit-log.service.js +32 -53
  40. package/audit/services/operation-description.service.d.ts +13 -3
  41. package/audit/services/operation-description.service.js +161 -24
  42. package/audit/services/transaction-audit.service.js +3 -3
  43. package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
  44. package/audit/subscribers/entity-audit.subscriber.js +47 -0
  45. package/file-upload/controllers/file-access.controller.d.ts +23 -0
  46. package/file-upload/controllers/file-access.controller.js +128 -0
  47. package/file-upload/decorators/csv-data.decorator.d.ts +44 -0
  48. package/file-upload/decorators/csv-data.decorator.js +131 -0
  49. package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
  50. package/file-upload/decorators/excel-data.decorator.js +125 -0
  51. package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
  52. package/file-upload/decorators/file-upload.decorator.js +172 -0
  53. package/file-upload/decorators/index.d.ts +4 -0
  54. package/file-upload/decorators/index.js +20 -0
  55. package/file-upload/decorators/process.decorator.d.ts +40 -0
  56. package/file-upload/decorators/process.decorator.js +52 -0
  57. package/file-upload/dto/create-file.dto.d.ts +24 -0
  58. package/file-upload/dto/create-file.dto.js +112 -0
  59. package/file-upload/dto/find-files.dto.d.ts +15 -0
  60. package/file-upload/dto/find-files.dto.js +76 -0
  61. package/file-upload/dto/index.d.ts +4 -0
  62. package/file-upload/dto/index.js +20 -0
  63. package/file-upload/dto/pagination.dto.d.ts +7 -0
  64. package/file-upload/dto/pagination.dto.js +39 -0
  65. package/file-upload/dto/update-file.dto.d.ts +16 -0
  66. package/file-upload/dto/update-file.dto.js +71 -0
  67. package/file-upload/entities/file-metadata.entity.d.ts +22 -0
  68. package/file-upload/entities/file-metadata.entity.js +84 -0
  69. package/file-upload/entities/file.entity.d.ts +129 -0
  70. package/file-upload/entities/file.entity.js +384 -0
  71. package/file-upload/entities/index.d.ts +2 -0
  72. package/file-upload/entities/index.js +18 -0
  73. package/file-upload/enums/file-type.enum.d.ts +72 -0
  74. package/file-upload/enums/file-type.enum.js +212 -0
  75. package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
  76. package/file-upload/exceptions/file-upload.exception.js +120 -0
  77. package/file-upload/exceptions/index.d.ts +1 -0
  78. package/file-upload/exceptions/index.js +17 -0
  79. package/file-upload/file-upload.module.d.ts +89 -0
  80. package/file-upload/file-upload.module.js +264 -0
  81. package/file-upload/index.d.ts +26 -0
  82. package/file-upload/index.js +59 -0
  83. package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
  84. package/file-upload/interceptors/file-upload.interceptor.js +434 -0
  85. package/file-upload/interceptors/index.d.ts +1 -0
  86. package/file-upload/interceptors/index.js +17 -0
  87. package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
  88. package/file-upload/interfaces/custom-file-type.interface.js +2 -0
  89. package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
  90. package/file-upload/interfaces/file-buffer.interface.js +2 -0
  91. package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
  92. package/file-upload/interfaces/file-entity.interface.js +28 -0
  93. package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
  94. package/file-upload/interfaces/file-metadata.interface.js +2 -0
  95. package/file-upload/interfaces/file-upload-options.interface.d.ts +117 -0
  96. package/file-upload/interfaces/file-upload-options.interface.js +2 -0
  97. package/file-upload/interfaces/index.d.ts +7 -0
  98. package/file-upload/interfaces/index.js +24 -0
  99. package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
  100. package/file-upload/interfaces/storage-provider.interface.js +2 -0
  101. package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
  102. package/file-upload/interfaces/upload-options.interface.js +2 -0
  103. package/file-upload/providers/index.d.ts +2 -0
  104. package/file-upload/providers/index.js +18 -0
  105. package/file-upload/providers/local-storage.provider.d.ts +98 -0
  106. package/file-upload/providers/local-storage.provider.js +484 -0
  107. package/file-upload/providers/s3-storage.provider.d.ts +87 -0
  108. package/file-upload/providers/s3-storage.provider.js +455 -0
  109. package/file-upload/services/file-signature-validator.service.d.ts +118 -0
  110. package/file-upload/services/file-signature-validator.service.js +376 -0
  111. package/file-upload/services/file.service.d.ts +190 -0
  112. package/file-upload/services/file.service.js +609 -0
  113. package/file-upload/services/index.d.ts +4 -0
  114. package/file-upload/services/index.js +20 -0
  115. package/file-upload/services/malicious-file-detector.service.d.ts +274 -0
  116. package/file-upload/services/malicious-file-detector.service.js +1035 -0
  117. package/file-upload/services/mime-registry.service.d.ts +47 -0
  118. package/file-upload/services/mime-registry.service.js +167 -0
  119. package/file-upload/utils/checksum.util.d.ts +28 -0
  120. package/file-upload/utils/checksum.util.js +65 -0
  121. package/file-upload/utils/dynamic-import.util.d.ts +50 -0
  122. package/file-upload/utils/dynamic-import.util.js +144 -0
  123. package/file-upload/utils/filename.util.d.ts +59 -0
  124. package/file-upload/utils/filename.util.js +184 -0
  125. package/file-upload/utils/filepath.util.d.ts +70 -0
  126. package/file-upload/utils/filepath.util.js +152 -0
  127. package/file-upload/utils/index.d.ts +4 -0
  128. package/file-upload/utils/index.js +20 -0
  129. package/http-client/http-client.module.js +1 -5
  130. package/index.d.ts +3 -1
  131. package/index.js +4 -1
  132. package/package.json +4 -5
  133. package/redis-lock/lock-heartbeat.service.d.ts +2 -2
  134. package/redis-lock/lock-heartbeat.service.js +4 -4
  135. package/redis-lock/redis-lock.service.d.ts +18 -0
  136. package/redis-lock/redis-lock.service.js +38 -8
  137. package/setup/bootstrap.setup.d.ts +1 -0
  138. package/setup/bootstrap.setup.js +1 -0
  139. package/setup/schedule.decorator.js +18 -8
  140. package/shared/index.d.ts +1 -1
  141. package/shared/index.js +1 -1
  142. package/shared/{serviceRegistryModule.js → service-registry.module.js} +9 -16
  143. package/shared/services/index.d.ts +0 -1
  144. package/shared/services/index.js +0 -1
  145. package/transaction/__tests__/mocks.d.ts +9 -0
  146. package/transaction/__tests__/mocks.js +33 -0
  147. package/transaction/base-service-transaction.d.ts +99 -0
  148. package/transaction/base-service-transaction.js +286 -0
  149. package/transaction/cls-compatibility.service.d.ts +55 -0
  150. package/transaction/cls-compatibility.service.js +127 -0
  151. package/transaction/data-source-registry.d.ts +91 -0
  152. package/transaction/data-source-registry.js +349 -0
  153. package/transaction/database-adapter.d.ts +44 -0
  154. package/transaction/database-adapter.js +240 -0
  155. package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
  156. package/transaction/decorators/entity-datasource.decorator.js +105 -0
  157. package/transaction/index.d.ts +14 -0
  158. package/transaction/index.js +57 -0
  159. package/transaction/logging-transactional.interceptor.d.ts +18 -0
  160. package/transaction/logging-transactional.interceptor.js +163 -0
  161. package/transaction/transaction-context.service.d.ts +137 -0
  162. package/transaction/transaction-context.service.js +411 -0
  163. package/transaction/transaction-manager.d.ts +230 -0
  164. package/transaction/transaction-manager.js +1001 -0
  165. package/transaction/transaction-synchronization.d.ts +171 -0
  166. package/transaction/transaction-synchronization.js +380 -0
  167. package/transaction/transaction.errors.d.ts +91 -0
  168. package/transaction/transaction.errors.js +206 -0
  169. package/transaction/transaction.module.d.ts +30 -0
  170. package/transaction/transaction.module.js +98 -0
  171. package/transaction/transactional.decorator.d.ts +82 -0
  172. package/transaction/transactional.decorator.js +319 -0
  173. package/transaction/typeorm-module-wrapper.d.ts +96 -0
  174. package/transaction/typeorm-module-wrapper.js +197 -0
  175. package/validators/file-mimetype.validator.d.ts +0 -2
  176. package/validators/file-mimetype.validator.js +4 -6
  177. package/validators/is-exists.validator.d.ts +2 -5
  178. package/validators/is-exists.validator.js +4 -6
  179. package/validators/is-unique.validator.d.ts +2 -5
  180. package/validators/is-unique.validator.js +6 -11
  181. package/shared/services/validator.service.d.ts +0 -3
  182. package/shared/services/validator.service.js +0 -20
  183. /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
@@ -0,0 +1,1035 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ var MaliciousFileDetector_1;
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.MaliciousFileDetector = void 0;
26
+ const common_1 = require("@nestjs/common");
27
+ const fs = require("fs-extra");
28
+ const path = require("path");
29
+ const child_process_1 = require("child_process");
30
+ const util_1 = require("util");
31
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
32
+ /**
33
+ * 恶意文件检测服务
34
+ */
35
+ /**
36
+ * 恶意文件检测服务(增强版)
37
+ *
38
+ * 功能:
39
+ * 1. 多编码检测 - 支持 UTF-8, UTF-16LE, Latin1, ASCII, Base64
40
+ * 2. 文件结构分析 - 检测嵌套压缩、可执行内容、Office宏、高熵值、多态性
41
+ * 3. ClamAV集成 - 支持本地命令行和远程服务两种模式
42
+ *
43
+ * 使用示例:
44
+ * ```typescript
45
+ * // 方式1: 本地ClamAV(需要在当前机器安装)
46
+ * FileUploadModule.forRoot({
47
+ * maliciousDetection: {
48
+ * enableClamAV: true,
49
+ * clamavMode: 'local', // 本地模式
50
+ * clamavCommand: 'clamdscan', // 或 'clamscan'
51
+ * },
52
+ * });
53
+ *
54
+ * // 方式2: 远程ClamAV服务(推荐用于生产环境)
55
+ * FileUploadModule.forRoot({
56
+ * maliciousDetection: {
57
+ * enableClamAV: true,
58
+ * clamavMode: 'remote', // 远程模式
59
+ * clamavRemote: {
60
+ * host: 'clamav-server.com', // ClamAV服务器地址
61
+ * port: 3310, // ClamAV守护进程端口
62
+ * timeout: 30000, // 超时时间(ms)
63
+ * },
64
+ * },
65
+ * });
66
+ *
67
+ * // 方式3: 仅使用多编码检测和结构分析(无需安装ClamAV)
68
+ * FileUploadModule.forRoot({
69
+ * maliciousDetection: {
70
+ * enableClamAV: false, // 禁用ClamAV
71
+ * enableMultiEncoding: true, // 启用多编码检测
72
+ * enableStructureAnalysis: true, // 启用结构分析
73
+ * riskThreshold: 50, // 风险阈值
74
+ * },
75
+ * });
76
+ *
77
+ * // 直接使用服务
78
+ * const result = await maliciousDetector.scanFile('/path/to/file');
79
+ * console.log('Is Safe:', result.safe);
80
+ * console.log('Risk Score:', result.riskScore);
81
+ * console.log('Threats:', result.threats);
82
+ * console.log('Details:', result.details);
83
+ * ```
84
+ *
85
+ * 远程ClamAV服务部署示例:
86
+ * ```bash
87
+ * # Docker部署ClamAV服务
88
+ * docker run -d -p 3310:3310 \
89
+ * --name clamav \
90
+ * clamav/clamav:latest
91
+ *
92
+ * # 配置clamd.conf允许远程连接
93
+ * TCPSocket 3310
94
+ * TCPAddr 0.0.0.0
95
+ * StreamMaxLength 100M
96
+ * ```
97
+ *
98
+ * 检测结果示例:
99
+ * ```json
100
+ * {
101
+ * "safe": false,
102
+ * "isMalware": true,
103
+ * "riskScore": 85,
104
+ * "threats": [
105
+ * "PE executable header (Windows)",
106
+ * "Script tag injection (base64 encoded)",
107
+ * "Executable content detected"
108
+ * ],
109
+ * "details": {
110
+ * "encodingThreats": ["Script tag injection (base64 encoded)"],
111
+ * "structureAnalysis": {
112
+ * "anomalies": ["Executable content detected"],
113
+ * "riskScore": 60,
114
+ * "characteristics": {
115
+ * "hasExecutableContent": true,
116
+ * "entropy": 6.8
117
+ * }
118
+ * },
119
+ * "clamavResult": {
120
+ * "scanned": true,
121
+ * "infected": true,
122
+ * "virusName": "Win.Trojan.Agent"
123
+ * }
124
+ * }
125
+ * }
126
+ * ```
127
+ */
128
+ let MaliciousFileDetector = MaliciousFileDetector_1 = class MaliciousFileDetector {
129
+ constructor(options) {
130
+ this.options = options;
131
+ this.logger = new common_1.Logger(MaliciousFileDetector_1.name);
132
+ // 支持的编码列表
133
+ this.encodings = ['utf8', 'utf16le', 'latin1', 'ascii', 'base64'];
134
+ // 恶意模式库(多编码)
135
+ this.maliciousPatterns = [
136
+ // 脚本注入模式
137
+ {
138
+ pattern: /<script[^>]*>.*?<\/script>/gi,
139
+ description: 'Script tag injection',
140
+ riskScore: 60,
141
+ },
142
+ {
143
+ pattern: /<iframe[^>]*>/gi,
144
+ description: 'Iframe injection',
145
+ riskScore: 50,
146
+ },
147
+ {
148
+ pattern: /on\w+\s*=\s*["'][^"']*["']/gi,
149
+ description: 'Event handler injection',
150
+ riskScore: 55,
151
+ },
152
+ {
153
+ pattern: /javascript:\s*[^\s]+/gi,
154
+ description: 'JavaScript protocol',
155
+ riskScore: 50,
156
+ },
157
+ {
158
+ pattern: /vbscript:\s*[^\s]+/gi,
159
+ description: 'VBScript protocol',
160
+ riskScore: 50,
161
+ },
162
+ // Shell命令模式
163
+ {
164
+ pattern: /\$\([^)]+\)/g,
165
+ description: 'Shell command substitution',
166
+ riskScore: 65,
167
+ },
168
+ {
169
+ pattern: /`[^`]+`/g,
170
+ description: 'Backtick command execution',
171
+ riskScore: 65,
172
+ },
173
+ {
174
+ pattern: /eval\s*\(/gi,
175
+ description: 'Eval function call',
176
+ riskScore: 70,
177
+ },
178
+ {
179
+ pattern: /exec\s*\(/gi,
180
+ description: 'Exec function call',
181
+ riskScore: 70,
182
+ },
183
+ // SQL注入模式
184
+ {
185
+ pattern: /union\s+select/gi,
186
+ description: 'SQL injection pattern',
187
+ riskScore: 45,
188
+ },
189
+ {
190
+ pattern: /';\s*drop\s+table/gi,
191
+ description: 'SQL drop table pattern',
192
+ riskScore: 80,
193
+ },
194
+ // 二进制注入标记
195
+ { pattern: /\x00/g, description: 'Null byte injection', riskScore: 40 },
196
+ ];
197
+ this.suspiciousSignatures = [
198
+ // 可执行文件
199
+ {
200
+ pattern: [0x4d, 0x5a],
201
+ description: 'PE executable header (Windows)',
202
+ riskScore: 70,
203
+ },
204
+ {
205
+ pattern: [0x7f, 0x45, 0x4c, 0x46],
206
+ description: 'ELF executable header (Linux)',
207
+ riskScore: 70,
208
+ },
209
+ {
210
+ pattern: [0xfe, 0xed, 0xfa, 0xce],
211
+ description: 'Mach-O executable header (macOS)',
212
+ riskScore: 70,
213
+ },
214
+ {
215
+ pattern: [0xfe, 0xed, 0xfa, 0xcf],
216
+ description: 'Mach-O 64-bit executable (macOS)',
217
+ riskScore: 70,
218
+ },
219
+ // 脚本内容
220
+ {
221
+ pattern: '<script',
222
+ type: 'string',
223
+ description: 'Script tag detected',
224
+ riskScore: 40,
225
+ },
226
+ {
227
+ pattern: 'javascript:',
228
+ type: 'string',
229
+ description: 'JavaScript protocol',
230
+ riskScore: 50,
231
+ },
232
+ {
233
+ pattern: 'vbscript:',
234
+ type: 'string',
235
+ description: 'VBScript protocol',
236
+ riskScore: 50,
237
+ },
238
+ {
239
+ pattern: 'data:text/html',
240
+ type: 'string',
241
+ description: 'Data URI with HTML',
242
+ riskScore: 45,
243
+ },
244
+ // 宏病毒相关
245
+ {
246
+ pattern: 'vbaProject',
247
+ type: 'string',
248
+ description: 'VBA Project marker',
249
+ riskScore: 60,
250
+ },
251
+ {
252
+ pattern: 'Auto_Open',
253
+ type: 'string',
254
+ description: 'Auto-execute macro',
255
+ riskScore: 40,
256
+ },
257
+ {
258
+ pattern: 'Workbook_Open',
259
+ type: 'string',
260
+ description: 'Auto-execute macro',
261
+ riskScore: 40,
262
+ },
263
+ // 压缩炸弹检测
264
+ {
265
+ pattern: [0x50, 0x4b, 0x03, 0x04],
266
+ description: 'ZIP header',
267
+ checkRatio: true,
268
+ riskScore: 0, // 需要进一步检查压缩比
269
+ },
270
+ ];
271
+ this.options = Object.assign({ enabled: true, enableClamAV: false, clamavMode: 'local', clamavCommand: 'clamdscan', enableMultiEncoding: true, enableStructureAnalysis: true, riskThreshold: 50 }, options);
272
+ this.logger.debug(`MaliciousFileDetector constructor - options: ${JSON.stringify(this.options)}`);
273
+ }
274
+ /**
275
+ * 扫描文件(增强版)
276
+ */
277
+ scanFile(filePath) {
278
+ return __awaiter(this, void 0, void 0, function* () {
279
+ // 检查是否启用
280
+ if (this.options.enabled === false) {
281
+ this.logger.debug('Malicious detection is disabled');
282
+ return {
283
+ safe: true,
284
+ threats: [],
285
+ riskScore: 0,
286
+ isMalware: false,
287
+ };
288
+ }
289
+ this.logger.debug(`MaliciousFileDetector: Starting scan for file: ${filePath}`);
290
+ this.logger.debug(`MaliciousFileDetector: Options: ${JSON.stringify(this.options)}`);
291
+ const threats = [];
292
+ let riskScore = 0;
293
+ const details = {};
294
+ try {
295
+ const stats = yield fs.stat(filePath);
296
+ this.logger.debug(`MaliciousFileDetector: File stats - size: ${stats.size}`);
297
+ // 读取文件:只读取前10MB以防止内存耗尽
298
+ const readSize = Math.min(stats.size, 10 * 1024 * 1024);
299
+ let buffer;
300
+ if (stats.size > 10 * 1024 * 1024) {
301
+ // 大文件:只读取头部
302
+ const fileHandle = yield fs.open(filePath, 'r');
303
+ buffer = Buffer.alloc(readSize);
304
+ try {
305
+ yield fileHandle.read(buffer, 0, readSize, 0);
306
+ }
307
+ finally {
308
+ yield fileHandle.close();
309
+ }
310
+ }
311
+ else {
312
+ // 小文件:直接读取全部
313
+ buffer = yield fs.readFile(filePath);
314
+ }
315
+ this.logger.debug(`MaliciousFileDetector: File read successfully, buffer size: ${buffer.length}`);
316
+ // 1. 检查可疑签名
317
+ for (const signature of this.suspiciousSignatures) {
318
+ if (this.checkSignature(buffer, signature)) {
319
+ threats.push(signature.description);
320
+ riskScore += signature.riskScore;
321
+ this.logger.debug(`MaliciousFileDetector: Found suspicious signature: ${signature.description}, risk: ${signature.riskScore}`);
322
+ // 如果需要检查压缩比
323
+ if (signature.checkRatio) {
324
+ const ratio = yield this.checkCompressionRatio(filePath, buffer);
325
+ if (ratio > 100) {
326
+ threats.push('Suspicious compression ratio (possible zip bomb)');
327
+ riskScore += 50;
328
+ this.logger.debug(`MaliciousFileDetector: Found suspicious compression ratio: ${ratio}`);
329
+ }
330
+ }
331
+ }
332
+ }
333
+ // 2. 多编码检测(新增)
334
+ if (this.options.enableMultiEncoding) {
335
+ const encodingResult = this.detectMultiEncodingThreats(buffer);
336
+ if (encodingResult.threats.length > 0) {
337
+ threats.push(...encodingResult.threats);
338
+ riskScore += encodingResult.riskScore;
339
+ details.encodingThreats = encodingResult.threats;
340
+ this.logger.debug(`MaliciousFileDetector: Multi-encoding threats found: ${encodingResult.threats.join(', ')}, risk: ${encodingResult.riskScore}`);
341
+ }
342
+ }
343
+ // 3. 文件结构分析(新增)
344
+ if (this.options.enableStructureAnalysis) {
345
+ const structureResult = yield this.analyzeFileStructure(filePath, buffer);
346
+ if (structureResult.anomalies.length > 0) {
347
+ threats.push(...structureResult.anomalies);
348
+ riskScore += structureResult.riskScore;
349
+ details.structureAnalysis = structureResult;
350
+ this.logger.debug(`MaliciousFileDetector: Structure analysis threats found: ${structureResult.anomalies.join(', ')}, risk: ${structureResult.riskScore}`);
351
+ }
352
+ }
353
+ // 4. ClamAV扫描(新增,可选)
354
+ if (this.options.enableClamAV) {
355
+ const clamavResult = yield this.scanWithClamAV(filePath);
356
+ details.clamavResult = clamavResult;
357
+ if (clamavResult.infected) {
358
+ threats.push(`Virus detected: ${clamavResult.virusName}`);
359
+ riskScore += 100; // 直接判定为恶意
360
+ this.logger.debug(`MaliciousFileDetector: ClamAV detected virus: ${clamavResult.virusName}`);
361
+ }
362
+ }
363
+ // 5. 检查文件名
364
+ const fileName = path.basename(filePath);
365
+ if (this.hasSuspiciousName(fileName)) {
366
+ threats.push('Suspicious filename pattern');
367
+ riskScore += 20;
368
+ this.logger.debug(`MaliciousFileDetector: Suspicious filename pattern detected: ${fileName}`);
369
+ }
370
+ // 6. 检查文件大小异常
371
+ if (stats.size > 500 * 1024 * 1024) {
372
+ // 大于500MB
373
+ threats.push('Unusually large file size');
374
+ riskScore += 15;
375
+ this.logger.debug(`MaliciousFileDetector: Unusually large file size: ${stats.size}`);
376
+ }
377
+ // 7. 检查双扩展名
378
+ if (this.hasDoubleExtension(fileName)) {
379
+ threats.push('Double extension detected');
380
+ riskScore += 30;
381
+ this.logger.debug(`MaliciousFileDetector: Double extension detected: ${fileName}`);
382
+ }
383
+ // 8. 检查隐藏扩展名技巧
384
+ if (this.hasHiddenExtension(fileName)) {
385
+ threats.push('Hidden extension trick detected');
386
+ riskScore += 40;
387
+ this.logger.debug(`MaliciousFileDetector: Hidden extension trick detected: ${fileName}`);
388
+ }
389
+ const threshold = this.options.riskThreshold || 50;
390
+ const isSafe = riskScore < threshold;
391
+ this.logger.debug(`MaliciousFileDetector: Final result - safe: ${isSafe}, riskScore: ${riskScore}, threshold: ${threshold}, threats: ${threats.join(', ')}`);
392
+ return {
393
+ safe: isSafe,
394
+ threats,
395
+ riskScore: Math.min(riskScore, 100),
396
+ isMalware: riskScore >= 70,
397
+ details: Object.keys(details).length > 0 ? details : undefined,
398
+ };
399
+ }
400
+ catch (error) {
401
+ this.logger.error(`Error scanning file: ${error.message}`);
402
+ return {
403
+ safe: false,
404
+ threats: ['Unable to scan file: ' + error.message],
405
+ riskScore: 100,
406
+ isMalware: true,
407
+ };
408
+ }
409
+ });
410
+ }
411
+ /**
412
+ * 检查签名
413
+ */
414
+ checkSignature(buffer, signature) {
415
+ if (signature.type === 'string') {
416
+ const content = buffer.toString('ascii').toLowerCase();
417
+ return content.includes(signature.pattern.toLowerCase());
418
+ }
419
+ else {
420
+ return this.bufferContains(buffer, signature.pattern);
421
+ }
422
+ }
423
+ /**
424
+ * 缓冲区包含检查
425
+ */
426
+ bufferContains(buffer, pattern) {
427
+ for (let i = 0; i <= buffer.length - pattern.length; i++) {
428
+ let match = true;
429
+ for (let j = 0; j < pattern.length; j++) {
430
+ if (buffer[i + j] !== pattern[j]) {
431
+ match = false;
432
+ break;
433
+ }
434
+ }
435
+ if (match)
436
+ return true;
437
+ }
438
+ return false;
439
+ }
440
+ /**
441
+ * 检查压缩比(简单实现)
442
+ */
443
+ checkCompressionRatio(filePath, buffer) {
444
+ return __awaiter(this, void 0, void 0, function* () {
445
+ // 简化版本:检查ZIP文件的压缩信息
446
+ // 完整实现需要解析ZIP结构
447
+ const compressedSize = (yield fs.stat(filePath)).size;
448
+ // 读取ZIP中央目录来估算未压缩大小
449
+ // 这里使用简单的启发式:如果文件很小但声称很大,可能是炸弹
450
+ if (compressedSize < 10 * 1024 && buffer.length < 10 * 1024) {
451
+ // 小于10KB的文件,假设正常
452
+ return 10;
453
+ }
454
+ // 保守估计:返回较低的比率避免误报
455
+ return 10;
456
+ });
457
+ }
458
+ /**
459
+ * 检查可疑文件名
460
+ */
461
+ hasSuspiciousName(fileName) {
462
+ const suspiciousPatterns = [
463
+ /\.exe$/i,
464
+ /\.scr$/i,
465
+ /\.bat$/i,
466
+ /\.cmd$/i,
467
+ /\.vbs$/i,
468
+ /\.js$/i,
469
+ /\.jar$/i,
470
+ /\.com$/i,
471
+ /\.pif$/i,
472
+ /\.application$/i,
473
+ /\.gadget$/i,
474
+ /\.msi$/i,
475
+ /\.msp$/i,
476
+ /\.cpl$/i,
477
+ /\.dll$/i,
478
+ /\.hta$/i,
479
+ /\.ws$/i,
480
+ /\.wsf$/i,
481
+ ];
482
+ return suspiciousPatterns.some((pattern) => pattern.test(fileName));
483
+ }
484
+ /**
485
+ * 检查双扩展名
486
+ */
487
+ hasDoubleExtension(fileName) {
488
+ // 例如: document.pdf.exe, photo.jpg.scr
489
+ const dangerousExtensions = [
490
+ '.exe',
491
+ '.scr',
492
+ '.bat',
493
+ '.cmd',
494
+ '.vbs',
495
+ '.com',
496
+ ];
497
+ const lowerName = fileName.toLowerCase();
498
+ const parts = lowerName.split('.');
499
+ if (parts.length < 3) {
500
+ return false;
501
+ }
502
+ // 检查倒数第二个扩展名是否是安全的,最后一个是危险的
503
+ const lastExt = '.' + parts[parts.length - 1];
504
+ const secondLastExt = '.' + parts[parts.length - 2];
505
+ const safeExtensions = [
506
+ '.jpg',
507
+ '.jpeg',
508
+ '.png',
509
+ '.gif',
510
+ '.pdf',
511
+ '.doc',
512
+ '.docx',
513
+ '.txt',
514
+ ];
515
+ return (safeExtensions.includes(secondLastExt) &&
516
+ dangerousExtensions.includes(lastExt));
517
+ }
518
+ /**
519
+ * 检查隐藏扩展名技巧
520
+ */
521
+ hasHiddenExtension(fileName) {
522
+ // 检查是否使用空格或特殊字符隐藏真实扩展名
523
+ // 例如: "document.pdf .exe"
524
+ // 检查文件名中是否有多个连续空格
525
+ if (/\s{5,}/.test(fileName)) {
526
+ return true;
527
+ }
528
+ // 检查是否使用了右到左覆盖字符 (RTLO attack)
529
+ // U+202E
530
+ if (fileName.includes('\u202E')) {
531
+ return true;
532
+ }
533
+ // 检查是否使用了零宽字符
534
+ if (/[\u200B-\u200D\uFEFF]/.test(fileName)) {
535
+ return true;
536
+ }
537
+ return false;
538
+ }
539
+ /**
540
+ * 多编码检测(新增)
541
+ * 通过多种编码方式解析文件内容,检测隐藏的恶意模式
542
+ */
543
+ detectMultiEncodingThreats(buffer) {
544
+ const threats = [];
545
+ let riskScore = 0;
546
+ // 遍历所有编码方式
547
+ for (const encoding of this.encodings) {
548
+ try {
549
+ let content;
550
+ // 特殊处理base64编码
551
+ if (encoding === 'base64') {
552
+ // 尝试将buffer解码为base64
553
+ const base64Pattern = /[A-Za-z0-9+/]{20,}={0,2}/g;
554
+ const asciiContent = buffer.toString('ascii');
555
+ const matches = asciiContent.match(base64Pattern);
556
+ if (matches) {
557
+ for (const match of matches) {
558
+ try {
559
+ const decoded = Buffer.from(match, 'base64').toString('utf8');
560
+ content = decoded;
561
+ // 检查解码后的内容
562
+ for (const pattern of this.maliciousPatterns) {
563
+ if (pattern.pattern.test(content)) {
564
+ threats.push(`${pattern.description} (base64 encoded)`);
565
+ riskScore += pattern.riskScore;
566
+ }
567
+ }
568
+ }
569
+ catch (_a) {
570
+ // 解码失败,跳过
571
+ }
572
+ }
573
+ }
574
+ continue;
575
+ }
576
+ // 其他编码方式
577
+ content = buffer.toString(encoding).toLowerCase();
578
+ // 检查所有恶意模式
579
+ for (const pattern of this.maliciousPatterns) {
580
+ if (pattern.pattern.test(content)) {
581
+ const threatDesc = encoding === 'utf8'
582
+ ? pattern.description
583
+ : `${pattern.description} (${encoding} encoding)`;
584
+ // 避免重复添加相同威胁
585
+ if (!threats.includes(threatDesc)) {
586
+ threats.push(threatDesc);
587
+ riskScore += pattern.riskScore;
588
+ }
589
+ }
590
+ }
591
+ }
592
+ catch (error) {
593
+ // 编码转换失败,跳过该编码
594
+ }
595
+ }
596
+ return { threats, riskScore };
597
+ }
598
+ /**
599
+ * 文件结构分析(新增)
600
+ * 分析文件的深层结构,检测异常特征
601
+ */
602
+ analyzeFileStructure(filePath, buffer) {
603
+ return __awaiter(this, void 0, void 0, function* () {
604
+ const anomalies = [];
605
+ let riskScore = 0;
606
+ const characteristics = {};
607
+ // 1. 检测嵌套压缩文件
608
+ const hasNestedArchive = this.detectNestedArchive(buffer);
609
+ if (hasNestedArchive) {
610
+ anomalies.push('Nested archive detected');
611
+ riskScore += 35;
612
+ characteristics.hasNestedArchive = true;
613
+ }
614
+ // 2. 检测可执行内容
615
+ const hasExecutable = this.detectExecutableContent(buffer);
616
+ if (hasExecutable) {
617
+ anomalies.push('Executable content detected');
618
+ riskScore += 60;
619
+ characteristics.hasExecutableContent = true;
620
+ }
621
+ // 3. 检测Office宏
622
+ const hasMacros = this.detectOfficeMacros(buffer);
623
+ if (hasMacros) {
624
+ anomalies.push('Office macros detected');
625
+ riskScore += 50;
626
+ characteristics.hasMacros = true;
627
+ }
628
+ // 4. 计算文件熵值(检测加密/混淆)
629
+ const entropy = this.calculateEntropy(buffer);
630
+ characteristics.entropy = entropy;
631
+ if (entropy > 7.5) {
632
+ // 高熵值可能表示加密或压缩
633
+ anomalies.push('High entropy detected (possible encryption/obfuscation)');
634
+ riskScore += 30;
635
+ }
636
+ // 5. 检测多态性(文件头与内容不匹配)
637
+ const hasPolymorphism = yield this.detectPolymorphism(buffer);
638
+ if (hasPolymorphism) {
639
+ anomalies.push('Polymorphic file structure detected');
640
+ riskScore += 45;
641
+ }
642
+ return {
643
+ anomalies,
644
+ riskScore,
645
+ characteristics,
646
+ };
647
+ });
648
+ }
649
+ /**
650
+ * ClamAV病毒扫描(增强版 - 支持本地/远程)
651
+ */
652
+ scanWithClamAV(filePath) {
653
+ return __awaiter(this, void 0, void 0, function* () {
654
+ if (this.options.clamavMode === 'remote') {
655
+ return this.scanWithRemoteClamAV(filePath);
656
+ }
657
+ else {
658
+ return this.scanWithLocalClamAV(filePath);
659
+ }
660
+ });
661
+ }
662
+ /**
663
+ * 本地ClamAV扫描
664
+ */
665
+ scanWithLocalClamAV(filePath) {
666
+ return __awaiter(this, void 0, void 0, function* () {
667
+ try {
668
+ // 验证文件路径安全性,防止命令注入
669
+ this.validateFilePath(filePath);
670
+ // 使用 spawn 和参数数组,避免命令注入
671
+ const { spawn } = yield Promise.resolve().then(() => require('child_process'));
672
+ return new Promise((resolve, reject) => {
673
+ const args = ['--no-summary', filePath];
674
+ const child = spawn(this.options.clamavCommand, args);
675
+ let stdout = '';
676
+ let stderr = '';
677
+ child.stdout.on('data', (data) => {
678
+ stdout += data.toString();
679
+ });
680
+ child.stderr.on('data', (data) => {
681
+ stderr += data.toString();
682
+ });
683
+ // 设置超时
684
+ const timeout = setTimeout(() => {
685
+ child.kill('SIGKILL');
686
+ reject(new Error('ClamAV scan timeout'));
687
+ }, 30000);
688
+ child.on('close', (code) => {
689
+ clearTimeout(timeout);
690
+ if (code !== 0 && code !== 1) {
691
+ reject(new Error(`ClamAV process exited with code ${code}`));
692
+ return;
693
+ }
694
+ // 解析ClamAV输出
695
+ const output = stdout + stderr;
696
+ if (output.includes('FOUND')) {
697
+ // 提取病毒名称
698
+ const match = output.match(/: ([^:]+) FOUND/);
699
+ resolve({
700
+ scanned: true,
701
+ infected: true,
702
+ virusName: match ? match[1] : 'Unknown',
703
+ });
704
+ }
705
+ else {
706
+ resolve({
707
+ scanned: true,
708
+ infected: false,
709
+ });
710
+ }
711
+ });
712
+ child.on('error', (error) => {
713
+ clearTimeout(timeout);
714
+ reject(error);
715
+ });
716
+ });
717
+ }
718
+ catch (error) {
719
+ // ClamAV不可用或扫描失败
720
+ this.logger.warn(`Local ClamAV scan failed: ${error.message}`);
721
+ return {
722
+ scanned: false,
723
+ infected: false,
724
+ error: error.message,
725
+ };
726
+ }
727
+ });
728
+ }
729
+ /**
730
+ * 验证文件路径,防止命令注入
731
+ */
732
+ validateFilePath(filePath) {
733
+ // 检查是否包含危险字符
734
+ const dangerousChars = [
735
+ '"',
736
+ "'",
737
+ '`',
738
+ ';',
739
+ '&',
740
+ '|',
741
+ '$',
742
+ '(',
743
+ ')',
744
+ '<',
745
+ '>',
746
+ '\n',
747
+ '\r',
748
+ ];
749
+ for (const char of dangerousChars) {
750
+ if (filePath.includes(char)) {
751
+ throw new Error(`Dangerous character detected in file path: ${char}`);
752
+ }
753
+ }
754
+ // 检查路径遍历
755
+ if (filePath.includes('..')) {
756
+ throw new Error('Path traversal not allowed in file path');
757
+ }
758
+ // 检查空字节注入
759
+ if (filePath.includes('\0')) {
760
+ throw new Error('Null byte detected in file path');
761
+ }
762
+ // 检查是否为绝对路径(根据业务需求决定是否允许)
763
+ if (path.isAbsolute(filePath)) {
764
+ // 可以根据需要抛出错误或允许
765
+ // throw new Error('Absolute paths not allowed');
766
+ }
767
+ }
768
+ /**
769
+ * 远程ClamAV扫描(通过ClamAV守护进程的网络接口)
770
+ */
771
+ scanWithRemoteClamAV(filePath) {
772
+ return __awaiter(this, void 0, void 0, function* () {
773
+ try {
774
+ const { host, port, timeout = 30000 } = this.options.clamavRemote || {};
775
+ if (!host || !port) {
776
+ throw new Error('Remote ClamAV host and port must be configured');
777
+ }
778
+ // 读取文件内容
779
+ const fileBuffer = yield fs.readFile(filePath);
780
+ // 使用Node.js net模块连接到ClamAV守护进程
781
+ const net = yield Promise.resolve().then(() => require('net'));
782
+ // 响应大小限制(1MB)
783
+ const MAX_RESPONSE_SIZE = 1024 * 1024;
784
+ return new Promise((resolve, reject) => {
785
+ const client = new net.Socket();
786
+ let response = '';
787
+ let responseSize = 0;
788
+ // 设置超时
789
+ const timeoutId = setTimeout(() => {
790
+ client.destroy();
791
+ reject(new Error('Remote ClamAV scan timeout'));
792
+ }, timeout);
793
+ client.connect(port, host, () => {
794
+ // 发送INSTREAM命令
795
+ client.write('zINSTREAM\0');
796
+ // 发送文件大小(4字节,网络字节序)
797
+ const sizeBuffer = Buffer.alloc(4);
798
+ sizeBuffer.writeUInt32BE(fileBuffer.length, 0);
799
+ client.write(sizeBuffer);
800
+ // 发送文件内容
801
+ client.write(fileBuffer);
802
+ // 发送结束标记(长度为0)
803
+ const endBuffer = Buffer.alloc(4);
804
+ endBuffer.writeUInt32BE(0, 0);
805
+ client.write(endBuffer);
806
+ });
807
+ client.on('data', (data) => {
808
+ responseSize += data.length;
809
+ // 防止响应过大
810
+ if (responseSize > MAX_RESPONSE_SIZE) {
811
+ client.destroy();
812
+ clearTimeout(timeoutId);
813
+ this.logger.warn('Remote ClamAV response size exceeded limit');
814
+ resolve({
815
+ scanned: false,
816
+ infected: false,
817
+ error: 'Response size exceeded limit',
818
+ });
819
+ return;
820
+ }
821
+ response += data.toString('utf8', 0, Math.min(data.length, MAX_RESPONSE_SIZE - response.length));
822
+ });
823
+ client.on('end', () => {
824
+ clearTimeout(timeoutId);
825
+ // 验证响应格式
826
+ if (!this.isValidClamAVResponse(response)) {
827
+ this.logger.warn('Invalid ClamAV response format');
828
+ resolve({
829
+ scanned: false,
830
+ infected: false,
831
+ error: 'Invalid response format',
832
+ });
833
+ return;
834
+ }
835
+ // 解析响应
836
+ if (response.includes('FOUND')) {
837
+ const match = response.match(/stream: ([^\s]+) FOUND/);
838
+ const virusName = match ? match[1] : 'Unknown';
839
+ // 验证病毒名称格式
840
+ if (!this.isValidVirusName(virusName)) {
841
+ this.logger.warn('Invalid virus name format');
842
+ resolve({
843
+ scanned: true,
844
+ infected: true,
845
+ virusName: 'Unknown',
846
+ });
847
+ return;
848
+ }
849
+ resolve({
850
+ scanned: true,
851
+ infected: true,
852
+ virusName,
853
+ });
854
+ }
855
+ else if (response.includes('OK')) {
856
+ resolve({
857
+ scanned: true,
858
+ infected: false,
859
+ });
860
+ }
861
+ else {
862
+ resolve({
863
+ scanned: false,
864
+ infected: false,
865
+ error: 'Unexpected response: ' + response.substring(0, 100),
866
+ });
867
+ }
868
+ });
869
+ client.on('error', (error) => {
870
+ clearTimeout(timeoutId);
871
+ this.logger.warn(`Remote ClamAV connection error: ${error.message}`);
872
+ resolve({
873
+ scanned: false,
874
+ infected: false,
875
+ error: error.message,
876
+ });
877
+ });
878
+ });
879
+ }
880
+ catch (error) {
881
+ this.logger.warn(`Remote ClamAV scan failed: ${error.message}`);
882
+ return {
883
+ scanned: false,
884
+ infected: false,
885
+ error: error.message,
886
+ };
887
+ }
888
+ });
889
+ }
890
+ /**
891
+ * 验证 ClamAV 响应格式
892
+ */
893
+ isValidClamAVResponse(response) {
894
+ if (!response || typeof response !== 'string') {
895
+ return false;
896
+ }
897
+ // ClamAV 响应应该包含 'stream:' 关键字
898
+ // 和 'FOUND' 或 'OK' 等状态
899
+ const validPatterns = [
900
+ /^stream: .+ FOUND$/m,
901
+ /^stream: OK$/m,
902
+ /^stream: .+ ERROR$/m,
903
+ ];
904
+ return validPatterns.some((pattern) => pattern.test(response));
905
+ }
906
+ /**
907
+ * 验证病毒名称格式
908
+ */
909
+ isValidVirusName(name) {
910
+ if (!name || typeof name !== 'string') {
911
+ return false;
912
+ }
913
+ // 病毒名称应该只包含字母、数字、连字符、下划线和点
914
+ // 长度不超过 100 字符
915
+ return /^[a-zA-Z0-9._-]{1,100}$/.test(name);
916
+ }
917
+ /**
918
+ * 检测嵌套压缩文件
919
+ */
920
+ detectNestedArchive(buffer) {
921
+ const archiveSignatures = [
922
+ { pattern: [0x50, 0x4b, 0x03, 0x04], name: 'ZIP' },
923
+ { pattern: [0x52, 0x61, 0x72, 0x21], name: 'RAR' },
924
+ { pattern: [0x1f, 0x8b], name: 'GZIP' },
925
+ { pattern: [0x42, 0x5a, 0x68], name: 'BZIP2' },
926
+ { pattern: [0x37, 0x7a, 0xbc, 0xaf], name: '7Z' },
927
+ ];
928
+ // 检测每个签名出现的次数
929
+ for (const sig of archiveSignatures) {
930
+ let occurrences = 0;
931
+ let offset = 0;
932
+ // 查找所有出现位置
933
+ while (offset < buffer.length) {
934
+ const index = this.bufferIndexOf(buffer, sig.pattern, offset);
935
+ if (index === -1)
936
+ break;
937
+ occurrences++;
938
+ offset = index + sig.pattern.length;
939
+ // 如果同一个签名出现多次,可能是嵌套
940
+ if (occurrences > 1) {
941
+ return true;
942
+ }
943
+ }
944
+ }
945
+ return false;
946
+ }
947
+ /**
948
+ * 检测可执行内容
949
+ */
950
+ detectExecutableContent(buffer) {
951
+ const executableSignatures = [
952
+ [0x4d, 0x5a], // PE (Windows)
953
+ [0x7f, 0x45, 0x4c, 0x46], // ELF (Linux)
954
+ [0xfe, 0xed, 0xfa, 0xce], // Mach-O (macOS)
955
+ [0xfe, 0xed, 0xfa, 0xcf], // Mach-O 64-bit
956
+ [0xca, 0xfe, 0xba, 0xbe], // Java class
957
+ ];
958
+ return executableSignatures.some((sig) => this.bufferContains(buffer, sig));
959
+ }
960
+ /**
961
+ * 检测Office宏
962
+ */
963
+ detectOfficeMacros(buffer) {
964
+ const macroIndicators = [
965
+ 'vbaProject.bin',
966
+ 'VBA',
967
+ 'AutoOpen',
968
+ 'Auto_Open',
969
+ 'Workbook_Open',
970
+ 'Document_Open',
971
+ 'AutoExec',
972
+ ];
973
+ const content = buffer.toString('ascii').toLowerCase();
974
+ return macroIndicators.some((indicator) => content.includes(indicator.toLowerCase()));
975
+ }
976
+ /**
977
+ * 计算Shannon熵值
978
+ * 用于检测加密或高度压缩的内容
979
+ */
980
+ calculateEntropy(buffer) {
981
+ const freq = new Map();
982
+ // 统计字节频率
983
+ for (let i = 0; i < buffer.length; i++) {
984
+ const byte = buffer[i];
985
+ freq.set(byte, (freq.get(byte) || 0) + 1);
986
+ }
987
+ // 计算熵值
988
+ let entropy = 0;
989
+ const len = buffer.length;
990
+ for (const count of freq.values()) {
991
+ const p = count / len;
992
+ entropy -= p * Math.log2(p);
993
+ }
994
+ return entropy;
995
+ }
996
+ /**
997
+ * 检测多态性(文件类型伪装)
998
+ */
999
+ detectPolymorphism(buffer) {
1000
+ return __awaiter(this, void 0, void 0, function* () {
1001
+ // 简化实现:检查文件头是否与内容一致
1002
+ // 例如:声称是图片但包含可执行代码
1003
+ const isImage = this.bufferContains(buffer, [0xff, 0xd8, 0xff]) || // JPEG
1004
+ this.bufferContains(buffer, [0x89, 0x50, 0x4e, 0x47]); // PNG
1005
+ if (isImage && this.detectExecutableContent(buffer)) {
1006
+ return true; // 图片中嵌入了可执行代码
1007
+ }
1008
+ return false;
1009
+ });
1010
+ }
1011
+ /**
1012
+ * 在buffer中查找pattern的位置
1013
+ */
1014
+ bufferIndexOf(buffer, pattern, startOffset = 0) {
1015
+ for (let i = startOffset; i <= buffer.length - pattern.length; i++) {
1016
+ let match = true;
1017
+ for (let j = 0; j < pattern.length; j++) {
1018
+ if (buffer[i + j] !== pattern[j]) {
1019
+ match = false;
1020
+ break;
1021
+ }
1022
+ }
1023
+ if (match)
1024
+ return i;
1025
+ }
1026
+ return -1;
1027
+ }
1028
+ };
1029
+ exports.MaliciousFileDetector = MaliciousFileDetector;
1030
+ exports.MaliciousFileDetector = MaliciousFileDetector = MaliciousFileDetector_1 = __decorate([
1031
+ (0, common_1.Injectable)(),
1032
+ __param(0, (0, common_1.Optional)()),
1033
+ __param(0, (0, common_1.Inject)('MALICIOUS_DETECTOR_OPTIONS')),
1034
+ __metadata("design:paramtypes", [Object])
1035
+ ], MaliciousFileDetector);