exisjs 0.7.11 → 0.7.13

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 (150) hide show
  1. package/README.md +195 -39
  2. package/dist/cli/commands/dev.d.ts +4 -0
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/dev.js +136 -32
  5. package/dist/cli/commands/dev.js.map +1 -1
  6. package/dist/cli/commands/run.d.ts +7 -0
  7. package/dist/cli/commands/run.d.ts.map +1 -0
  8. package/dist/cli/commands/run.js +99 -0
  9. package/dist/cli/commands/run.js.map +1 -0
  10. package/dist/cli/index.js +29 -13
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/manifest.d.ts.map +1 -1
  13. package/dist/cli/manifest.js +18 -3
  14. package/dist/cli/manifest.js.map +1 -1
  15. package/dist/cli/routes.d.ts +6 -0
  16. package/dist/cli/routes.d.ts.map +1 -1
  17. package/dist/cli/routes.js +224 -135
  18. package/dist/cli/routes.js.map +1 -1
  19. package/dist/config/config.d.ts +2 -1
  20. package/dist/config/config.d.ts.map +1 -1
  21. package/dist/config/config.js.map +1 -1
  22. package/dist/config/types.d.ts +30 -9
  23. package/dist/config/types.d.ts.map +1 -1
  24. package/dist/database/lifecycle.d.ts +6 -1
  25. package/dist/database/lifecycle.d.ts.map +1 -1
  26. package/dist/database/lifecycle.js +15 -4
  27. package/dist/database/lifecycle.js.map +1 -1
  28. package/dist/decorators/params.d.ts +21 -0
  29. package/dist/decorators/params.d.ts.map +1 -1
  30. package/dist/decorators/params.js +21 -1
  31. package/dist/decorators/params.js.map +1 -1
  32. package/dist/di/container.d.ts +15 -6
  33. package/dist/di/container.d.ts.map +1 -1
  34. package/dist/di/container.js +26 -3
  35. package/dist/di/container.js.map +1 -1
  36. package/dist/di/index.d.ts +1 -1
  37. package/dist/di/index.d.ts.map +1 -1
  38. package/dist/di/index.js.map +1 -1
  39. package/dist/di/inject.d.ts +2 -1
  40. package/dist/di/inject.d.ts.map +1 -1
  41. package/dist/di/inject.js +2 -1
  42. package/dist/di/inject.js.map +1 -1
  43. package/dist/docs/BENCHMARKS.md +4 -4
  44. package/dist/docs/content/README.md +43 -44
  45. package/dist/docs/content/boundaries.mdx +141 -48
  46. package/dist/docs/content/cli/commands.mdx +161 -107
  47. package/dist/docs/content/cli/overview.mdx +61 -46
  48. package/dist/docs/content/cli/usage.mdx +107 -30
  49. package/dist/docs/content/config.mdx +145 -147
  50. package/dist/docs/content/controllers.mdx +193 -464
  51. package/dist/docs/content/cron.mdx +183 -69
  52. package/dist/docs/content/decorators.mdx +449 -154
  53. package/dist/docs/content/errors.mdx +202 -305
  54. package/dist/docs/content/exisjs-mcp.mdx +59 -49
  55. package/dist/docs/content/installation.mdx +243 -157
  56. package/dist/docs/content/introduction.mdx +220 -48
  57. package/dist/docs/content/middleware.mdx +171 -497
  58. package/dist/docs/content/modules.mdx +141 -83
  59. package/dist/docs/content/providers.mdx +216 -180
  60. package/dist/docs/content/requests.mdx +168 -488
  61. package/dist/docs/content/responses.mdx +200 -533
  62. package/dist/docs/content/routing.mdx +239 -558
  63. package/dist/docs/content/security.mdx +233 -57
  64. package/dist/docs/content/structure.mdx +151 -240
  65. package/dist/docs/content/swagger.mdx +192 -91
  66. package/dist/docs/content/testing.mdx +205 -74
  67. package/dist/docs/content/validation.mdx +479 -341
  68. package/dist/docs/reference/README.md +34 -2
  69. package/dist/docs/reference/config.mdx +277 -73
  70. package/dist/docs/reference/context.mdx +197 -106
  71. package/dist/docs/reference/cron.mdx +266 -152
  72. package/dist/docs/reference/database.mdx +135 -53
  73. package/dist/docs/reference/decorators.mdx +234 -116
  74. package/dist/docs/reference/di.mdx +146 -112
  75. package/dist/docs/reference/error.mdx +155 -178
  76. package/dist/docs/reference/exis.mdx +141 -70
  77. package/dist/docs/reference/logger.mdx +110 -126
  78. package/dist/docs/reference/middleware.mdx +235 -158
  79. package/dist/docs/reference/plugin.mdx +136 -64
  80. package/dist/docs/reference/router.mdx +277 -251
  81. package/dist/docs/reference/sanitize.mdx +91 -87
  82. package/dist/docs/reference/swagger.mdx +280 -100
  83. package/dist/docs/reference/testing.mdx +223 -89
  84. package/dist/docs/reference/validator.mdx +194 -206
  85. package/dist/lib/inspect-routes.d.ts +2 -0
  86. package/dist/lib/inspect-routes.d.ts.map +1 -0
  87. package/dist/lib/inspect-routes.js +148 -0
  88. package/dist/lib/inspect-routes.js.map +1 -0
  89. package/dist/lib/start-server.js +13 -8
  90. package/dist/lib/start-server.js.map +1 -1
  91. package/dist/module/module.d.ts +6 -3
  92. package/dist/module/module.d.ts.map +1 -1
  93. package/dist/module/module.js +17 -3
  94. package/dist/module/module.js.map +1 -1
  95. package/dist/router/controller-registrar.d.ts.map +1 -1
  96. package/dist/router/controller-registrar.js +26 -4
  97. package/dist/router/controller-registrar.js.map +1 -1
  98. package/dist/router/scanner/cron-mounter.d.ts.map +1 -1
  99. package/dist/router/scanner/cron-mounter.js +9 -2
  100. package/dist/router/scanner/cron-mounter.js.map +1 -1
  101. package/dist/server/app.d.ts +3 -1
  102. package/dist/server/app.d.ts.map +1 -1
  103. package/dist/server/app.js +29 -8
  104. package/dist/server/app.js.map +1 -1
  105. package/dist/server/define.d.ts.map +1 -1
  106. package/dist/server/define.js +5 -0
  107. package/dist/server/define.js.map +1 -1
  108. package/dist/server/request.d.ts +2 -0
  109. package/dist/server/request.d.ts.map +1 -1
  110. package/dist/server/request.js +11 -0
  111. package/dist/server/request.js.map +1 -1
  112. package/dist/swagger/generator.d.ts.map +1 -1
  113. package/dist/swagger/generator.js +130 -10
  114. package/dist/swagger/generator.js.map +1 -1
  115. package/dist/telemetry/index.d.ts +12 -0
  116. package/dist/telemetry/index.d.ts.map +1 -0
  117. package/dist/telemetry/index.js +121 -0
  118. package/dist/telemetry/index.js.map +1 -0
  119. package/dist/testing/client.d.ts +1 -0
  120. package/dist/testing/client.d.ts.map +1 -1
  121. package/dist/testing/client.js +4 -1
  122. package/dist/testing/client.js.map +1 -1
  123. package/dist/testing/index.d.ts.map +1 -1
  124. package/dist/testing/index.js +20 -4
  125. package/dist/testing/index.js.map +1 -1
  126. package/dist/validator/builder.d.ts +26 -1
  127. package/dist/validator/builder.d.ts.map +1 -1
  128. package/dist/validator/builder.js +31 -0
  129. package/dist/validator/builder.js.map +1 -1
  130. package/dist/validator/engine.d.ts +23 -0
  131. package/dist/validator/engine.d.ts.map +1 -1
  132. package/dist/validator/engine.js +228 -31
  133. package/dist/validator/engine.js.map +1 -1
  134. package/dist/validator/index.d.ts +8 -4
  135. package/dist/validator/index.d.ts.map +1 -1
  136. package/dist/validator/index.js +4 -1
  137. package/dist/validator/index.js.map +1 -1
  138. package/dist/validator/tex-types.d.ts +15 -0
  139. package/dist/validator/tex-types.d.ts.map +1 -1
  140. package/dist/validator/tex-types.js +102 -0
  141. package/dist/validator/tex-types.js.map +1 -1
  142. package/dist/validator/tex.d.ts +1 -1
  143. package/dist/validator/tex.d.ts.map +1 -1
  144. package/dist/validator/tex.js +3 -1
  145. package/dist/validator/tex.js.map +1 -1
  146. package/dist/validator/types.d.ts +1 -0
  147. package/dist/validator/types.d.ts.map +1 -1
  148. package/package.json +47 -14
  149. package/dist/docs/content/dataloader.mdx +0 -170
  150. package/dist/docs/reference/dataloader.mdx +0 -91
