@push.rocks/smartmta 5.1.3 → 5.2.1

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 (98) hide show
  1. package/changelog.md +15 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/index.d.ts +3 -0
  5. package/dist_ts/index.js +4 -0
  6. package/dist_ts/logger.d.ts +17 -0
  7. package/dist_ts/logger.js +76 -0
  8. package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
  9. package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
  10. package/dist_ts/mail/core/classes.email.d.ts +291 -0
  11. package/dist_ts/mail/core/classes.email.js +802 -0
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
  13. package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
  15. package/dist_ts/mail/core/classes.templatemanager.js +240 -0
  16. package/dist_ts/mail/core/index.d.ts +4 -0
  17. package/dist_ts/mail/core/index.js +6 -0
  18. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
  19. package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
  20. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
  21. package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
  22. package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
  23. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
  24. package/dist_ts/mail/delivery/index.d.ts +4 -0
  25. package/dist_ts/mail/delivery/index.js +6 -0
  26. package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
  27. package/dist_ts/mail/delivery/interfaces.js +17 -0
  28. package/dist_ts/mail/index.d.ts +7 -0
  29. package/dist_ts/mail/index.js +12 -0
  30. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
  31. package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
  32. package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
  33. package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
  34. package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
  35. package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
  36. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
  37. package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
  38. package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
  39. package/dist_ts/mail/routing/classes.email.router.js +494 -0
  40. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
  41. package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
  42. package/dist_ts/mail/routing/index.d.ts +7 -0
  43. package/dist_ts/mail/routing/index.js +9 -0
  44. package/dist_ts/mail/routing/interfaces.d.ts +187 -0
  45. package/dist_ts/mail/routing/interfaces.js +2 -0
  46. package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
  47. package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
  48. package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
  49. package/dist_ts/mail/security/classes.spfverifier.js +87 -0
  50. package/dist_ts/mail/security/index.d.ts +2 -0
  51. package/dist_ts/mail/security/index.js +4 -0
  52. package/dist_ts/paths.d.ts +14 -0
  53. package/dist_ts/paths.js +39 -0
  54. package/dist_ts/plugins.d.ts +24 -0
  55. package/dist_ts/plugins.js +28 -0
  56. package/dist_ts/security/classes.contentscanner.d.ts +130 -0
  57. package/dist_ts/security/classes.contentscanner.js +338 -0
  58. package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
  59. package/dist_ts/security/classes.ipreputationchecker.js +263 -0
  60. package/dist_ts/security/classes.rustsecuritybridge.d.ts +403 -0
  61. package/dist_ts/security/classes.rustsecuritybridge.js +502 -0
  62. package/dist_ts/security/classes.securitylogger.d.ts +140 -0
  63. package/dist_ts/security/classes.securitylogger.js +235 -0
  64. package/dist_ts/security/index.d.ts +4 -0
  65. package/dist_ts/security/index.js +5 -0
  66. package/package.json +6 -1
  67. package/ts/00_commitinfo_data.ts +8 -0
  68. package/ts/index.ts +3 -0
  69. package/ts/logger.ts +91 -0
  70. package/ts/mail/core/classes.bouncemanager.ts +731 -0
  71. package/ts/mail/core/classes.email.ts +942 -0
  72. package/ts/mail/core/classes.emailvalidator.ts +239 -0
  73. package/ts/mail/core/classes.templatemanager.ts +320 -0
  74. package/ts/mail/core/index.ts +5 -0
  75. package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
  76. package/ts/mail/delivery/classes.delivery.system.ts +816 -0
  77. package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
  78. package/ts/mail/delivery/index.ts +5 -0
  79. package/ts/mail/delivery/interfaces.ts +167 -0
  80. package/ts/mail/index.ts +17 -0
  81. package/ts/mail/routing/classes.dkim.manager.ts +157 -0
  82. package/ts/mail/routing/classes.dns.manager.ts +573 -0
  83. package/ts/mail/routing/classes.domain.registry.ts +139 -0
  84. package/ts/mail/routing/classes.email.action.executor.ts +175 -0
  85. package/ts/mail/routing/classes.email.router.ts +575 -0
  86. package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
  87. package/ts/mail/routing/index.ts +9 -0
  88. package/ts/mail/routing/interfaces.ts +202 -0
  89. package/ts/mail/security/classes.dkimcreator.ts +447 -0
  90. package/ts/mail/security/classes.spfverifier.ts +126 -0
  91. package/ts/mail/security/index.ts +3 -0
  92. package/ts/paths.ts +48 -0
  93. package/ts/plugins.ts +53 -0
  94. package/ts/security/classes.contentscanner.ts +400 -0
  95. package/ts/security/classes.ipreputationchecker.ts +315 -0
  96. package/ts/security/classes.rustsecuritybridge.ts +964 -0
  97. package/ts/security/classes.securitylogger.ts +299 -0
  98. package/ts/security/index.ts +40 -0
