@push.rocks/smartproxy 16.0.2 → 16.0.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.
Files changed (115) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/core/models/index.d.ts +2 -0
  3. package/dist_ts/core/models/index.js +3 -1
  4. package/dist_ts/core/models/route-context.d.ts +62 -0
  5. package/dist_ts/core/models/route-context.js +43 -0
  6. package/dist_ts/core/models/socket-augmentation.d.ts +12 -0
  7. package/dist_ts/core/models/socket-augmentation.js +18 -0
  8. package/dist_ts/core/utils/event-system.d.ts +200 -0
  9. package/dist_ts/core/utils/event-system.js +224 -0
  10. package/dist_ts/core/utils/index.d.ts +7 -0
  11. package/dist_ts/core/utils/index.js +8 -1
  12. package/dist_ts/core/utils/route-manager.d.ts +118 -0
  13. package/dist_ts/core/utils/route-manager.js +383 -0
  14. package/dist_ts/core/utils/route-utils.d.ts +94 -0
  15. package/dist_ts/core/utils/route-utils.js +264 -0
  16. package/dist_ts/core/utils/security-utils.d.ts +111 -0
  17. package/dist_ts/core/utils/security-utils.js +212 -0
  18. package/dist_ts/core/utils/shared-security-manager.d.ts +110 -0
  19. package/dist_ts/core/utils/shared-security-manager.js +252 -0
  20. package/dist_ts/core/utils/template-utils.d.ts +37 -0
  21. package/dist_ts/core/utils/template-utils.js +104 -0
  22. package/dist_ts/core/utils/websocket-utils.d.ts +23 -0
  23. package/dist_ts/core/utils/websocket-utils.js +86 -0
  24. package/dist_ts/http/router/index.d.ts +5 -1
  25. package/dist_ts/http/router/index.js +4 -2
  26. package/dist_ts/http/router/route-router.d.ts +108 -0
  27. package/dist_ts/http/router/route-router.js +393 -0
  28. package/dist_ts/index.d.ts +8 -2
  29. package/dist_ts/index.js +10 -3
  30. package/dist_ts/proxies/index.d.ts +7 -2
  31. package/dist_ts/proxies/index.js +10 -4
  32. package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +21 -0
  33. package/dist_ts/proxies/network-proxy/certificate-manager.js +92 -1
  34. package/dist_ts/proxies/network-proxy/context-creator.d.ts +34 -0
  35. package/dist_ts/proxies/network-proxy/context-creator.js +108 -0
  36. package/dist_ts/proxies/network-proxy/function-cache.d.ts +90 -0
  37. package/dist_ts/proxies/network-proxy/function-cache.js +198 -0
  38. package/dist_ts/proxies/network-proxy/http-request-handler.d.ts +40 -0
  39. package/dist_ts/proxies/network-proxy/http-request-handler.js +256 -0
  40. package/dist_ts/proxies/network-proxy/http2-request-handler.d.ts +24 -0
  41. package/dist_ts/proxies/network-proxy/http2-request-handler.js +201 -0
  42. package/dist_ts/proxies/network-proxy/models/types.d.ts +73 -1
  43. package/dist_ts/proxies/network-proxy/models/types.js +242 -1
  44. package/dist_ts/proxies/network-proxy/network-proxy.d.ts +23 -20
  45. package/dist_ts/proxies/network-proxy/network-proxy.js +149 -60
  46. package/dist_ts/proxies/network-proxy/request-handler.d.ts +38 -5
  47. package/dist_ts/proxies/network-proxy/request-handler.js +584 -198
  48. package/dist_ts/proxies/network-proxy/security-manager.d.ts +65 -0
  49. package/dist_ts/proxies/network-proxy/security-manager.js +255 -0
  50. package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +13 -2
  51. package/dist_ts/proxies/network-proxy/websocket-handler.js +238 -20
  52. package/dist_ts/proxies/smart-proxy/index.d.ts +1 -1
  53. package/dist_ts/proxies/smart-proxy/index.js +3 -3
  54. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +3 -5
  55. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +56 -4
  56. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +4 -57
  57. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +19 -228
  58. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +81 -0
  59. package/dist_ts/proxies/smart-proxy/port-manager.js +166 -0
  60. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +5 -0
  61. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +131 -15
  62. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +3 -1
  63. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +5 -3
  64. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +5 -178
  65. package/dist_ts/proxies/smart-proxy/route-helpers.js +8 -296
  66. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +11 -2
  67. package/dist_ts/proxies/smart-proxy/route-manager.js +79 -10
  68. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +29 -2
  69. package/dist_ts/proxies/smart-proxy/smart-proxy.js +48 -43
  70. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +67 -1
  71. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +120 -1
  72. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +3 -3
  73. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +27 -5
  74. package/package.json +1 -1
  75. package/readme.md +102 -14
  76. package/readme.plan.md +103 -168
  77. package/ts/00_commitinfo_data.ts +1 -1
  78. package/ts/core/models/index.ts +2 -0
  79. package/ts/core/models/route-context.ts +113 -0
  80. package/ts/core/models/socket-augmentation.ts +33 -0
  81. package/ts/core/utils/event-system.ts +376 -0
  82. package/ts/core/utils/index.ts +7 -0
  83. package/ts/core/utils/route-manager.ts +489 -0
  84. package/ts/core/utils/route-utils.ts +312 -0
  85. package/ts/core/utils/security-utils.ts +309 -0
  86. package/ts/core/utils/shared-security-manager.ts +333 -0
  87. package/ts/core/utils/template-utils.ts +124 -0
  88. package/ts/core/utils/websocket-utils.ts +81 -0
  89. package/ts/http/router/index.ts +8 -1
  90. package/ts/http/router/route-router.ts +482 -0
  91. package/ts/index.ts +14 -2
  92. package/ts/proxies/index.ts +12 -3
  93. package/ts/proxies/network-proxy/certificate-manager.ts +114 -10
  94. package/ts/proxies/network-proxy/context-creator.ts +145 -0
  95. package/ts/proxies/network-proxy/function-cache.ts +259 -0
  96. package/ts/proxies/network-proxy/http-request-handler.ts +330 -0
  97. package/ts/proxies/network-proxy/http2-request-handler.ts +255 -0
  98. package/ts/proxies/network-proxy/models/types.ts +312 -1
  99. package/ts/proxies/network-proxy/network-proxy.ts +197 -85
  100. package/ts/proxies/network-proxy/request-handler.ts +698 -246
  101. package/ts/proxies/network-proxy/security-manager.ts +298 -0
  102. package/ts/proxies/network-proxy/websocket-handler.ts +276 -33
  103. package/ts/proxies/smart-proxy/index.ts +2 -12
  104. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -4
  105. package/ts/proxies/smart-proxy/models/route-types.ts +77 -10
  106. package/ts/proxies/smart-proxy/network-proxy-bridge.ts +20 -257
  107. package/ts/proxies/smart-proxy/port-manager.ts +195 -0
  108. package/ts/proxies/smart-proxy/route-connection-handler.ts +156 -21
  109. package/ts/proxies/smart-proxy/route-manager.ts +98 -14
  110. package/ts/proxies/smart-proxy/smart-proxy.ts +56 -55
  111. package/ts/proxies/smart-proxy/utils/route-helpers.ts +167 -1
  112. package/ts/proxies/smart-proxy/utils/route-validators.ts +24 -5
  113. package/ts/proxies/smart-proxy/domain-config-manager.ts.bak +0 -441
  114. package/ts/proxies/smart-proxy/route-helpers/index.ts +0 -9
  115. package/ts/proxies/smart-proxy/route-helpers.ts +0 -498
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Route matching utilities for SmartProxy components
3
+ *
4
+ * Contains shared logic for domain matching, path matching, and IP matching
5
+ * to be used by different proxy components throughout the system.
6
+ */
7
+ /**
8
+ * Match a domain pattern against a domain
9
+ *
10
+ * @param pattern Domain pattern with optional wildcards (e.g., "*.example.com")
11
+ * @param domain Domain to match against the pattern
12
+ * @returns Whether the domain matches the pattern
13
+ */
14
+ export function matchDomain(pattern, domain) {
15
+ // Handle exact match (case-insensitive)
16
+ if (pattern.toLowerCase() === domain.toLowerCase()) {
17
+ return true;
18
+ }
19
+ // Handle wildcard pattern
20
+ if (pattern.includes('*')) {
21
+ const regexPattern = pattern
22
+ .replace(/\./g, '\\.') // Escape dots
23
+ .replace(/\*/g, '.*'); // Convert * to .*
24
+ const regex = new RegExp(`^${regexPattern}$`, 'i');
25
+ return regex.test(domain);
26
+ }
27
+ return false;
28
+ }
29
+ /**
30
+ * Match domains from a route against a given domain
31
+ *
32
+ * @param domains Array or single domain pattern to match against
33
+ * @param domain Domain to match
34
+ * @returns Whether the domain matches any of the patterns
35
+ */
36
+ export function matchRouteDomain(domains, domain) {
37
+ // If no domains specified in the route, match all domains
38
+ if (!domains) {
39
+ return true;
40
+ }
41
+ // If no domain in the request, can't match domain-specific routes
42
+ if (!domain) {
43
+ return false;
44
+ }
45
+ const patterns = Array.isArray(domains) ? domains : [domains];
46
+ return patterns.some(pattern => matchDomain(pattern, domain));
47
+ }
48
+ /**
49
+ * Match a path pattern against a path
50
+ *
51
+ * @param pattern Path pattern with optional wildcards
52
+ * @param path Path to match against the pattern
53
+ * @returns Whether the path matches the pattern
54
+ */
55
+ export function matchPath(pattern, path) {
56
+ // Handle exact match
57
+ if (pattern === path) {
58
+ return true;
59
+ }
60
+ // Handle simple wildcard at the end (like /api/*)
61
+ if (pattern.endsWith('*')) {
62
+ const prefix = pattern.slice(0, -1);
63
+ return path.startsWith(prefix);
64
+ }
65
+ // Handle more complex wildcard patterns
66
+ if (pattern.includes('*')) {
67
+ const regexPattern = pattern
68
+ .replace(/\./g, '\\.') // Escape dots
69
+ .replace(/\*/g, '.*') // Convert * to .*
70
+ .replace(/\//g, '\\/'); // Escape slashes
71
+ const regex = new RegExp(`^${regexPattern}$`);
72
+ return regex.test(path);
73
+ }
74
+ return false;
75
+ }
76
+ /**
77
+ * Parse CIDR notation into subnet and mask bits
78
+ *
79
+ * @param cidr CIDR string (e.g., "192.168.1.0/24")
80
+ * @returns Object with subnet and bits, or null if invalid
81
+ */
82
+ export function parseCidr(cidr) {
83
+ try {
84
+ const [subnet, bitsStr] = cidr.split('/');
85
+ const bits = parseInt(bitsStr, 10);
86
+ if (isNaN(bits) || bits < 0 || bits > 32) {
87
+ return null;
88
+ }
89
+ return { subnet, bits };
90
+ }
91
+ catch (e) {
92
+ return null;
93
+ }
94
+ }
95
+ /**
96
+ * Convert an IP address to a numeric value
97
+ *
98
+ * @param ip IPv4 address string (e.g., "192.168.1.1")
99
+ * @returns Numeric representation of the IP
100
+ */
101
+ export function ipToNumber(ip) {
102
+ // Handle IPv6-mapped IPv4 addresses (::ffff:192.168.1.1)
103
+ if (ip.startsWith('::ffff:')) {
104
+ ip = ip.slice(7);
105
+ }
106
+ const parts = ip.split('.').map(part => parseInt(part, 10));
107
+ return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
108
+ }
109
+ /**
110
+ * Match an IP against a CIDR pattern
111
+ *
112
+ * @param cidr CIDR pattern (e.g., "192.168.1.0/24")
113
+ * @param ip IP to match against the pattern
114
+ * @returns Whether the IP is in the CIDR range
115
+ */
116
+ export function matchIpCidr(cidr, ip) {
117
+ const parsed = parseCidr(cidr);
118
+ if (!parsed) {
119
+ return false;
120
+ }
121
+ try {
122
+ const { subnet, bits } = parsed;
123
+ // Normalize IPv6-mapped IPv4 addresses
124
+ const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
125
+ const normalizedSubnet = subnet.startsWith('::ffff:') ? subnet.substring(7) : subnet;
126
+ // Convert IP addresses to numeric values
127
+ const ipNum = ipToNumber(normalizedIp);
128
+ const subnetNum = ipToNumber(normalizedSubnet);
129
+ // Calculate subnet mask
130
+ const maskNum = ~(2 ** (32 - bits) - 1);
131
+ // Check if IP is in subnet
132
+ return (ipNum & maskNum) === (subnetNum & maskNum);
133
+ }
134
+ catch (e) {
135
+ return false;
136
+ }
137
+ }
138
+ /**
139
+ * Match an IP pattern against an IP
140
+ *
141
+ * @param pattern IP pattern (exact, CIDR, or with wildcards)
142
+ * @param ip IP to match against the pattern
143
+ * @returns Whether the IP matches the pattern
144
+ */
145
+ export function matchIpPattern(pattern, ip) {
146
+ // Normalize IPv6-mapped IPv4 addresses
147
+ const normalizedIp = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
148
+ const normalizedPattern = pattern.startsWith('::ffff:') ? pattern.substring(7) : pattern;
149
+ // Handle exact match with all variations
150
+ if (pattern === ip || normalizedPattern === normalizedIp ||
151
+ pattern === normalizedIp || normalizedPattern === ip) {
152
+ return true;
153
+ }
154
+ // Handle "all" wildcard
155
+ if (pattern === '*' || normalizedPattern === '*') {
156
+ return true;
157
+ }
158
+ // Handle CIDR notation (e.g., 192.168.1.0/24)
159
+ if (pattern.includes('/')) {
160
+ return matchIpCidr(pattern, normalizedIp) ||
161
+ (normalizedPattern !== pattern && matchIpCidr(normalizedPattern, normalizedIp));
162
+ }
163
+ // Handle glob pattern (e.g., 192.168.1.*)
164
+ if (pattern.includes('*')) {
165
+ const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
166
+ const regex = new RegExp(`^${regexPattern}$`);
167
+ if (regex.test(ip) || regex.test(normalizedIp)) {
168
+ return true;
169
+ }
170
+ // If pattern was normalized, also test with normalized pattern
171
+ if (normalizedPattern !== pattern) {
172
+ const normalizedRegexPattern = normalizedPattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
173
+ const normalizedRegex = new RegExp(`^${normalizedRegexPattern}$`);
174
+ return normalizedRegex.test(ip) || normalizedRegex.test(normalizedIp);
175
+ }
176
+ }
177
+ return false;
178
+ }
179
+ /**
180
+ * Match an IP against allowed and blocked IP patterns
181
+ *
182
+ * @param ip IP to check
183
+ * @param allowedIps Array of allowed IP patterns
184
+ * @param blockedIps Array of blocked IP patterns
185
+ * @returns Whether the IP is allowed
186
+ */
187
+ export function isIpAuthorized(ip, allowedIps = ['*'], blockedIps = []) {
188
+ // Check blocked IPs first
189
+ if (blockedIps.length > 0) {
190
+ for (const pattern of blockedIps) {
191
+ if (matchIpPattern(pattern, ip)) {
192
+ return false; // IP is blocked
193
+ }
194
+ }
195
+ }
196
+ // If there are allowed IPs, check them
197
+ if (allowedIps.length > 0) {
198
+ // Special case: if '*' is in allowed IPs, all non-blocked IPs are allowed
199
+ if (allowedIps.includes('*')) {
200
+ return true;
201
+ }
202
+ for (const pattern of allowedIps) {
203
+ if (matchIpPattern(pattern, ip)) {
204
+ return true; // IP is allowed
205
+ }
206
+ }
207
+ return false; // IP not in allowed list
208
+ }
209
+ // No allowed IPs specified, so IP is allowed by default
210
+ return true;
211
+ }
212
+ /**
213
+ * Match an HTTP header pattern against a header value
214
+ *
215
+ * @param pattern Expected header value (string or RegExp)
216
+ * @param value Actual header value
217
+ * @returns Whether the header matches the pattern
218
+ */
219
+ export function matchHeader(pattern, value) {
220
+ if (typeof pattern === 'string') {
221
+ return pattern === value;
222
+ }
223
+ else if (pattern instanceof RegExp) {
224
+ return pattern.test(value);
225
+ }
226
+ return false;
227
+ }
228
+ /**
229
+ * Calculate route specificity score
230
+ * Higher score means more specific matching criteria
231
+ *
232
+ * @param match Match criteria to evaluate
233
+ * @returns Numeric specificity score
234
+ */
235
+ export function calculateRouteSpecificity(match) {
236
+ let score = 0;
237
+ // Path is very specific
238
+ if (match.path) {
239
+ // More specific if it doesn't use wildcards
240
+ score += match.path.includes('*') ? 3 : 4;
241
+ }
242
+ // Domain is next most specific
243
+ if (match.domains) {
244
+ const domains = Array.isArray(match.domains) ? match.domains : [match.domains];
245
+ // More domains or more specific domains (without wildcards) increase specificity
246
+ score += domains.length;
247
+ // Add bonus for exact domains (without wildcards)
248
+ score += domains.some(d => !d.includes('*')) ? 1 : 0;
249
+ }
250
+ // Headers are quite specific
251
+ if (match.headers) {
252
+ score += Object.keys(match.headers).length * 2;
253
+ }
254
+ // Client IP adds some specificity
255
+ if (match.clientIp && match.clientIp.length > 0) {
256
+ score += 1;
257
+ }
258
+ // TLS version adds minimal specificity
259
+ if (match.tlsVersion && match.tlsVersion.length > 0) {
260
+ score += 1;
261
+ }
262
+ return score;
263
+ }
264
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL3JvdXRlLXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBRUg7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxPQUFlLEVBQUUsTUFBYztJQUN6RCx3Q0FBd0M7SUFDeEMsSUFBSSxPQUFPLENBQUMsV0FBVyxFQUFFLEtBQUssTUFBTSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7UUFDbkQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsMEJBQTBCO0lBQzFCLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzFCLE1BQU0sWUFBWSxHQUFHLE9BQU87YUFDekIsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBRSxjQUFjO2FBQ3JDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBRSxrQkFBa0I7UUFFNUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxZQUFZLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNuRCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDNUIsQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxPQUFzQyxFQUFFLE1BQTBCO0lBQ2pHLDBEQUEwRDtJQUMxRCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxrRUFBa0U7SUFDbEUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1osT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxP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
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Security utilities for IP validation, rate limiting,
3
+ * authentication, and other security features
4
+ */
5
+ /**
6
+ * Result of IP validation
7
+ */
8
+ export interface IIpValidationResult {
9
+ allowed: boolean;
10
+ reason?: string;
11
+ }
12
+ /**
13
+ * IP connection tracking information
14
+ */
15
+ export interface IIpConnectionInfo {
16
+ connections: Set<string>;
17
+ timestamps: number[];
18
+ ipVariants: string[];
19
+ }
20
+ /**
21
+ * Rate limit tracking
22
+ */
23
+ export interface IRateLimitInfo {
24
+ count: number;
25
+ expiry: number;
26
+ }
27
+ /**
28
+ * Logger interface for security utilities
29
+ */
30
+ export interface ISecurityLogger {
31
+ info: (message: string, ...args: any[]) => void;
32
+ warn: (message: string, ...args: any[]) => void;
33
+ error: (message: string, ...args: any[]) => void;
34
+ debug?: (message: string, ...args: any[]) => void;
35
+ }
36
+ /**
37
+ * Normalize IP addresses for comparison
38
+ * Handles IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
39
+ *
40
+ * @param ip IP address to normalize
41
+ * @returns Array of equivalent IP representations
42
+ */
43
+ export declare function normalizeIP(ip: string): string[];
44
+ /**
45
+ * Check if an IP is authorized based on allow and block lists
46
+ *
47
+ * @param ip - The IP address to check
48
+ * @param allowedIPs - Array of allowed IP patterns
49
+ * @param blockedIPs - Array of blocked IP patterns
50
+ * @returns Whether the IP is authorized
51
+ */
52
+ export declare function isIPAuthorized(ip: string, allowedIPs?: string[], blockedIPs?: string[]): boolean;
53
+ /**
54
+ * Check if an IP exceeds maximum connections
55
+ *
56
+ * @param ip - The IP address to check
57
+ * @param ipConnectionsMap - Map of IPs to connection info
58
+ * @param maxConnectionsPerIP - Maximum allowed connections per IP
59
+ * @returns Result with allowed status and reason if blocked
60
+ */
61
+ export declare function checkMaxConnections(ip: string, ipConnectionsMap: Map<string, IIpConnectionInfo>, maxConnectionsPerIP: number): IIpValidationResult;
62
+ /**
63
+ * Check if an IP exceeds connection rate limit
64
+ *
65
+ * @param ip - The IP address to check
66
+ * @param ipConnectionsMap - Map of IPs to connection info
67
+ * @param rateLimit - Maximum connections per minute
68
+ * @returns Result with allowed status and reason if blocked
69
+ */
70
+ export declare function checkConnectionRate(ip: string, ipConnectionsMap: Map<string, IIpConnectionInfo>, rateLimit: number): IIpValidationResult;
71
+ /**
72
+ * Track a connection for an IP
73
+ *
74
+ * @param ip - The IP address
75
+ * @param connectionId - The connection ID to track
76
+ * @param ipConnectionsMap - Map of IPs to connection info
77
+ */
78
+ export declare function trackConnection(ip: string, connectionId: string, ipConnectionsMap: Map<string, IIpConnectionInfo>): void;
79
+ /**
80
+ * Remove connection tracking for an IP
81
+ *
82
+ * @param ip - The IP address
83
+ * @param connectionId - The connection ID to remove
84
+ * @param ipConnectionsMap - Map of IPs to connection info
85
+ */
86
+ export declare function removeConnection(ip: string, connectionId: string, ipConnectionsMap: Map<string, IIpConnectionInfo>): void;
87
+ /**
88
+ * Clean up expired rate limits
89
+ *
90
+ * @param rateLimits - Map of rate limits to clean up
91
+ * @param logger - Logger for debug messages
92
+ */
93
+ export declare function cleanupExpiredRateLimits(rateLimits: Map<string, Map<string, IRateLimitInfo>>, logger?: ISecurityLogger): void;
94
+ /**
95
+ * Generate basic auth header value from username and password
96
+ *
97
+ * @param username - The username
98
+ * @param password - The password
99
+ * @returns Base64 encoded basic auth string
100
+ */
101
+ export declare function generateBasicAuthHeader(username: string, password: string): string;
102
+ /**
103
+ * Parse basic auth header
104
+ *
105
+ * @param authHeader - The Authorization header value
106
+ * @returns Username and password, or null if invalid
107
+ */
108
+ export declare function parseBasicAuthHeader(authHeader: string): {
109
+ username: string;
110
+ password: string;
111
+ } | null;
@@ -0,0 +1,212 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { matchIpPattern, ipToNumber, matchIpCidr } from './route-utils.js';
3
+ /**
4
+ * Normalize IP addresses for comparison
5
+ * Handles IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
6
+ *
7
+ * @param ip IP address to normalize
8
+ * @returns Array of equivalent IP representations
9
+ */
10
+ export function normalizeIP(ip) {
11
+ if (!ip)
12
+ return [];
13
+ // Handle IPv4-mapped IPv6 addresses (::ffff:127.0.0.1)
14
+ if (ip.startsWith('::ffff:')) {
15
+ const ipv4 = ip.slice(7);
16
+ return [ip, ipv4];
17
+ }
18
+ // Handle IPv4 addresses by also checking IPv4-mapped form
19
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
20
+ return [ip, `::ffff:${ip}`];
21
+ }
22
+ return [ip];
23
+ }
24
+ /**
25
+ * Check if an IP is authorized based on allow and block lists
26
+ *
27
+ * @param ip - The IP address to check
28
+ * @param allowedIPs - Array of allowed IP patterns
29
+ * @param blockedIPs - Array of blocked IP patterns
30
+ * @returns Whether the IP is authorized
31
+ */
32
+ export function isIPAuthorized(ip, allowedIPs = ['*'], blockedIPs = []) {
33
+ // Skip IP validation if no rules
34
+ if (!ip || (allowedIPs.length === 0 && blockedIPs.length === 0)) {
35
+ return true;
36
+ }
37
+ // First check if IP is blocked - blocked IPs take precedence
38
+ if (blockedIPs.length > 0) {
39
+ for (const pattern of blockedIPs) {
40
+ if (matchIpPattern(pattern, ip)) {
41
+ return false;
42
+ }
43
+ }
44
+ }
45
+ // If allowed IPs list has wildcard, all non-blocked IPs are allowed
46
+ if (allowedIPs.includes('*')) {
47
+ return true;
48
+ }
49
+ // Then check if IP is allowed in the explicit allow list
50
+ if (allowedIPs.length > 0) {
51
+ for (const pattern of allowedIPs) {
52
+ if (matchIpPattern(pattern, ip)) {
53
+ return true;
54
+ }
55
+ }
56
+ // If allowedIPs is specified but no match, deny access
57
+ return false;
58
+ }
59
+ // Default allow if no explicit allow list
60
+ return true;
61
+ }
62
+ /**
63
+ * Check if an IP exceeds maximum connections
64
+ *
65
+ * @param ip - The IP address to check
66
+ * @param ipConnectionsMap - Map of IPs to connection info
67
+ * @param maxConnectionsPerIP - Maximum allowed connections per IP
68
+ * @returns Result with allowed status and reason if blocked
69
+ */
70
+ export function checkMaxConnections(ip, ipConnectionsMap, maxConnectionsPerIP) {
71
+ if (!ipConnectionsMap.has(ip)) {
72
+ return { allowed: true };
73
+ }
74
+ const connectionCount = ipConnectionsMap.get(ip).connections.size;
75
+ if (connectionCount >= maxConnectionsPerIP) {
76
+ return {
77
+ allowed: false,
78
+ reason: `Maximum connections per IP (${maxConnectionsPerIP}) exceeded`
79
+ };
80
+ }
81
+ return { allowed: true };
82
+ }
83
+ /**
84
+ * Check if an IP exceeds connection rate limit
85
+ *
86
+ * @param ip - The IP address to check
87
+ * @param ipConnectionsMap - Map of IPs to connection info
88
+ * @param rateLimit - Maximum connections per minute
89
+ * @returns Result with allowed status and reason if blocked
90
+ */
91
+ export function checkConnectionRate(ip, ipConnectionsMap, rateLimit) {
92
+ const now = Date.now();
93
+ const minute = 60 * 1000;
94
+ // Get or create connection info
95
+ if (!ipConnectionsMap.has(ip)) {
96
+ const info = {
97
+ connections: new Set(),
98
+ timestamps: [now],
99
+ ipVariants: normalizeIP(ip)
100
+ };
101
+ ipConnectionsMap.set(ip, info);
102
+ return { allowed: true };
103
+ }
104
+ // Get timestamps and filter out entries older than 1 minute
105
+ const info = ipConnectionsMap.get(ip);
106
+ const timestamps = info.timestamps.filter(time => now - time < minute);
107
+ timestamps.push(now);
108
+ info.timestamps = timestamps;
109
+ // Check if rate exceeds limit
110
+ if (timestamps.length > rateLimit) {
111
+ return {
112
+ allowed: false,
113
+ reason: `Connection rate limit (${rateLimit}/min) exceeded`
114
+ };
115
+ }
116
+ return { allowed: true };
117
+ }
118
+ /**
119
+ * Track a connection for an IP
120
+ *
121
+ * @param ip - The IP address
122
+ * @param connectionId - The connection ID to track
123
+ * @param ipConnectionsMap - Map of IPs to connection info
124
+ */
125
+ export function trackConnection(ip, connectionId, ipConnectionsMap) {
126
+ if (!ipConnectionsMap.has(ip)) {
127
+ ipConnectionsMap.set(ip, {
128
+ connections: new Set([connectionId]),
129
+ timestamps: [Date.now()],
130
+ ipVariants: normalizeIP(ip)
131
+ });
132
+ return;
133
+ }
134
+ const info = ipConnectionsMap.get(ip);
135
+ info.connections.add(connectionId);
136
+ }
137
+ /**
138
+ * Remove connection tracking for an IP
139
+ *
140
+ * @param ip - The IP address
141
+ * @param connectionId - The connection ID to remove
142
+ * @param ipConnectionsMap - Map of IPs to connection info
143
+ */
144
+ export function removeConnection(ip, connectionId, ipConnectionsMap) {
145
+ if (!ipConnectionsMap.has(ip))
146
+ return;
147
+ const info = ipConnectionsMap.get(ip);
148
+ info.connections.delete(connectionId);
149
+ if (info.connections.size === 0) {
150
+ ipConnectionsMap.delete(ip);
151
+ }
152
+ }
153
+ /**
154
+ * Clean up expired rate limits
155
+ *
156
+ * @param rateLimits - Map of rate limits to clean up
157
+ * @param logger - Logger for debug messages
158
+ */
159
+ export function cleanupExpiredRateLimits(rateLimits, logger) {
160
+ const now = Date.now();
161
+ let totalRemoved = 0;
162
+ for (const [routeId, routeLimits] of rateLimits.entries()) {
163
+ let removed = 0;
164
+ for (const [key, limit] of routeLimits.entries()) {
165
+ if (limit.expiry < now) {
166
+ routeLimits.delete(key);
167
+ removed++;
168
+ totalRemoved++;
169
+ }
170
+ }
171
+ if (removed > 0 && logger?.debug) {
172
+ logger.debug(`Cleaned up ${removed} expired rate limits for route ${routeId}`);
173
+ }
174
+ }
175
+ if (totalRemoved > 0 && logger?.info) {
176
+ logger.info(`Cleaned up ${totalRemoved} expired rate limits total`);
177
+ }
178
+ }
179
+ /**
180
+ * Generate basic auth header value from username and password
181
+ *
182
+ * @param username - The username
183
+ * @param password - The password
184
+ * @returns Base64 encoded basic auth string
185
+ */
186
+ export function generateBasicAuthHeader(username, password) {
187
+ return `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
188
+ }
189
+ /**
190
+ * Parse basic auth header
191
+ *
192
+ * @param authHeader - The Authorization header value
193
+ * @returns Username and password, or null if invalid
194
+ */
195
+ export function parseBasicAuthHeader(authHeader) {
196
+ if (!authHeader || !authHeader.startsWith('Basic ')) {
197
+ return null;
198
+ }
199
+ try {
200
+ const base64 = authHeader.slice(6); // Remove 'Basic '
201
+ const decoded = Buffer.from(base64, 'base64').toString();
202
+ const [username, password] = decoded.split(':');
203
+ if (!username || !password) {
204
+ return null;
205
+ }
206
+ return { username, password };
207
+ }
208
+ catch (err) {
209
+ return null;
210
+ }
211
+ }
212
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJpdHktdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL3NlY3VyaXR5LXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUNMLGNBQWMsRUFDZCxVQUFVLEVBQ1YsV0FBVyxFQUNaLE1BQU0sa0JBQWtCLENBQUM7QUEwQzFCOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsRUFBVTtJQUNwQyxJQUFJLENBQUMsRUFBRTtRQUFFLE9BQU8sRUFBRSxDQUFDO0lBRW5CLHVEQUF1RDtJQUN2RCxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUM3QixNQUFNLElBQUksR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDcEIsQ0FBQztJQUVELDBEQUEwRDtJQUMxRCxJQUFJLHlCQUF5QixDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ3ZDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRCxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDZCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQzVCLEVBQVUsRUFDVixhQUF1QixDQUFDLEdBQUcsQ0FBQyxFQUM1QixhQUF1QixFQUFFO0lBRXpCLGlDQUFpQztJQUNqQyxJQUFJLENBQUMsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ2hFLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVE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