@@ -1,109 +1,238 @@
1
1
  ---
2
- nextUrl: '/docs/structure'
3
- nextTitle: 'Project Structure'
2
+ title: "Security and Traffic Control"
3
+ description: "Configure HTTP security headers, CSRF double-submit cookies, CIDR IP filtering, rate limiting, and exploit probe protection in ExisJS."
4
+ nextUrl: "/docs/middleware"
5
+ nextTitle: "Middleware Pipeline"
4
6
  ---
5
7
 
6
- # Security & Edge Middleware
8
+ # Security and Traffic Control
7
9
 
8
- ExisJS provides native, zero-dependency edge and request security middleware built directly into core.
10
+ ExisJS includes built-in security and traffic control middlewares in `exisjs/middleware` and security decorators in `exisjs/decorators`. These components protect against common web vulnerabilities, brute-force requests, parameter pollution, and scanner abuse.
9
11
 
10
12
  ---
11
13
 
12
- ## 1. Edge Security Middlewares
14
+ ## Security Middleware Reference
13
15
 
14
- The following security middlewares are available from `exisjs/middleware` and can be registered globally in `server.ts` or scoped to route trees via `boundary.ts`:
16
+ The following security middlewares are exported from `exisjs/middleware` and can be registered globally in `server.ts`, in `exis.config.ts`, or scoped to route hierarchies in `boundary.ts`:
15
17
 
