@voltrix/security 0.3.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/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/decorators/index.d.ts +26 -0
- package/dist/decorators/index.js +37 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/cors.d.ts +8 -0
- package/dist/modules/cors.js +71 -0
- package/dist/modules/cors.js.map +1 -0
- package/dist/modules/csrf.d.ts +20 -0
- package/dist/modules/csrf.js +98 -0
- package/dist/modules/csrf.js.map +1 -0
- package/dist/modules/helmet.d.ts +7 -0
- package/dist/modules/helmet.js +65 -0
- package/dist/modules/helmet.js.map +1 -0
- package/dist/modules/ip-filter.d.ts +21 -0
- package/dist/modules/ip-filter.js +99 -0
- package/dist/modules/ip-filter.js.map +1 -0
- package/dist/modules/rate-limit.d.ts +11 -0
- package/dist/modules/rate-limit.js +58 -0
- package/dist/modules/rate-limit.js.map +1 -0
- package/dist/modules/session.d.ts +16 -0
- package/dist/modules/session.js +157 -0
- package/dist/modules/session.js.map +1 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +3 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/memory-store.d.ts +23 -0
- package/dist/store/memory-store.js +75 -0
- package/dist/store/memory-store.js.map +1 -0
- package/dist/store/redis-store.d.ts +20 -0
- package/dist/store/redis-store.js +55 -0
- package/dist/store/redis-store.js.map +1 -0
- package/dist/types/index.d.ts +98 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Randy stiven Valentin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# 🔐 `@voltrix/security`
|
|
2
|
+
|
|
3
|
+
High-performance, zero-allocation, distributed security suite natively optimized for `uWebSockets.js` and `@voltrix/server`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Overview
|
|
8
|
+
|
|
9
|
+
`@voltrix/security` is a production-grade security package designed from the ground up for extreme throughput and minimal latency. By combining pre-compiled structures, startup-baked security configurations, and zero-allocation hot paths, it adds virtually **0% framework overhead** while providing complete protection against common web vulnerabilities.
|
|
10
|
+
|
|
11
|
+
It implements 6 robust, state-of-the-art security modules that can be registered globally or declaratively via decorators.
|
|
12
|
+
|
|
13
|
+
### 🛡️ Features
|
|
14
|
+
|
|
15
|
+
1. **Helmet**: Pre-baked static HTTP security headers (CSP, HSTS, CSP, X-Frame) compiled at startup into frozen arrays for zero-allocation flushes.
|
|
16
|
+
2. **CORS**: dynamic pre-normalized cross-origin protection with sub-millisecond OPTIONS preflight short-circuiting.
|
|
17
|
+
3. **Rate Limiting**: IP-based or proxy-aware sliding window limiter with support for local `MemoryStore` or distributed `RedisStore` atomic Lua scripts.
|
|
18
|
+
4. **IP Filter**: Nanosecond-level binary IPv4/IPv6 CIDR bitwise firewall supporting Whitelists and Blacklists with early connection termination.
|
|
19
|
+
5. **CSRF**: Timing-attack safe Double Submit Cookie pattern using authenticated base64url cookies and timingSafeEqual header verification.
|
|
20
|
+
6. **Sessions**: Cookie-based stateful and stateless session containers secured via AES-256-GCM authenticated encryption with automatic response save interception.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📦 Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @voltrix/security
|
|
28
|
+
# or
|
|
29
|
+
pnpm add @voltrix/security
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🛠️ Usage
|
|
35
|
+
|
|
36
|
+
### Global Integration
|
|
37
|
+
|
|
38
|
+
The package exposes a unified `security` middleware factory that acts both as a standard middleware and a native `VoltrixPlugin`. It chains active security modules in the optimal execution order: **IP Filter ➔ CORS ➔ Helmet ➔ Rate Limiter ➔ CSRF ➔ Sessions**.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { createServer } from '@voltrix/server';
|
|
42
|
+
import { security, RedisStore } from '@voltrix/security';
|
|
43
|
+
|
|
44
|
+
const server = createServer();
|
|
45
|
+
|
|
46
|
+
// Register the security suite globally
|
|
47
|
+
server.register(security({
|
|
48
|
+
helmet: true,
|
|
49
|
+
cors: {
|
|
50
|
+
origin: ['https://app.voltrix.com', 'https://admin.voltrix.com'],
|
|
51
|
+
credentials: true,
|
|
52
|
+
},
|
|
53
|
+
rateLimit: {
|
|
54
|
+
limit: 100,
|
|
55
|
+
windowMs: 60000,
|
|
56
|
+
store: new RedisStore({ host: '127.0.0.1', port: 6379 }) // Clustered sliding-window!
|
|
57
|
+
},
|
|
58
|
+
ipFilter: {
|
|
59
|
+
blacklist: ['192.168.1.0/24', '10.0.0.0/8'] // Block internal CIDR blocks
|
|
60
|
+
},
|
|
61
|
+
csrf: true,
|
|
62
|
+
session: {
|
|
63
|
+
secret: 'super-secure-cryptographic-signing-key-32chars',
|
|
64
|
+
cookieName: 'voltrix_session',
|
|
65
|
+
ttlMs: 86400000 // 24h
|
|
66
|
+
}
|
|
67
|
+
}));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Declarative Route-level Decorators
|
|
71
|
+
|
|
72
|
+
Apply granular restrictions to specific Controllers or route methods using declarative class and method decorators.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { Controller, GET, POST } from '@voltrix/decorator';
|
|
76
|
+
import { RateLimit, IpFilter } from '@voltrix/security';
|
|
77
|
+
|
|
78
|
+
@Controller('admin')
|
|
79
|
+
@IpFilter({ whitelist: ['10.0.0.0/8', '127.0.0.1'] }) // CIDR-filtered admin panel
|
|
80
|
+
export class AdminController {
|
|
81
|
+
|
|
82
|
+
@GET('/logs')
|
|
83
|
+
@RateLimit({ limit: 5, windowMs: 60000 }) // Strictly rate-limited endpoint
|
|
84
|
+
async getLogs() {
|
|
85
|
+
return { logs: ['Server booted successfully'] };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 📖 Module Reference & Options
|
|
93
|
+
|
|
94
|
+
### 1. IP Filter
|
|
95
|
+
Nanosecond bitwise matcher for IPv4 CIDRs and IPv6 exact ranges. It operates in the earliest hook and closes the underlying socket instantly if blacklisted, saving CPU cycles under DDoS.
|
|
96
|
+
|
|
97
|
+
* **whitelist**: String array of permitted ranges. If specified, only matches here will pass.
|
|
98
|
+
* **blacklist**: String array of prohibited ranges.
|
|
99
|
+
* **handler**: Custom block handler `(req, res) => void`.
|
|
100
|
+
|
|
101
|
+
### 2. CORS
|
|
102
|
+
Intercepts preflight `OPTIONS` requests before they hit the app router.
|
|
103
|
+
* **origin**: Allowlist origins (string, array of strings, or a dynamic async function `(origin) => boolean`).
|
|
104
|
+
* **methods**: Allowed HTTP methods.
|
|
105
|
+
* **allowedHeaders** / **exposedHeaders**: Custom headers array.
|
|
106
|
+
* **credentials**: Enables Cookies over CORS.
|
|
107
|
+
* **maxAge**: Cache lifetime in seconds.
|
|
108
|
+
|
|
109
|
+
### 3. Helmet
|
|
110
|
+
Pre-renders HSTS, CSP, X-Frame-Options, X-Content-Type, and Referrer headers.
|
|
111
|
+
* **csp**: CSP Directives object.
|
|
112
|
+
* **hsts**: HSTS settings (maxAge, includeSubDomains, preload).
|
|
113
|
+
* **xFrame**: 'DENY' or 'SAMEORIGIN'.
|
|
114
|
+
* **xContentType**: Boolean (defaults to `nosniff`).
|
|
115
|
+
|
|
116
|
+
### 4. Rate Limiting
|
|
117
|
+
Proxy-aware rate counter.
|
|
118
|
+
* **limit**: Max requests in the window.
|
|
119
|
+
* **windowMs**: Window length in milliseconds.
|
|
120
|
+
* **store**: Store instance (defaults to local map-based `MemoryStore`).
|
|
121
|
+
* **keyGenerator**: Custom client key resolution `(req) => string`.
|
|
122
|
+
|
|
123
|
+
### 5. CSRF
|
|
124
|
+
Timing-safe Double Submit Cookie validator.
|
|
125
|
+
* **cookieName** / **headerName**: Defaults to `_csrf` and `x-csrf-token`.
|
|
126
|
+
* **ignoreMethods**: Methods exempted (defaults to `['GET', 'HEAD', 'OPTIONS']`).
|
|
127
|
+
|
|
128
|
+
### 6. Sessions
|
|
129
|
+
AES-256-GCM cookie session container with zero-cost response hijacking (json/send/end) and fire-and-forget background state saves.
|
|
130
|
+
* **secret**: 32-byte signing key.
|
|
131
|
+
* **cookieName**: Default session cookie name.
|
|
132
|
+
* **ttlMs**: Time to live.
|
|
133
|
+
* **store**: Optional stateful session store (defaults to stateless GCM cookie session).
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🚀 Performance Guidelines
|
|
138
|
+
|
|
139
|
+
* Use **`MemoryStore`** for single-instance applications where zero latency is the ultimate goal.
|
|
140
|
+
* Use **`RedisStore`** for horizontally-scalable, multi-node clustered servers. It runs sliding-window increments atomically using dedicated custom Lua scripts to prevent racing conditions under extreme loads.
|
|
141
|
+
* The Helmet module pre-renders headers at startup, making its per-request impact exactly **0% CPU**.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 📄 License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RateLimitOptions, IpFilterOptions } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decorator to attach Rate Limiting policies to a Controller class or individual route methods.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* @Controller('auth')
|
|
8
|
+
* class AuthController {
|
|
9
|
+
* @POST('/login')
|
|
10
|
+
* @RateLimit({ limit: 5, windowMs: 60000 })
|
|
11
|
+
* async login() { ... }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function RateLimit(options: RateLimitOptions): ClassDecorator & MethodDecorator;
|
|
16
|
+
/**
|
|
17
|
+
* Decorator to apply IP and CIDR whitelist/blacklist policies to a Controller class or individual route methods.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* @Controller('admin')
|
|
22
|
+
* @IpFilter({ whitelist: ['10.0.0.0/8'] })
|
|
23
|
+
* class AdminController { ... }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function IpFilter(options: IpFilterOptions): ClassDecorator & MethodDecorator;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Metadata } from '@voltrix/core';
|
|
2
|
+
/**
|
|
3
|
+
* Decorator to attach Rate Limiting policies to a Controller class or individual route methods.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* @Controller('auth')
|
|
8
|
+
* class AuthController {
|
|
9
|
+
* @POST('/login')
|
|
10
|
+
* @RateLimit({ limit: 5, windowMs: 60000 })
|
|
11
|
+
* async login() { ... }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function RateLimit(options) {
|
|
16
|
+
return (target, propertyKey) => {
|
|
17
|
+
const actualTarget = propertyKey ? target : target.prototype;
|
|
18
|
+
Metadata.prefix('security').set(actualTarget, propertyKey, { rateLimit: options });
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decorator to apply IP and CIDR whitelist/blacklist policies to a Controller class or individual route methods.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* @Controller('admin')
|
|
27
|
+
* @IpFilter({ whitelist: ['10.0.0.0/8'] })
|
|
28
|
+
* class AdminController { ... }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function IpFilter(options) {
|
|
32
|
+
return (target, propertyKey) => {
|
|
33
|
+
const actualTarget = propertyKey ? target : target.prototype;
|
|
34
|
+
Metadata.prefix('security').set(actualTarget, propertyKey, { ipFilter: options });
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,OAAyB;IACjD,OAAO,CAAC,MAAW,EAAE,WAA6B,EAAE,EAAE;QACpD,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAC7D,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAwB;IAC/C,OAAO,CAAC,MAAW,EAAE,WAA6B,EAAE,EAAE;QACpD,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAC7D,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACpF,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SecurityOptions } from './types/index.js';
|
|
2
|
+
export * from './types/index.js';
|
|
3
|
+
export * from './store/index.js';
|
|
4
|
+
export * from './decorators/index.js';
|
|
5
|
+
export * from './modules/ip-filter.js';
|
|
6
|
+
export * from './modules/cors.js';
|
|
7
|
+
export * from './modules/helmet.js';
|
|
8
|
+
export * from './modules/rate-limit.js';
|
|
9
|
+
export * from './modules/csrf.js';
|
|
10
|
+
export * from './modules/session.js';
|
|
11
|
+
export declare function security(options?: SecurityOptions): any;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ipFilter } from './modules/ip-filter.js';
|
|
2
|
+
import { cors } from './modules/cors.js';
|
|
3
|
+
import { helmet } from './modules/helmet.js';
|
|
4
|
+
import { rateLimit } from './modules/rate-limit.js';
|
|
5
|
+
import { csrf } from './modules/csrf.js';
|
|
6
|
+
import { session } from './modules/session.js';
|
|
7
|
+
// Export everything for public consumption
|
|
8
|
+
export * from './types/index.js';
|
|
9
|
+
export * from './store/index.js';
|
|
10
|
+
export * from './decorators/index.js';
|
|
11
|
+
export * from './modules/ip-filter.js';
|
|
12
|
+
export * from './modules/cors.js';
|
|
13
|
+
export * from './modules/helmet.js';
|
|
14
|
+
export * from './modules/rate-limit.js';
|
|
15
|
+
export * from './modules/csrf.js';
|
|
16
|
+
export * from './modules/session.js';
|
|
17
|
+
export function security(options) {
|
|
18
|
+
const opts = options ?? {};
|
|
19
|
+
const pipeline = [];
|
|
20
|
+
// 1. IP Filter runs first to immediately discard blacklisted IPs in onRequest
|
|
21
|
+
if (opts.ipFilter !== false && opts.ipFilter !== undefined) {
|
|
22
|
+
pipeline.push(ipFilter(opts.ipFilter));
|
|
23
|
+
}
|
|
24
|
+
// 2. CORS runs early to capture OPTIONS Preflight requests directly
|
|
25
|
+
if (opts.cors !== false && opts.cors !== undefined) {
|
|
26
|
+
pipeline.push(cors(opts.cors));
|
|
27
|
+
}
|
|
28
|
+
// 3. Helmet sets security headers
|
|
29
|
+
if (opts.helmet !== false && opts.helmet !== undefined) {
|
|
30
|
+
pipeline.push(helmet(opts.helmet));
|
|
31
|
+
}
|
|
32
|
+
// 4. Rate Limit limits frequencies
|
|
33
|
+
if (opts.rateLimit !== false && opts.rateLimit !== undefined) {
|
|
34
|
+
pipeline.push(rateLimit(opts.rateLimit));
|
|
35
|
+
}
|
|
36
|
+
// 5. CSRF checks mutative endpoints
|
|
37
|
+
if (opts.csrf !== false && opts.csrf !== undefined) {
|
|
38
|
+
pipeline.push(csrf(opts.csrf));
|
|
39
|
+
}
|
|
40
|
+
// 6. Session reads and writes state
|
|
41
|
+
if (opts.session !== false && opts.session !== undefined) {
|
|
42
|
+
pipeline.push(session(opts.session));
|
|
43
|
+
}
|
|
44
|
+
// Freeze pipeline to enforce zero state bleed and maximum CPU optimization
|
|
45
|
+
const frozenPipeline = Object.freeze(pipeline);
|
|
46
|
+
const middleware = (req, res, next) => {
|
|
47
|
+
let index = 0;
|
|
48
|
+
function nextStep(err) {
|
|
49
|
+
if (err) {
|
|
50
|
+
return next(err);
|
|
51
|
+
}
|
|
52
|
+
if (index >= frozenPipeline.length) {
|
|
53
|
+
return next();
|
|
54
|
+
}
|
|
55
|
+
const mw = frozenPipeline[index++];
|
|
56
|
+
try {
|
|
57
|
+
const resVal = mw(req, res, nextStep);
|
|
58
|
+
if (resVal instanceof Promise) {
|
|
59
|
+
resVal.catch(nextStep);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (mwErr) {
|
|
63
|
+
nextStep(mwErr);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
nextStep();
|
|
67
|
+
};
|
|
68
|
+
// Hybrid Plugin Interface support
|
|
69
|
+
Object.defineProperty(middleware, 'name', { value: 'security', configurable: true });
|
|
70
|
+
middleware.version = '0.2.1';
|
|
71
|
+
middleware.register = (api, pluginOpts) => {
|
|
72
|
+
const activeMw = pluginOpts ? security(pluginOpts) : middleware;
|
|
73
|
+
api.addHook('onRequest', async (ctx) => {
|
|
74
|
+
await new Promise((resolve, reject) => {
|
|
75
|
+
activeMw(ctx, ctx, (err) => {
|
|
76
|
+
if (err)
|
|
77
|
+
reject(err);
|
|
78
|
+
else
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return middleware;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CorsOptions } from '../types/index.js';
|
|
2
|
+
import type { Middleware } from '@voltrix/core';
|
|
3
|
+
/**
|
|
4
|
+
* Hyper-performance CORS Middleware.
|
|
5
|
+
* Captures, processes, and validates cross-origin accesses.
|
|
6
|
+
* Short-circuits preflight OPTIONS requests immediately to prevent them from reaching app-level routers.
|
|
7
|
+
*/
|
|
8
|
+
export declare function cors(options?: CorsOptions | boolean): Middleware;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hyper-performance CORS Middleware.
|
|
3
|
+
* Captures, processes, and validates cross-origin accesses.
|
|
4
|
+
* Short-circuits preflight OPTIONS requests immediately to prevent them from reaching app-level routers.
|
|
5
|
+
*/
|
|
6
|
+
export function cors(options) {
|
|
7
|
+
if (options === false) {
|
|
8
|
+
return (req, res, next) => next();
|
|
9
|
+
}
|
|
10
|
+
const opts = typeof options === 'object' ? options : {};
|
|
11
|
+
// Pre-normalize standard methods and headers configurations
|
|
12
|
+
const methods = Array.isArray(opts.methods) ? opts.methods.join(', ') : opts.methods ?? 'GET,HEAD,PUT,PATCH,POST,DELETE';
|
|
13
|
+
const allowedHeaders = Array.isArray(opts.allowedHeaders) ? opts.allowedHeaders.join(', ') : opts.allowedHeaders;
|
|
14
|
+
const exposedHeaders = Array.isArray(opts.exposedHeaders) ? opts.exposedHeaders.join(', ') : opts.exposedHeaders;
|
|
15
|
+
const maxAge = opts.maxAge !== undefined ? String(opts.maxAge) : undefined;
|
|
16
|
+
return async (req, res, next) => {
|
|
17
|
+
const origin = req.header('origin');
|
|
18
|
+
if (!origin) {
|
|
19
|
+
return next();
|
|
20
|
+
}
|
|
21
|
+
let isAllowed = false;
|
|
22
|
+
if (!opts.origin || opts.origin === '*') {
|
|
23
|
+
isAllowed = true;
|
|
24
|
+
}
|
|
25
|
+
else if (typeof opts.origin === 'string') {
|
|
26
|
+
isAllowed = origin === opts.origin;
|
|
27
|
+
}
|
|
28
|
+
else if (Array.isArray(opts.origin)) {
|
|
29
|
+
isAllowed = opts.origin.includes(origin);
|
|
30
|
+
}
|
|
31
|
+
else if (typeof opts.origin === 'function') {
|
|
32
|
+
try {
|
|
33
|
+
const resVal = opts.origin(origin);
|
|
34
|
+
isAllowed = resVal instanceof Promise ? await resVal : resVal;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
isAllowed = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!isAllowed) {
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
// Set standard CORS origin response headers
|
|
44
|
+
res.setHeader('Access-Control-Allow-Origin', opts.origin === '*' && !opts.credentials ? '*' : origin);
|
|
45
|
+
if (opts.credentials) {
|
|
46
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
47
|
+
}
|
|
48
|
+
if (exposedHeaders) {
|
|
49
|
+
res.setHeader('Access-Control-Expose-Headers', exposedHeaders);
|
|
50
|
+
}
|
|
51
|
+
// ─── Preflight OPTIONS Intercept ─────────────────────────────────────────
|
|
52
|
+
if (req.method === 'OPTIONS') {
|
|
53
|
+
res.setHeader('Access-Control-Allow-Methods', methods);
|
|
54
|
+
const reqHeaders = req.header('access-control-request-headers');
|
|
55
|
+
if (allowedHeaders) {
|
|
56
|
+
res.setHeader('Access-Control-Allow-Headers', allowedHeaders);
|
|
57
|
+
}
|
|
58
|
+
else if (reqHeaders) {
|
|
59
|
+
res.setHeader('Access-Control-Allow-Headers', reqHeaders);
|
|
60
|
+
}
|
|
61
|
+
if (maxAge) {
|
|
62
|
+
res.setHeader('Access-Control-Max-Age', maxAge);
|
|
63
|
+
}
|
|
64
|
+
// Short-circuit: complete Preflight immediately (204 No Content)
|
|
65
|
+
res.status(204).end();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
next();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=cors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/modules/cors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CsrfOptions } from '../types/index.js';
|
|
2
|
+
import type { Middleware } from '@voltrix/core';
|
|
3
|
+
/**
|
|
4
|
+
* Fast, zero-allocation cookie parser.
|
|
5
|
+
* Extracts cookie pairs directly in a single pass without large array allocations.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseCookies(cookieHeader?: string): Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Utility to serialize a cookie name-value pair with options.
|
|
10
|
+
*/
|
|
11
|
+
export declare function serializeCookie(name: string, val: string, opts?: any): string;
|
|
12
|
+
/**
|
|
13
|
+
* Timing-attack safe string comparison.
|
|
14
|
+
* Prevents side-channel timing analysis during token verification.
|
|
15
|
+
*/
|
|
16
|
+
export declare function timingSafeCompare(a: string, b: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* CSRF Protection Middleware using Double Submit Cookie pattern.
|
|
19
|
+
*/
|
|
20
|
+
export declare function csrf(options?: CsrfOptions | boolean): Middleware;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Fast, zero-allocation cookie parser.
|
|
4
|
+
* Extracts cookie pairs directly in a single pass without large array allocations.
|
|
5
|
+
*/
|
|
6
|
+
export function parseCookies(cookieHeader) {
|
|
7
|
+
const cookies = {};
|
|
8
|
+
if (!cookieHeader)
|
|
9
|
+
return cookies;
|
|
10
|
+
const parts = cookieHeader.split(';');
|
|
11
|
+
for (let i = 0; i < parts.length; i++) {
|
|
12
|
+
const p = parts[i];
|
|
13
|
+
const eqIdx = p.indexOf('=');
|
|
14
|
+
if (eqIdx === -1)
|
|
15
|
+
continue;
|
|
16
|
+
const name = p.slice(0, eqIdx).trim();
|
|
17
|
+
const val = p.slice(eqIdx + 1).trim();
|
|
18
|
+
cookies[name] = val;
|
|
19
|
+
}
|
|
20
|
+
return cookies;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Utility to serialize a cookie name-value pair with options.
|
|
24
|
+
*/
|
|
25
|
+
export function serializeCookie(name, val, opts = {}) {
|
|
26
|
+
let str = `${name}=${val}`;
|
|
27
|
+
if (opts.path)
|
|
28
|
+
str += `; Path=${opts.path}`;
|
|
29
|
+
if (opts.domain)
|
|
30
|
+
str += `; Domain=${opts.domain}`;
|
|
31
|
+
if (opts.maxAge !== undefined)
|
|
32
|
+
str += `; Max-Age=${opts.maxAge}`;
|
|
33
|
+
if (opts.expires instanceof Date)
|
|
34
|
+
str += `; Expires=${opts.expires.toUTCString()}`;
|
|
35
|
+
if (opts.secure)
|
|
36
|
+
str += '; Secure';
|
|
37
|
+
if (opts.httpOnly)
|
|
38
|
+
str += '; HttpOnly';
|
|
39
|
+
if (opts.sameSite)
|
|
40
|
+
str += `; SameSite=${opts.sameSite}`;
|
|
41
|
+
return str;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Timing-attack safe string comparison.
|
|
45
|
+
* Prevents side-channel timing analysis during token verification.
|
|
46
|
+
*/
|
|
47
|
+
export function timingSafeCompare(a, b) {
|
|
48
|
+
const bufA = Buffer.from(a, 'utf-8');
|
|
49
|
+
const bufB = Buffer.from(b, 'utf-8');
|
|
50
|
+
if (bufA.length !== bufB.length)
|
|
51
|
+
return false;
|
|
52
|
+
return timingSafeEqual(bufA, bufB);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* CSRF Protection Middleware using Double Submit Cookie pattern.
|
|
56
|
+
*/
|
|
57
|
+
export function csrf(options) {
|
|
58
|
+
if (options === false || !options) {
|
|
59
|
+
return (req, res, next) => next();
|
|
60
|
+
}
|
|
61
|
+
const opts = typeof options === 'object' ? options : {};
|
|
62
|
+
const cookieName = opts.cookieName ?? '_csrf';
|
|
63
|
+
const headerName = opts.headerName ?? 'x-csrf-token';
|
|
64
|
+
const cookieOpts = opts.cookieOptions ?? {
|
|
65
|
+
path: '/',
|
|
66
|
+
secure: false, // Default false, developers toggle true in production
|
|
67
|
+
httpOnly: false, // Must be readable by client script for double-submit
|
|
68
|
+
sameSite: 'Lax'
|
|
69
|
+
};
|
|
70
|
+
const ignoreMethods = opts.ignoreMethods ?? ['GET', 'HEAD', 'OPTIONS'];
|
|
71
|
+
return async (req, res, next) => {
|
|
72
|
+
const cookies = parseCookies(req.header('cookie'));
|
|
73
|
+
let token = cookies[cookieName];
|
|
74
|
+
// 1. Generate new CSRF token if not already present
|
|
75
|
+
if (!token) {
|
|
76
|
+
token = randomBytes(24).toString('base64url');
|
|
77
|
+
res.setHeader('Set-Cookie', serializeCookie(cookieName, token, cookieOpts));
|
|
78
|
+
}
|
|
79
|
+
// 2. Ignore non-mutative safe methods
|
|
80
|
+
if (ignoreMethods.includes(req.method)) {
|
|
81
|
+
return next();
|
|
82
|
+
}
|
|
83
|
+
// 3. Extract validation token from incoming header
|
|
84
|
+
const incomingToken = req.header(headerName);
|
|
85
|
+
if (!incomingToken) {
|
|
86
|
+
res.status(403).json({ error: 'Missing CSRF token' });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// 4. Perform timing-safe validation check
|
|
90
|
+
const isValid = timingSafeCompare(token, incomingToken);
|
|
91
|
+
if (!isValid) {
|
|
92
|
+
res.status(403).json({ error: 'Invalid CSRF token' });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
next();
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=csrf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/modules/csrf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HelmetOptions } from '../types/index.js';
|
|
2
|
+
import type { Middleware } from '@voltrix/core';
|
|
3
|
+
/**
|
|
4
|
+
* Hyper-optimized, pre-baked Security Headers Middleware.
|
|
5
|
+
* Compiles static headers at startup into a frozen, flat array to achieve zero-overhead O(1) header writing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function helmet(options?: HelmetOptions | boolean): Middleware;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hyper-optimized, pre-baked Security Headers Middleware.
|
|
3
|
+
* Compiles static headers at startup into a frozen, flat array to achieve zero-overhead O(1) header writing.
|
|
4
|
+
*/
|
|
5
|
+
export function helmet(options) {
|
|
6
|
+
if (options === false) {
|
|
7
|
+
return (req, res, next) => next();
|
|
8
|
+
}
|
|
9
|
+
const opts = typeof options === 'object' ? options : {};
|
|
10
|
+
const headers = {};
|
|
11
|
+
// 1. Content-Security-Policy (CSP)
|
|
12
|
+
if (opts.csp !== false) {
|
|
13
|
+
if (typeof opts.csp === 'object') {
|
|
14
|
+
const directives = Object.entries(opts.csp)
|
|
15
|
+
.map(([key, val]) => `${key} ${val.join(' ')}`)
|
|
16
|
+
.join('; ');
|
|
17
|
+
headers['Content-Security-Policy'] = directives;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
headers['Content-Security-Policy'] = "default-src 'self'";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// 2. Strict-Transport-Security (HSTS)
|
|
24
|
+
if (opts.hsts !== false) {
|
|
25
|
+
if (typeof opts.hsts === 'object') {
|
|
26
|
+
const maxAge = opts.hsts.maxAge ?? 15552000;
|
|
27
|
+
const sub = opts.hsts.includeSubDomains !== false ? '; includeSubDomains' : '';
|
|
28
|
+
const preload = opts.hsts.preload ? '; preload' : '';
|
|
29
|
+
headers['Strict-Transport-Security'] = `max-age=${maxAge}${sub}${preload}`;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
headers['Strict-Transport-Security'] = 'max-age=15552000; includeSubDomains';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 3. X-Frame-Options (Clickjacking Protection)
|
|
36
|
+
if (opts.xFrame !== false) {
|
|
37
|
+
headers['X-Frame-Options'] = typeof opts.xFrame === 'string' ? opts.xFrame : 'SAMEORIGIN';
|
|
38
|
+
}
|
|
39
|
+
// 4. X-Content-Type-Options (MIME Sniffing Protection)
|
|
40
|
+
if (opts.xContentType !== false) {
|
|
41
|
+
headers['X-Content-Type-Options'] = 'nosniff';
|
|
42
|
+
}
|
|
43
|
+
// 5. Referrer-Policy
|
|
44
|
+
if (opts.referrerPolicy !== false) {
|
|
45
|
+
headers['Referrer-Policy'] = typeof opts.referrerPolicy === 'string' ? opts.referrerPolicy : 'no-referrer';
|
|
46
|
+
}
|
|
47
|
+
// 6. Permissions-Policy
|
|
48
|
+
if (opts.permissionsPolicy) {
|
|
49
|
+
const policy = Object.entries(opts.permissionsPolicy)
|
|
50
|
+
.map(([key, val]) => `${key}=(${val})`)
|
|
51
|
+
.join(', ');
|
|
52
|
+
headers['Permissions-Policy'] = policy;
|
|
53
|
+
}
|
|
54
|
+
// Pre-bake and freeze headers as a flat list of key-value pairs
|
|
55
|
+
const preBakedHeaders = Object.freeze(Object.entries(headers));
|
|
56
|
+
return (req, res, next) => {
|
|
57
|
+
// Zero-allocation loop directly sets pre-baked headers
|
|
58
|
+
for (let i = 0; i < preBakedHeaders.length; i++) {
|
|
59
|
+
const [key, val] = preBakedHeaders[i];
|
|
60
|
+
res.setHeader(key, val);
|
|
61
|
+
}
|
|
62
|
+
next();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=helmet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helmet.js","sourceRoot":"","sources":["../../src/modules/helmet.ts"],"names":[],"mappings":"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"}
|