waengine 1.7.3 → 1.7.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.
- package/CHANGELOG.md +29 -0
- package/README.md +34 -3
- package/package.json +3 -2
- package/src/ab-testing.js +698 -0
- package/src/advanced-scheduler.js +577 -0
- package/src/ai-features.js +459 -0
- package/src/ai-integration.js +2 -1
- package/src/analytics-manager.js +458 -0
- package/src/business-manager.js +362 -0
- package/src/client.js +447 -39
- package/src/console-logger.js +256 -0
- package/src/core.js +28 -3
- package/src/cross-platform.js +538 -0
- package/src/database-manager.js +766 -0
- package/src/device-manager.js +1 -1
- package/src/easy-bot-fixed.js +341 -0
- package/src/easy-bot.js +503 -22
- package/src/error-handler.js +230 -0
- package/src/gaming-manager.js +842 -0
- package/src/http-client.js +1 -1
- package/src/index.js +15 -0
- package/src/message.js +197 -94
- package/src/multi-client.js +26 -12
- package/src/plugin-manager.js +59 -10
- package/src/prefix-manager.js +48 -1
- package/src/qr-terminal-fix.js +239 -0
- package/src/qr.js +170 -27
- package/src/quick-bot.js +63 -0
- package/src/reporting-manager.js +867 -0
- package/src/scheduler.js +14 -1
- package/src/security-manager.js +678 -0
- package/src/session-manager-old.js +314 -0
- package/src/session-manager.js +429 -24
- package/src/storage.js +254 -194
- package/src/ui-components.js +560 -0
package/src/scheduler.js
CHANGED
|
@@ -10,12 +10,25 @@ export class Scheduler {
|
|
|
10
10
|
// Gespeicherte Jobs beim Start laden
|
|
11
11
|
this.loadScheduledJobs();
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Stille Initialisierung - keine Console-Spam
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// ===== CRON SCHEDULING =====
|
|
17
17
|
|
|
18
18
|
schedule(cronExpression, chatId, message, options = {}) {
|
|
19
|
+
// Input-Validierung
|
|
20
|
+
if (!cronExpression || typeof cronExpression !== 'string') {
|
|
21
|
+
throw new Error('❌ Cron-Ausdruck ist erforderlich');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!chatId || typeof chatId !== 'string') {
|
|
25
|
+
throw new Error('❌ Chat-ID ist erforderlich');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!message || typeof message !== 'string') {
|
|
29
|
+
throw new Error('❌ Nachricht ist erforderlich');
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
const jobId = options.id || `job_${Date.now()}`;
|
|
20
33
|
|
|
21
34
|
if (!cron.validate(cronExpression)) {
|
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { getStorage } from "./storage.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
|
|
4
|
+
export class SecurityManager {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.storage = getStorage();
|
|
8
|
+
this.rateLimits = new Map();
|
|
9
|
+
this.blockedUsers = new Set();
|
|
10
|
+
this.suspiciousActivity = new Map();
|
|
11
|
+
this.encryptionKeys = new Map();
|
|
12
|
+
this.auditLog = [];
|
|
13
|
+
|
|
14
|
+
this.initializeSecurity();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ===== INITIALIZATION =====
|
|
18
|
+
|
|
19
|
+
initializeSecurity() {
|
|
20
|
+
this.loadSecurityConfig();
|
|
21
|
+
this.startSecurityMonitoring();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
loadSecurityConfig() {
|
|
25
|
+
const config = this.storage.read.from("security").get("config") || {};
|
|
26
|
+
|
|
27
|
+
this.config = {
|
|
28
|
+
rateLimitEnabled: config.rateLimitEnabled !== false,
|
|
29
|
+
maxMessagesPerMinute: config.maxMessagesPerMinute || 10,
|
|
30
|
+
maxMessagesPerHour: config.maxMessagesPerHour || 100,
|
|
31
|
+
spamDetectionEnabled: config.spamDetectionEnabled !== false,
|
|
32
|
+
encryptionEnabled: config.encryptionEnabled || false,
|
|
33
|
+
auditLogEnabled: config.auditLogEnabled !== false,
|
|
34
|
+
suspiciousActivityThreshold: config.suspiciousActivityThreshold || 5,
|
|
35
|
+
autoBlockEnabled: config.autoBlockEnabled || false,
|
|
36
|
+
...config
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Load blocked users
|
|
40
|
+
const blocked = this.storage.read.from("security").get("blockedUsers") || [];
|
|
41
|
+
blocked.forEach(userId => this.blockedUsers.add(userId));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
startSecurityMonitoring() {
|
|
45
|
+
// Clean up old rate limit data every minute
|
|
46
|
+
setInterval(() => {
|
|
47
|
+
this.cleanupRateLimits();
|
|
48
|
+
}, 60000);
|
|
49
|
+
|
|
50
|
+
// Clean up suspicious activity data every hour
|
|
51
|
+
setInterval(() => {
|
|
52
|
+
this.cleanupSuspiciousActivity();
|
|
53
|
+
}, 3600000);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ===== RATE LIMITING =====
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if user is rate limited
|
|
60
|
+
*/
|
|
61
|
+
checkRateLimit(userId) {
|
|
62
|
+
if (!this.config.rateLimitEnabled) return { allowed: true };
|
|
63
|
+
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const userLimits = this.rateLimits.get(userId) || { messages: [], lastReset: now };
|
|
66
|
+
|
|
67
|
+
// Clean old messages (older than 1 hour)
|
|
68
|
+
userLimits.messages = userLimits.messages.filter(timestamp =>
|
|
69
|
+
now - timestamp < 3600000
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Check per-minute limit
|
|
73
|
+
const recentMessages = userLimits.messages.filter(timestamp =>
|
|
74
|
+
now - timestamp < 60000
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (recentMessages.length >= this.config.maxMessagesPerMinute) {
|
|
78
|
+
this.logSecurityEvent('rate_limit_exceeded', userId, {
|
|
79
|
+
type: 'per_minute',
|
|
80
|
+
count: recentMessages.length,
|
|
81
|
+
limit: this.config.maxMessagesPerMinute
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
allowed: false,
|
|
86
|
+
reason: 'rate_limit_per_minute',
|
|
87
|
+
resetIn: 60000 - (now - Math.min(...recentMessages))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check per-hour limit
|
|
92
|
+
if (userLimits.messages.length >= this.config.maxMessagesPerHour) {
|
|
93
|
+
this.logSecurityEvent('rate_limit_exceeded', userId, {
|
|
94
|
+
type: 'per_hour',
|
|
95
|
+
count: userLimits.messages.length,
|
|
96
|
+
limit: this.config.maxMessagesPerHour
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
allowed: false,
|
|
101
|
+
reason: 'rate_limit_per_hour',
|
|
102
|
+
resetIn: 3600000 - (now - Math.min(...userLimits.messages))
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Add current message
|
|
107
|
+
userLimits.messages.push(now);
|
|
108
|
+
this.rateLimits.set(userId, userLimits);
|
|
109
|
+
|
|
110
|
+
return { allowed: true };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Set custom rate limit for user
|
|
115
|
+
*/
|
|
116
|
+
setUserRateLimit(userId, messagesPerMinute, messagesPerHour) {
|
|
117
|
+
this.storage.write.in("security").set(`customRateLimits.${userId}`, {
|
|
118
|
+
messagesPerMinute,
|
|
119
|
+
messagesPerHour,
|
|
120
|
+
setAt: Date.now()
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Remove rate limit for user
|
|
126
|
+
*/
|
|
127
|
+
removeUserRateLimit(userId) {
|
|
128
|
+
this.rateLimits.delete(userId);
|
|
129
|
+
this.storage.delete.from("security").key(`customRateLimits.${userId}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ===== SPAM DETECTION =====
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Analyze message for spam
|
|
136
|
+
*/
|
|
137
|
+
analyzeSpam(message) {
|
|
138
|
+
if (!this.config.spamDetectionEnabled) return { isSpam: false };
|
|
139
|
+
|
|
140
|
+
const text = message.text || '';
|
|
141
|
+
let spamScore = 0;
|
|
142
|
+
const reasons = [];
|
|
143
|
+
|
|
144
|
+
// Check for excessive caps
|
|
145
|
+
const capsRatio = (text.match(/[A-Z]/g) || []).length / text.length;
|
|
146
|
+
if (capsRatio > 0.7 && text.length > 10) {
|
|
147
|
+
spamScore += 2;
|
|
148
|
+
reasons.push('excessive_caps');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check for excessive punctuation
|
|
152
|
+
const punctuationRatio = (text.match(/[!?.,;:]/g) || []).length / text.length;
|
|
153
|
+
if (punctuationRatio > 0.3) {
|
|
154
|
+
spamScore += 1;
|
|
155
|
+
reasons.push('excessive_punctuation');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check for repeated characters
|
|
159
|
+
if (/(.)\1{4,}/.test(text)) {
|
|
160
|
+
spamScore += 2;
|
|
161
|
+
reasons.push('repeated_characters');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check for common spam words
|
|
165
|
+
const spamWords = [
|
|
166
|
+
'gewinn', 'gratis', 'kostenlos', 'sofort', 'jetzt', 'schnell',
|
|
167
|
+
'geld', 'verdienen', 'reich', 'millionär', 'bitcoin', 'crypto',
|
|
168
|
+
'klick', 'link', 'website', 'angebot', 'rabatt', 'prozent'
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const foundSpamWords = spamWords.filter(word =>
|
|
172
|
+
text.toLowerCase().includes(word)
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (foundSpamWords.length > 2) {
|
|
176
|
+
spamScore += foundSpamWords.length;
|
|
177
|
+
reasons.push('spam_keywords');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for URLs
|
|
181
|
+
const urlCount = (text.match(/https?:\/\/[^\s]+/g) || []).length;
|
|
182
|
+
if (urlCount > 1) {
|
|
183
|
+
spamScore += urlCount;
|
|
184
|
+
reasons.push('multiple_urls');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check message frequency
|
|
188
|
+
const recentMessages = this.rateLimits.get(message.from)?.messages || [];
|
|
189
|
+
const recentCount = recentMessages.filter(timestamp =>
|
|
190
|
+
Date.now() - timestamp < 300000 // 5 minutes
|
|
191
|
+
).length;
|
|
192
|
+
|
|
193
|
+
if (recentCount > 5) {
|
|
194
|
+
spamScore += Math.floor(recentCount / 5);
|
|
195
|
+
reasons.push('high_frequency');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const isSpam = spamScore >= 5;
|
|
199
|
+
|
|
200
|
+
if (isSpam) {
|
|
201
|
+
this.logSecurityEvent('spam_detected', message.from, {
|
|
202
|
+
score: spamScore,
|
|
203
|
+
reasons,
|
|
204
|
+
text: text.substring(0, 100)
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
isSpam,
|
|
210
|
+
score: spamScore,
|
|
211
|
+
reasons,
|
|
212
|
+
confidence: Math.min(spamScore / 10, 1)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ===== USER BLOCKING =====
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Block user
|
|
220
|
+
*/
|
|
221
|
+
blockUser(userId, reason = 'manual', duration = null) {
|
|
222
|
+
this.blockedUsers.add(userId);
|
|
223
|
+
|
|
224
|
+
const blockData = {
|
|
225
|
+
userId,
|
|
226
|
+
reason,
|
|
227
|
+
blockedAt: Date.now(),
|
|
228
|
+
duration,
|
|
229
|
+
expiresAt: duration ? Date.now() + duration : null
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
this.storage.write.in("security").set(`blocks.${userId}`, blockData);
|
|
233
|
+
this.storage.write.in("security").push("blockedUsers", userId);
|
|
234
|
+
|
|
235
|
+
this.logSecurityEvent('user_blocked', userId, { reason, duration });
|
|
236
|
+
|
|
237
|
+
return blockData;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Unblock user
|
|
242
|
+
*/
|
|
243
|
+
unblockUser(userId) {
|
|
244
|
+
this.blockedUsers.delete(userId);
|
|
245
|
+
this.storage.delete.from("security").key(`blocks.${userId}`);
|
|
246
|
+
|
|
247
|
+
// Remove from blocked users list
|
|
248
|
+
const blockedList = this.storage.read.from("security").get("blockedUsers") || [];
|
|
249
|
+
const updatedList = blockedList.filter(id => id !== userId);
|
|
250
|
+
this.storage.write.in("security").set("blockedUsers", updatedList);
|
|
251
|
+
|
|
252
|
+
this.logSecurityEvent('user_unblocked', userId);
|
|
253
|
+
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Check if user is blocked
|
|
259
|
+
*/
|
|
260
|
+
isUserBlocked(userId) {
|
|
261
|
+
if (!this.blockedUsers.has(userId)) return false;
|
|
262
|
+
|
|
263
|
+
// Check if temporary block has expired
|
|
264
|
+
const blockData = this.storage.read.from("security").get(`blocks.${userId}`);
|
|
265
|
+
if (blockData && blockData.expiresAt && Date.now() > blockData.expiresAt) {
|
|
266
|
+
this.unblockUser(userId);
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Auto-block user based on suspicious activity
|
|
275
|
+
*/
|
|
276
|
+
checkAutoBlock(userId) {
|
|
277
|
+
if (!this.config.autoBlockEnabled) return false;
|
|
278
|
+
|
|
279
|
+
const activity = this.suspiciousActivity.get(userId) || { count: 0, events: [] };
|
|
280
|
+
|
|
281
|
+
if (activity.count >= this.config.suspiciousActivityThreshold) {
|
|
282
|
+
this.blockUser(userId, 'auto_block_suspicious_activity', 24 * 60 * 60 * 1000); // 24 hours
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ===== SUSPICIOUS ACTIVITY TRACKING =====
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Track suspicious activity
|
|
293
|
+
*/
|
|
294
|
+
trackSuspiciousActivity(userId, type, data = {}) {
|
|
295
|
+
const activity = this.suspiciousActivity.get(userId) || { count: 0, events: [] };
|
|
296
|
+
|
|
297
|
+
activity.count++;
|
|
298
|
+
activity.events.push({
|
|
299
|
+
type,
|
|
300
|
+
data,
|
|
301
|
+
timestamp: Date.now()
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Keep only last 50 events
|
|
305
|
+
if (activity.events.length > 50) {
|
|
306
|
+
activity.events.shift();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.suspiciousActivity.set(userId, activity);
|
|
310
|
+
|
|
311
|
+
this.logSecurityEvent('suspicious_activity', userId, { type, data });
|
|
312
|
+
|
|
313
|
+
// Check for auto-block
|
|
314
|
+
this.checkAutoBlock(userId);
|
|
315
|
+
|
|
316
|
+
return activity;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get user's suspicious activity
|
|
321
|
+
*/
|
|
322
|
+
getUserSuspiciousActivity(userId) {
|
|
323
|
+
return this.suspiciousActivity.get(userId) || { count: 0, events: [] };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ===== MESSAGE ENCRYPTION =====
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Encrypt message with secure AES-256-GCM
|
|
330
|
+
*/
|
|
331
|
+
encryptMessage(message, userId) {
|
|
332
|
+
if (!this.config.encryptionEnabled) return message;
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const key = this.getOrCreateEncryptionKey(userId);
|
|
336
|
+
const keyBuffer = Buffer.from(key, 'hex');
|
|
337
|
+
|
|
338
|
+
// Generate random IV (12 bytes for GCM)
|
|
339
|
+
const iv = crypto.randomBytes(12);
|
|
340
|
+
|
|
341
|
+
// Create cipher with GCM mode for authenticated encryption
|
|
342
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', keyBuffer, iv);
|
|
343
|
+
|
|
344
|
+
let encrypted = cipher.update(message, 'utf8', 'hex');
|
|
345
|
+
encrypted += cipher.final('hex');
|
|
346
|
+
|
|
347
|
+
// Get authentication tag
|
|
348
|
+
const authTag = cipher.getAuthTag();
|
|
349
|
+
|
|
350
|
+
// Combine IV + authTag + encrypted data
|
|
351
|
+
const result = iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
|
|
352
|
+
|
|
353
|
+
return result;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error('❌ Encryption error:', error.message);
|
|
356
|
+
return message; // Fallback to unencrypted
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Decrypt message with secure AES-256-GCM
|
|
362
|
+
*/
|
|
363
|
+
decryptMessage(encryptedMessage, userId) {
|
|
364
|
+
if (!this.config.encryptionEnabled) return encryptedMessage;
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
const key = this.getOrCreateEncryptionKey(userId);
|
|
368
|
+
const keyBuffer = Buffer.from(key, 'hex');
|
|
369
|
+
|
|
370
|
+
// Split IV:authTag:encrypted
|
|
371
|
+
const parts = encryptedMessage.split(':');
|
|
372
|
+
if (parts.length !== 3) {
|
|
373
|
+
throw new Error('Invalid encrypted message format');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
377
|
+
const authTag = Buffer.from(parts[1], 'hex');
|
|
378
|
+
const encrypted = parts[2];
|
|
379
|
+
|
|
380
|
+
// Create decipher
|
|
381
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', keyBuffer, iv);
|
|
382
|
+
decipher.setAuthTag(authTag);
|
|
383
|
+
|
|
384
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
385
|
+
decrypted += decipher.final('utf8');
|
|
386
|
+
|
|
387
|
+
return decrypted;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error('❌ Decryption error:', error.message);
|
|
390
|
+
return encryptedMessage; // Fallback to encrypted
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get or create encryption key for user
|
|
396
|
+
*/
|
|
397
|
+
getOrCreateEncryptionKey(userId) {
|
|
398
|
+
if (this.encryptionKeys.has(userId)) {
|
|
399
|
+
return this.encryptionKeys.get(userId);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const key = crypto.randomBytes(32).toString('hex');
|
|
403
|
+
this.encryptionKeys.set(userId, key);
|
|
404
|
+
|
|
405
|
+
// Store encrypted key
|
|
406
|
+
const masterKey = this.getMasterKey();
|
|
407
|
+
const encryptedKey = this.encryptWithMasterKey(key, masterKey);
|
|
408
|
+
this.storage.write.in("security").set(`encryptionKeys.${userId}`, encryptedKey);
|
|
409
|
+
|
|
410
|
+
return key;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get master encryption key
|
|
415
|
+
*/
|
|
416
|
+
getMasterKey() {
|
|
417
|
+
let masterKey = this.storage.read.from("security").get("masterKey");
|
|
418
|
+
|
|
419
|
+
if (!masterKey) {
|
|
420
|
+
masterKey = crypto.randomBytes(32).toString('hex');
|
|
421
|
+
this.storage.write.in("security").set("masterKey", masterKey);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return masterKey;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Encrypt with master key using secure AES-256-GCM
|
|
429
|
+
*/
|
|
430
|
+
encryptWithMasterKey(data, masterKey) {
|
|
431
|
+
try {
|
|
432
|
+
const keyBuffer = Buffer.from(masterKey, 'hex');
|
|
433
|
+
const iv = crypto.randomBytes(12); // 12 bytes for GCM
|
|
434
|
+
|
|
435
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', keyBuffer, iv);
|
|
436
|
+
|
|
437
|
+
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
438
|
+
encrypted += cipher.final('hex');
|
|
439
|
+
|
|
440
|
+
const authTag = cipher.getAuthTag();
|
|
441
|
+
|
|
442
|
+
// Return IV:authTag:encrypted
|
|
443
|
+
return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('❌ Master key encryption error:', error.message);
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Decrypt with master key using secure AES-256-GCM
|
|
452
|
+
*/
|
|
453
|
+
decryptWithMasterKey(encryptedData, masterKey) {
|
|
454
|
+
try {
|
|
455
|
+
const keyBuffer = Buffer.from(masterKey, 'hex');
|
|
456
|
+
|
|
457
|
+
const parts = encryptedData.split(':');
|
|
458
|
+
if (parts.length !== 3) {
|
|
459
|
+
throw new Error('Invalid encrypted data format');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
463
|
+
const authTag = Buffer.from(parts[1], 'hex');
|
|
464
|
+
const encrypted = parts[2];
|
|
465
|
+
|
|
466
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', keyBuffer, iv);
|
|
467
|
+
decipher.setAuthTag(authTag);
|
|
468
|
+
|
|
469
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
470
|
+
decrypted += decipher.final('utf8');
|
|
471
|
+
|
|
472
|
+
return decrypted;
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error('❌ Master key decryption error:', error.message);
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ===== AUDIT LOGGING =====
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Log security event
|
|
483
|
+
*/
|
|
484
|
+
logSecurityEvent(type, userId, data = {}) {
|
|
485
|
+
if (!this.config.auditLogEnabled) return;
|
|
486
|
+
|
|
487
|
+
const event = {
|
|
488
|
+
id: crypto.randomUUID(),
|
|
489
|
+
type,
|
|
490
|
+
userId,
|
|
491
|
+
data,
|
|
492
|
+
timestamp: Date.now(),
|
|
493
|
+
ip: data.ip || 'unknown'
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
this.auditLog.push(event);
|
|
497
|
+
|
|
498
|
+
// Keep only last 1000 events in memory
|
|
499
|
+
if (this.auditLog.length > 1000) {
|
|
500
|
+
this.auditLog.shift();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Store in persistent storage
|
|
504
|
+
this.storage.write.in("security").push("auditLog", event);
|
|
505
|
+
|
|
506
|
+
// Emit security event
|
|
507
|
+
this.client.emit('security_event', event);
|
|
508
|
+
|
|
509
|
+
return event;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get audit log
|
|
514
|
+
*/
|
|
515
|
+
getAuditLog(limit = 100, type = null) {
|
|
516
|
+
let logs = this.storage.read.from("security").get("auditLog") || [];
|
|
517
|
+
|
|
518
|
+
if (type) {
|
|
519
|
+
logs = logs.filter(log => log.type === type);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return logs.slice(-limit);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Get security events for user
|
|
527
|
+
*/
|
|
528
|
+
getUserSecurityEvents(userId, limit = 50) {
|
|
529
|
+
const logs = this.storage.read.from("security").get("auditLog") || [];
|
|
530
|
+
return logs
|
|
531
|
+
.filter(log => log.userId === userId)
|
|
532
|
+
.slice(-limit);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ===== SECURITY ANALYSIS =====
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Analyze message security
|
|
539
|
+
*/
|
|
540
|
+
analyzeMessageSecurity(message) {
|
|
541
|
+
const analysis = {
|
|
542
|
+
userId: message.from,
|
|
543
|
+
timestamp: Date.now(),
|
|
544
|
+
checks: {}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// Rate limit check
|
|
548
|
+
analysis.checks.rateLimit = this.checkRateLimit(message.from);
|
|
549
|
+
|
|
550
|
+
// Spam check
|
|
551
|
+
analysis.checks.spam = this.analyzeSpam(message);
|
|
552
|
+
|
|
553
|
+
// Block check
|
|
554
|
+
analysis.checks.blocked = this.isUserBlocked(message.from);
|
|
555
|
+
|
|
556
|
+
// Suspicious activity check
|
|
557
|
+
analysis.checks.suspiciousActivity = this.getUserSuspiciousActivity(message.from);
|
|
558
|
+
|
|
559
|
+
// Overall risk score
|
|
560
|
+
let riskScore = 0;
|
|
561
|
+
|
|
562
|
+
if (!analysis.checks.rateLimit.allowed) riskScore += 3;
|
|
563
|
+
if (analysis.checks.spam.isSpam) riskScore += analysis.checks.spam.score;
|
|
564
|
+
if (analysis.checks.blocked) riskScore += 10;
|
|
565
|
+
if (analysis.checks.suspiciousActivity.count > 0) riskScore += analysis.checks.suspiciousActivity.count;
|
|
566
|
+
|
|
567
|
+
analysis.riskScore = riskScore;
|
|
568
|
+
analysis.riskLevel = this.getRiskLevel(riskScore);
|
|
569
|
+
analysis.shouldBlock = riskScore >= 10;
|
|
570
|
+
|
|
571
|
+
return analysis;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Get risk level from score
|
|
576
|
+
*/
|
|
577
|
+
getRiskLevel(score) {
|
|
578
|
+
if (score >= 10) return 'high';
|
|
579
|
+
if (score >= 5) return 'medium';
|
|
580
|
+
if (score >= 2) return 'low';
|
|
581
|
+
return 'minimal';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ===== CLEANUP METHODS =====
|
|
585
|
+
|
|
586
|
+
cleanupRateLimits() {
|
|
587
|
+
const now = Date.now();
|
|
588
|
+
|
|
589
|
+
for (const [userId, limits] of this.rateLimits.entries()) {
|
|
590
|
+
limits.messages = limits.messages.filter(timestamp =>
|
|
591
|
+
now - timestamp < 3600000 // Keep last hour
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
if (limits.messages.length === 0) {
|
|
595
|
+
this.rateLimits.delete(userId);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
cleanupSuspiciousActivity() {
|
|
601
|
+
const now = Date.now();
|
|
602
|
+
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
|
|
603
|
+
|
|
604
|
+
for (const [userId, activity] of this.suspiciousActivity.entries()) {
|
|
605
|
+
activity.events = activity.events.filter(event =>
|
|
606
|
+
now - event.timestamp < maxAge
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
activity.count = activity.events.length;
|
|
610
|
+
|
|
611
|
+
if (activity.count === 0) {
|
|
612
|
+
this.suspiciousActivity.delete(userId);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// ===== STATISTICS =====
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Get security statistics
|
|
621
|
+
*/
|
|
622
|
+
getSecurityStats() {
|
|
623
|
+
const auditLog = this.storage.read.from("security").get("auditLog") || [];
|
|
624
|
+
const blockedUsers = this.storage.read.from("security").get("blockedUsers") || [];
|
|
625
|
+
|
|
626
|
+
const last24h = Date.now() - (24 * 60 * 60 * 1000);
|
|
627
|
+
const recentEvents = auditLog.filter(event => event.timestamp > last24h);
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
blockedUsers: {
|
|
631
|
+
total: blockedUsers.length,
|
|
632
|
+
active: Array.from(this.blockedUsers).length
|
|
633
|
+
},
|
|
634
|
+
rateLimits: {
|
|
635
|
+
activeUsers: this.rateLimits.size,
|
|
636
|
+
totalChecks: auditLog.filter(e => e.type === 'rate_limit_exceeded').length
|
|
637
|
+
},
|
|
638
|
+
spam: {
|
|
639
|
+
detected: auditLog.filter(e => e.type === 'spam_detected').length,
|
|
640
|
+
last24h: recentEvents.filter(e => e.type === 'spam_detected').length
|
|
641
|
+
},
|
|
642
|
+
suspiciousActivity: {
|
|
643
|
+
activeUsers: this.suspiciousActivity.size,
|
|
644
|
+
totalEvents: auditLog.filter(e => e.type === 'suspicious_activity').length
|
|
645
|
+
},
|
|
646
|
+
auditLog: {
|
|
647
|
+
totalEvents: auditLog.length,
|
|
648
|
+
last24h: recentEvents.length,
|
|
649
|
+
eventTypes: this.getEventTypeDistribution(recentEvents)
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Get event type distribution
|
|
656
|
+
*/
|
|
657
|
+
getEventTypeDistribution(events) {
|
|
658
|
+
const distribution = {};
|
|
659
|
+
|
|
660
|
+
events.forEach(event => {
|
|
661
|
+
distribution[event.type] = (distribution[event.type] || 0) + 1;
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
return distribution;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Export security data
|
|
669
|
+
*/
|
|
670
|
+
exportSecurityData() {
|
|
671
|
+
return {
|
|
672
|
+
config: this.config,
|
|
673
|
+
blockedUsers: Array.from(this.blockedUsers),
|
|
674
|
+
auditLog: this.getAuditLog(1000),
|
|
675
|
+
stats: this.getSecurityStats()
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|