16
- - `helmet(options?)`: Sets 14+ secure HTTP headers (Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and CSP).
17
- - `csrf(options)`: Enforces the Signed Double Submit Cookie pattern to protect state-changing requests (POST, PUT, DELETE) from CSRF attacks.
18
- - `hpp()`: Protects against HTTP Parameter Pollution attacks by ensuring array query parameters/form fields are safely handled.
19
- - `mongoSanitize()`: Strips MongoDB operator keys (keys starting with `$` or containing `.`) from `req.body`, `req.query`, and `req.params`.
20
- - `dbSanitize(options?)`: Protects against NoSQL query operator injection (`$where` and other malicious keys) before reaching database layers.
21
- - `timeout(ms)`: Automatically terminates requests that exceed the specified timeout limit, returning a 503 response.
18
+ | Middleware | Signature | Description |
19
+ | :--- | :--- | :--- |
20
+ | `helmet` | `helmet(options?)` | Sets secure HTTP headers (`Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`, `Content-Security-Policy`, etc.). |
21
+ | `csrf` | `csrf(options)` | Protects state-changing requests (`POST`, `PUT`, `DELETE`, `PATCH`) using the Signed Double-Submit Cookie pattern. |
22
+ | `rateLimit` | `rateLimit(options?)` | Enforces request volume limits per IP or custom key, returning `429 Too Many Requests` with standard rate-limit headers. |
23
+ | `ipFilter` | `ipFilter(options)` | Grants or restricts access based on client IP addresses and CIDR subnets. |
24
+ | `idempotent` | `idempotent(options?)` | Caches mutation responses keyed by the `Idempotency-Key` header to prevent duplicate operations. |
25
+ | `hpp` | `hpp()` | Protects against HTTP Parameter Pollution by normalizing repeated query parameters and form fields. |
26
+ | `mongoSanitize` | `mongoSanitize()` | Strips MongoDB query operator keys (keys starting with `$` or containing `.`) from bodies, queries, and parameters. |
27
+ | `timeout` | `timeout(ms \| options)` | Terminates requests exceeding a specified duration with an HTTP `503 Service Unavailable` response. |
28
+ | `blockSuspiciousProbes` | `blockSuspiciousProbes(options?)` | Intercepts automated vulnerability scanners (probing for `.env`, `/.git`, WordPress files) and terminates them immediately. |
29
+ | `timingSafeEqual` | `timingSafeEqual(a, b)` | Compares secrets, tokens, or hashes in constant time to prevent side-channel timing attacks. |
22
30
 
23
- > [!TIP]
24
- > Security middlewares like `hpp()`, `mongoSanitize()`, and `dbSanitize()` automatically synchronize with the request body parser. If registered globally before route handlers, they safely await the body and sanitize it deterministically without runtime crashes.
31
+ ---
32
+
33
+ ## 1. Security Headers (`helmet`)
34
+
35
+ The `helmet` middleware sets secure response headers to harden the application against cross-site scripting (XSS), clickjacking, and content sniffing:
25
36
 
