@arcis/node 1.0.0 → 1.2.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 +156 -222
- package/dist/core/index.d.mts +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +13 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +13 -2
- package/dist/core/index.mjs.map +1 -1
- package/dist/index-A-m-pPeW.d.mts +340 -0
- package/dist/index-CgK94hY_.d.mts +532 -0
- package/dist/index-Co5kPRZz.d.ts +340 -0
- package/dist/index-D_bdJcF0.d.ts +532 -0
- package/dist/index.d.mts +144 -108
- package/dist/index.d.ts +144 -108
- package/dist/index.js +1541 -211
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1515 -212
- package/dist/index.mjs.map +1 -1
- package/dist/logging/index.d.mts +1 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.js +12 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs +12 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.js +524 -4
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +517 -5
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/{headers-DBQedhrb.d.mts → pii-CXcHMlnX.d.mts} +156 -2
- package/dist/{headers-BJq2OA0i.d.ts → pii-DhNpl7M3.d.ts} +156 -2
- package/dist/sanitizers/index.d.mts +2 -2
- package/dist/sanitizers/index.d.ts +2 -2
- package/dist/sanitizers/index.js +331 -3
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +321 -4
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/stores/index.d.mts +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs.map +1 -1
- package/dist/{types-BOdL3ZWo.d.mts → types-CsOFHoD9.d.mts} +6 -1
- package/dist/{types-BOdL3ZWo.d.ts → types-CsOFHoD9.d.ts} +6 -1
- package/dist/validation/index.d.mts +2 -2
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +504 -2
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +498 -3
- package/dist/validation/index.mjs.map +1 -1
- package/package.json +114 -109
- package/dist/index-BgHPM7LC.d.ts +0 -129
- package/dist/index-BpT7flAQ.d.ts +0 -255
- package/dist/index-JaFOUKyK.d.mts +0 -255
- package/dist/index-nAgXexwD.d.mts +0 -129
package/dist/index.d.mts
CHANGED
|
@@ -1,139 +1,175 @@
|
|
|
1
|
-
export { C as CorsOptions, S as SecureCookieOptions,
|
|
2
|
-
export { c as createSanitizer, d as detectCommandInjection, a as detectHeaderInjection, b as
|
|
3
|
-
export { F as FileInput, V as ValidateFileOptions,
|
|
1
|
+
export { B as BotCategory, a as BotDetectionResult, b as BotProtectionOptions, C as CorsOptions, c as CsrfOptions, S as SecureCookieOptions, d as SlidingWindowMiddleware, e as SlidingWindowOptions, T as TokenBucketMiddleware, f as TokenBucketOptions, g as arcis, h as arcisFunction, i as botProtection, j as createCors, k as createCsrf, l as createErrorHandler, m as createHeaders, n as createRateLimiter, o as createSecureCookies, p as createSlidingWindowLimiter, q as createTokenBucketLimiter, r as csrfProtection, h as default, s as detectBot, t as enforceSecureCookie, u as errorHandler, v as generateCsrfToken, w as rateLimit, x as safeCors, y as secureCookieDefaults, z as securityHeaders, A as validateCsrfToken } from './index-CgK94hY_.mjs';
|
|
2
|
+
export { P as PiiMatch, F as PiiRedactOptions, G as PiiScanOptions, H as PiiType, c as createSanitizer, d as detectCommandInjection, a as detectHeaderInjection, b as detectJsonpInjection, e as detectNoSqlInjection, f as detectPathTraversal, g as detectPii, h as detectPrototypePollution, i as detectSql, j as detectSsti, k as detectXss, l as detectXxe, o as isDangerousNoSqlKey, p as isDangerousProtoKey, r as redactObjectPii, q as redactPii, s as sanitizeCommand, t as sanitizeHeaderValue, u as sanitizeHeaders, v as sanitizeJsonpCallback, w as sanitizeObject, x as sanitizePath, y as sanitizeSql, z as sanitizeSsti, A as sanitizeString, B as sanitizeXss, C as sanitizeXxe, D as scanObjectPii, E as scanPii } from './pii-CXcHMlnX.mjs';
|
|
3
|
+
export { E as EmailValidationOptions, a as EmailValidationResult, F as FileInput, V as ValidateFileOptions, b as ValidateFileResult, c as ValidateRedirectOptions, d as ValidateRedirectResult, e as ValidateUrlOptions, f as ValidateUrlResult, g as createValidator, i as isDangerousExtension, h as isRedirectSafe, j as isUrlSafe, k as isValidEmailSyntax, s as sanitizeFilename, v as validate, l as validateEmail, m as validateFile, n as validateRedirect, o as validateUrl, p as verifyEmailMx } from './index-A-m-pPeW.mjs';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
4
5
|
export { createRedactor, createSafeLogger, safeLog } from './logging/index.mjs';
|
|
5
6
|
export { MemoryStore, RedisClientLike, RedisStore, RedisStoreOptions, createRedisStore } from './stores/index.mjs';
|
|
6
|
-
export { A as ArcisFunction, a as ArcisMiddleware, b as ArcisOptions, E as ErrorHandlerOptions, F as FieldValidator, H as HeaderOptions, c as HstsOptions, d as HttpError, L as LogOptions, R as RateLimitEntry, e as RateLimitOptions, f as RateLimitResult, g as RateLimitStore, h as RateLimiterMiddleware, S as SafeLogger, i as SanitizeOptions, j as SanitizeResult, T as ThreatInfo, k as ThreatType, V as ValidationConfig, l as ValidationError, m as ValidationResult, n as ValidationSchema } from './types-
|
|
7
|
+
export { A as ArcisFunction, a as ArcisMiddleware, b as ArcisOptions, E as ErrorHandlerOptions, F as FieldValidator, H as HeaderOptions, c as HstsOptions, d as HttpError, L as LogOptions, R as RateLimitEntry, e as RateLimitOptions, f as RateLimitResult, g as RateLimitStore, h as RateLimiterMiddleware, S as SafeLogger, i as SanitizeOptions, j as SanitizeResult, T as ThreatInfo, k as ThreatType, V as ValidationConfig, l as ValidationError, m as ValidationResult, n as ValidationSchema } from './types-CsOFHoD9.mjs';
|
|
7
8
|
export { ArcisError, ArcisValidationError, BLOCKED, ERRORS, HEADERS, INPUT, InputTooLargeError, RATE_LIMIT, REDACTION, RateLimitError, SanitizationError, SecurityThreatError, VALIDATION } from './core/index.mjs';
|
|
8
9
|
import 'express';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* @module @arcis/node/
|
|
12
|
-
*
|
|
12
|
+
* @module @arcis/node/utils/duration
|
|
13
|
+
* Parse human-readable duration strings into milliseconds.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
* localhost, cloud metadata endpoints, or use dangerous protocols.
|
|
15
|
+
* Supports: ms, s, m, h, d
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
|
-
*
|
|
18
|
+
* parseDuration('5m') // 300000
|
|
19
|
+
* parseDuration('2h') // 7200000
|
|
20
|
+
* parseDuration(60000) // 60000 (passthrough)
|
|
21
|
+
* parseDuration('500ms') // 500
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Parse a duration string or number into milliseconds.
|
|
19
25
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* validateUrl('http://localhost/secret') // { safe: false, reason: 'loopback address' }
|
|
24
|
-
* validateUrl('file:///etc/passwd') // { safe: false, reason: 'disallowed protocol: file:' }
|
|
26
|
+
* @param value - Duration string (e.g. "5m", "2h", "30s") or number (ms)
|
|
27
|
+
* @returns Duration in milliseconds
|
|
28
|
+
* @throws {Error} If the value is not a valid duration
|
|
25
29
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
30
|
+
* @example
|
|
31
|
+
* parseDuration('15m') // 900000
|
|
32
|
+
* parseDuration('1d') // 86400000
|
|
33
|
+
* parseDuration('500ms') // 500
|
|
34
|
+
* parseDuration(60000) // 60000
|
|
28
35
|
*/
|
|
29
|
-
|
|
30
|
-
interface ValidateUrlOptions {
|
|
31
|
-
/** Allowed protocols. Default: ['http:', 'https:'] */
|
|
32
|
-
allowedProtocols?: string[];
|
|
33
|
-
/** Additional hostnames to block (e.g., internal service names) */
|
|
34
|
-
blockedHosts?: string[];
|
|
35
|
-
/** Additional hostnames to always allow (bypass IP checks) */
|
|
36
|
-
allowedHosts?: string[];
|
|
37
|
-
/** Allow localhost/loopback. Default: false */
|
|
38
|
-
allowLocalhost?: boolean;
|
|
39
|
-
/** Allow private/internal IPs. Default: false */
|
|
40
|
-
allowPrivate?: boolean;
|
|
41
|
-
}
|
|
42
|
-
/** Result of URL validation */
|
|
43
|
-
interface ValidateUrlResult {
|
|
44
|
-
/** Whether the URL is safe to fetch */
|
|
45
|
-
safe: boolean;
|
|
46
|
-
/** Reason the URL was blocked (only set when safe=false) */
|
|
47
|
-
reason?: string;
|
|
48
|
-
}
|
|
36
|
+
declare function parseDuration(value: string | number): number;
|
|
49
37
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 2. Allowed protocol (default: http, https only)
|
|
55
|
-
* 3. Not localhost/loopback (127.x.x.x, ::1, localhost)
|
|
56
|
-
* 4. Not private IP (10.x, 172.16-31.x, 192.168.x)
|
|
57
|
-
* 5. Not link-local (169.254.x.x — includes AWS/GCP/Azure metadata)
|
|
58
|
-
* 6. Not blocked hostname
|
|
59
|
-
* 7. No credentials in URL (user:pass@host)
|
|
60
|
-
*
|
|
61
|
-
* @param url - The URL string to validate
|
|
62
|
-
* @param options - Validation options
|
|
63
|
-
* @returns Validation result with safe flag and optional reason
|
|
38
|
+
* Format milliseconds into a human-readable duration string.
|
|
39
|
+
*
|
|
40
|
+
* @param ms - Duration in milliseconds
|
|
41
|
+
* @returns Human-readable string (e.g. "5m", "2h 30m")
|
|
64
42
|
*/
|
|
65
|
-
declare function
|
|
43
|
+
declare function formatDuration(ms: number): string;
|
|
44
|
+
|
|
66
45
|
/**
|
|
67
|
-
*
|
|
46
|
+
* @module @arcis/node/utils/ip
|
|
47
|
+
* Platform-aware client IP detection.
|
|
68
48
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
49
|
+
* Prevents IP spoofing by reading platform-specific headers
|
|
50
|
+
* instead of blindly trusting X-Forwarded-For.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Auto-detect platform from environment
|
|
54
|
+
* const ip = detectClientIp(req);
|
|
55
|
+
*
|
|
56
|
+
* // Explicit platform
|
|
57
|
+
* const ip = detectClientIp(req, { platform: 'cloudflare' });
|
|
72
58
|
*/
|
|
73
|
-
declare function isUrlSafe(url: string, options?: ValidateUrlOptions): boolean;
|
|
74
59
|
|
|
60
|
+
type Platform = 'auto' | 'cloudflare' | 'vercel' | 'flyio' | 'render' | 'firebase' | 'aws-alb' | 'generic';
|
|
61
|
+
interface DetectIpOptions {
|
|
62
|
+
/** Platform to use for header selection. Default: 'auto' */
|
|
63
|
+
platform?: Platform;
|
|
64
|
+
/** Number of trusted proxies (for X-Forwarded-For parsing). Default: 1 */
|
|
65
|
+
trustedProxyCount?: number;
|
|
66
|
+
}
|
|
67
|
+
interface RequestLike$1 {
|
|
68
|
+
headers: Record<string, string | string[] | undefined>;
|
|
69
|
+
socket?: {
|
|
70
|
+
remoteAddress?: string;
|
|
71
|
+
};
|
|
72
|
+
connection?: {
|
|
73
|
+
remoteAddress?: string;
|
|
74
|
+
};
|
|
75
|
+
ip?: string;
|
|
76
|
+
}
|
|
75
77
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
+
* Detect the real client IP address from a request.
|
|
79
|
+
*
|
|
80
|
+
* Uses platform-specific headers when available to prevent IP spoofing.
|
|
81
|
+
* Falls back to X-Forwarded-For (parsed from the right) and then
|
|
82
|
+
* the socket remote address.
|
|
78
83
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
84
|
+
* @param req - HTTP request object (Express, raw http, etc.)
|
|
85
|
+
* @param options - Detection options
|
|
86
|
+
* @returns Client IP address, or 'unknown' if unresolvable
|
|
81
87
|
*
|
|
82
88
|
* @example
|
|
83
|
-
*
|
|
89
|
+
* // Auto-detect platform
|
|
90
|
+
* app.use((req, res, next) => {
|
|
91
|
+
* const clientIp = detectClientIp(req);
|
|
92
|
+
* console.log('Client IP:', clientIp);
|
|
93
|
+
* next();
|
|
94
|
+
* });
|
|
84
95
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* validateRedirect('javascript:alert(1)') // { safe: false, reason: 'dangerous protocol: javascript:' }
|
|
96
|
+
* @example
|
|
97
|
+
* // Behind Cloudflare
|
|
98
|
+
* const ip = detectClientIp(req, { platform: 'cloudflare' });
|
|
89
99
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* validateRedirect('https://myapp.com/home', { allowedHosts: ['myapp.com'] }) // { safe: true }
|
|
100
|
+
* @example
|
|
101
|
+
* // Behind 2 proxies (e.g. CDN + load balancer)
|
|
102
|
+
* const ip = detectClientIp(req, { trustedProxyCount: 2 });
|
|
94
103
|
*/
|
|
95
|
-
|
|
96
|
-
interface ValidateRedirectOptions {
|
|
97
|
-
/** Hostnames that are allowed for absolute URL redirects */
|
|
98
|
-
allowedHosts?: string[];
|
|
99
|
-
/** Allow protocol-relative URLs (//example.com). Default: false */
|
|
100
|
-
allowProtocolRelative?: boolean;
|
|
101
|
-
/** Allowed protocols for absolute URLs. Default: ['http:', 'https:'] */
|
|
102
|
-
allowedProtocols?: string[];
|
|
103
|
-
}
|
|
104
|
-
/** Result of redirect validation */
|
|
105
|
-
interface ValidateRedirectResult {
|
|
106
|
-
/** Whether the redirect URL is safe */
|
|
107
|
-
safe: boolean;
|
|
108
|
-
/** Reason the redirect was blocked (only set when safe=false) */
|
|
109
|
-
reason?: string;
|
|
110
|
-
}
|
|
104
|
+
declare function detectClientIp(req: RequestLike$1 | IncomingMessage, options?: DetectIpOptions): string;
|
|
111
105
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* - Relative paths: /dashboard, /users?page=2, ../settings
|
|
116
|
-
* - Absolute URLs to allowed hosts (when configured)
|
|
117
|
-
*
|
|
118
|
-
* Blocked redirects:
|
|
119
|
-
* - Absolute URLs to unknown hosts
|
|
120
|
-
* - Protocol-relative URLs (//evil.com)
|
|
121
|
-
* - javascript:, data:, vbscript:, blob: protocols
|
|
122
|
-
* - Backslash-prefixed paths (\\evil.com — browser treats as //)
|
|
123
|
-
* - URLs with control characters that could disguise the target
|
|
124
|
-
*
|
|
125
|
-
* @param url - The redirect target URL to validate
|
|
126
|
-
* @param options - Validation options
|
|
127
|
-
* @returns Validation result with safe flag and optional reason
|
|
106
|
+
* Check if an IP address is a private/internal address.
|
|
107
|
+
*
|
|
108
|
+
* Detects: loopback, private ranges (RFC 1918), link-local, IPv6 equivalents.
|
|
128
109
|
*/
|
|
129
|
-
declare function
|
|
110
|
+
declare function isPrivateIp(ip: string): boolean;
|
|
111
|
+
|
|
130
112
|
/**
|
|
131
|
-
*
|
|
113
|
+
* @module @arcis/node/utils/fingerprint
|
|
114
|
+
* Deterministic request fingerprinting via SHA-256.
|
|
115
|
+
*
|
|
116
|
+
* Generates a stable hash from request characteristics for
|
|
117
|
+
* rate limiting keys, abuse detection, and analytics.
|
|
132
118
|
*
|
|
133
|
-
* @
|
|
134
|
-
*
|
|
135
|
-
*
|
|
119
|
+
* @example
|
|
120
|
+
* const fp = await fingerprint(req);
|
|
121
|
+
* // "a3f2b8c1d4e5..."
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
interface FingerprintOptions {
|
|
125
|
+
/** Include IP address in fingerprint. Default: true */
|
|
126
|
+
ip?: boolean;
|
|
127
|
+
/** Include User-Agent header. Default: true */
|
|
128
|
+
userAgent?: boolean;
|
|
129
|
+
/** Include Accept header. Default: true */
|
|
130
|
+
accept?: boolean;
|
|
131
|
+
/** Include Accept-Language header. Default: true */
|
|
132
|
+
acceptLanguage?: boolean;
|
|
133
|
+
/** Include Accept-Encoding header. Default: true */
|
|
134
|
+
acceptEncoding?: boolean;
|
|
135
|
+
/** Additional custom components to include */
|
|
136
|
+
custom?: string[];
|
|
137
|
+
/** IP detection options */
|
|
138
|
+
ipOptions?: DetectIpOptions;
|
|
139
|
+
}
|
|
140
|
+
interface RequestLike {
|
|
141
|
+
headers: Record<string, string | string[] | undefined>;
|
|
142
|
+
socket?: {
|
|
143
|
+
remoteAddress?: string;
|
|
144
|
+
};
|
|
145
|
+
connection?: {
|
|
146
|
+
remoteAddress?: string;
|
|
147
|
+
};
|
|
148
|
+
ip?: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generate a deterministic fingerprint for a request.
|
|
152
|
+
*
|
|
153
|
+
* Creates a SHA-256 hash from configurable request components.
|
|
154
|
+
* The fingerprint is stable across requests from the same client
|
|
155
|
+
* (same IP, browser, language settings).
|
|
156
|
+
*
|
|
157
|
+
* @param req - HTTP request object
|
|
158
|
+
* @param options - Fingerprint configuration
|
|
159
|
+
* @returns Hex-encoded SHA-256 hash (64 characters)
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // Default fingerprint (IP + UA + Accept headers)
|
|
163
|
+
* const fp = fingerprint(req);
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // IP-only fingerprint (for simple rate limiting)
|
|
167
|
+
* const fp = fingerprint(req, { userAgent: false, accept: false, acceptLanguage: false, acceptEncoding: false });
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* // With custom components
|
|
171
|
+
* const fp = fingerprint(req, { custom: [req.body?.userId] });
|
|
136
172
|
*/
|
|
137
|
-
declare function
|
|
173
|
+
declare function fingerprint(req: RequestLike, options?: FingerprintOptions): string;
|
|
138
174
|
|
|
139
|
-
export { type
|
|
175
|
+
export { type DetectIpOptions, type FingerprintOptions, type Platform, detectClientIp, fingerprint, formatDuration, isPrivateIp, parseDuration };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,139 +1,175 @@
|
|
|
1
|
-
export { C as CorsOptions, S as SecureCookieOptions,
|
|
2
|
-
export { c as createSanitizer, d as detectCommandInjection, a as detectHeaderInjection, b as
|
|
3
|
-
export { F as FileInput, V as ValidateFileOptions,
|
|
1
|
+
export { B as BotCategory, a as BotDetectionResult, b as BotProtectionOptions, C as CorsOptions, c as CsrfOptions, S as SecureCookieOptions, d as SlidingWindowMiddleware, e as SlidingWindowOptions, T as TokenBucketMiddleware, f as TokenBucketOptions, g as arcis, h as arcisFunction, i as botProtection, j as createCors, k as createCsrf, l as createErrorHandler, m as createHeaders, n as createRateLimiter, o as createSecureCookies, p as createSlidingWindowLimiter, q as createTokenBucketLimiter, r as csrfProtection, h as default, s as detectBot, t as enforceSecureCookie, u as errorHandler, v as generateCsrfToken, w as rateLimit, x as safeCors, y as secureCookieDefaults, z as securityHeaders, A as validateCsrfToken } from './index-D_bdJcF0.js';
|
|
2
|
+
export { P as PiiMatch, F as PiiRedactOptions, G as PiiScanOptions, H as PiiType, c as createSanitizer, d as detectCommandInjection, a as detectHeaderInjection, b as detectJsonpInjection, e as detectNoSqlInjection, f as detectPathTraversal, g as detectPii, h as detectPrototypePollution, i as detectSql, j as detectSsti, k as detectXss, l as detectXxe, o as isDangerousNoSqlKey, p as isDangerousProtoKey, r as redactObjectPii, q as redactPii, s as sanitizeCommand, t as sanitizeHeaderValue, u as sanitizeHeaders, v as sanitizeJsonpCallback, w as sanitizeObject, x as sanitizePath, y as sanitizeSql, z as sanitizeSsti, A as sanitizeString, B as sanitizeXss, C as sanitizeXxe, D as scanObjectPii, E as scanPii } from './pii-DhNpl7M3.js';
|
|
3
|
+
export { E as EmailValidationOptions, a as EmailValidationResult, F as FileInput, V as ValidateFileOptions, b as ValidateFileResult, c as ValidateRedirectOptions, d as ValidateRedirectResult, e as ValidateUrlOptions, f as ValidateUrlResult, g as createValidator, i as isDangerousExtension, h as isRedirectSafe, j as isUrlSafe, k as isValidEmailSyntax, s as sanitizeFilename, v as validate, l as validateEmail, m as validateFile, n as validateRedirect, o as validateUrl, p as verifyEmailMx } from './index-Co5kPRZz.js';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
4
5
|
export { createRedactor, createSafeLogger, safeLog } from './logging/index.js';
|
|
5
6
|
export { MemoryStore, RedisClientLike, RedisStore, RedisStoreOptions, createRedisStore } from './stores/index.js';
|
|
6
|
-
export { A as ArcisFunction, a as ArcisMiddleware, b as ArcisOptions, E as ErrorHandlerOptions, F as FieldValidator, H as HeaderOptions, c as HstsOptions, d as HttpError, L as LogOptions, R as RateLimitEntry, e as RateLimitOptions, f as RateLimitResult, g as RateLimitStore, h as RateLimiterMiddleware, S as SafeLogger, i as SanitizeOptions, j as SanitizeResult, T as ThreatInfo, k as ThreatType, V as ValidationConfig, l as ValidationError, m as ValidationResult, n as ValidationSchema } from './types-
|
|
7
|
+
export { A as ArcisFunction, a as ArcisMiddleware, b as ArcisOptions, E as ErrorHandlerOptions, F as FieldValidator, H as HeaderOptions, c as HstsOptions, d as HttpError, L as LogOptions, R as RateLimitEntry, e as RateLimitOptions, f as RateLimitResult, g as RateLimitStore, h as RateLimiterMiddleware, S as SafeLogger, i as SanitizeOptions, j as SanitizeResult, T as ThreatInfo, k as ThreatType, V as ValidationConfig, l as ValidationError, m as ValidationResult, n as ValidationSchema } from './types-CsOFHoD9.js';
|
|
7
8
|
export { ArcisError, ArcisValidationError, BLOCKED, ERRORS, HEADERS, INPUT, InputTooLargeError, RATE_LIMIT, REDACTION, RateLimitError, SanitizationError, SecurityThreatError, VALIDATION } from './core/index.js';
|
|
8
9
|
import 'express';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* @module @arcis/node/
|
|
12
|
-
*
|
|
12
|
+
* @module @arcis/node/utils/duration
|
|
13
|
+
* Parse human-readable duration strings into milliseconds.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
* localhost, cloud metadata endpoints, or use dangerous protocols.
|
|
15
|
+
* Supports: ms, s, m, h, d
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
|
-
*
|
|
18
|
+
* parseDuration('5m') // 300000
|
|
19
|
+
* parseDuration('2h') // 7200000
|
|
20
|
+
* parseDuration(60000) // 60000 (passthrough)
|
|
21
|
+
* parseDuration('500ms') // 500
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Parse a duration string or number into milliseconds.
|
|
19
25
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* validateUrl('http://localhost/secret') // { safe: false, reason: 'loopback address' }
|
|
24
|
-
* validateUrl('file:///etc/passwd') // { safe: false, reason: 'disallowed protocol: file:' }
|
|
26
|
+
* @param value - Duration string (e.g. "5m", "2h", "30s") or number (ms)
|
|
27
|
+
* @returns Duration in milliseconds
|
|
28
|
+
* @throws {Error} If the value is not a valid duration
|
|
25
29
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
30
|
+
* @example
|
|
31
|
+
* parseDuration('15m') // 900000
|
|
32
|
+
* parseDuration('1d') // 86400000
|
|
33
|
+
* parseDuration('500ms') // 500
|
|
34
|
+
* parseDuration(60000) // 60000
|
|
28
35
|
*/
|
|
29
|
-
|
|
30
|
-
interface ValidateUrlOptions {
|
|
31
|
-
/** Allowed protocols. Default: ['http:', 'https:'] */
|
|
32
|
-
allowedProtocols?: string[];
|
|
33
|
-
/** Additional hostnames to block (e.g., internal service names) */
|
|
34
|
-
blockedHosts?: string[];
|
|
35
|
-
/** Additional hostnames to always allow (bypass IP checks) */
|
|
36
|
-
allowedHosts?: string[];
|
|
37
|
-
/** Allow localhost/loopback. Default: false */
|
|
38
|
-
allowLocalhost?: boolean;
|
|
39
|
-
/** Allow private/internal IPs. Default: false */
|
|
40
|
-
allowPrivate?: boolean;
|
|
41
|
-
}
|
|
42
|
-
/** Result of URL validation */
|
|
43
|
-
interface ValidateUrlResult {
|
|
44
|
-
/** Whether the URL is safe to fetch */
|
|
45
|
-
safe: boolean;
|
|
46
|
-
/** Reason the URL was blocked (only set when safe=false) */
|
|
47
|
-
reason?: string;
|
|
48
|
-
}
|
|
36
|
+
declare function parseDuration(value: string | number): number;
|
|
49
37
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 2. Allowed protocol (default: http, https only)
|
|
55
|
-
* 3. Not localhost/loopback (127.x.x.x, ::1, localhost)
|
|
56
|
-
* 4. Not private IP (10.x, 172.16-31.x, 192.168.x)
|
|
57
|
-
* 5. Not link-local (169.254.x.x — includes AWS/GCP/Azure metadata)
|
|
58
|
-
* 6. Not blocked hostname
|
|
59
|
-
* 7. No credentials in URL (user:pass@host)
|
|
60
|
-
*
|
|
61
|
-
* @param url - The URL string to validate
|
|
62
|
-
* @param options - Validation options
|
|
63
|
-
* @returns Validation result with safe flag and optional reason
|
|
38
|
+
* Format milliseconds into a human-readable duration string.
|
|
39
|
+
*
|
|
40
|
+
* @param ms - Duration in milliseconds
|
|
41
|
+
* @returns Human-readable string (e.g. "5m", "2h 30m")
|
|
64
42
|
*/
|
|
65
|
-
declare function
|
|
43
|
+
declare function formatDuration(ms: number): string;
|
|
44
|
+
|
|
66
45
|
/**
|
|
67
|
-
*
|
|
46
|
+
* @module @arcis/node/utils/ip
|
|
47
|
+
* Platform-aware client IP detection.
|
|
68
48
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
49
|
+
* Prevents IP spoofing by reading platform-specific headers
|
|
50
|
+
* instead of blindly trusting X-Forwarded-For.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Auto-detect platform from environment
|
|
54
|
+
* const ip = detectClientIp(req);
|
|
55
|
+
*
|
|
56
|
+
* // Explicit platform
|
|
57
|
+
* const ip = detectClientIp(req, { platform: 'cloudflare' });
|
|
72
58
|
*/
|
|
73
|
-
declare function isUrlSafe(url: string, options?: ValidateUrlOptions): boolean;
|
|
74
59
|
|
|
60
|
+
type Platform = 'auto' | 'cloudflare' | 'vercel' | 'flyio' | 'render' | 'firebase' | 'aws-alb' | 'generic';
|
|
61
|
+
interface DetectIpOptions {
|
|
62
|
+
/** Platform to use for header selection. Default: 'auto' */
|
|
63
|
+
platform?: Platform;
|
|
64
|
+
/** Number of trusted proxies (for X-Forwarded-For parsing). Default: 1 */
|
|
65
|
+
trustedProxyCount?: number;
|
|
66
|
+
}
|
|
67
|
+
interface RequestLike$1 {
|
|
68
|
+
headers: Record<string, string | string[] | undefined>;
|
|
69
|
+
socket?: {
|
|
70
|
+
remoteAddress?: string;
|
|
71
|
+
};
|
|
72
|
+
connection?: {
|
|
73
|
+
remoteAddress?: string;
|
|
74
|
+
};
|
|
75
|
+
ip?: string;
|
|
76
|
+
}
|
|
75
77
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
+
* Detect the real client IP address from a request.
|
|
79
|
+
*
|
|
80
|
+
* Uses platform-specific headers when available to prevent IP spoofing.
|
|
81
|
+
* Falls back to X-Forwarded-For (parsed from the right) and then
|
|
82
|
+
* the socket remote address.
|
|
78
83
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
84
|
+
* @param req - HTTP request object (Express, raw http, etc.)
|
|
85
|
+
* @param options - Detection options
|
|
86
|
+
* @returns Client IP address, or 'unknown' if unresolvable
|
|
81
87
|
*
|
|
82
88
|
* @example
|
|
83
|
-
*
|
|
89
|
+
* // Auto-detect platform
|
|
90
|
+
* app.use((req, res, next) => {
|
|
91
|
+
* const clientIp = detectClientIp(req);
|
|
92
|
+
* console.log('Client IP:', clientIp);
|
|
93
|
+
* next();
|
|
94
|
+
* });
|
|
84
95
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* validateRedirect('javascript:alert(1)') // { safe: false, reason: 'dangerous protocol: javascript:' }
|
|
96
|
+
* @example
|
|
97
|
+
* // Behind Cloudflare
|
|
98
|
+
* const ip = detectClientIp(req, { platform: 'cloudflare' });
|
|
89
99
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* validateRedirect('https://myapp.com/home', { allowedHosts: ['myapp.com'] }) // { safe: true }
|
|
100
|
+
* @example
|
|
101
|
+
* // Behind 2 proxies (e.g. CDN + load balancer)
|
|
102
|
+
* const ip = detectClientIp(req, { trustedProxyCount: 2 });
|
|
94
103
|
*/
|
|
95
|
-
|
|
96
|
-
interface ValidateRedirectOptions {
|
|
97
|
-
/** Hostnames that are allowed for absolute URL redirects */
|
|
98
|
-
allowedHosts?: string[];
|
|
99
|
-
/** Allow protocol-relative URLs (//example.com). Default: false */
|
|
100
|
-
allowProtocolRelative?: boolean;
|
|
101
|
-
/** Allowed protocols for absolute URLs. Default: ['http:', 'https:'] */
|
|
102
|
-
allowedProtocols?: string[];
|
|
103
|
-
}
|
|
104
|
-
/** Result of redirect validation */
|
|
105
|
-
interface ValidateRedirectResult {
|
|
106
|
-
/** Whether the redirect URL is safe */
|
|
107
|
-
safe: boolean;
|
|
108
|
-
/** Reason the redirect was blocked (only set when safe=false) */
|
|
109
|
-
reason?: string;
|
|
110
|
-
}
|
|
104
|
+
declare function detectClientIp(req: RequestLike$1 | IncomingMessage, options?: DetectIpOptions): string;
|
|
111
105
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* - Relative paths: /dashboard, /users?page=2, ../settings
|
|
116
|
-
* - Absolute URLs to allowed hosts (when configured)
|
|
117
|
-
*
|
|
118
|
-
* Blocked redirects:
|
|
119
|
-
* - Absolute URLs to unknown hosts
|
|
120
|
-
* - Protocol-relative URLs (//evil.com)
|
|
121
|
-
* - javascript:, data:, vbscript:, blob: protocols
|
|
122
|
-
* - Backslash-prefixed paths (\\evil.com — browser treats as //)
|
|
123
|
-
* - URLs with control characters that could disguise the target
|
|
124
|
-
*
|
|
125
|
-
* @param url - The redirect target URL to validate
|
|
126
|
-
* @param options - Validation options
|
|
127
|
-
* @returns Validation result with safe flag and optional reason
|
|
106
|
+
* Check if an IP address is a private/internal address.
|
|
107
|
+
*
|
|
108
|
+
* Detects: loopback, private ranges (RFC 1918), link-local, IPv6 equivalents.
|
|
128
109
|
*/
|
|
129
|
-
declare function
|
|
110
|
+
declare function isPrivateIp(ip: string): boolean;
|
|
111
|
+
|
|
130
112
|
/**
|
|
131
|
-
*
|
|
113
|
+
* @module @arcis/node/utils/fingerprint
|
|
114
|
+
* Deterministic request fingerprinting via SHA-256.
|
|
115
|
+
*
|
|
116
|
+
* Generates a stable hash from request characteristics for
|
|
117
|
+
* rate limiting keys, abuse detection, and analytics.
|
|
132
118
|
*
|
|
133
|
-
* @
|
|
134
|
-
*
|
|
135
|
-
*
|
|
119
|
+
* @example
|
|
120
|
+
* const fp = await fingerprint(req);
|
|
121
|
+
* // "a3f2b8c1d4e5..."
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
interface FingerprintOptions {
|
|
125
|
+
/** Include IP address in fingerprint. Default: true */
|
|
126
|
+
ip?: boolean;
|
|
127
|
+
/** Include User-Agent header. Default: true */
|
|
128
|
+
userAgent?: boolean;
|
|
129
|
+
/** Include Accept header. Default: true */
|
|
130
|
+
accept?: boolean;
|
|
131
|
+
/** Include Accept-Language header. Default: true */
|
|
132
|
+
acceptLanguage?: boolean;
|
|
133
|
+
/** Include Accept-Encoding header. Default: true */
|
|
134
|
+
acceptEncoding?: boolean;
|
|
135
|
+
/** Additional custom components to include */
|
|
136
|
+
custom?: string[];
|
|
137
|
+
/** IP detection options */
|
|
138
|
+
ipOptions?: DetectIpOptions;
|
|
139
|
+
}
|
|
140
|
+
interface RequestLike {
|
|
141
|
+
headers: Record<string, string | string[] | undefined>;
|
|
142
|
+
socket?: {
|
|
143
|
+
remoteAddress?: string;
|
|
144
|
+
};
|
|
145
|
+
connection?: {
|
|
146
|
+
remoteAddress?: string;
|
|
147
|
+
};
|
|
148
|
+
ip?: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generate a deterministic fingerprint for a request.
|
|
152
|
+
*
|
|
153
|
+
* Creates a SHA-256 hash from configurable request components.
|
|
154
|
+
* The fingerprint is stable across requests from the same client
|
|
155
|
+
* (same IP, browser, language settings).
|
|
156
|
+
*
|
|
157
|
+
* @param req - HTTP request object
|
|
158
|
+
* @param options - Fingerprint configuration
|
|
159
|
+
* @returns Hex-encoded SHA-256 hash (64 characters)
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // Default fingerprint (IP + UA + Accept headers)
|
|
163
|
+
* const fp = fingerprint(req);
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // IP-only fingerprint (for simple rate limiting)
|
|
167
|
+
* const fp = fingerprint(req, { userAgent: false, accept: false, acceptLanguage: false, acceptEncoding: false });
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* // With custom components
|
|
171
|
+
* const fp = fingerprint(req, { custom: [req.body?.userId] });
|
|
136
172
|
*/
|
|
137
|
-
declare function
|
|
173
|
+
declare function fingerprint(req: RequestLike, options?: FingerprintOptions): string;
|
|
138
174
|
|
|
139
|
-
export { type
|
|
175
|
+
export { type DetectIpOptions, type FingerprintOptions, type Platform, detectClientIp, fingerprint, formatDuration, isPrivateIp, parseDuration };
|