@@ -0,0 +1,315 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as paths from '../paths.js';
3
+ import { logger } from '../logger.js';
4
+ import { SecurityLogger, SecurityLogLevel, SecurityEventType } from './classes.securitylogger.js';
5
+ import { RustSecurityBridge } from './classes.rustsecuritybridge.js';
6
+ import { LRUCache } from 'lru-cache';
7
+
8
+ /**
9
+ * Reputation check result information
10
+ */
11
+ export interface IReputationResult {
12
+ score: number; // 0 (worst) to 100 (best)
13
+ isSpam: boolean; // true if the IP is known for spam
14
+ isProxy: boolean; // true if the IP is a known proxy
15
+ isTor: boolean; // true if the IP is a known Tor exit node
16
+ isVPN: boolean; // true if the IP is a known VPN
17
+ country?: string; // Country code (if available)
18
+ asn?: string; // Autonomous System Number (if available)
19
+ org?: string; // Organization name (if available)
20
+ blacklists?: string[]; // Names of blacklists that include this IP
21
+ timestamp: number; // When this result was created/retrieved
22
+ error?: string; // Error message if check failed
23
+ }
24
+
25
+ /**
26
+ * Reputation threshold scores
27
+ */
28
+ export enum ReputationThreshold {
29
+ HIGH_RISK = 20, // Score below this is considered high risk
30
+ MEDIUM_RISK = 50, // Score below this is considered medium risk
31
+ LOW_RISK = 80 // Score below this is considered low risk (but not trusted)
32
+ }
33
+
34
+ /**
35
+ * IP type classifications
36
+ */
37
+ export enum IPType {
38
+ RESIDENTIAL = 'residential',
39
+ DATACENTER = 'datacenter',
40
+ PROXY = 'proxy',
41
+ TOR = 'tor',
42
+ VPN = 'vpn',
43
+ UNKNOWN = 'unknown'
44
+ }
45
+
46
+ /**
47
+ * Options for the IP Reputation Checker
48
+ */
49
+ export interface IIPReputationOptions {
50
+ maxCacheSize?: number; // Maximum number of IPs to cache (default: 10000)
51
+ cacheTTL?: number; // TTL for cache entries in ms (default: 24 hours)
52
+ dnsblServers?: string[]; // List of DNSBL servers to check
53
+ highRiskThreshold?: number; // Score below this is high risk
54
+ mediumRiskThreshold?: number; // Score below this is medium risk
55
+ lowRiskThreshold?: number; // Score below this is low risk
56
+ enableLocalCache?: boolean; // Whether to persist cache to disk (default: true)
57
+ enableDNSBL?: boolean; // Whether to use DNSBL checks (default: true)
58
+ enableIPInfo?: boolean; // Whether to use IP info service (default: true)
59
+ }
60
+
61
+ /**
62
+ * IP reputation checker — delegates DNSBL lookups to the Rust security bridge.
63
+ * Retains LRU caching and disk persistence in TypeScript.
64
+ */
65
+ export class IPReputationChecker {
66
+ private static instance: IPReputationChecker;
67
+ private reputationCache: LRUCache<string, IReputationResult>;
68
+ private options: Required<IIPReputationOptions>;
69
+ private storageManager?: any;
70
+
71
+ private static readonly DEFAULT_OPTIONS: Required<IIPReputationOptions> = {
72
+ maxCacheSize: 10000,
73
+ cacheTTL: 24 * 60 * 60 * 1000,
74
+ dnsblServers: [],
75
+ highRiskThreshold: ReputationThreshold.HIGH_RISK,
76
+ mediumRiskThreshold: ReputationThreshold.MEDIUM_RISK,
77
+ lowRiskThreshold: ReputationThreshold.LOW_RISK,
78
+ enableLocalCache: true,
79
+ enableDNSBL: true,
80
+ enableIPInfo: true
81
+ };
82
+
83
+ constructor(options: IIPReputationOptions = {}, storageManager?: any) {
84
+ this.options = {
85
+ ...IPReputationChecker.DEFAULT_OPTIONS,
86
+ ...options
87
+ };
88
+
89
+ this.storageManager = storageManager;
90
+
91
+ this.reputationCache = new LRUCache<string, IReputationResult>({
92
+ max: this.options.maxCacheSize,
93
+ ttl: this.options.cacheTTL,
94
+ });
95
+
96
+ if (this.options.enableLocalCache) {
97
+ this.loadCache().catch(error => {
98
+ logger.log('error', `Failed to load IP reputation cache during initialization: ${error.message}`);
99
+ });
100
+ }
101
+ }
102
+
103
+ public static getInstance(options: IIPReputationOptions = {}, storageManager?: any): IPReputationChecker {
104
+ if (!IPReputationChecker.instance) {
105
+ IPReputationChecker.instance = new IPReputationChecker(options, storageManager);
106
+ }
107
+ return IPReputationChecker.instance;
108
+ }
109
+
110
+ /**
111
+ * Check an IP address's reputation via the Rust bridge
112
+ */
113
+ public async checkReputation(ip: string): Promise<IReputationResult> {
114
+ try {
115
+ if (!this.isValidIPAddress(ip)) {
116
+ logger.log('warn', `Invalid IP address format: ${ip}`);
117
+ return this.createErrorResult(ip, 'Invalid IP address format');
118
+ }
119
+
120
+ // Check cache first
121
+ const cachedResult = this.reputationCache.get(ip);
122
+ if (cachedResult) {
123
+ logger.log('info', `Using cached reputation data for IP ${ip}`, {
124
+ score: cachedResult.score,
125
+ isSpam: cachedResult.isSpam
126
+ });
127
+ return cachedResult;
128
+ }
129
+
130
+ // Delegate to Rust bridge
131
+ const bridge = RustSecurityBridge.getInstance();
132
+ const rustResult = await bridge.checkIpReputation(ip);
133
+
134
+ const result: IReputationResult = {
135
+ score: rustResult.score,
136
+ isSpam: rustResult.listed_count > 0,
137
+ isProxy: rustResult.ip_type === 'proxy',
138
+ isTor: rustResult.ip_type === 'tor',
139
+ isVPN: rustResult.ip_type === 'vpn',
140
+ blacklists: rustResult.dnsbl_results
141
+ .filter(d => d.listed)
142
+ .map(d => d.server),
143
+ timestamp: Date.now(),
144
+ };
145
+
146
+ this.reputationCache.set(ip, result);
147
+
148
+ if (this.options.enableLocalCache) {
149
+ this.saveCache().catch(error => {
150
+ logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
151
+ });
152
+ }
153
+
154
+ this.logReputationCheck(ip, result);
155
+ return result;
156
+ } catch (error) {
157
+ logger.log('error', `Error checking IP reputation for ${ip}: ${error.message}`, {
158
+ ip,
159
+ stack: error.stack
160
+ });
161
+ const errorResult = this.createErrorResult(ip, error.message);
162
+ // Cache error results to avoid repeated failing lookups
163
+ this.reputationCache.set(ip, errorResult);
164
+ return errorResult;
165
+ }
166
+ }
167
+
168
+ private createErrorResult(ip: string, errorMessage: string): IReputationResult {
169
+ return {
170
+ score: 50,
171
+ isSpam: false,
172
+ isProxy: false,
173
+ isTor: false,
174
+ isVPN: false,
175
+ timestamp: Date.now(),
176
+ error: errorMessage
177
+ };
178
+ }
179
+
180
+ private isValidIPAddress(ip: string): boolean {
181
+ 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]?)$/;
182
+ return ipv4Pattern.test(ip);
183
+ }
184
+
185
+ private logReputationCheck(ip: string, result: IReputationResult): void {
186
+ let logLevel = SecurityLogLevel.INFO;
187
+ if (result.score < this.options.highRiskThreshold) {
188
+ logLevel = SecurityLogLevel.WARN;
189
+ }
190
+
191
+ SecurityLogger.getInstance().logEvent({
192
+ level: logLevel,
193
+ type: SecurityEventType.IP_REPUTATION,
194
+ message: `IP reputation check ${result.isSpam ? 'flagged spam' : 'completed'} for ${ip}`,
195
+ ipAddress: ip,
196
+ details: {
197
+ score: result.score,
198
+ isSpam: result.isSpam,
199
+ isProxy: result.isProxy,
200
+ isTor: result.isTor,
201
+ isVPN: result.isVPN,
202
+ country: result.country,
203
+ blacklists: result.blacklists
204
+ },
205
+ success: !result.isSpam
206
+ });
207
+ }
208
+
209
+ private async saveCache(): Promise<void> {
210
+ try {
211
+ const entries = Array.from(this.reputationCache.entries()).map(([ip, data]) => ({
212
+ ip,
213
+ data
214
+ }));
215
+
216
+ if (entries.length === 0) {
217
+ return;
218
+ }
219
+
220
+ const cacheData = JSON.stringify(entries);
221
+
222
+ if (this.storageManager) {
223
+ await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
224
+ logger.log('info', `Saved ${entries.length} IP reputation cache entries to StorageManager`);
225
+ } else {
226
+ const cacheDir = plugins.path.join(paths.dataDir, 'security');
227
+ await plugins.smartfs.directory(cacheDir).recursive().create();
228
+ const cacheFile = plugins.path.join(cacheDir, 'ip_reputation_cache.json');
229
+ await plugins.smartfs.file(cacheFile).write(cacheData);
230
+ logger.log('info', `Saved ${entries.length} IP reputation cache entries to disk`);
231
+ }
232
+ } catch (error) {
233
+ logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
234
+ }
235
+ }
236
+
237
+ private async loadCache(): Promise<void> {
238
+ try {
239
+ let cacheData: string | null = null;
240
+ let fromFilesystem = false;
241
+
242
+ if (this.storageManager) {
243
+ try {
244
+ cacheData = await this.storageManager.get('/security/ip-reputation-cache.json');
245
+
246
+ if (!cacheData) {
247
+ const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
248
+ if (plugins.fs.existsSync(cacheFile)) {
249
+ logger.log('info', 'Migrating IP reputation cache from filesystem to StorageManager');
250
+ cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
251
+ fromFilesystem = true;
252
+ await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
253
+ logger.log('info', 'IP reputation cache migrated to StorageManager successfully');
254
+ try {
255
+ plugins.fs.unlinkSync(cacheFile);
256
+ logger.log('info', 'Old cache file removed after migration');
257
+ } catch (deleteError) {
258
+ logger.log('warn', `Could not delete old cache file: ${deleteError.message}`);
259
+ }
260
+ }
261
+ }
262
+ } catch (error) {
263
+ logger.log('error', `Error loading from StorageManager: ${error.message}`);
264
+ }
265
+ } else {
266
+ const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
267
+ if (plugins.fs.existsSync(cacheFile)) {
268
+ cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
269
+ fromFilesystem = true;
270
+ }
271
+ }
272
+
273
+ if (cacheData) {
274
+ const entries = JSON.parse(cacheData);
275
+ const now = Date.now();
276
+ const validEntries = entries.filter(entry => {
277
+ const age = now - entry.data.timestamp;
278
+ return age < this.options.cacheTTL;
279
+ });
280
+
281
+ for (const entry of validEntries) {
282
+ this.reputationCache.set(entry.ip, entry.data);
283
+ }
284
+
285
+ const source = fromFilesystem ? 'disk' : 'StorageManager';
286
+ logger.log('info', `Loaded ${validEntries.length} IP reputation cache entries from ${source}`);
287
+ }
288
+ } catch (error) {
289
+ logger.log('error', `Failed to load IP reputation cache: ${error.message}`);
290
+ }
291
+ }
292
+
293
+ public static getRiskLevel(score: number): 'high' | 'medium' | 'low' | 'trusted' {
294
+ if (score < ReputationThreshold.HIGH_RISK) {
295
+ return 'high';
296
+ } else if (score < ReputationThreshold.MEDIUM_RISK) {
297
+ return 'medium';
298
+ } else if (score < ReputationThreshold.LOW_RISK) {
299
+ return 'low';
300
+ } else {
301
+ return 'trusted';
302
+ }
303
+ }
304
+
305
+ public updateStorageManager(storageManager: any): void {
306
+ this.storageManager = storageManager;
307
+ logger.log('info', 'IPReputationChecker storage manager updated');
308
+
309
+ if (this.options.enableLocalCache && this.reputationCache.size > 0) {
310
+ this.saveCache().catch(error => {
311
+ logger.log('error', `Failed to save cache to new storage manager: ${error.message}`);
312
+ });
313
+ }
314
+ }
315
+ }