26
37
  ```typescript filename="src/http/server.ts"
27
38
  import { exis } from 'exisjs'
28
- import { helmet, csrf, timeout, hpp, mongoSanitize } from 'exisjs/middleware'
39
+ import { helmet } from 'exisjs/middleware'
29
40
 
30
41
  export default exis({
31
42
  async onStart(app) {
32
- app.use(helmet())
33
- app.use(timeout(10000))
34
- app.use(hpp())
35
- app.use(mongoSanitize())
43
+ app.use(
44
+ helmet({
45
+ hsts: { maxAge: 31536000, includeSubDomains: true },
46
+ frameguard: { action: 'DENY' },
47
+ noSniff: true,
48
+ xssFilter: true,
49
+ contentSecurityPolicy: "default-src 'self'",
50
+ })
51
+ )
52
+ },
53
+ })
54
+ ```
55
+
56
+ ---
57
+
58
+ ## 2. CSRF Protection (`csrf`)
59
+
60
+ ExisJS implements the **Signed Double-Submit Cookie** pattern for CSRF defense:
61
+ - On safe requests (`GET`, `HEAD`, `OPTIONS`), a cryptographically signed cookie is issued to the client.
62
+ - On state-changing requests (`POST`, `PUT`, `DELETE`, `PATCH`), the client must supply the unsigned token in the `x-csrf-token` header.
63
+
64
+ ```typescript filename="src/http/server.ts"
65
+ import { exis } from 'exisjs'
66
+ import { csrf } from 'exisjs/middleware'
67
+
68
+ export default exis({
69
+ async onStart(app) {
70
+ app.use(
71
+ csrf({
72
+ secret: process.env.CSRNF_SECRET!, // Must be at least 32 characters
73
+ cookieName: 'csrf-token',
74
+ headerName: 'x-csrf-token',
75
+ cookieOptions: {
76
+ secure: process.env.NODE_ENV === 'production',
77
+ sameSite: 'Lax',
78
+ },
79
+ })
80
+ )
36
81
  },
37
82
  })
38
83
  ```
39
84
 
40
85
  ---
41
86
 
42
- ## 2. In-Memory Rate Limiting
87
+ ## 3. Rate Limiting (`rateLimit`)
43
88
 
44
- The core `rateLimit` middleware provides high-performance in-memory and off-heap native rate limiting with standard HTTP response headers:
89
+ Use `rateLimit` to prevent brute-force attacks and abuse by capping incoming requests over a sliding time window.
45
90
 
46
91
  ```typescript filename="src/http/boundary.ts"
47
92
  import { defineBoundary } from 'exisjs/router'
48
93
  import { rateLimit } from 'exisjs/middleware'
49
94
 
50
- export const config = defineBoundary({})
51
-
52
- // Apply rate limiting chain step across the boundary
53
95
  export const limiter = rateLimit({
54
- windowMs: 60 * 1000, // 1 minute window
55
- max: 100, // 100 requests per window
96
+ windowMs: 60 * 1000, // 1 minute
97
+ max: 100, // Limit each IP to 100 requests per windowMs
56
98
  message: 'Too many requests from this IP, please try again after a minute',
57
- keyGenerator: (req) => req.ip, // Uses normalized IPv4/IPv6 client IP
99
+ keyGenerator: (req) => req.ip,
100
+ })
101
+
102
+ export const config = defineBoundary({
103
+ middleware: [limiter],
58
104
  })
59
105
  ```
60
106
 
61
- ### Standard Rate Limit Headers
107
+ ### Rate Limit Response Headers
108
+
109
+ `rateLimit` attaches standard IETF and legacy rate-limiting headers to every response:
62
110
 
63
- `rateLimit` automatically emits standard IETF draft and legacy rate-limiting headers on every response:
111
+ | Header | Description | Example |
112
+ | :--- | :--- | :--- |
113
+ | `RateLimit-Limit` | Request quota allocated for the configured window. | `100` |
114
+ | `RateLimit-Remaining` | Remaining requests permitted within the active window. | `42` |
115
+ | `RateLimit-Reset` | Time in seconds until the current quota window resets. | `18` |
116
+ | `Retry-After` | Included on `429 Too Many Requests` responses, indicating wait time. | `18` |
117
+ | `X-RateLimit-*` | Legacy header equivalents provided for compatibility. | `X-RateLimit-Reset: 1727871234` |
118
+
119
+ ---
64
120
 
