@mikkelscheike/email-provider-links 4.0.10 → 5.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/README.md +23 -5
- package/dist/alias-detection.js +4 -3
- package/dist/api.d.ts +3 -3
- package/dist/api.js +98 -122
- package/dist/concurrent-dns.d.ts +7 -2
- package/dist/concurrent-dns.js +10 -3
- package/dist/hash-verifier.d.ts +7 -2
- package/dist/hash-verifier.js +13 -4
- package/dist/index.d.ts +15 -20
- package/dist/index.js +35 -34
- package/dist/loader.js +5 -47
- package/dist/provider-loader.d.ts +12 -2
- package/dist/provider-loader.js +6 -40
- package/dist/provider-store.d.ts +9 -0
- package/dist/provider-store.js +49 -0
- package/dist/url-validator.d.ts +7 -2
- package/dist/url-validator.js +19 -3
- package/package.json +2 -2
- package/providers/emailproviders.json +103 -24
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Email Provider Links
|
|
4
4
|
*
|
|
5
5
|
* A modern, robust email provider detection library with:
|
|
6
|
-
* -
|
|
6
|
+
* - 130+ verified email providers covering 210+ domains
|
|
7
7
|
* - Concurrent DNS detection for business domains
|
|
8
8
|
* - Zero runtime dependencies
|
|
9
9
|
* - Comprehensive error handling with detailed context
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @author Email Provider Links Team
|
|
15
15
|
* @license MIT
|
|
16
|
-
* @version
|
|
16
|
+
* @version See package.json
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.VERSION = exports.DOMAIN_COUNT = exports.PROVIDER_COUNT = exports.isValidEmailAddress = exports.domainToPunycode = exports.emailToPunycode = exports.validateInternationalEmail = exports.detectProviderConcurrent = exports.loadProviders = exports.Config = exports.emailsMatch = exports.normalizeEmail = exports.getEmailProviderFast = exports.getEmailProviderSync = exports.getEmailProvider = void 0;
|
|
@@ -24,22 +24,18 @@ exports.extractDomain = extractDomain;
|
|
|
24
24
|
exports.isValidEmail = isValidEmail;
|
|
25
25
|
exports.getLibraryStats = getLibraryStats;
|
|
26
26
|
exports.batchProcessEmails = batchProcessEmails;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const loader_1 = require("./loader");
|
|
28
|
+
Object.defineProperty(exports, "loadProviders", { enumerable: true, get: function () { return loader_1.loadProviders; } });
|
|
29
|
+
const api_1 = require("./api");
|
|
30
30
|
Object.defineProperty(exports, "getEmailProvider", { enumerable: true, get: function () { return api_1.getEmailProvider; } });
|
|
31
31
|
Object.defineProperty(exports, "getEmailProviderSync", { enumerable: true, get: function () { return api_1.getEmailProviderSync; } });
|
|
32
32
|
Object.defineProperty(exports, "getEmailProviderFast", { enumerable: true, get: function () { return api_1.getEmailProviderFast; } });
|
|
33
33
|
Object.defineProperty(exports, "normalizeEmail", { enumerable: true, get: function () { return api_1.normalizeEmail; } });
|
|
34
34
|
Object.defineProperty(exports, "emailsMatch", { enumerable: true, get: function () { return api_1.emailsMatch; } });
|
|
35
35
|
Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return api_1.Config; } });
|
|
36
|
-
|
|
37
|
-
// For power users and custom implementations
|
|
38
|
-
var loader_1 = require("./loader");
|
|
39
|
-
Object.defineProperty(exports, "loadProviders", { enumerable: true, get: function () { return loader_1.loadProviders; } });
|
|
40
|
-
var concurrent_dns_1 = require("./concurrent-dns");
|
|
36
|
+
const concurrent_dns_1 = require("./concurrent-dns");
|
|
41
37
|
Object.defineProperty(exports, "detectProviderConcurrent", { enumerable: true, get: function () { return concurrent_dns_1.detectProviderConcurrent; } });
|
|
42
|
-
|
|
38
|
+
const idn_1 = require("./idn");
|
|
43
39
|
Object.defineProperty(exports, "validateInternationalEmail", { enumerable: true, get: function () { return idn_1.validateInternationalEmail; } });
|
|
44
40
|
Object.defineProperty(exports, "emailToPunycode", { enumerable: true, get: function () { return idn_1.emailToPunycode; } });
|
|
45
41
|
Object.defineProperty(exports, "domainToPunycode", { enumerable: true, get: function () { return idn_1.domainToPunycode; } });
|
|
@@ -86,7 +82,7 @@ function validateEmailAddress(email) {
|
|
|
86
82
|
};
|
|
87
83
|
}
|
|
88
84
|
// Use international validation
|
|
89
|
-
const idnError = (0,
|
|
85
|
+
const idnError = (0, idn_1.validateInternationalEmail)(trimmedEmail);
|
|
90
86
|
if (idnError) {
|
|
91
87
|
return {
|
|
92
88
|
isValid: false,
|
|
@@ -104,10 +100,6 @@ function validateEmailAddress(email) {
|
|
|
104
100
|
}
|
|
105
101
|
// ===== UTILITY FUNCTIONS =====
|
|
106
102
|
// Helper functions for common tasks
|
|
107
|
-
const loader_2 = require("./loader");
|
|
108
|
-
const api_2 = require("./api");
|
|
109
|
-
const concurrent_dns_2 = require("./concurrent-dns");
|
|
110
|
-
const idn_2 = require("./idn");
|
|
111
103
|
/**
|
|
112
104
|
* Get comprehensive list of all supported email providers
|
|
113
105
|
*
|
|
@@ -124,7 +116,7 @@ const idn_2 = require("./idn");
|
|
|
124
116
|
*/
|
|
125
117
|
function getSupportedProviders() {
|
|
126
118
|
try {
|
|
127
|
-
const { providers } = (0,
|
|
119
|
+
const { providers } = (0, loader_1.loadProviders)();
|
|
128
120
|
return [...providers]; // Return defensive copy to prevent external mutations
|
|
129
121
|
}
|
|
130
122
|
catch (error) {
|
|
@@ -150,7 +142,7 @@ function isEmailProviderSupported(email) {
|
|
|
150
142
|
if (!email || typeof email !== 'string') {
|
|
151
143
|
return false;
|
|
152
144
|
}
|
|
153
|
-
const result = (0,
|
|
145
|
+
const result = (0, api_1.getEmailProviderSync)(email);
|
|
154
146
|
return result.provider !== null;
|
|
155
147
|
}
|
|
156
148
|
catch {
|
|
@@ -227,7 +219,7 @@ function getLibraryStats() {
|
|
|
227
219
|
return {
|
|
228
220
|
providerCount: providers.length,
|
|
229
221
|
domainCount,
|
|
230
|
-
version:
|
|
222
|
+
version: exports.VERSION,
|
|
231
223
|
supportsAsync: true,
|
|
232
224
|
supportsIDN: true,
|
|
233
225
|
supportsAliasDetection: true,
|
|
@@ -238,7 +230,7 @@ function getLibraryStats() {
|
|
|
238
230
|
return {
|
|
239
231
|
providerCount: 0,
|
|
240
232
|
domainCount: 0,
|
|
241
|
-
version:
|
|
233
|
+
version: exports.VERSION,
|
|
242
234
|
supportsAsync: true,
|
|
243
235
|
supportsIDN: true,
|
|
244
236
|
supportsAliasDetection: true,
|
|
@@ -282,7 +274,7 @@ function batchProcessEmails(emails, options = {}) {
|
|
|
282
274
|
// Add normalized email if requested
|
|
283
275
|
if (normalizeEmails && validation.normalizedEmail) {
|
|
284
276
|
try {
|
|
285
|
-
result.normalized = (0,
|
|
277
|
+
result.normalized = (0, api_1.normalizeEmail)(validation.normalizedEmail);
|
|
286
278
|
}
|
|
287
279
|
catch {
|
|
288
280
|
result.normalized = validation.normalizedEmail;
|
|
@@ -300,7 +292,7 @@ function batchProcessEmails(emails, options = {}) {
|
|
|
300
292
|
// Add provider info if requested
|
|
301
293
|
if (includeProviderInfo && validation.normalizedEmail) {
|
|
302
294
|
try {
|
|
303
|
-
const providerResult = (0,
|
|
295
|
+
const providerResult = (0, api_1.getEmailProviderSync)(validation.normalizedEmail);
|
|
304
296
|
result.provider = providerResult.provider?.companyProvider || null;
|
|
305
297
|
result.loginUrl = providerResult.loginUrl;
|
|
306
298
|
}
|
|
@@ -329,8 +321,8 @@ exports.isValidEmailAddress = isValidEmail;
|
|
|
329
321
|
/**
|
|
330
322
|
* Library metadata (legacy constants)
|
|
331
323
|
*/
|
|
332
|
-
exports.PROVIDER_COUNT =
|
|
333
|
-
exports.DOMAIN_COUNT =
|
|
324
|
+
exports.PROVIDER_COUNT = api_1.Config?.SUPPORTED_PROVIDERS_COUNT ?? 130;
|
|
325
|
+
exports.DOMAIN_COUNT = api_1.Config?.SUPPORTED_DOMAINS_COUNT ?? 218;
|
|
334
326
|
/**
|
|
335
327
|
* Default export for convenience
|
|
336
328
|
*
|
|
@@ -343,14 +335,14 @@ exports.DOMAIN_COUNT = 178;
|
|
|
343
335
|
*/
|
|
344
336
|
exports.default = {
|
|
345
337
|
// Core functions
|
|
346
|
-
getEmailProvider:
|
|
347
|
-
getEmailProviderSync:
|
|
348
|
-
getEmailProviderFast:
|
|
338
|
+
getEmailProvider: api_1.getEmailProvider,
|
|
339
|
+
getEmailProviderSync: api_1.getEmailProviderSync,
|
|
340
|
+
getEmailProviderFast: api_1.getEmailProviderFast,
|
|
349
341
|
// Validation
|
|
350
342
|
validateEmailAddress,
|
|
351
343
|
isValidEmail,
|
|
352
|
-
normalizeEmail:
|
|
353
|
-
emailsMatch:
|
|
344
|
+
normalizeEmail: api_1.normalizeEmail,
|
|
345
|
+
emailsMatch: api_1.emailsMatch,
|
|
354
346
|
// Utilities
|
|
355
347
|
getSupportedProviders,
|
|
356
348
|
isEmailProviderSupported,
|
|
@@ -358,16 +350,25 @@ exports.default = {
|
|
|
358
350
|
getLibraryStats,
|
|
359
351
|
batchProcessEmails,
|
|
360
352
|
// Advanced
|
|
361
|
-
loadProviders:
|
|
362
|
-
detectProviderConcurrent:
|
|
363
|
-
validateInternationalEmail:
|
|
353
|
+
loadProviders: loader_1.loadProviders,
|
|
354
|
+
detectProviderConcurrent: concurrent_dns_1.detectProviderConcurrent,
|
|
355
|
+
validateInternationalEmail: idn_1.validateInternationalEmail,
|
|
364
356
|
// Constants
|
|
365
|
-
Config:
|
|
357
|
+
Config: api_1.Config,
|
|
366
358
|
PROVIDER_COUNT: exports.PROVIDER_COUNT,
|
|
367
359
|
DOMAIN_COUNT: exports.DOMAIN_COUNT
|
|
368
360
|
};
|
|
369
361
|
/**
|
|
370
362
|
* Version information
|
|
371
363
|
*/
|
|
372
|
-
|
|
364
|
+
function readPackageVersion() {
|
|
365
|
+
try {
|
|
366
|
+
const pkg = require('../package.json');
|
|
367
|
+
return pkg.version || 'unknown';
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
return 'unknown';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
exports.VERSION = readPackageVersion();
|
|
373
374
|
//# sourceMappingURL=index.js.map
|
package/dist/loader.js
CHANGED
|
@@ -10,9 +10,8 @@ exports.clearCache = clearCache;
|
|
|
10
10
|
exports.getLoadingStats = getLoadingStats;
|
|
11
11
|
exports.loadProviders = loadProviders;
|
|
12
12
|
exports.loadProvidersDebug = loadProvidersDebug;
|
|
13
|
-
const fs_1 = require("fs");
|
|
14
13
|
const path_1 = require("path");
|
|
15
|
-
const
|
|
14
|
+
const provider_store_1 = require("./provider-store");
|
|
16
15
|
/**
|
|
17
16
|
* Internal cached data
|
|
18
17
|
*/
|
|
@@ -25,35 +24,6 @@ let loadingStats = null;
|
|
|
25
24
|
const DEFAULT_CONFIG = {
|
|
26
25
|
debug: false
|
|
27
26
|
};
|
|
28
|
-
/**
|
|
29
|
-
* Convert compressed provider to EmailProvider format
|
|
30
|
-
*/
|
|
31
|
-
function convertProviderToEmailProvider(compressedProvider) {
|
|
32
|
-
if (!compressedProvider.type) {
|
|
33
|
-
console.warn(`Missing type for provider ${compressedProvider.id}`);
|
|
34
|
-
}
|
|
35
|
-
const provider = {
|
|
36
|
-
companyProvider: compressedProvider.companyProvider,
|
|
37
|
-
loginUrl: compressedProvider.loginUrl || null,
|
|
38
|
-
domains: compressedProvider.domains || [],
|
|
39
|
-
type: compressedProvider.type,
|
|
40
|
-
alias: compressedProvider.alias
|
|
41
|
-
};
|
|
42
|
-
// Include DNS detection patterns for business email services and proxy services
|
|
43
|
-
const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
|
|
44
|
-
compressedProvider.type === 'proxy_service';
|
|
45
|
-
if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
|
|
46
|
-
provider.customDomainDetection = {};
|
|
47
|
-
if (compressedProvider.mx?.length) {
|
|
48
|
-
provider.customDomainDetection.mxPatterns = compressedProvider.mx;
|
|
49
|
-
}
|
|
50
|
-
if (compressedProvider.txt?.length) {
|
|
51
|
-
// Decompress TXT patterns
|
|
52
|
-
provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return provider;
|
|
56
|
-
}
|
|
57
27
|
/**
|
|
58
28
|
* Internal provider data loader with configuration
|
|
59
29
|
*/
|
|
@@ -73,14 +43,8 @@ function loadProvidersInternal(config = {}) {
|
|
|
73
43
|
const dataPath = mergedConfig.path || (0, path_1.join)(basePath, 'emailproviders.json');
|
|
74
44
|
if (mergedConfig.debug)
|
|
75
45
|
console.log('🔄 Loading provider data...');
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
// Validate format
|
|
79
|
-
if (!data.version || !data.providers || !Array.isArray(data.providers)) {
|
|
80
|
-
throw new Error('Invalid provider data format');
|
|
81
|
-
}
|
|
82
|
-
const providers = data.providers.map(convertProviderToEmailProvider);
|
|
83
|
-
const fileSize = content.length;
|
|
46
|
+
const { data, fileSize } = (0, provider_store_1.readProvidersDataFile)(dataPath);
|
|
47
|
+
const providers = data.providers.map(provider_store_1.convertProviderToEmailProviderShared);
|
|
84
48
|
if (mergedConfig.debug) {
|
|
85
49
|
console.log(`✅ Loaded ${providers.length} providers`);
|
|
86
50
|
console.log(`📊 File size: ${(fileSize / 1024).toFixed(1)} KB`);
|
|
@@ -120,14 +84,8 @@ function buildDomainMap(providers) {
|
|
|
120
84
|
if (cachedDomainMap) {
|
|
121
85
|
return cachedDomainMap;
|
|
122
86
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for (const domain of provider.domains) {
|
|
126
|
-
domainMap.set(domain.toLowerCase(), provider);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
cachedDomainMap = domainMap;
|
|
130
|
-
return domainMap;
|
|
87
|
+
cachedDomainMap = (0, provider_store_1.buildDomainMapShared)(providers);
|
|
88
|
+
return cachedDomainMap;
|
|
131
89
|
}
|
|
132
90
|
/**
|
|
133
91
|
* Clear all caches (useful for testing or hot reloading)
|
|
@@ -4,7 +4,17 @@
|
|
|
4
4
|
* Integrates URL validation and hash verification to load and validate
|
|
5
5
|
* email provider data with security checks.
|
|
6
6
|
*/
|
|
7
|
-
import type { EmailProvider } from './
|
|
7
|
+
import type { EmailProvider } from './api';
|
|
8
|
+
type MiddlewareRequestLike = Record<string, unknown> & {
|
|
9
|
+
secureProviders?: EmailProvider[];
|
|
10
|
+
securityReport?: LoadResult['securityReport'];
|
|
11
|
+
};
|
|
12
|
+
type MiddlewareResponseLike = {
|
|
13
|
+
status: (code: number) => {
|
|
14
|
+
json: (body: unknown) => unknown;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
type MiddlewareNextLike = () => void;
|
|
8
18
|
export interface LoadResult {
|
|
9
19
|
success: boolean;
|
|
10
20
|
providers: EmailProvider[];
|
|
@@ -43,7 +53,7 @@ interface SecurityMiddlewareOptions {
|
|
|
43
53
|
onSecurityIssue?: (report: LoadResult['securityReport']) => void;
|
|
44
54
|
getProviders?: () => LoadResult;
|
|
45
55
|
}
|
|
46
|
-
export declare function createSecurityMiddleware(options?: SecurityMiddlewareOptions): (req:
|
|
56
|
+
export declare function createSecurityMiddleware(options?: SecurityMiddlewareOptions): (req: MiddlewareRequestLike, res: MiddlewareResponseLike, next: MiddlewareNextLike) => void;
|
|
47
57
|
declare const _default: {
|
|
48
58
|
loadProviders: typeof loadProviders;
|
|
49
59
|
initializeSecurity: typeof initializeSecurity;
|
package/dist/provider-loader.js
CHANGED
|
@@ -10,11 +10,10 @@ exports.clearCache = clearCache;
|
|
|
10
10
|
exports.loadProviders = loadProviders;
|
|
11
11
|
exports.initializeSecurity = initializeSecurity;
|
|
12
12
|
exports.createSecurityMiddleware = createSecurityMiddleware;
|
|
13
|
-
const fs_1 = require("fs");
|
|
14
13
|
const path_1 = require("path");
|
|
15
14
|
const url_validator_1 = require("./url-validator");
|
|
16
15
|
const hash_verifier_1 = require("./hash-verifier");
|
|
17
|
-
const
|
|
16
|
+
const provider_store_1 = require("./provider-store");
|
|
18
17
|
// Cache for load results
|
|
19
18
|
let cachedLoadResult = null;
|
|
20
19
|
/**
|
|
@@ -23,35 +22,6 @@ let cachedLoadResult = null;
|
|
|
23
22
|
function clearCache() {
|
|
24
23
|
cachedLoadResult = null;
|
|
25
24
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Convert compressed provider to EmailProvider format
|
|
28
|
-
*/
|
|
29
|
-
function convertProviderToEmailProvider(compressedProvider) {
|
|
30
|
-
if (!compressedProvider.type) {
|
|
31
|
-
console.warn(`Missing type for provider ${compressedProvider.id}`);
|
|
32
|
-
}
|
|
33
|
-
const provider = {
|
|
34
|
-
companyProvider: compressedProvider.companyProvider,
|
|
35
|
-
loginUrl: compressedProvider.loginUrl || null,
|
|
36
|
-
domains: compressedProvider.domains || [],
|
|
37
|
-
type: compressedProvider.type,
|
|
38
|
-
alias: compressedProvider.alias
|
|
39
|
-
};
|
|
40
|
-
// Include DNS detection patterns for business email services and proxy services
|
|
41
|
-
const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
|
|
42
|
-
compressedProvider.type === 'proxy_service';
|
|
43
|
-
if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
|
|
44
|
-
provider.customDomainDetection = {};
|
|
45
|
-
if (compressedProvider.mx?.length) {
|
|
46
|
-
provider.customDomainDetection.mxPatterns = compressedProvider.mx;
|
|
47
|
-
}
|
|
48
|
-
if (compressedProvider.txt?.length) {
|
|
49
|
-
// Decompress TXT patterns
|
|
50
|
-
provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return provider;
|
|
54
|
-
}
|
|
55
25
|
/**
|
|
56
26
|
* Loads and validates email provider data with security checks
|
|
57
27
|
*
|
|
@@ -83,14 +53,8 @@ function loadProviders(providersPath, expectedHash) {
|
|
|
83
53
|
}
|
|
84
54
|
// Step 2: Load and parse JSON
|
|
85
55
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
// Validate format
|
|
89
|
-
if (!data.version || !data.providers || !Array.isArray(data.providers)) {
|
|
90
|
-
throw new Error('Invalid provider data format');
|
|
91
|
-
}
|
|
92
|
-
// Convert to EmailProvider format
|
|
93
|
-
providers = data.providers.map(convertProviderToEmailProvider);
|
|
56
|
+
const { data } = (0, provider_store_1.readProvidersDataFile)(filePath);
|
|
57
|
+
providers = data.providers.map(provider_store_1.convertProviderToEmailProviderShared);
|
|
94
58
|
}
|
|
95
59
|
catch (error) {
|
|
96
60
|
issues.push(`Failed to load providers file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -178,15 +142,17 @@ function createSecurityMiddleware(options = {}) {
|
|
|
178
142
|
if (options.onSecurityIssue) {
|
|
179
143
|
options.onSecurityIssue(result.securityReport);
|
|
180
144
|
}
|
|
181
|
-
|
|
145
|
+
res.status(500).json({
|
|
182
146
|
error: 'Security validation failed',
|
|
183
147
|
details: result.securityReport
|
|
184
148
|
});
|
|
149
|
+
return;
|
|
185
150
|
}
|
|
186
151
|
// Attach secure providers to request
|
|
187
152
|
req.secureProviders = result.providers;
|
|
188
153
|
req.securityReport = result.securityReport;
|
|
189
154
|
next();
|
|
155
|
+
return;
|
|
190
156
|
};
|
|
191
157
|
}
|
|
192
158
|
exports.default = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EmailProvider } from './api';
|
|
2
|
+
import { Provider, ProvidersData } from './schema';
|
|
3
|
+
export declare function convertProviderToEmailProviderShared(compressedProvider: Provider): EmailProvider;
|
|
4
|
+
export declare function readProvidersDataFile(filePath: string): {
|
|
5
|
+
data: ProvidersData;
|
|
6
|
+
fileSize: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function buildDomainMapShared(providers: EmailProvider[]): Map<string, EmailProvider>;
|
|
9
|
+
//# sourceMappingURL=provider-store.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertProviderToEmailProviderShared = convertProviderToEmailProviderShared;
|
|
4
|
+
exports.readProvidersDataFile = readProvidersDataFile;
|
|
5
|
+
exports.buildDomainMapShared = buildDomainMapShared;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const schema_1 = require("./schema");
|
|
8
|
+
function convertProviderToEmailProviderShared(compressedProvider) {
|
|
9
|
+
if (!compressedProvider.type) {
|
|
10
|
+
console.warn(`Missing type for provider ${compressedProvider.id}`);
|
|
11
|
+
}
|
|
12
|
+
const provider = {
|
|
13
|
+
companyProvider: compressedProvider.companyProvider,
|
|
14
|
+
loginUrl: compressedProvider.loginUrl || null,
|
|
15
|
+
domains: compressedProvider.domains || [],
|
|
16
|
+
type: compressedProvider.type,
|
|
17
|
+
alias: compressedProvider.alias
|
|
18
|
+
};
|
|
19
|
+
const needsCustomDomainDetection = compressedProvider.type === 'custom_provider' ||
|
|
20
|
+
compressedProvider.type === 'proxy_service';
|
|
21
|
+
if (needsCustomDomainDetection && (compressedProvider.mx?.length || compressedProvider.txt?.length)) {
|
|
22
|
+
provider.customDomainDetection = {};
|
|
23
|
+
if (compressedProvider.mx?.length) {
|
|
24
|
+
provider.customDomainDetection.mxPatterns = compressedProvider.mx;
|
|
25
|
+
}
|
|
26
|
+
if (compressedProvider.txt?.length) {
|
|
27
|
+
provider.customDomainDetection.txtPatterns = compressedProvider.txt.map(schema_1.decompressTxtPattern);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return provider;
|
|
31
|
+
}
|
|
32
|
+
function readProvidersDataFile(filePath) {
|
|
33
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf8');
|
|
34
|
+
const data = JSON.parse(content);
|
|
35
|
+
if (!data.version || !data.providers || !Array.isArray(data.providers)) {
|
|
36
|
+
throw new Error('Invalid provider data format');
|
|
37
|
+
}
|
|
38
|
+
return { data, fileSize: content.length };
|
|
39
|
+
}
|
|
40
|
+
function buildDomainMapShared(providers) {
|
|
41
|
+
const domainMap = new Map();
|
|
42
|
+
for (const provider of providers) {
|
|
43
|
+
for (const domain of provider.domains) {
|
|
44
|
+
domainMap.set(domain.toLowerCase(), provider);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return domainMap;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=provider-store.js.map
|
package/dist/url-validator.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Provides validation and allowlisting for email provider URLs to prevent
|
|
5
5
|
* malicious redirects and supply chain attacks.
|
|
6
6
|
*/
|
|
7
|
+
type ProviderUrlLike = {
|
|
8
|
+
companyProvider?: string;
|
|
9
|
+
loginUrl?: string | null;
|
|
10
|
+
};
|
|
7
11
|
export interface URLValidationResult {
|
|
8
12
|
isValid: boolean;
|
|
9
13
|
reason?: string;
|
|
@@ -23,7 +27,7 @@ export declare function validateEmailProviderUrl(url: string): URLValidationResu
|
|
|
23
27
|
* @param providers - Array of email providers to validate
|
|
24
28
|
* @returns Array of validation results
|
|
25
29
|
*/
|
|
26
|
-
export declare function validateAllProviderUrls(providers:
|
|
30
|
+
export declare function validateAllProviderUrls(providers: ProviderUrlLike[]): Array<{
|
|
27
31
|
provider: string;
|
|
28
32
|
url: string;
|
|
29
33
|
validation: URLValidationResult;
|
|
@@ -34,7 +38,7 @@ export declare function validateAllProviderUrls(providers: any[]): Array<{
|
|
|
34
38
|
* @param providers - Array of email providers to audit
|
|
35
39
|
* @returns Security audit report
|
|
36
40
|
*/
|
|
37
|
-
export declare function auditProviderSecurity(providers:
|
|
41
|
+
export declare function auditProviderSecurity(providers: ProviderUrlLike[]): {
|
|
38
42
|
total: number;
|
|
39
43
|
valid: number;
|
|
40
44
|
invalid: number;
|
|
@@ -45,4 +49,5 @@ export declare function auditProviderSecurity(providers: any[]): {
|
|
|
45
49
|
}[];
|
|
46
50
|
report: string;
|
|
47
51
|
};
|
|
52
|
+
export {};
|
|
48
53
|
//# sourceMappingURL=url-validator.d.ts.map
|
package/dist/url-validator.js
CHANGED
|
@@ -9,19 +9,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.validateEmailProviderUrl = validateEmailProviderUrl;
|
|
10
10
|
exports.validateAllProviderUrls = validateAllProviderUrls;
|
|
11
11
|
exports.auditProviderSecurity = auditProviderSecurity;
|
|
12
|
-
const
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const hash_verifier_1 = require("./hash-verifier");
|
|
13
15
|
/**
|
|
14
16
|
* Get allowlisted domains from provider data
|
|
15
17
|
* Only URLs from these domains will be considered safe.
|
|
16
18
|
*/
|
|
17
19
|
function getAllowedDomains() {
|
|
18
|
-
const
|
|
20
|
+
const filePath = (0, path_1.join)(__dirname, '..', 'providers', 'emailproviders.json');
|
|
21
|
+
const integrity = (0, hash_verifier_1.verifyProvidersIntegrity)(filePath);
|
|
22
|
+
if (!integrity.isValid) {
|
|
23
|
+
return new Set();
|
|
24
|
+
}
|
|
25
|
+
if (cachedAllowedDomains && cachedAllowlistHash === integrity.actualHash) {
|
|
26
|
+
return cachedAllowedDomains;
|
|
27
|
+
}
|
|
28
|
+
const fileContent = (0, fs_1.readFileSync)(filePath, 'utf8');
|
|
29
|
+
const data = JSON.parse(fileContent);
|
|
30
|
+
const providers = data.providers;
|
|
19
31
|
const allowedDomains = new Set();
|
|
20
32
|
for (const provider of providers) {
|
|
21
33
|
if (provider.loginUrl) {
|
|
22
34
|
try {
|
|
23
35
|
const url = new URL(provider.loginUrl);
|
|
24
|
-
allowedDomains.add(url.hostname);
|
|
36
|
+
allowedDomains.add((0, idn_1.domainToPunycode)(url.hostname.toLowerCase()));
|
|
25
37
|
}
|
|
26
38
|
catch {
|
|
27
39
|
// Skip invalid URLs
|
|
@@ -29,8 +41,12 @@ function getAllowedDomains() {
|
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
}
|
|
44
|
+
cachedAllowedDomains = allowedDomains;
|
|
45
|
+
cachedAllowlistHash = integrity.actualHash;
|
|
32
46
|
return allowedDomains;
|
|
33
47
|
}
|
|
48
|
+
let cachedAllowedDomains = null;
|
|
49
|
+
let cachedAllowlistHash = null;
|
|
34
50
|
/**
|
|
35
51
|
* Suspicious URL patterns that should always be rejected
|
|
36
52
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikkelscheike/email-provider-links",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "TypeScript library for email provider detection with
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "TypeScript library for email provider detection with 130 providers (218 domains), concurrent DNS resolution, optimized performance, 94.65% test coverage, and enterprise security for login and password reset flows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|