@arcis/node 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/{index.d.mts → constants.d.ts} +21 -70
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/errors.d.ts +53 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/index.d.ts +6 -168
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{types-CsOFHoD9.d.mts → core/types.d.ts} +38 -31
- package/dist/core/types.d.ts.map +1 -0
- package/dist/index.d.ts +71 -166
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +151 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +145 -5
- package/dist/index.mjs.map +1 -1
- package/dist/logging/index.d.ts +4 -36
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/{index.d.mts → redactor.d.ts} +5 -9
- package/dist/logging/redactor.d.ts.map +1 -0
- package/dist/middleware/bot-detection.d.ts +86 -0
- package/dist/middleware/bot-detection.d.ts.map +1 -0
- package/dist/middleware/cookies.d.ts +48 -0
- package/dist/middleware/cookies.d.ts.map +1 -0
- package/dist/middleware/cors.d.ts +65 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/csrf.d.ts +109 -0
- package/dist/middleware/csrf.d.ts.map +1 -0
- package/dist/middleware/error-handler.d.ts +43 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/headers.d.ts +29 -0
- package/dist/middleware/headers.d.ts.map +1 -0
- package/dist/middleware/hpp.d.ts +56 -0
- package/dist/middleware/hpp.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +16 -3
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +28 -3
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +28 -3
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/main.d.ts +40 -0
- package/dist/middleware/main.d.ts.map +1 -0
- package/dist/middleware/rate-limit-sliding.d.ts +46 -0
- package/dist/middleware/rate-limit-sliding.d.ts.map +1 -0
- package/dist/middleware/rate-limit-token.d.ts +51 -0
- package/dist/middleware/rate-limit-token.d.ts.map +1 -0
- package/dist/middleware/rate-limit.d.ts +34 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/sanitizers/command.d.ts +28 -0
- package/dist/sanitizers/command.d.ts.map +1 -0
- package/dist/sanitizers/encode.d.ts +46 -0
- package/dist/sanitizers/encode.d.ts.map +1 -0
- package/dist/sanitizers/headers.d.ts +46 -0
- package/dist/sanitizers/headers.d.ts.map +1 -0
- package/dist/sanitizers/index.d.ts +17 -22
- package/dist/sanitizers/index.d.ts.map +1 -0
- package/dist/sanitizers/index.js +72 -0
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +68 -1
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/jsonp.d.ts +34 -0
- package/dist/sanitizers/jsonp.d.ts.map +1 -0
- package/dist/sanitizers/nosql.d.ts +31 -0
- package/dist/sanitizers/nosql.d.ts.map +1 -0
- package/dist/sanitizers/path.d.ts +28 -0
- package/dist/sanitizers/path.d.ts.map +1 -0
- package/dist/sanitizers/pii.d.ts +80 -0
- package/dist/sanitizers/pii.d.ts.map +1 -0
- package/dist/sanitizers/prototype.d.ts +34 -0
- package/dist/sanitizers/prototype.d.ts.map +1 -0
- package/dist/sanitizers/sanitize.d.ts +51 -0
- package/dist/sanitizers/sanitize.d.ts.map +1 -0
- package/dist/sanitizers/sql.d.ts +28 -0
- package/dist/sanitizers/sql.d.ts.map +1 -0
- package/dist/sanitizers/ssti.d.ts +20 -0
- package/dist/sanitizers/ssti.d.ts.map +1 -0
- package/dist/sanitizers/utils.d.ts +19 -0
- package/dist/sanitizers/utils.d.ts.map +1 -0
- package/dist/sanitizers/xss.d.ts +35 -0
- package/dist/sanitizers/xss.d.ts.map +1 -0
- package/dist/sanitizers/xxe.d.ts +20 -0
- package/dist/sanitizers/xxe.d.ts.map +1 -0
- package/dist/stores/index.d.ts +6 -104
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/memory.d.ts +35 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/{index.d.mts → redis.d.ts} +6 -45
- package/dist/stores/redis.d.ts.map +1 -0
- package/dist/utils/duration.d.ts +34 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/fingerprint.d.ts +64 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +188 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +182 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/ip.d.ts +70 -0
- package/dist/utils/ip.d.ts.map +1 -0
- package/dist/validation/email.d.ts +82 -0
- package/dist/validation/email.d.ts.map +1 -0
- package/dist/validation/file.d.ts +90 -0
- package/dist/validation/file.d.ts.map +1 -0
- package/dist/validation/index.d.ts +10 -3
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/redirect.d.ts +64 -0
- package/dist/validation/redirect.d.ts.map +1 -0
- package/dist/validation/schema.d.ts +36 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/url.d.ts +65 -0
- package/dist/validation/url.d.ts.map +1 -0
- package/package.json +8 -6
- package/dist/index-A-m-pPeW.d.mts +0 -340
- package/dist/index-CgK94hY_.d.mts +0 -532
- package/dist/index-Co5kPRZz.d.ts +0 -340
- package/dist/index-D_bdJcF0.d.ts +0 -532
- package/dist/index.d.mts +0 -175
- package/dist/middleware/index.d.mts +0 -3
- package/dist/pii-CXcHMlnX.d.mts +0 -438
- package/dist/pii-DhNpl7M3.d.ts +0 -438
- package/dist/sanitizers/index.d.mts +0 -24
- package/dist/types-CsOFHoD9.d.ts +0 -269
- package/dist/validation/index.d.mts +0 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation/email
|
|
3
|
+
* Advanced email validation with disposable detection and typo suggestions.
|
|
4
|
+
*
|
|
5
|
+
* Three levels of validation:
|
|
6
|
+
* 1. Syntax — RFC-compliant format checking
|
|
7
|
+
* 2. Domain intelligence — disposable/free provider detection, typo correction
|
|
8
|
+
* 3. MX verification — DNS MX record lookup (async, optional)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const result = validateEmail('user@tempmail.com');
|
|
12
|
+
* // { valid: false, reason: 'disposable' }
|
|
13
|
+
*
|
|
14
|
+
* const result = validateEmail('user@gmial.com');
|
|
15
|
+
* // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }
|
|
16
|
+
*/
|
|
17
|
+
export interface EmailValidationOptions {
|
|
18
|
+
/** Check for disposable email providers. Default: true */
|
|
19
|
+
checkDisposable?: boolean;
|
|
20
|
+
/** Suggest corrections for typos. Default: true */
|
|
21
|
+
suggestTypoFix?: boolean;
|
|
22
|
+
/** Verify MX records via DNS. Default: false */
|
|
23
|
+
checkMx?: boolean;
|
|
24
|
+
/** Additional blocked domains */
|
|
25
|
+
blockedDomains?: string[];
|
|
26
|
+
/** Additional allowed domains (bypasses disposable check) */
|
|
27
|
+
allowedDomains?: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface EmailValidationResult {
|
|
30
|
+
/** Whether the email is valid */
|
|
31
|
+
valid: boolean;
|
|
32
|
+
/** Reason for the result */
|
|
33
|
+
reason: 'valid' | 'invalid_syntax' | 'disposable' | 'no_mx' | 'blocked' | 'typo';
|
|
34
|
+
/** Suggested correction if a typo was detected */
|
|
35
|
+
suggestion: string | null;
|
|
36
|
+
/** Whether the domain is a free email provider */
|
|
37
|
+
isFree: boolean;
|
|
38
|
+
/** Whether the domain is a disposable email provider */
|
|
39
|
+
isDisposable: boolean;
|
|
40
|
+
/** The normalized email address */
|
|
41
|
+
normalized: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate an email address with syntax checking, disposable detection,
|
|
45
|
+
* and typo suggestions.
|
|
46
|
+
*
|
|
47
|
+
* @param email - Email address to validate
|
|
48
|
+
* @param options - Validation options
|
|
49
|
+
* @returns Validation result
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* validateEmail('user@gmail.com')
|
|
53
|
+
* // { valid: true, reason: 'valid', isFree: true }
|
|
54
|
+
*
|
|
55
|
+
* validateEmail('user@tempmail.com')
|
|
56
|
+
* // { valid: false, reason: 'disposable' }
|
|
57
|
+
*
|
|
58
|
+
* validateEmail('user@gmial.com')
|
|
59
|
+
* // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateEmail(email: string, options?: EmailValidationOptions): EmailValidationResult;
|
|
62
|
+
/**
|
|
63
|
+
* Verify that the email domain has MX records (can receive email).
|
|
64
|
+
*
|
|
65
|
+
* This performs a DNS lookup and requires network access.
|
|
66
|
+
* Use for registration flows where you need high confidence.
|
|
67
|
+
*
|
|
68
|
+
* @param email - Email address to verify
|
|
69
|
+
* @returns True if the domain has MX records
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* if (await verifyEmailMx('user@example.com')) {
|
|
73
|
+
* // Domain can receive email
|
|
74
|
+
* }
|
|
75
|
+
*/
|
|
76
|
+
export declare function verifyEmailMx(email: string): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Quick check if an email address has valid syntax.
|
|
79
|
+
* Faster than validateEmail() — just syntax, no domain intelligence.
|
|
80
|
+
*/
|
|
81
|
+
export declare function isValidEmailSyntax(email: string): boolean;
|
|
82
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/validation/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mDAAmD;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,4BAA4B;IAC5B,MAAM,EAAE,OAAO,GAAG,gBAAgB,GAAG,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACjF,kDAAkD;IAClD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;IACtB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAkHD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,sBAA2B,GACnC,qBAAqB,CAqGvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAanE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWzD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation/file
|
|
3
|
+
* File upload validation and filename sanitization
|
|
4
|
+
*/
|
|
5
|
+
/** File upload validation options */
|
|
6
|
+
export interface ValidateFileOptions {
|
|
7
|
+
/** Maximum file size in bytes. Default: 5MB */
|
|
8
|
+
maxSize?: number;
|
|
9
|
+
/** Allowed MIME types (e.g., ['image/jpeg', 'image/png']) */
|
|
10
|
+
allowedTypes?: string[];
|
|
11
|
+
/** Allowed file extensions (e.g., ['.jpg', '.png']). Includes dot. */
|
|
12
|
+
allowedExtensions?: string[];
|
|
13
|
+
/** Block dangerous/executable extensions. Default: true */
|
|
14
|
+
blockExecutables?: boolean;
|
|
15
|
+
/** Validate magic bytes match the claimed MIME type. Default: true */
|
|
16
|
+
validateMagicBytes?: boolean;
|
|
17
|
+
/** Block files with no extension. Default: true */
|
|
18
|
+
blockNoExtension?: boolean;
|
|
19
|
+
/** Block double extensions (e.g., file.php.jpg). Default: true */
|
|
20
|
+
blockDoubleExtensions?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** File metadata for validation */
|
|
23
|
+
export interface FileInput {
|
|
24
|
+
/** Original filename */
|
|
25
|
+
filename: string;
|
|
26
|
+
/** MIME type (as claimed by client) */
|
|
27
|
+
mimetype: string;
|
|
28
|
+
/** File size in bytes */
|
|
29
|
+
size: number;
|
|
30
|
+
/** File content buffer (for magic byte validation) */
|
|
31
|
+
buffer?: Buffer;
|
|
32
|
+
}
|
|
33
|
+
/** File validation result */
|
|
34
|
+
export interface ValidateFileResult {
|
|
35
|
+
/** Whether the file passed validation */
|
|
36
|
+
valid: boolean;
|
|
37
|
+
/** Validation errors (empty if valid) */
|
|
38
|
+
errors: string[];
|
|
39
|
+
/** Sanitized filename (safe for storage) */
|
|
40
|
+
sanitizedFilename: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Sanitize a filename for safe storage.
|
|
44
|
+
*
|
|
45
|
+
* Strips path traversal, null bytes, control characters, and special characters.
|
|
46
|
+
* Preserves the extension and converts to a filesystem-safe name.
|
|
47
|
+
*
|
|
48
|
+
* @param filename - The original filename
|
|
49
|
+
* @returns A sanitized filename safe for storage
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* sanitizeFilename('../../etc/passwd') // 'etc_passwd'
|
|
53
|
+
* sanitizeFilename('file<name>.jpg') // 'filename.jpg'
|
|
54
|
+
* sanitizeFilename('photo (1).jpg') // 'photo_1.jpg'
|
|
55
|
+
* sanitizeFilename('.htaccess') // 'htaccess'
|
|
56
|
+
*/
|
|
57
|
+
export declare function sanitizeFilename(filename: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Validate a file upload for security.
|
|
60
|
+
*
|
|
61
|
+
* Checks file size, MIME type, extension, magic bytes, and dangerous patterns.
|
|
62
|
+
* Returns a result with validation errors and a sanitized filename.
|
|
63
|
+
*
|
|
64
|
+
* @param file - File metadata and optional content
|
|
65
|
+
* @param options - Validation options
|
|
66
|
+
* @returns Validation result
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const result = validateFile(
|
|
70
|
+
* { filename: 'photo.jpg', mimetype: 'image/jpeg', size: 1024, buffer },
|
|
71
|
+
* { allowedTypes: ['image/jpeg', 'image/png'], maxSize: 2 * 1024 * 1024 }
|
|
72
|
+
* );
|
|
73
|
+
* if (!result.valid) {
|
|
74
|
+
* return res.status(400).json({ errors: result.errors });
|
|
75
|
+
* }
|
|
76
|
+
* // Use result.sanitizedFilename for storage
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Block executables only (no whitelist)
|
|
80
|
+
* const result = validateFile(file, { blockExecutables: true });
|
|
81
|
+
*/
|
|
82
|
+
export declare function validateFile(file: FileInput, options?: ValidateFileOptions): ValidateFileResult;
|
|
83
|
+
/**
|
|
84
|
+
* Check if a file extension is considered dangerous/executable.
|
|
85
|
+
*
|
|
86
|
+
* @param filename - Filename or extension to check
|
|
87
|
+
* @returns true if the extension is dangerous
|
|
88
|
+
*/
|
|
89
|
+
export declare function isDangerousExtension(filename: string): boolean;
|
|
90
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/validation/file.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuDH,qCAAqC;AACrC,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,KAAK,EAAE,OAAO,CAAC;IACf,yCAAyC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAYD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqCzD;AAmDD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CA6DpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation
|
|
3
|
+
* Request validation for Arcis
|
|
4
|
+
*/
|
|
5
|
+
export { validate, createValidator } from './schema';
|
|
6
|
+
export { validateFile, sanitizeFilename, isDangerousExtension } from './file';
|
|
7
|
+
export { validateUrl, isUrlSafe } from './url';
|
|
8
|
+
export { validateRedirect, isRedirectSafe } from './redirect';
|
|
9
|
+
export { validateEmail, verifyEmailMx, isValidEmailSyntax } from './email';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation/redirect
|
|
3
|
+
* Open Redirect prevention
|
|
4
|
+
*
|
|
5
|
+
* Prevents attackers from using your app to redirect users to malicious sites
|
|
6
|
+
* via manipulated query parameters like ?returnUrl=http://evil.com
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { validateRedirect, isRedirectSafe } from '@arcis/node';
|
|
10
|
+
*
|
|
11
|
+
* // Block open redirects
|
|
12
|
+
* validateRedirect('http://evil.com') // { safe: false, reason: 'absolute URL not in allowed hosts' }
|
|
13
|
+
* validateRedirect('//evil.com') // { safe: false, reason: 'protocol-relative URL not in allowed hosts' }
|
|
14
|
+
* validateRedirect('javascript:alert(1)') // { safe: false, reason: 'dangerous protocol: javascript:' }
|
|
15
|
+
*
|
|
16
|
+
* // Allow safe redirects
|
|
17
|
+
* validateRedirect('/dashboard') // { safe: true }
|
|
18
|
+
* validateRedirect('/users?page=2') // { safe: true }
|
|
19
|
+
* validateRedirect('https://myapp.com/home', { allowedHosts: ['myapp.com'] }) // { safe: true }
|
|
20
|
+
*/
|
|
21
|
+
/** Options for redirect validation */
|
|
22
|
+
export interface ValidateRedirectOptions {
|
|
23
|
+
/** Hostnames that are allowed for absolute URL redirects */
|
|
24
|
+
allowedHosts?: string[];
|
|
25
|
+
/** Allow protocol-relative URLs (//example.com). Default: false */
|
|
26
|
+
allowProtocolRelative?: boolean;
|
|
27
|
+
/** Allowed protocols for absolute URLs. Default: ['http:', 'https:'] */
|
|
28
|
+
allowedProtocols?: string[];
|
|
29
|
+
}
|
|
30
|
+
/** Result of redirect validation */
|
|
31
|
+
export interface ValidateRedirectResult {
|
|
32
|
+
/** Whether the redirect URL is safe */
|
|
33
|
+
safe: boolean;
|
|
34
|
+
/** Reason the redirect was blocked (only set when safe=false) */
|
|
35
|
+
reason?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate a redirect URL to prevent open redirect attacks.
|
|
39
|
+
*
|
|
40
|
+
* Safe redirects:
|
|
41
|
+
* - Relative paths: /dashboard, /users?page=2, ../settings
|
|
42
|
+
* - Absolute URLs to allowed hosts (when configured)
|
|
43
|
+
*
|
|
44
|
+
* Blocked redirects:
|
|
45
|
+
* - Absolute URLs to unknown hosts
|
|
46
|
+
* - Protocol-relative URLs (//evil.com)
|
|
47
|
+
* - javascript:, data:, vbscript:, blob: protocols
|
|
48
|
+
* - Backslash-prefixed paths (\\evil.com — browser treats as //)
|
|
49
|
+
* - URLs with control characters that could disguise the target
|
|
50
|
+
*
|
|
51
|
+
* @param url - The redirect target URL to validate
|
|
52
|
+
* @param options - Validation options
|
|
53
|
+
* @returns Validation result with safe flag and optional reason
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateRedirect(url: string, options?: ValidateRedirectOptions): ValidateRedirectResult;
|
|
56
|
+
/**
|
|
57
|
+
* Convenience wrapper that returns true/false.
|
|
58
|
+
*
|
|
59
|
+
* @param url - The redirect URL to check
|
|
60
|
+
* @param options - Validation options
|
|
61
|
+
* @returns true if the redirect is safe
|
|
62
|
+
*/
|
|
63
|
+
export declare function isRedirectSafe(url: string, options?: ValidateRedirectOptions): boolean;
|
|
64
|
+
//# sourceMappingURL=redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/validation/redirect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,sCAAsC;AACtC,MAAM,WAAW,uBAAuB;IACtC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,oCAAoC;AACpC,MAAM,WAAW,sBAAsB;IACrC,uCAAuC;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAqExB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAE1F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation/schema
|
|
3
|
+
* Request validation middleware
|
|
4
|
+
*/
|
|
5
|
+
import type { RequestHandler } from 'express';
|
|
6
|
+
import type { ValidationSchema } from '../core/types';
|
|
7
|
+
/**
|
|
8
|
+
* Create Express middleware for request validation.
|
|
9
|
+
* Prevents mass assignment by only allowing fields defined in the schema.
|
|
10
|
+
*
|
|
11
|
+
* @param schema - Validation schema defining expected fields
|
|
12
|
+
* @param source - Request property to validate ('body', 'query', or 'params')
|
|
13
|
+
* @returns Express middleware
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* app.post('/users', validate({
|
|
17
|
+
* email: { type: 'email', required: true },
|
|
18
|
+
* name: { type: 'string', min: 2, max: 50 },
|
|
19
|
+
* age: { type: 'number', min: 0, max: 150 },
|
|
20
|
+
* role: { type: 'string', enum: ['user', 'admin'] }
|
|
21
|
+
* }), handler);
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Validate query params
|
|
25
|
+
* app.get('/search', validate({
|
|
26
|
+
* q: { type: 'string', required: true, min: 1 },
|
|
27
|
+
* page: { type: 'number', min: 1 }
|
|
28
|
+
* }, 'query'), handler);
|
|
29
|
+
*/
|
|
30
|
+
export declare function validate(schema: ValidationSchema, source?: 'body' | 'query' | 'params'): RequestHandler;
|
|
31
|
+
/**
|
|
32
|
+
* Alias for validate
|
|
33
|
+
* @see validate
|
|
34
|
+
*/
|
|
35
|
+
export declare const createValidator: typeof validate;
|
|
36
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/validation/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,eAAe,CAAC;AAGtE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,gBAAgB,EACxB,MAAM,GAAE,MAAM,GAAG,OAAO,GAAG,QAAiB,GAC3C,cAAc,CA0BhB;AAoKD;;;GAGG;AACH,eAAO,MAAM,eAAe,iBAAW,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @arcis/node/validation/url
|
|
3
|
+
* SSRF (Server-Side Request Forgery) prevention
|
|
4
|
+
*
|
|
5
|
+
* Validates URLs to ensure they don't target private/internal networks,
|
|
6
|
+
* localhost, cloud metadata endpoints, or use dangerous protocols.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { validateUrl } from '@arcis/node';
|
|
10
|
+
*
|
|
11
|
+
* // Block SSRF attempts
|
|
12
|
+
* validateUrl('http://169.254.169.254/latest/meta-data/') // { safe: false, reason: 'link-local address' }
|
|
13
|
+
* validateUrl('http://10.0.0.1/admin') // { safe: false, reason: 'private address (10.0.0.0/8)' }
|
|
14
|
+
* validateUrl('http://localhost/secret') // { safe: false, reason: 'loopback address' }
|
|
15
|
+
* validateUrl('file:///etc/passwd') // { safe: false, reason: 'disallowed protocol: file:' }
|
|
16
|
+
*
|
|
17
|
+
* // Allow safe URLs
|
|
18
|
+
* validateUrl('https://api.example.com/data') // { safe: true }
|
|
19
|
+
*/
|
|
20
|
+
/** Options for URL validation */
|
|
21
|
+
export interface ValidateUrlOptions {
|
|
22
|
+
/** Allowed protocols. Default: ['http:', 'https:'] */
|
|
23
|
+
allowedProtocols?: string[];
|
|
24
|
+
/** Additional hostnames to block (e.g., internal service names) */
|
|
25
|
+
blockedHosts?: string[];
|
|
26
|
+
/** Additional hostnames to always allow (bypass IP checks) */
|
|
27
|
+
allowedHosts?: string[];
|
|
28
|
+
/** Allow localhost/loopback. Default: false */
|
|
29
|
+
allowLocalhost?: boolean;
|
|
30
|
+
/** Allow private/internal IPs. Default: false */
|
|
31
|
+
allowPrivate?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Result of URL validation */
|
|
34
|
+
export interface ValidateUrlResult {
|
|
35
|
+
/** Whether the URL is safe to fetch */
|
|
36
|
+
safe: boolean;
|
|
37
|
+
/** Reason the URL was blocked (only set when safe=false) */
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validate a URL for SSRF safety.
|
|
42
|
+
*
|
|
43
|
+
* Checks:
|
|
44
|
+
* 1. Valid URL format
|
|
45
|
+
* 2. Allowed protocol (default: http, https only)
|
|
46
|
+
* 3. Not localhost/loopback (127.x.x.x, ::1, localhost)
|
|
47
|
+
* 4. Not private IP (10.x, 172.16-31.x, 192.168.x)
|
|
48
|
+
* 5. Not link-local (169.254.x.x — includes AWS/GCP/Azure metadata)
|
|
49
|
+
* 6. Not blocked hostname
|
|
50
|
+
* 7. No credentials in URL (user:pass@host)
|
|
51
|
+
*
|
|
52
|
+
* @param url - The URL string to validate
|
|
53
|
+
* @param options - Validation options
|
|
54
|
+
* @returns Validation result with safe flag and optional reason
|
|
55
|
+
*/
|
|
56
|
+
export declare function validateUrl(url: string, options?: ValidateUrlOptions): ValidateUrlResult;
|
|
57
|
+
/**
|
|
58
|
+
* Convenience wrapper that returns true/false.
|
|
59
|
+
*
|
|
60
|
+
* @param url - The URL to check
|
|
61
|
+
* @param options - Validation options
|
|
62
|
+
* @returns true if the URL is safe to fetch
|
|
63
|
+
*/
|
|
64
|
+
export declare function isUrlSafe(url: string, options?: ValidateUrlOptions): boolean;
|
|
65
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/validation/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAChC,uCAAuC;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAuF5F;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAEhF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcis/node",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "One
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "One line of code protects your Node.js app against 45+ security flaws at runtime — XSS, SQL injection, CSRF, SSRF, HPP, rate limiting, bot detection, and more. Zero dependencies. Supply chain attack scanner included.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
"dist"
|
|
52
52
|
],
|
|
53
53
|
"scripts": {
|
|
54
|
-
"build": "tsup",
|
|
54
|
+
"build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap",
|
|
55
55
|
"dev": "tsup --watch",
|
|
56
56
|
"test": "vitest",
|
|
57
57
|
"test:unit": "vitest run tests/index.test.ts",
|
|
58
58
|
"test:integration": "vitest run tests/integration.test.ts",
|
|
59
59
|
"test:conformance": "vitest run tests/conformance.test.ts",
|
|
60
60
|
"test:coverage": "vitest --coverage",
|
|
61
|
-
"lint": "eslint src
|
|
61
|
+
"lint": "eslint src",
|
|
62
62
|
"typecheck": "tsc --noEmit",
|
|
63
63
|
"prepublishOnly": "npm run build"
|
|
64
64
|
},
|
|
@@ -94,10 +94,12 @@
|
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/express": "^5.0.6",
|
|
96
96
|
"@types/node": "^25.5.0",
|
|
97
|
-
"eslint": "^8.
|
|
97
|
+
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
98
|
+
"@typescript-eslint/parser": "^8.58.0",
|
|
99
|
+
"eslint": "^10.1.0",
|
|
98
100
|
"express": "^5.2.1",
|
|
99
101
|
"tsup": "^8.0.1",
|
|
100
|
-
"typescript": "^
|
|
102
|
+
"typescript": "^6.0.2",
|
|
101
103
|
"vitest": "^4.1.0"
|
|
102
104
|
},
|
|
103
105
|
"repository": {
|