65
- | Header | Description | Example |
66
- | :-------------------- | :------------------------------------------------------------------------- | :---------------------- |
67
- | `RateLimit-Limit` | Maximum request quota for the configured window. | `100` |
68
- | `RateLimit-Remaining` | Remaining requests permitted within the current window. | `42` |
69
- | `RateLimit-Reset` | Time remaining in seconds until the window quota resets. | `18` |
70
- | `Retry-After` | Included on `429 Too Many Requests` (RFC 6585) indicating seconds to wait. | `18` |
71
- | `X-RateLimit-*` | Legacy header equivalents for backwards compatibility. | `X-RateLimit-Reset: 18` |
121
+ ## 4. IP Filtering and CIDR Subnet Control (`ipFilter`)
72
122
 
73
- > [!NOTE]
74
- > The core `rateLimit` middleware runs using an in-memory native off-heap store. For distributed, multi-instance rate limiting backed by Redis, use `@exisjs/cache`.
123
+ Restrict access to specific IP addresses or CIDR ranges using `ipFilter`:
124
+
125
+ ```typescript filename="src/http/admin/boundary.ts"
126
+ import { defineBoundary } from 'exisjs/router'
127
+ import { ipFilter } from 'exisjs/middleware'
128
+
129
+ export const adminIpGuard = ipFilter({
130
+ allow: [
131
+ '127.0.0.1',
132
+ '10.0.0.0/8', // Internal VPN subnet
133
+ '192.168.1.0/24',
134
+ ],
135
+ deny: [
136
+ '10.0.0.99', // Block specific compromised host
137
+ ],
138
+ message: 'Access Denied: IP address not authorized',
139
+ trustProxy: true,
140
+ })
141
+
142
+ export const config = defineBoundary({
143
+ middleware: [adminIpGuard],
144
+ })
145
+ ```
75
146
 
76
147
  ---
77
148
 
78
- ## 3. Role-Based Access Control (RBAC) Metadata
149
+ ## 5. Idempotency Support (`idempotent`)
150
+
151
+ Prevent duplicate mutations (e.g. double-charging a credit card or duplicate database inserts) by caching responses associated with an `Idempotency-Key` header:
152
+
153
+ ```typescript filename="src/http/checkout/route.ts"
154
+ import { controller, route } from 'exisjs/router'
155
+ import { idempotent } from 'exisjs/middleware'
156
+
157
+ export default controller({
158
+ charge: route.post('/', {
159
+ middleware: [
160
+ idempotent({
161
+ headerName: 'Idempotency-Key',
162
+ ttlMs: 24 * 60 * 60 * 1000, // Cache for 24 hours
163
+ }),
164
+ ],
165
+ async handle({ body, res }) {
166
+ const charge = await paymentService.createCharge(body)
167
+ res.status(201).json({ success: true, charge })
168
+ },
169
+ }),
170
+ })
171
+ ```
79
172
 
80
173
  ---
81
174
 
82
- ## 4. Timing-Safe Operations & Authentication
175
+ ## 6. Parameter Pollution and NoSQL Injection Protection
83
176
 
84
- To prevent side-channel timing attacks when validating authentication tokens, API keys, HMAC signatures, or webhook secrets, ExisJS provides the `timingSafeEqual` helper:
177
+ ### HTTP Parameter Pollution (`hpp`)
85
178
 
86
- ```typescript filename="src/http/common/guards/auth.guard.ts"
87
- import { timingSafeEqual } from 'exisjs/middleware'
88
- import { UnauthorizedException } from 'exisjs/error'
179
+ Prevents parameter pollution attacks where query string or body keys are duplicated (e.g. `?role=user&role=admin`):
89
180
 
90
- export function apiKeyGuard(req: any) {
91
- const apiKey = req.headers['x-api-key']
92
- const secretKey = process.env.API_KEY
181
+ ```typescript filename="src/http/server.ts"
182
+ import { exis } from 'exisjs'
183
+ import { hpp } from 'exisjs/middleware'
93
184
 
