@serve.zone/dcrouter 11.23.5 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.cert-provision-scheduler.d.ts +6 -8
- package/dist_ts/classes.cert-provision-scheduler.js +37 -17
- package/dist_ts/classes.dcrouter.d.ts +15 -29
- package/dist_ts/classes.dcrouter.js +96 -91
- package/dist_ts/classes.storage-cert-manager.d.ts +3 -6
- package/dist_ts/classes.storage-cert-manager.js +35 -25
- package/dist_ts/config/classes.api-token-manager.d.ts +1 -3
- package/dist_ts/config/classes.api-token-manager.js +45 -15
- package/dist_ts/config/classes.route-config-manager.d.ts +1 -3
- package/dist_ts/config/classes.route-config-manager.js +62 -24
- package/dist_ts/{cache → db}/classes.cache.cleaner.d.ts +3 -3
- package/dist_ts/db/classes.cache.cleaner.js +130 -0
- package/dist_ts/{cache → db}/classes.cached.document.js +1 -1
- package/dist_ts/db/classes.dcrouter-db.d.ts +70 -0
- package/dist_ts/db/classes.dcrouter-db.js +146 -0
- package/dist_ts/db/documents/classes.accounting-session.doc.d.ts +32 -0
- package/dist_ts/db/documents/classes.accounting-session.doc.js +214 -0
- package/dist_ts/db/documents/classes.acme-cert.doc.d.ts +13 -0
- package/dist_ts/db/documents/classes.acme-cert.doc.js +109 -0
- package/dist_ts/db/documents/classes.api-token.doc.d.ts +18 -0
- package/dist_ts/db/documents/classes.api-token.doc.js +127 -0
- package/dist_ts/{cache → db}/documents/classes.cached.email.js +3 -3
- package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.js +3 -3
- package/dist_ts/db/documents/classes.cert-backoff.doc.d.ts +11 -0
- package/dist_ts/db/documents/classes.cert-backoff.doc.js +97 -0
- package/dist_ts/db/documents/classes.proxy-cert.doc.d.ts +12 -0
- package/dist_ts/db/documents/classes.proxy-cert.doc.js +103 -0
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.d.ts +17 -0
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.js +130 -0
- package/dist_ts/db/documents/classes.route-override.doc.d.ts +10 -0
- package/dist_ts/db/documents/classes.route-override.doc.js +91 -0
- package/dist_ts/db/documents/classes.stored-route.doc.d.ts +12 -0
- package/dist_ts/db/documents/classes.stored-route.doc.js +103 -0
- package/dist_ts/db/documents/classes.vlan-mappings.doc.d.ts +15 -0
- package/dist_ts/db/documents/classes.vlan-mappings.doc.js +77 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +18 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.js +136 -0
- package/dist_ts/db/documents/classes.vpn-server-keys.doc.d.ts +10 -0
- package/dist_ts/db/documents/classes.vpn-server-keys.doc.js +94 -0
- package/dist_ts/db/documents/index.d.ts +13 -0
- package/dist_ts/db/documents/index.js +20 -0
- package/dist_ts/{cache → db}/index.d.ts +1 -1
- package/dist_ts/db/index.js +9 -0
- package/dist_ts/opsserver/handlers/certificate.handler.js +66 -66
- package/dist_ts/opsserver/handlers/config.handler.js +14 -15
- package/dist_ts/paths.d.ts +0 -1
- package/dist_ts/paths.js +1 -2
- package/dist_ts/radius/classes.accounting.manager.d.ts +4 -12
- package/dist_ts/radius/classes.accounting.manager.js +80 -93
- package/dist_ts/radius/classes.radius.server.d.ts +1 -3
- package/dist_ts/radius/classes.radius.server.js +4 -6
- package/dist_ts/radius/classes.vlan.manager.d.ts +3 -7
- package/dist_ts/radius/classes.vlan.manager.js +21 -28
- package/dist_ts/radius/index.d.ts +1 -1
- package/dist_ts/radius/index.js +1 -1
- package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +3 -5
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +41 -21
- package/dist_ts/security/classes.ipreputationchecker.d.ts +6 -21
- package/dist_ts/security/classes.ipreputationchecker.js +59 -138
- package/dist_ts/vpn/classes.vpn-manager.d.ts +4 -22
- package/dist_ts/vpn/classes.vpn-manager.js +40 -45
- package/dist_ts_oci_container/index.js +4 -4
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/package.json +1 -1
- package/readme.storage.md +55 -91
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.cert-provision-scheduler.ts +35 -17
- package/ts/classes.dcrouter.ts +113 -125
- package/ts/classes.storage-cert-manager.ts +34 -22
- package/ts/config/classes.api-token-manager.ts +42 -11
- package/ts/config/classes.route-config-manager.ts +56 -21
- package/ts/{cache → db}/classes.cache.cleaner.ts +6 -6
- package/ts/db/classes.dcrouter-db.ts +179 -0
- package/ts/db/documents/classes.accounting-session.doc.ts +106 -0
- package/ts/db/documents/classes.acme-cert.doc.ts +41 -0
- package/ts/db/documents/classes.api-token.doc.ts +56 -0
- package/ts/{cache → db}/documents/classes.cached.email.ts +2 -2
- package/ts/{cache → db}/documents/classes.cached.ip.reputation.ts +2 -2
- package/ts/db/documents/classes.cert-backoff.doc.ts +35 -0
- package/ts/db/documents/classes.proxy-cert.doc.ts +38 -0
- package/ts/db/documents/classes.remote-ingress-edge.doc.ts +54 -0
- package/ts/db/documents/classes.route-override.doc.ts +32 -0
- package/ts/db/documents/classes.stored-route.doc.ts +38 -0
- package/ts/db/documents/classes.vlan-mappings.doc.ts +32 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +57 -0
- package/ts/db/documents/classes.vpn-server-keys.doc.ts +31 -0
- package/ts/db/documents/index.ts +24 -0
- package/ts/{cache → db}/index.ts +6 -2
- package/ts/opsserver/handlers/certificate.handler.ts +67 -65
- package/ts/opsserver/handlers/config.handler.ts +13 -14
- package/ts/paths.ts +0 -1
- package/ts/radius/classes.accounting.manager.ts +81 -103
- package/ts/radius/classes.radius.server.ts +3 -6
- package/ts/radius/classes.vlan.manager.ts +20 -32
- package/ts/radius/index.ts +1 -1
- package/ts/remoteingress/classes.remoteingress-manager.ts +40 -22
- package/ts/security/classes.ipreputationchecker.ts +103 -196
- package/ts/vpn/classes.vpn-manager.ts +44 -75
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/dist_ts/cache/classes.cache.cleaner.js +0 -130
- package/dist_ts/cache/classes.cachedb.d.ts +0 -60
- package/dist_ts/cache/classes.cachedb.js +0 -126
- package/dist_ts/cache/documents/index.d.ts +0 -2
- package/dist_ts/cache/documents/index.js +0 -3
- package/dist_ts/cache/index.js +0 -7
- package/dist_ts/storage/classes.storagemanager.d.ts +0 -83
- package/dist_ts/storage/classes.storagemanager.js +0 -348
- package/dist_ts/storage/index.d.ts +0 -1
- package/dist_ts/storage/index.js +0 -3
- package/ts/cache/classes.cachedb.ts +0 -155
- package/ts/cache/documents/index.ts +0 -2
- package/ts/storage/classes.storagemanager.ts +0 -404
- package/ts/storage/index.ts +0 -2
- /package/dist_ts/{cache → db}/classes.cached.document.d.ts +0 -0
- /package/dist_ts/{cache → db}/documents/classes.cached.email.d.ts +0 -0
- /package/dist_ts/{cache → db}/documents/classes.cached.ip.reputation.d.ts +0 -0
- /package/ts/{cache → db}/classes.cached.document.ts +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import * as paths from '../paths.js';
|
|
3
2
|
import { logger } from '../logger.js';
|
|
4
3
|
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from './classes.securitylogger.js';
|
|
5
4
|
import { LRUCache } from 'lru-cache';
|
|
5
|
+
import { CachedIPReputation } from '../db/documents/classes.cached.ip.reputation.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Reputation check result information
|
|
@@ -52,7 +52,7 @@ export interface IIPReputationOptions {
|
|
|
52
52
|
highRiskThreshold?: number; // Score below this is high risk
|
|
53
53
|
mediumRiskThreshold?: number; // Score below this is medium risk
|
|
54
54
|
lowRiskThreshold?: number; // Score below this is low risk
|
|
55
|
-
enableLocalCache?: boolean; // Whether to persist cache to
|
|
55
|
+
enableLocalCache?: boolean; // Whether to persist cache to database (default: true)
|
|
56
56
|
enableDNSBL?: boolean; // Whether to use DNSBL checks (default: true)
|
|
57
57
|
enableIPInfo?: boolean; // Whether to use IP info service (default: true)
|
|
58
58
|
}
|
|
@@ -64,10 +64,7 @@ export class IPReputationChecker {
|
|
|
64
64
|
private static instance: IPReputationChecker | undefined;
|
|
65
65
|
private reputationCache: LRUCache<string, IReputationResult>;
|
|
66
66
|
private options: Required<IIPReputationOptions>;
|
|
67
|
-
|
|
68
|
-
private saveCacheTimer: ReturnType<typeof setTimeout> | null = null;
|
|
69
|
-
private static readonly SAVE_CACHE_DEBOUNCE_MS = 30_000;
|
|
70
|
-
|
|
67
|
+
|
|
71
68
|
// Default DNSBL servers
|
|
72
69
|
private static readonly DEFAULT_DNSBL_SERVERS = [
|
|
73
70
|
'zen.spamhaus.org', // Spamhaus
|
|
@@ -75,13 +72,13 @@ export class IPReputationChecker {
|
|
|
75
72
|
'b.barracudacentral.org', // Barracuda
|
|
76
73
|
'spam.dnsbl.sorbs.net', // SORBS
|
|
77
74
|
'dnsbl.sorbs.net', // SORBS (expanded)
|
|
78
|
-
'cbl.abuseat.org', // Composite Blocking List
|
|
75
|
+
'cbl.abuseat.org', // Composite Blocking List
|
|
79
76
|
'xbl.spamhaus.org', // Spamhaus XBL
|
|
80
77
|
'pbl.spamhaus.org', // Spamhaus PBL
|
|
81
78
|
'dnsbl-1.uceprotect.net', // UCEPROTECT
|
|
82
79
|
'psbl.surriel.com' // PSBL
|
|
83
80
|
];
|
|
84
|
-
|
|
81
|
+
|
|
85
82
|
// Default options
|
|
86
83
|
private static readonly DEFAULT_OPTIONS: Required<IIPReputationOptions> = {
|
|
87
84
|
maxCacheSize: 10000,
|
|
@@ -94,54 +91,40 @@ export class IPReputationChecker {
|
|
|
94
91
|
enableDNSBL: true,
|
|
95
92
|
enableIPInfo: true
|
|
96
93
|
};
|
|
97
|
-
|
|
94
|
+
|
|
98
95
|
/**
|
|
99
96
|
* Constructor for IPReputationChecker
|
|
100
97
|
* @param options Configuration options
|
|
101
|
-
* @param storageManager Optional StorageManager instance for persistence
|
|
102
98
|
*/
|
|
103
|
-
constructor(options: IIPReputationOptions = {}
|
|
99
|
+
constructor(options: IIPReputationOptions = {}) {
|
|
104
100
|
// Merge with default options
|
|
105
101
|
this.options = {
|
|
106
102
|
...IPReputationChecker.DEFAULT_OPTIONS,
|
|
107
103
|
...options
|
|
108
104
|
};
|
|
109
|
-
|
|
110
|
-
this.storageManager = storageManager;
|
|
111
|
-
|
|
112
|
-
// If no storage manager provided, log warning
|
|
113
|
-
if (!storageManager && this.options.enableLocalCache) {
|
|
114
|
-
logger.log('warn',
|
|
115
|
-
'⚠️ WARNING: IPReputationChecker initialized without StorageManager.\n' +
|
|
116
|
-
' IP reputation cache will only be stored to filesystem.\n' +
|
|
117
|
-
' Consider passing a StorageManager instance for better storage flexibility.'
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
105
|
+
|
|
121
106
|
// Initialize reputation cache
|
|
122
107
|
this.reputationCache = new LRUCache<string, IReputationResult>({
|
|
123
108
|
max: this.options.maxCacheSize,
|
|
124
109
|
ttl: this.options.cacheTTL, // Cache TTL
|
|
125
110
|
});
|
|
126
|
-
|
|
127
|
-
// Load
|
|
111
|
+
|
|
112
|
+
// Load persisted reputations into in-memory cache
|
|
128
113
|
if (this.options.enableLocalCache) {
|
|
129
|
-
|
|
130
|
-
this.loadCache().catch((error: unknown) => {
|
|
114
|
+
this.loadCacheFromDb().catch((error: unknown) => {
|
|
131
115
|
logger.log('error', `Failed to load IP reputation cache during initialization: ${(error as Error).message}`);
|
|
132
116
|
});
|
|
133
117
|
}
|
|
134
118
|
}
|
|
135
|
-
|
|
119
|
+
|
|
136
120
|
/**
|
|
137
121
|
* Get the singleton instance of the checker
|
|
138
122
|
* @param options Configuration options
|
|
139
|
-
* @param storageManager Optional StorageManager instance for persistence
|
|
140
123
|
* @returns Singleton instance
|
|
141
124
|
*/
|
|
142
|
-
public static getInstance(options: IIPReputationOptions = {}
|
|
125
|
+
public static getInstance(options: IIPReputationOptions = {}): IPReputationChecker {
|
|
143
126
|
if (!IPReputationChecker.instance) {
|
|
144
|
-
IPReputationChecker.instance = new IPReputationChecker(options
|
|
127
|
+
IPReputationChecker.instance = new IPReputationChecker(options);
|
|
145
128
|
}
|
|
146
129
|
return IPReputationChecker.instance;
|
|
147
130
|
}
|
|
@@ -150,12 +133,6 @@ export class IPReputationChecker {
|
|
|
150
133
|
* Reset the singleton instance (for shutdown/testing)
|
|
151
134
|
*/
|
|
152
135
|
public static resetInstance(): void {
|
|
153
|
-
if (IPReputationChecker.instance) {
|
|
154
|
-
if (IPReputationChecker.instance.saveCacheTimer) {
|
|
155
|
-
clearTimeout(IPReputationChecker.instance.saveCacheTimer);
|
|
156
|
-
IPReputationChecker.instance.saveCacheTimer = null;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
136
|
IPReputationChecker.instance = undefined;
|
|
160
137
|
}
|
|
161
138
|
|
|
@@ -171,8 +148,8 @@ export class IPReputationChecker {
|
|
|
171
148
|
logger.log('warn', `Invalid IP address format: ${ip}`);
|
|
172
149
|
return this.createErrorResult(ip, 'Invalid IP address format');
|
|
173
150
|
}
|
|
174
|
-
|
|
175
|
-
// Check cache first
|
|
151
|
+
|
|
152
|
+
// Check in-memory LRU cache first (fast path)
|
|
176
153
|
const cachedResult = this.reputationCache.get(ip);
|
|
177
154
|
if (cachedResult) {
|
|
178
155
|
logger.log('info', `Using cached reputation data for IP ${ip}`, {
|
|
@@ -181,7 +158,7 @@ export class IPReputationChecker {
|
|
|
181
158
|
});
|
|
182
159
|
return cachedResult;
|
|
183
160
|
}
|
|
184
|
-
|
|
161
|
+
|
|
185
162
|
// Initialize empty result
|
|
186
163
|
const result: IReputationResult = {
|
|
187
164
|
score: 100, // Start with perfect score
|
|
@@ -191,51 +168,53 @@ export class IPReputationChecker {
|
|
|
191
168
|
isVPN: false,
|
|
192
169
|
timestamp: Date.now()
|
|
193
170
|
};
|
|
194
|
-
|
|
171
|
+
|
|
195
172
|
// Check IP against DNS blacklists if enabled
|
|
196
173
|
if (this.options.enableDNSBL) {
|
|
197
174
|
const dnsblResult = await this.checkDNSBL(ip);
|
|
198
|
-
|
|
175
|
+
|
|
199
176
|
// Update result with DNSBL information
|
|
200
177
|
result.score -= dnsblResult.listCount * 10; // Subtract 10 points per blacklist
|
|
201
178
|
result.isSpam = dnsblResult.listCount > 0;
|
|
202
179
|
result.blacklists = dnsblResult.lists;
|
|
203
180
|
}
|
|
204
|
-
|
|
181
|
+
|
|
205
182
|
// Get additional IP information if enabled
|
|
206
183
|
if (this.options.enableIPInfo) {
|
|
207
184
|
const ipInfo = await this.getIPInfo(ip);
|
|
208
|
-
|
|
185
|
+
|
|
209
186
|
// Update result with IP info
|
|
210
187
|
result.country = ipInfo.country;
|
|
211
188
|
result.asn = ipInfo.asn;
|
|
212
189
|
result.org = ipInfo.org;
|
|
213
|
-
|
|
190
|
+
|
|
214
191
|
// Adjust score based on IP type
|
|
215
192
|
if (ipInfo.type === IPType.PROXY || ipInfo.type === IPType.TOR || ipInfo.type === IPType.VPN) {
|
|
216
193
|
result.score -= 30; // Subtract 30 points for proxies, Tor, VPNs
|
|
217
|
-
|
|
194
|
+
|
|
218
195
|
// Set proxy flags
|
|
219
196
|
result.isProxy = ipInfo.type === IPType.PROXY;
|
|
220
197
|
result.isTor = ipInfo.type === IPType.TOR;
|
|
221
198
|
result.isVPN = ipInfo.type === IPType.VPN;
|
|
222
199
|
}
|
|
223
200
|
}
|
|
224
|
-
|
|
201
|
+
|
|
225
202
|
// Ensure score is between 0 and 100
|
|
226
203
|
result.score = Math.max(0, Math.min(100, result.score));
|
|
227
|
-
|
|
228
|
-
// Update
|
|
204
|
+
|
|
205
|
+
// Update in-memory LRU cache
|
|
229
206
|
this.reputationCache.set(ip, result);
|
|
230
|
-
|
|
231
|
-
//
|
|
207
|
+
|
|
208
|
+
// Persist to database if enabled (fire and forget)
|
|
232
209
|
if (this.options.enableLocalCache) {
|
|
233
|
-
this.
|
|
210
|
+
this.persistReputationToDb(ip, result).catch((error: unknown) => {
|
|
211
|
+
logger.log('error', `Failed to persist IP reputation for ${ip}: ${(error as Error).message}`);
|
|
212
|
+
});
|
|
234
213
|
}
|
|
235
|
-
|
|
214
|
+
|
|
236
215
|
// Log the reputation check
|
|
237
216
|
this.logReputationCheck(ip, result);
|
|
238
|
-
|
|
217
|
+
|
|
239
218
|
return result;
|
|
240
219
|
} catch (error: unknown) {
|
|
241
220
|
logger.log('error', `Error checking IP reputation for ${ip}: ${(error as Error).message}`, {
|
|
@@ -246,7 +225,7 @@ export class IPReputationChecker {
|
|
|
246
225
|
return this.createErrorResult(ip, (error as Error).message);
|
|
247
226
|
}
|
|
248
227
|
}
|
|
249
|
-
|
|
228
|
+
|
|
250
229
|
/**
|
|
251
230
|
* Check an IP against DNS blacklists
|
|
252
231
|
* @param ip IP address to check
|
|
@@ -259,7 +238,7 @@ export class IPReputationChecker {
|
|
|
259
238
|
try {
|
|
260
239
|
// Reverse the IP for DNSBL queries
|
|
261
240
|
const reversedIP = this.reverseIP(ip);
|
|
262
|
-
|
|
241
|
+
|
|
263
242
|
const results = await Promise.allSettled(
|
|
264
243
|
this.options.dnsblServers.map(async (server) => {
|
|
265
244
|
try {
|
|
@@ -274,14 +253,14 @@ export class IPReputationChecker {
|
|
|
274
253
|
}
|
|
275
254
|
})
|
|
276
255
|
);
|
|
277
|
-
|
|
256
|
+
|
|
278
257
|
// Extract successful lookups (listed in DNSBL)
|
|
279
258
|
const lists = results
|
|
280
|
-
.filter((result): result is PromiseFulfilledResult<string> =>
|
|
259
|
+
.filter((result): result is PromiseFulfilledResult<string> =>
|
|
281
260
|
result.status === 'fulfilled' && result.value !== null
|
|
282
261
|
)
|
|
283
262
|
.map(result => result.value);
|
|
284
|
-
|
|
263
|
+
|
|
285
264
|
return {
|
|
286
265
|
listCount: lists.length,
|
|
287
266
|
lists
|
|
@@ -294,7 +273,7 @@ export class IPReputationChecker {
|
|
|
294
273
|
};
|
|
295
274
|
}
|
|
296
275
|
}
|
|
297
|
-
|
|
276
|
+
|
|
298
277
|
/**
|
|
299
278
|
* Get information about an IP address
|
|
300
279
|
* @param ip IP address to check
|
|
@@ -309,16 +288,16 @@ export class IPReputationChecker {
|
|
|
309
288
|
try {
|
|
310
289
|
// In a real implementation, this would use an IP data service API
|
|
311
290
|
// For this implementation, we'll use a simplified approach
|
|
312
|
-
|
|
291
|
+
|
|
313
292
|
// Check if it's a known Tor exit node (simplified)
|
|
314
293
|
const isTor = ip.startsWith('171.25.') || ip.startsWith('185.220.') || ip.startsWith('95.216.');
|
|
315
|
-
|
|
294
|
+
|
|
316
295
|
// Check if it's a known VPN (simplified)
|
|
317
296
|
const isVPN = ip.startsWith('185.156.') || ip.startsWith('37.120.');
|
|
318
|
-
|
|
297
|
+
|
|
319
298
|
// Check if it's a known proxy (simplified)
|
|
320
299
|
const isProxy = ip.startsWith('34.92.') || ip.startsWith('34.206.');
|
|
321
|
-
|
|
300
|
+
|
|
322
301
|
// Determine IP type
|
|
323
302
|
let type = IPType.UNKNOWN;
|
|
324
303
|
if (isTor) {
|
|
@@ -341,7 +320,7 @@ export class IPReputationChecker {
|
|
|
341
320
|
type = IPType.RESIDENTIAL;
|
|
342
321
|
}
|
|
343
322
|
}
|
|
344
|
-
|
|
323
|
+
|
|
345
324
|
// Return the information
|
|
346
325
|
return {
|
|
347
326
|
country: this.determineCountry(ip), // Simplified, would use geolocation service
|
|
@@ -356,7 +335,7 @@ export class IPReputationChecker {
|
|
|
356
335
|
};
|
|
357
336
|
}
|
|
358
337
|
}
|
|
359
|
-
|
|
338
|
+
|
|
360
339
|
/**
|
|
361
340
|
* Simplified method to determine country from IP
|
|
362
341
|
* In a real implementation, this would use a geolocation database or service
|
|
@@ -371,7 +350,7 @@ export class IPReputationChecker {
|
|
|
371
350
|
if (ip.startsWith('171.')) return 'DE';
|
|
372
351
|
return 'XX'; // Unknown
|
|
373
352
|
}
|
|
374
|
-
|
|
353
|
+
|
|
375
354
|
/**
|
|
376
355
|
* Simplified method to determine organization from IP
|
|
377
356
|
* In a real implementation, this would use an IP-to-org database or service
|
|
@@ -387,7 +366,7 @@ export class IPReputationChecker {
|
|
|
387
366
|
if (ip.startsWith('185.220.')) return 'Tor Exit Node';
|
|
388
367
|
return 'Unknown';
|
|
389
368
|
}
|
|
390
|
-
|
|
369
|
+
|
|
391
370
|
/**
|
|
392
371
|
* Reverse an IP address for DNSBL lookups (e.g., 1.2.3.4 -> 4.3.2.1)
|
|
393
372
|
* @param ip IP address to reverse
|
|
@@ -396,7 +375,7 @@ export class IPReputationChecker {
|
|
|
396
375
|
private reverseIP(ip: string): string {
|
|
397
376
|
return ip.split('.').reverse().join('.');
|
|
398
377
|
}
|
|
399
|
-
|
|
378
|
+
|
|
400
379
|
/**
|
|
401
380
|
* Create an error result for when reputation check fails
|
|
402
381
|
* @param ip IP address
|
|
@@ -414,7 +393,7 @@ export class IPReputationChecker {
|
|
|
414
393
|
error: errorMessage
|
|
415
394
|
};
|
|
416
395
|
}
|
|
417
|
-
|
|
396
|
+
|
|
418
397
|
/**
|
|
419
398
|
* Validate IP address format
|
|
420
399
|
* @param ip IP address to validate
|
|
@@ -425,7 +404,7 @@ export class IPReputationChecker {
|
|
|
425
404
|
const ipv4Pattern = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
426
405
|
return ipv4Pattern.test(ip);
|
|
427
406
|
}
|
|
428
|
-
|
|
407
|
+
|
|
429
408
|
/**
|
|
430
409
|
* Log reputation check to security logger
|
|
431
410
|
* @param ip IP address
|
|
@@ -439,7 +418,7 @@ export class IPReputationChecker {
|
|
|
439
418
|
} else if (result.score < this.options.mediumRiskThreshold) {
|
|
440
419
|
logLevel = SecurityLogLevel.INFO;
|
|
441
420
|
}
|
|
442
|
-
|
|
421
|
+
|
|
443
422
|
// Log the check
|
|
444
423
|
SecurityLogger.getInstance().logEvent({
|
|
445
424
|
level: logLevel,
|
|
@@ -458,131 +437,76 @@ export class IPReputationChecker {
|
|
|
458
437
|
success: !result.isSpam
|
|
459
438
|
});
|
|
460
439
|
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Schedule a debounced cache save (at most once per SAVE_CACHE_DEBOUNCE_MS)
|
|
464
|
-
*/
|
|
465
|
-
private debouncedSaveCache(): void {
|
|
466
|
-
if (this.saveCacheTimer) {
|
|
467
|
-
return; // already scheduled
|
|
468
|
-
}
|
|
469
|
-
this.saveCacheTimer = setTimeout(() => {
|
|
470
|
-
this.saveCacheTimer = null;
|
|
471
|
-
this.saveCache().catch((error: unknown) => {
|
|
472
|
-
logger.log('error', `Failed to save IP reputation cache: ${(error as Error).message}`);
|
|
473
|
-
});
|
|
474
|
-
}, IPReputationChecker.SAVE_CACHE_DEBOUNCE_MS);
|
|
475
|
-
}
|
|
476
440
|
|
|
477
441
|
/**
|
|
478
|
-
*
|
|
442
|
+
* Persist a single IP reputation result to the database via CachedIPReputation
|
|
479
443
|
*/
|
|
480
|
-
private async
|
|
444
|
+
private async persistReputationToDb(ip: string, result: IReputationResult): Promise<void> {
|
|
481
445
|
try {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
await
|
|
498
|
-
logger.log('info', `Saved ${entries.length} IP reputation cache entries to StorageManager`);
|
|
446
|
+
const data = {
|
|
447
|
+
score: result.score,
|
|
448
|
+
isSpam: result.isSpam,
|
|
449
|
+
isProxy: result.isProxy,
|
|
450
|
+
isTor: result.isTor,
|
|
451
|
+
isVPN: result.isVPN,
|
|
452
|
+
country: result.country,
|
|
453
|
+
asn: result.asn,
|
|
454
|
+
org: result.org,
|
|
455
|
+
blacklists: result.blacklists,
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const existing = await CachedIPReputation.findByIP(ip);
|
|
459
|
+
if (existing) {
|
|
460
|
+
existing.updateReputation(data);
|
|
461
|
+
await existing.save();
|
|
499
462
|
} else {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
plugins.fsUtils.ensureDirSync(cacheDir);
|
|
503
|
-
|
|
504
|
-
const cacheFile = plugins.path.join(cacheDir, 'ip_reputation_cache.json');
|
|
505
|
-
plugins.fsUtils.toFsSync(cacheData, cacheFile);
|
|
506
|
-
|
|
507
|
-
logger.log('info', `Saved ${entries.length} IP reputation cache entries to disk`);
|
|
463
|
+
const doc = CachedIPReputation.fromReputationData(ip, data);
|
|
464
|
+
await doc.save();
|
|
508
465
|
}
|
|
509
466
|
} catch (error: unknown) {
|
|
510
|
-
logger.log('error', `Failed to
|
|
467
|
+
logger.log('error', `Failed to persist IP reputation for ${ip}: ${(error as Error).message}`);
|
|
511
468
|
}
|
|
512
469
|
}
|
|
513
470
|
|
|
514
471
|
/**
|
|
515
|
-
* Load
|
|
472
|
+
* Load persisted reputations from CachedIPReputation documents into the in-memory LRU cache
|
|
516
473
|
*/
|
|
517
|
-
private async
|
|
474
|
+
private async loadCacheFromDb(): Promise<void> {
|
|
518
475
|
try {
|
|
519
|
-
|
|
520
|
-
let
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if (!cacheData) {
|
|
528
|
-
// Check if data exists in filesystem and migrate it
|
|
529
|
-
const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
|
|
530
|
-
|
|
531
|
-
if (plugins.fs.existsSync(cacheFile)) {
|
|
532
|
-
logger.log('info', 'Migrating IP reputation cache from filesystem to StorageManager');
|
|
533
|
-
cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
|
|
534
|
-
fromFilesystem = true;
|
|
535
|
-
|
|
536
|
-
// Migrate to storage manager
|
|
537
|
-
await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
|
|
538
|
-
logger.log('info', 'IP reputation cache migrated to StorageManager successfully');
|
|
539
|
-
|
|
540
|
-
// Optionally delete the old file after successful migration
|
|
541
|
-
try {
|
|
542
|
-
plugins.fs.unlinkSync(cacheFile);
|
|
543
|
-
logger.log('info', 'Old cache file removed after migration');
|
|
544
|
-
} catch (deleteError) {
|
|
545
|
-
logger.log('warn', `Could not delete old cache file: ${(deleteError as Error).message}`);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
} catch (error: unknown) {
|
|
550
|
-
logger.log('error', `Error loading from StorageManager: ${(error as Error).message}`);
|
|
551
|
-
}
|
|
552
|
-
} else {
|
|
553
|
-
// No storage manager, load from filesystem
|
|
554
|
-
const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
|
|
555
|
-
|
|
556
|
-
if (plugins.fs.existsSync(cacheFile)) {
|
|
557
|
-
cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
|
|
558
|
-
fromFilesystem = true;
|
|
476
|
+
const docs = await CachedIPReputation.getInstances({});
|
|
477
|
+
let loadedCount = 0;
|
|
478
|
+
|
|
479
|
+
for (const doc of docs) {
|
|
480
|
+
// Skip expired documents
|
|
481
|
+
if (doc.isExpired()) {
|
|
482
|
+
continue;
|
|
559
483
|
}
|
|
484
|
+
|
|
485
|
+
const result: IReputationResult = {
|
|
486
|
+
score: doc.score,
|
|
487
|
+
isSpam: doc.isSpam,
|
|
488
|
+
isProxy: doc.isProxy,
|
|
489
|
+
isTor: doc.isTor,
|
|
490
|
+
isVPN: doc.isVPN,
|
|
491
|
+
country: doc.country || undefined,
|
|
492
|
+
asn: doc.asn || undefined,
|
|
493
|
+
org: doc.org || undefined,
|
|
494
|
+
blacklists: doc.blacklists || [],
|
|
495
|
+
timestamp: doc.lastAccessedAt?.getTime() ?? doc.createdAt?.getTime() ?? Date.now(),
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
this.reputationCache.set(doc.ipAddress, result);
|
|
499
|
+
loadedCount++;
|
|
560
500
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const entries = JSON.parse(cacheData);
|
|
565
|
-
|
|
566
|
-
// Validate and filter entries
|
|
567
|
-
const now = Date.now();
|
|
568
|
-
const validEntries = entries.filter(entry => {
|
|
569
|
-
const age = now - entry.data.timestamp;
|
|
570
|
-
return age < this.options.cacheTTL; // Only load entries that haven't expired
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// Restore cache
|
|
574
|
-
for (const entry of validEntries) {
|
|
575
|
-
this.reputationCache.set(entry.ip, entry.data);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const source = fromFilesystem ? 'disk' : 'StorageManager';
|
|
579
|
-
logger.log('info', `Loaded ${validEntries.length} IP reputation cache entries from ${source}`);
|
|
501
|
+
|
|
502
|
+
if (loadedCount > 0) {
|
|
503
|
+
logger.log('info', `Loaded ${loadedCount} IP reputation cache entries from database`);
|
|
580
504
|
}
|
|
581
505
|
} catch (error: unknown) {
|
|
582
|
-
logger.log('error', `Failed to load IP reputation cache: ${(error as Error).message}`);
|
|
506
|
+
logger.log('error', `Failed to load IP reputation cache from database: ${(error as Error).message}`);
|
|
583
507
|
}
|
|
584
508
|
}
|
|
585
|
-
|
|
509
|
+
|
|
586
510
|
/**
|
|
587
511
|
* Get the risk level for a reputation score
|
|
588
512
|
* @param score Reputation score (0-100)
|
|
@@ -599,21 +523,4 @@ export class IPReputationChecker {
|
|
|
599
523
|
return 'trusted';
|
|
600
524
|
}
|
|
601
525
|
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Update the storage manager after instantiation
|
|
605
|
-
* This is useful when the storage manager is not available at construction time
|
|
606
|
-
* @param storageManager The StorageManager instance to use
|
|
607
|
-
*/
|
|
608
|
-
public updateStorageManager(storageManager: any): void {
|
|
609
|
-
this.storageManager = storageManager;
|
|
610
|
-
logger.log('info', 'IPReputationChecker storage manager updated');
|
|
611
|
-
|
|
612
|
-
// If cache is enabled and we have entries, save them to the new storage manager
|
|
613
|
-
if (this.options.enableLocalCache && this.reputationCache.size > 0) {
|
|
614
|
-
this.saveCache().catch((error: unknown) => {
|
|
615
|
-
logger.log('error', `Failed to save cache to new storage manager: ${(error as Error).message}`);
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
526
|
+
}
|