@arcis/node 1.3.0 → 1.4.2

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 (139) hide show
  1. package/README.md +1 -1
  2. package/dist/core/{index.d.mts → constants.d.ts} +21 -70
  3. package/dist/core/constants.d.ts.map +1 -0
  4. package/dist/core/errors.d.ts +53 -0
  5. package/dist/core/errors.d.ts.map +1 -0
  6. package/dist/core/index.d.ts +6 -168
  7. package/dist/core/index.d.ts.map +1 -0
  8. package/dist/core/index.js +11 -3
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/core/index.mjs +11 -3
  11. package/dist/core/index.mjs.map +1 -1
  12. package/dist/{types-BOkx5YJc.d.mts → core/types.d.ts} +27 -30
  13. package/dist/core/types.d.ts.map +1 -0
  14. package/dist/index.d.ts +71 -166
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +182 -48
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +182 -50
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/logging/index.d.ts +4 -36
  21. package/dist/logging/index.d.ts.map +1 -0
  22. package/dist/logging/index.js.map +1 -1
  23. package/dist/logging/index.mjs.map +1 -1
  24. package/dist/logging/{index.d.mts → redactor.d.ts} +5 -9
  25. package/dist/logging/redactor.d.ts.map +1 -0
  26. package/dist/middleware/bot-detection.d.ts +86 -0
  27. package/dist/middleware/bot-detection.d.ts.map +1 -0
  28. package/dist/middleware/cookies.d.ts +48 -0
  29. package/dist/middleware/cookies.d.ts.map +1 -0
  30. package/dist/middleware/cors.d.ts +65 -0
  31. package/dist/middleware/cors.d.ts.map +1 -0
  32. package/dist/middleware/csrf.d.ts +109 -0
  33. package/dist/middleware/csrf.d.ts.map +1 -0
  34. package/dist/middleware/error-handler.d.ts +43 -0
  35. package/dist/middleware/error-handler.d.ts.map +1 -0
  36. package/dist/middleware/headers.d.ts +29 -0
  37. package/dist/middleware/headers.d.ts.map +1 -0
  38. package/dist/middleware/hpp.d.ts +56 -0
  39. package/dist/middleware/hpp.d.ts.map +1 -0
  40. package/dist/middleware/index.d.ts +16 -3
  41. package/dist/middleware/index.d.ts.map +1 -0
  42. package/dist/middleware/index.js +68 -31
  43. package/dist/middleware/index.js.map +1 -1
  44. package/dist/middleware/index.mjs +69 -32
  45. package/dist/middleware/index.mjs.map +1 -1
  46. package/dist/middleware/main.d.ts +40 -0
  47. package/dist/middleware/main.d.ts.map +1 -0
  48. package/dist/middleware/rate-limit-sliding.d.ts +46 -0
  49. package/dist/middleware/rate-limit-sliding.d.ts.map +1 -0
  50. package/dist/middleware/rate-limit-token.d.ts +51 -0
  51. package/dist/middleware/rate-limit-token.d.ts.map +1 -0
  52. package/dist/middleware/rate-limit.d.ts +34 -0
  53. package/dist/middleware/rate-limit.d.ts.map +1 -0
  54. package/dist/sanitizers/command.d.ts +28 -0
  55. package/dist/sanitizers/command.d.ts.map +1 -0
  56. package/dist/sanitizers/encode.d.ts +46 -0
  57. package/dist/sanitizers/encode.d.ts.map +1 -0
  58. package/dist/sanitizers/headers.d.ts +46 -0
  59. package/dist/sanitizers/headers.d.ts.map +1 -0
  60. package/dist/sanitizers/index.d.ts +18 -22
  61. package/dist/sanitizers/index.d.ts.map +1 -0
  62. package/dist/sanitizers/index.js +90 -32
  63. package/dist/sanitizers/index.js.map +1 -1
  64. package/dist/sanitizers/index.mjs +88 -33
  65. package/dist/sanitizers/index.mjs.map +1 -1
  66. package/dist/sanitizers/jsonp.d.ts +34 -0
  67. package/dist/sanitizers/jsonp.d.ts.map +1 -0
  68. package/dist/sanitizers/ldap.d.ts +42 -0
  69. package/dist/sanitizers/ldap.d.ts.map +1 -0
  70. package/dist/sanitizers/nosql.d.ts +31 -0
  71. package/dist/sanitizers/nosql.d.ts.map +1 -0
  72. package/dist/sanitizers/path.d.ts +28 -0
  73. package/dist/sanitizers/path.d.ts.map +1 -0
  74. package/dist/sanitizers/pii.d.ts +80 -0
  75. package/dist/sanitizers/pii.d.ts.map +1 -0
  76. package/dist/sanitizers/prototype.d.ts +34 -0
  77. package/dist/sanitizers/prototype.d.ts.map +1 -0
  78. package/dist/sanitizers/sanitize.d.ts +51 -0
  79. package/dist/sanitizers/sanitize.d.ts.map +1 -0
  80. package/dist/sanitizers/sql.d.ts +28 -0
  81. package/dist/sanitizers/sql.d.ts.map +1 -0
  82. package/dist/sanitizers/ssti.d.ts +20 -0
  83. package/dist/sanitizers/ssti.d.ts.map +1 -0
  84. package/dist/sanitizers/utils.d.ts +19 -0
  85. package/dist/sanitizers/utils.d.ts.map +1 -0
  86. package/dist/sanitizers/xss.d.ts +35 -0
  87. package/dist/sanitizers/xss.d.ts.map +1 -0
  88. package/dist/sanitizers/xxe.d.ts +20 -0
  89. package/dist/sanitizers/xxe.d.ts.map +1 -0
  90. package/dist/stores/index.d.ts +6 -104
  91. package/dist/stores/index.d.ts.map +1 -0
  92. package/dist/stores/index.js +21 -1
  93. package/dist/stores/index.js.map +1 -1
  94. package/dist/stores/index.mjs +21 -1
  95. package/dist/stores/index.mjs.map +1 -1
  96. package/dist/stores/memory.d.ts +29 -0
  97. package/dist/stores/memory.d.ts.map +1 -0
  98. package/dist/stores/{index.d.mts → redis.d.ts} +6 -45
  99. package/dist/stores/redis.d.ts.map +1 -0
  100. package/dist/utils/duration.d.ts +34 -0
  101. package/dist/utils/duration.d.ts.map +1 -0
  102. package/dist/utils/fingerprint.d.ts +64 -0
  103. package/dist/utils/fingerprint.d.ts.map +1 -0
  104. package/dist/utils/index.d.ts +10 -0
  105. package/dist/utils/index.d.ts.map +1 -0
  106. package/dist/utils/index.js +188 -0
  107. package/dist/utils/index.js.map +1 -0
  108. package/dist/utils/index.mjs +182 -0
  109. package/dist/utils/index.mjs.map +1 -0
  110. package/dist/utils/ip.d.ts +70 -0
  111. package/dist/utils/ip.d.ts.map +1 -0
  112. package/dist/validation/email.d.ts +82 -0
  113. package/dist/validation/email.d.ts.map +1 -0
  114. package/dist/validation/file.d.ts +90 -0
  115. package/dist/validation/file.d.ts.map +1 -0
  116. package/dist/validation/index.d.ts +10 -3
  117. package/dist/validation/index.d.ts.map +1 -0
  118. package/dist/validation/index.js +38 -21
  119. package/dist/validation/index.js.map +1 -1
  120. package/dist/validation/index.mjs +38 -21
  121. package/dist/validation/index.mjs.map +1 -1
  122. package/dist/validation/redirect.d.ts +64 -0
  123. package/dist/validation/redirect.d.ts.map +1 -0
  124. package/dist/validation/schema.d.ts +36 -0
  125. package/dist/validation/schema.d.ts.map +1 -0
  126. package/dist/validation/url.d.ts +65 -0
  127. package/dist/validation/url.d.ts.map +1 -0
  128. package/package.json +8 -6
  129. package/dist/encode-CrQCGlBq.d.mts +0 -484
  130. package/dist/encode-jl9sOwmA.d.ts +0 -484
  131. package/dist/index-BAhgn9V2.d.ts +0 -532
  132. package/dist/index-BGNKspqH.d.ts +0 -340
  133. package/dist/index-Cd02z-0j.d.mts +0 -340
  134. package/dist/index-DgJtWMSj.d.mts +0 -532
  135. package/dist/index.d.mts +0 -175
  136. package/dist/middleware/index.d.mts +0 -3
  137. package/dist/sanitizers/index.d.mts +0 -24
  138. package/dist/types-BOkx5YJc.d.ts +0 -279
  139. package/dist/validation/index.d.mts +0 -3
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @module @arcis/node/middleware/csrf
3
+ * CSRF (Cross-Site Request Forgery) protection middleware
4
+ *
5
+ * Implements the double-submit cookie pattern:
6
+ * 1. Server sets a CSRF token in a cookie
7
+ * 2. Client must send the same token in a header or form field
8
+ * 3. Middleware rejects requests where cookie token !== header/field token
9
+ *
10
+ * This works because an attacker's cross-origin form submission will include
11
+ * the cookie automatically, but cannot read it (same-origin policy) to set
12
+ * the matching header.
13
+ */
14
+ import type { Request, Response, NextFunction, RequestHandler } from 'express';
15
+ /** CSRF protection configuration */
16
+ export interface CsrfOptions {
17
+ /** Cookie name for the CSRF token. Default: '_csrf' */
18
+ cookieName?: string;
19
+ /** Header name to check for the token. Default: 'x-csrf-token' */
20
+ headerName?: string;
21
+ /** Form field name to check for the token. Default: '_csrf' */
22
+ fieldName?: string;
23
+ /** Token byte length (hex-encoded = 2x chars). Default: 32 */
24
+ tokenLength?: number;
25
+ /** HTTP methods to protect. Default: ['POST', 'PUT', 'PATCH', 'DELETE'] */
26
+ protectedMethods?: string[];
27
+ /** Paths to exclude from CSRF checks (e.g., webhook endpoints) */
28
+ excludePaths?: string[];
29
+ /**
30
+ * Per-request skip function. If it returns true, CSRF check is skipped
31
+ * for that request. Useful for API key auth or signed webhooks.
32
+ *
33
+ * @example
34
+ * skipCsrf: (req) => Boolean(req.headers['x-api-key'])
35
+ */
36
+ skipCsrf?: (req: Request) => boolean;
37
+ /**
38
+ * Use the __Host- cookie prefix for stronger cookie security.
39
+ * When enabled, the browser enforces: Secure=true, no Domain, Path=/.
40
+ * This prevents CSRF cookie theft across subdomains.
41
+ * Default: false
42
+ */
43
+ useHostPrefix?: boolean;
44
+ /** Cookie options */
45
+ cookie?: {
46
+ /** Cookie path. Default: '/' */
47
+ path?: string;
48
+ /** HttpOnly — set false so client JS can read it for headers. Default: false */
49
+ httpOnly?: boolean;
50
+ /** Secure flag (HTTPS only). Default: true in production */
51
+ secure?: boolean;
52
+ /** SameSite attribute. Default: 'Lax' */
53
+ sameSite?: 'Strict' | 'Lax' | 'None';
54
+ /** Cookie domain */
55
+ domain?: string;
56
+ };
57
+ /** Custom error handler when CSRF validation fails */
58
+ onError?: (req: Request, res: Response, next: NextFunction) => void;
59
+ }
60
+ /**
61
+ * Generate a cryptographically random CSRF token.
62
+ *
63
+ * @param length - Byte length (output is hex, so 2x chars). Default: 32
64
+ * @returns Hex-encoded random token
65
+ *
66
+ * @example
67
+ * const token = generateCsrfToken(); // 64 hex chars
68
+ */
69
+ export declare function generateCsrfToken(length?: number): string;
70
+ /**
71
+ * Validate that two CSRF tokens match using constant-time comparison.
72
+ *
73
+ * @param cookieToken - Token from the cookie
74
+ * @param requestToken - Token from the header or form field
75
+ * @returns true if tokens match
76
+ */
77
+ export declare function validateCsrfToken(cookieToken: string, requestToken: string): boolean;
78
+ /**
79
+ * Create CSRF protection middleware using double-submit cookie pattern.
80
+ *
81
+ * For safe methods (GET, HEAD, OPTIONS), sets a CSRF token cookie if not present.
82
+ * For unsafe methods (POST, PUT, PATCH, DELETE), validates the token.
83
+ *
84
+ * @param options - CSRF configuration
85
+ * @returns Express middleware
86
+ *
87
+ * @example
88
+ * // Basic usage
89
+ * app.use(csrfProtection());
90
+ *
91
+ * @example
92
+ * // Exclude webhook paths
93
+ * app.use(csrfProtection({
94
+ * excludePaths: ['/api/webhooks/stripe', '/api/webhooks/github']
95
+ * }));
96
+ *
97
+ * @example
98
+ * // Client-side: read cookie + set header
99
+ * const token = document.cookie.match(/_csrf=([^;]+)/)?.[1];
100
+ * fetch('/api/data', {
101
+ * method: 'POST',
102
+ * headers: { 'X-CSRF-Token': token },
103
+ * credentials: 'same-origin'
104
+ * });
105
+ */
106
+ export declare function csrfProtection(options?: CsrfOptions): RequestHandler;
107
+ /** Alias for csrfProtection */
108
+ export declare const createCsrf: typeof csrfProtection;
109
+ //# sourceMappingURL=csrf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/middleware/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACrC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qBAAqB;IACrB,MAAM,CAAC,EAAE;QACP,gCAAgC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,gFAAgF;QAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,4DAA4D;QAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,yCAAyC;QACzC,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;QACrC,oBAAoB;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,sDAAsD;IACtD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACrE;AAUD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAE7D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAQpF;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,WAAgB,GAAG,cAAc,CAmFxE;AAsDD,+BAA+B;AAC/B,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @module @arcis/node/middleware/error-handler
3
+ * Production-safe error handler middleware
4
+ */
5
+ import type { Request, Response, NextFunction } from 'express';
6
+ import type { ErrorHandlerOptions } from '../core/types';
7
+ /**
8
+ * Check if an error message contains sensitive infrastructure details.
9
+ */
10
+ export declare function containsSensitiveInfo(message: string): boolean;
11
+ /**
12
+ * Create Express error handler that hides sensitive details in production.
13
+ *
14
+ * Prevents information leakage by:
15
+ * - Hiding stack traces in production
16
+ * - Hiding error messages unless explicitly exposed
17
+ * - Scrubbing database errors, connection strings, and internal IPs
18
+ *
19
+ * @param options - Error handler configuration (or boolean for isDev)
20
+ * @returns Express error handling middleware
21
+ *
22
+ * @example
23
+ * // Production mode (default) - hides error details
24
+ * app.use(errorHandler());
25
+ *
26
+ * @example
27
+ * // Development mode - shows error details and stack traces
28
+ * app.use(errorHandler({ isDev: true }));
29
+ *
30
+ * @example
31
+ * // With custom logger
32
+ * app.use(errorHandler({
33
+ * isDev: false,
34
+ * logger: arcis.logger()
35
+ * }));
36
+ */
37
+ export declare function errorHandler(options?: ErrorHandlerOptions | boolean): (err: Error, req: Request, res: Response, next: NextFunction) => void;
38
+ /**
39
+ * Alias for errorHandler
40
+ * @see errorHandler
41
+ */
42
+ export declare const createErrorHandler: typeof errorHandler;
43
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAE,mBAAmB,EAAa,MAAM,eAAe,CAAC;AAyBpE;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,GAAE,mBAAmB,GAAG,OAAe,GAC7C,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CA2DvE;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,qBAAe,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @module @arcis/node/middleware/headers
3
+ * Security headers middleware
4
+ */
5
+ import type { RequestHandler } from 'express';
6
+ import type { HeaderOptions } from '../core/types';
7
+ /**
8
+ * Create Express middleware for security headers.
9
+ * Sets CSP, HSTS, X-Frame-Options, and other security headers.
10
+ *
11
+ * @param options - Header configuration
12
+ * @returns Express middleware
13
+ *
14
+ * @example
15
+ * app.use(createHeaders());
16
+ *
17
+ * @example
18
+ * app.use(createHeaders({
19
+ * frameOptions: 'SAMEORIGIN',
20
+ * contentSecurityPolicy: "default-src 'self'"
21
+ * }));
22
+ */
23
+ export declare function createHeaders(options?: HeaderOptions): RequestHandler;
24
+ /**
25
+ * Alias for createHeaders
26
+ * @see createHeaders
27
+ */
28
+ export declare const securityHeaders: typeof createHeaders;
29
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/middleware/headers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,eAAe,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAwHzE;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,sBAAgB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @module @arcis/node/middleware/hpp
3
+ * HTTP Parameter Pollution (HPP) protection middleware
4
+ *
5
+ * Normalizes duplicate query and body parameters to their last value,
6
+ * preventing attackers from bypassing validation by repeating parameters.
7
+ *
8
+ * Attack example:
9
+ * GET /search?role=user&role=admin
10
+ * Without HPP: req.query.role = ['user', 'admin']
11
+ * With HPP: req.query.role = 'admin' (last value wins)
12
+ *
13
+ * Originals are preserved in req.queryPolluted / req.bodyPolluted
14
+ * for logging or auditing without blocking the request.
15
+ */
16
+ import type { RequestHandler } from 'express';
17
+ /** HPP protection configuration */
18
+ export interface HppOptions {
19
+ /**
20
+ * Parameters that legitimately accept arrays and should not be normalized.
21
+ * Example: ['tags', 'ids', 'filter']
22
+ */
23
+ whitelist?: string[];
24
+ /** Normalize duplicate query string parameters. Default: true */
25
+ checkQuery?: boolean;
26
+ /** Normalize duplicate body parameters. Default: true */
27
+ checkBody?: boolean;
28
+ }
29
+ /**
30
+ * HTTP Parameter Pollution protection middleware.
31
+ *
32
+ * Normalizes duplicate query/body parameters to a single value (last wins).
33
+ * Whitelisted parameters are allowed to remain as arrays.
34
+ *
35
+ * @param options - HPP configuration
36
+ * @returns Express middleware
37
+ *
38
+ * @example
39
+ * // Basic — normalize all duplicates
40
+ * app.use(hpp());
41
+ *
42
+ * @example
43
+ * // Allow arrays for specific params (e.g., tag filters, IDs)
44
+ * app.use(hpp({ whitelist: ['tags', 'ids'] }));
45
+ *
46
+ * @example
47
+ * // Inspect what was removed (for logging)
48
+ * app.use((req, res, next) => {
49
+ * const polluted = (req as any).queryPolluted;
50
+ * if (Object.keys(polluted).length) logger.warn('HPP detected', polluted);
51
+ * next();
52
+ * });
53
+ */
54
+ export declare function hpp(options?: HppOptions): RequestHandler;
55
+ export declare const createHpp: typeof hpp;
56
+ //# sourceMappingURL=hpp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hpp.d.ts","sourceRoot":"","sources":["../../src/middleware/hpp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,mCAAmC;AACnC,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,cAAc,CAwD5D;AAED,eAAO,MAAM,SAAS,YAAM,CAAC"}
@@ -1,3 +1,16 @@
1
- export { 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-BAhgn9V2.js';
2
- import '../types-BOkx5YJc.js';
3
- import 'express';
1
+ /**
2
+ * @module @arcis/node/middleware
3
+ * All middleware for Arcis
4
+ */
5
+ export { arcis, arcisFunction } from './main';
6
+ export { default } from './main';
7
+ export { createRateLimiter, rateLimit } from './rate-limit';
8
+ export { createSlidingWindowLimiter } from './rate-limit-sliding';
9
+ export { createTokenBucketLimiter } from './rate-limit-token';
10
+ export { createHeaders, securityHeaders } from './headers';
11
+ export { errorHandler, createErrorHandler } from './error-handler';
12
+ export { safeCors, createCors } from './cors';
13
+ export { secureCookieDefaults, createSecureCookies, enforceSecureCookie } from './cookies';
14
+ export { botProtection, detectBot } from './bot-detection';
15
+ export { csrfProtection, createCsrf, generateCsrfToken, validateCsrfToken } from './csrf';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGjC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC"}
@@ -69,7 +69,15 @@ var XSS_REMOVE_PATTERNS = [
69
69
  /javascript\s*:/gi,
70
70
  /vbscript\s*:/gi,
71
71
  /** data: URIs with HTML/script content */
72
- /data\s*:\s*text\/html[^>\s]*/gi
72
+ /data\s*:\s*text\/html[^>\s]*/gi,
73
+ /** form tag injection — phishing via action= redirection */
74
+ /<form[\s>][^>]*/gi,
75
+ /** meta tag injection — http-equiv refresh or CSP bypass */
76
+ /<meta[\s>][^>]*/gi,
77
+ /** base href hijacking */
78
+ /<base[\s>][^>]*/gi,
79
+ /** link tag injection — stylesheet or preload attacks */
80
+ /<link[\s>][^>]*/gi
73
81
  ];
74
82
  var SQL_PATTERNS = [
75
83
  /** SQL keywords */
@@ -133,8 +141,8 @@ var COMMAND_PATTERNS = [
133
141
  /[;&|`]/g,
134
142
  /** Command substitution: $( ... ) — matched as a pair to reduce false positives */
135
143
  /\$\(/g,
136
- /** URL-encoded newline/carriage-return injection (%0a, %0d) */
137
- /%0[ad]/gi
144
+ /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
145
+ /%0[0-9a-f]/gi
138
146
  ];
139
147
  var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
140
148
  "__proto__",
@@ -422,7 +430,24 @@ function createRateLimiter(options = {}) {
422
430
  }
423
431
  next();
424
432
  } catch (error) {
425
- console.error("[arcis] Rate limiter error:", error);
433
+ console.error("[arcis] Rate limiter store error, using in-memory fallback:", error);
434
+ try {
435
+ const key = keyGenerator(req);
436
+ const now = Date.now();
437
+ if (!inMemoryStore[key] || inMemoryStore[key].resetTime < now) {
438
+ inMemoryStore[key] = { count: 1, resetTime: now + windowMs };
439
+ } else {
440
+ inMemoryStore[key].count++;
441
+ }
442
+ const count = inMemoryStore[key].count;
443
+ if (count > max) {
444
+ const resetSeconds = Math.ceil((inMemoryStore[key].resetTime - now) / 1e3);
445
+ res.setHeader("Retry-After", resetSeconds.toString());
446
+ res.status(statusCode).json({ error: message, retryAfter: resetSeconds });
447
+ return;
448
+ }
449
+ } catch {
450
+ }
426
451
  next();
427
452
  }
428
453
  };
@@ -632,26 +657,31 @@ function sanitizePath(input, collectThreats = false) {
632
657
  const threats = [];
633
658
  let value = input;
634
659
  let wasSanitized = false;
635
- for (const pattern of PATH_PATTERNS) {
636
- pattern.lastIndex = 0;
637
- if (pattern.test(value)) {
660
+ value = value.normalize("NFKC");
661
+ let prev;
662
+ do {
663
+ prev = value;
664
+ for (const pattern of PATH_PATTERNS) {
638
665
  pattern.lastIndex = 0;
639
- if (collectThreats) {
640
- const matches = value.match(pattern);
641
- if (matches) {
642
- for (const match of matches) {
643
- threats.push({
644
- type: "path_traversal",
645
- pattern: pattern.source,
646
- original: match
647
- });
666
+ if (pattern.test(value)) {
667
+ pattern.lastIndex = 0;
668
+ if (collectThreats) {
669
+ const matches = value.match(pattern);
670
+ if (matches) {
671
+ for (const match of matches) {
672
+ threats.push({
673
+ type: "path_traversal",
674
+ pattern: pattern.source,
675
+ original: match
676
+ });
677
+ }
648
678
  }
649
679
  }
680
+ value = value.replace(pattern, "");
681
+ wasSanitized = true;
650
682
  }
651
- value = value.replace(pattern, "");
652
- wasSanitized = true;
653
683
  }
654
- }
684
+ } while (value !== prev);
655
685
  if (collectThreats) {
656
686
  return { value, wasSanitized, threats };
657
687
  }
@@ -709,7 +739,7 @@ function sanitizeString(value, options = {}) {
709
739
  if (value.length > maxSize) {
710
740
  throw new InputTooLargeError(maxSize, value.length);
711
741
  }
712
- const reject = options.mode !== "sanitize";
742
+ const reject = options.mode === "reject";
713
743
  let result = value;
714
744
  if (options.sql !== false) {
715
745
  if (reject) {
@@ -1586,11 +1616,9 @@ function generateCsrfToken(length = 32) {
1586
1616
  function validateCsrfToken(cookieToken, requestToken) {
1587
1617
  if (!cookieToken || !requestToken) return false;
1588
1618
  if (cookieToken.length !== requestToken.length) return false;
1589
- let result = 0;
1590
- for (let i = 0; i < cookieToken.length; i++) {
1591
- result |= cookieToken.charCodeAt(i) ^ requestToken.charCodeAt(i);
1592
- }
1593
- return result === 0;
1619
+ const a = Buffer.from(cookieToken);
1620
+ const b = Buffer.from(requestToken);
1621
+ return crypto.timingSafeEqual(a, b);
1594
1622
  }
1595
1623
  function getRequestToken(req, headerName, fieldName) {
1596
1624
  const headerToken = req.headers[headerName.toLowerCase()];
@@ -1599,19 +1627,17 @@ function getRequestToken(req, headerName, fieldName) {
1599
1627
  const bodyToken = req.body[fieldName];
1600
1628
  if (typeof bodyToken === "string" && bodyToken) return bodyToken;
1601
1629
  }
1602
- if (req.query && fieldName in req.query) {
1603
- const queryToken = req.query[fieldName];
1604
- if (typeof queryToken === "string" && queryToken) return queryToken;
1605
- }
1606
1630
  return void 0;
1607
1631
  }
1608
1632
  function csrfProtection(options = {}) {
1609
- const cookieName = options.cookieName ?? DEFAULTS.cookieName;
1633
+ const baseCookieName = options.cookieName ?? DEFAULTS.cookieName;
1634
+ const cookieName = options.useHostPrefix ? `__Host-${baseCookieName}` : baseCookieName;
1610
1635
  const headerName = options.headerName ?? DEFAULTS.headerName;
1611
1636
  const fieldName = options.fieldName ?? DEFAULTS.fieldName;
1612
1637
  const tokenLength = options.tokenLength ?? DEFAULTS.tokenLength;
1613
1638
  const protectedMethods = options.protectedMethods ?? [...DEFAULTS.protectedMethods];
1614
1639
  const excludePaths = options.excludePaths ?? [];
1640
+ const skipCsrf = options.skipCsrf;
1615
1641
  const isProduction = process.env.NODE_ENV === "production";
1616
1642
  const cookieOpts = {
1617
1643
  path: options.cookie?.path ?? "/",
@@ -1631,6 +1657,9 @@ function csrfProtection(options = {}) {
1631
1657
  const protectedSet = new Set(protectedMethods.map((m) => m.toUpperCase()));
1632
1658
  return (req, res, next) => {
1633
1659
  const method = req.method.toUpperCase();
1660
+ if (skipCsrf && skipCsrf(req)) {
1661
+ return next();
1662
+ }
1634
1663
  const requestPath = req.path || req.url;
1635
1664
  if (excludePaths.some((p) => requestPath === p || requestPath.startsWith(p + "/"))) {
1636
1665
  return next();
@@ -1680,7 +1709,15 @@ function setCsrfCookie(res, name, token, opts) {
1680
1709
  if (opts.secure) parts.push("Secure");
1681
1710
  parts.push(`SameSite=${opts.sameSite}`);
1682
1711
  if (opts.domain) parts.push(`Domain=${opts.domain}`);
1683
- res.setHeader("Set-Cookie", parts.join("; "));
1712
+ const newCookie = parts.join("; ");
1713
+ const existing = res.getHeader("Set-Cookie");
1714
+ if (existing === void 0) {
1715
+ res.setHeader("Set-Cookie", newCookie);
1716
+ } else if (Array.isArray(existing)) {
1717
+ res.setHeader("Set-Cookie", [...existing, newCookie]);
1718
+ } else {
1719
+ res.setHeader("Set-Cookie", [existing, newCookie]);
1720
+ }
1684
1721
  }
1685
1722
  function escapeRegex(str) {
1686
1723
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");