94
- if (!timingSafeEqual(apiKey, secretKey)) {
95
- throw new UnauthorizedException('Invalid API Key')
96
- }
97
- }
185
+ export default exis({
186
+ async onStart(app) {
187
+ app.use(hpp())
188
+ },
189
+ })
98
190
  ```
99
191
 
100
- `timingSafeEqual` executes in constant time regardless of string lengths or partial matching prefixes, neutralizing timing analysis exploits.
192
+ ### NoSQL Injection Sanitization (`mongoSanitize` / `dbSanitize`)
193
+
194
+ Strips prohibited operator keys containing `$` or `.` from `req.body`, `req.query`, and `req.params`:
195
+
196
+ ```typescript filename="src/http/server.ts"
197
+ import { exis } from 'exisjs'
198
+ import { mongoSanitize } from 'exisjs/middleware'
199
+
200
+ export default exis({
201
+ async onStart(app) {
202
+ app.use(mongoSanitize())
203
+ },
204
+ })
205
+ ```
206
+
207
+ ---
208
+
209
+ ## 7. Request Timeout Control (`timeout`)
210
+
211
+ Set maximum request duration thresholds to avoid hung connections:
212
+
213
+ ```typescript filename="src/http/server.ts"
214
+ import { exis } from 'exisjs'
215
+ import { timeout } from 'exisjs/middleware'
216
+
217
+ export default exis({
218
+ async onStart(app) {
219
+ app.use(
220
+ timeout({
221
+ ms: 30000, // 30 seconds
222
+ statusCode: 503,
223
+ message: 'Request timed out',
224
+ exclude: ['/upload', '/stream'],
225
+ })
226
+ )
227
+ },
228
+ })
229
+ ```
101
230
 
102
231
  ---
103
232
 
104
- ## 5. Security Scanner Noise Suppression & Blackhole Handler
233
+ ## 8. Bot Probe Blackholing (`blockSuspiciousProbes`)
105
234
 
106
- ExisJS includes built-in protection against automated vulnerability bots scanning for `.env`, `/.git`, `/.DS_Store`, WordPress/PHP configs, and Java actuator dumps:
235
+ Automated vulnerability bots frequently scan applications for sensitive files (`.env`, `/.git`, `wp-login.php`, `actuator/heapdump`). Use `blockSuspiciousProbes` to intercept and terminate these requests immediately without polluting production application logs:
107
236
 
108
237
  ```typescript filename="src/http/server.ts"
109
238
  import { exis } from 'exisjs'
@@ -111,13 +240,60 @@ import { blockSuspiciousProbes } from 'exisjs/middleware'
111
240
 
112
241
  export default exis({
113
242
  async onStart(app) {
114
- // Intercepts scanner probes and blackholes them without triggering downstream error logs
115
243
  app.use(
116
244
  blockSuspiciousProbes({
117
- blackhole: true, // Terminate immediately with 404/empty body
118
- silent: true, // Suppress 404 noise from production logs
245
+ blackhole: true, // Terminates connection immediately with empty body
246
+ statusCode: 404,
247
+ silent: true, // Suppresses 404 noise from standard logger
248
+ patterns: [/^\/admin-backup/i],
119
249
  })
120
250
  )
121
251
  },
122
252
  })
123
253
  ```
254
+
255
+ ---
256
+
257
+ ## 9. Timing-Safe Comparison (`timingSafeEqual`)
258
+
259
+ Use `timingSafeEqual` from `exisjs/middleware` when validating API tokens, signatures, or webhook secrets to guard against timing attacks:
260
+
261
+ ```typescript filename="src/common/guards/api-key.guard.ts"
262
+ import { timingSafeEqual } from 'exisjs/middleware'
263
+ import { UnauthorizedException } from 'exisjs/error'
264
+
265
+ export function apiKeyGuard(req: any) {
266
+ const providedKey = req.headers['x-api-key']
267
+ const expectedKey = process.env.API_KEY
268
+
269
+ if (!timingSafeEqual(providedKey, expectedKey)) {
270
+ throw new UnauthorizedException('Invalid API Key')
271
+ }
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## 10. Role-Based Access Control (RBAC) Decorators
278
+
279
+ ExisJS provides declarative security decorators in `exisjs/decorators` for class-based controllers:
280
+
281
+ ```typescript filename="src/http/users/controller.ts"
282
+ import { Controller, Get, Post, Roles, Permissions, Public } from 'exisjs/decorators'
283
+
284
+ @Controller('/users')
285
+ @Roles('admin', 'manager')
286
+ export class UsersController {
287
+ @Get('/')
288
+ @Permissions('users:read')
289
+ async listUsers() {
290
+ return []
291
+ }
292
+
293
+ @Post('/login')
294
+ @Public() // Bypasses authentication guards
295
+ async login() {
296
+ return { token: 'jwt' }
297
+ }
298
+ }
299
+ ```