@stacksjs/bun-router 0.0.16 → 0.0.17
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/auth.d.ts +22 -8
- package/dist/chunk-1ahs68ys.js +18 -0
- package/dist/{chunk-0145g1me.js → chunk-cgptvjdf.js} +147 -75
- package/dist/chunk-g3ybefhg.js +1923 -0
- package/dist/cli.js +3 -2
- package/dist/container/container.d.ts +1 -0
- package/dist/container/contextual-binding.d.ts +0 -1
- package/dist/container/decorators.d.ts +2 -5
- package/dist/container/index.d.ts +16 -0
- package/dist/container/index.js +322 -0
- package/dist/file-serving/static-files.d.ts +16 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1159 -1380
- package/dist/middleware/cors.d.ts +9 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +16 -1
- package/dist/middleware/rate_limit.d.ts +7 -1
- package/dist/middleware/request_tracer.d.ts +15 -9
- package/dist/middleware/response_cache.d.ts +2 -1
- package/dist/request/context.d.ts +12 -1
- package/dist/request/macros.d.ts +40 -2
- package/dist/response/macros.d.ts +3 -3
- package/dist/router/fluent-routing.d.ts +50 -13
- package/dist/router/handler-resolver.d.ts +8 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/route-compiler.d.ts +8 -10
- package/dist/router/route-trie.d.ts +7 -1
- package/dist/router/router.d.ts +13 -2
- package/dist/router/validation-integration.d.ts +10 -4
- package/dist/types/middleware-types.d.ts +1 -1
- package/dist/types/route-inference.d.ts +9 -2
- package/dist/types.d.ts +62 -9
- package/dist/utils.d.ts +1 -1
- package/package.json +11 -1
- package/src/auth.ts +32 -12
- package/src/container/container.ts +58 -12
- package/src/container/contextual-binding.ts +4 -1
- package/src/container/decorators.ts +16 -7
- package/src/container/index.ts +49 -0
- package/src/errors/circuit-breaker.ts +3 -2
- package/src/errors/graceful-degradation.ts +2 -0
- package/src/file-serving/static-files.ts +144 -7
- package/src/index.ts +53 -1
- package/src/middleware/cors.ts +76 -8
- package/src/middleware/csrf.ts +70 -22
- package/src/middleware/ddos_protection.ts +27 -3
- package/src/middleware/input_validation.ts +10 -6
- package/src/middleware/performance_alerting.ts +2 -0
- package/src/middleware/performance_monitor.ts +2 -0
- package/src/middleware/rate_limit.ts +7 -1
- package/src/middleware/request_signing.ts +2 -0
- package/src/middleware/request_tracer.ts +34 -17
- package/src/middleware/response_cache.ts +48 -13
- package/src/middleware/session.ts +3 -3
- package/src/observability/metrics.ts +1 -1
- package/src/request/context.ts +50 -3
- package/src/request/enhanced-request.ts +20 -1
- package/src/request/macros.ts +183 -49
- package/src/response/macros.ts +3 -3
- package/src/router/file-based-routing.ts +17 -10
- package/src/router/fluent-routing.ts +117 -48
- package/src/router/group-organization.ts +17 -1
- package/src/router/handler-resolver.ts +36 -0
- package/src/router/http-methods.ts +33 -3
- package/src/router/index.ts +6 -0
- package/src/router/middleware.ts +3 -0
- package/src/router/optimized-route-matching.ts +58 -9
- package/src/router/route-compiler.ts +62 -73
- package/src/router/route-matching.ts +19 -0
- package/src/router/route-trie.ts +34 -48
- package/src/router/router.ts +72 -60
- package/src/router/server.ts +361 -139
- package/src/router/validation-integration.ts +11 -5
- package/src/testing/auth-testing.ts +6 -4
- package/src/types/middleware-types.ts +7 -4
- package/src/types/route-inference.ts +29 -14
- package/src/types.ts +63 -7
- package/src/url.ts +7 -2
- package/src/utils.ts +9 -6
- package/src/validation/validator.ts +7 -4
- package/src/websocket/clustering.ts +8 -1
- package/dist/router/fluent-router.d.ts +0 -315
- package/src/router/fluent-router.ts +0 -927
package/dist/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EnhancedRequest, JwtPayload } from './types';
|
|
1
|
+
import type { EnhancedRequest, JwtHeader, JwtPayload } from './types';
|
|
2
2
|
export interface JwtVerifyOptions {
|
|
3
3
|
issuer?: string;
|
|
4
4
|
audience?: string;
|
|
@@ -19,9 +19,23 @@ export interface JwtSignOptions {
|
|
|
19
19
|
keyid?: string;
|
|
20
20
|
jwtid?: string;
|
|
21
21
|
noTimestamp?: boolean;
|
|
22
|
-
header?:
|
|
22
|
+
header?: Partial<JwtHeader>;
|
|
23
23
|
encoding?: string;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Token endpoint response for the OAuth2 authorization-code flow
|
|
27
|
+
* (RFC 6749 §5.1). Providers may include extra fields — they're preserved
|
|
28
|
+
* under the index signature.
|
|
29
|
+
*/
|
|
30
|
+
export interface OAuth2TokenResponse {
|
|
31
|
+
access_token: string;
|
|
32
|
+
token_type: string;
|
|
33
|
+
expires_in?: number;
|
|
34
|
+
refresh_token?: string;
|
|
35
|
+
scope?: string;
|
|
36
|
+
id_token?: string;
|
|
37
|
+
[key: string]: string | number | undefined;
|
|
38
|
+
}
|
|
25
39
|
export interface ApiKeyOptions {
|
|
26
40
|
source?: 'header' | 'query' | 'cookie';
|
|
27
41
|
keyName?: string;
|
|
@@ -46,17 +60,17 @@ export declare class JWT {
|
|
|
46
60
|
/**
|
|
47
61
|
* Sign payload into a JWT token
|
|
48
62
|
*/
|
|
49
|
-
sign(payload:
|
|
63
|
+
sign(payload: JwtPayload, options?: JwtSignOptions): string;
|
|
50
64
|
/**
|
|
51
65
|
* Verify a JWT token and return the decoded payload
|
|
52
66
|
*/
|
|
53
|
-
verify(token: string, options?: JwtVerifyOptions):
|
|
67
|
+
verify(token: string, options?: JwtVerifyOptions): JwtPayload | null;
|
|
54
68
|
/**
|
|
55
69
|
* Decode a JWT token without verifying the signature
|
|
56
70
|
*/
|
|
57
71
|
decode(token: string): {
|
|
58
|
-
header:
|
|
59
|
-
payload:
|
|
72
|
+
header: JwtHeader;
|
|
73
|
+
payload: JwtPayload;
|
|
60
74
|
} | null;
|
|
61
75
|
/**
|
|
62
76
|
* Simple signature creation - Note: This is NOT secure for production
|
|
@@ -117,11 +131,11 @@ export declare class OAuth2Helper {
|
|
|
117
131
|
/**
|
|
118
132
|
* Exchange authorization code for access token
|
|
119
133
|
*/
|
|
120
|
-
exchangeCodeForToken(code: string): Promise<
|
|
134
|
+
exchangeCodeForToken(code: string): Promise<OAuth2TokenResponse>;
|
|
121
135
|
/**
|
|
122
136
|
* Refresh an access token using a refresh token
|
|
123
137
|
*/
|
|
124
|
-
refreshToken(refreshToken: string): Promise<
|
|
138
|
+
refreshToken(refreshToken: string): Promise<OAuth2TokenResponse>;
|
|
125
139
|
}
|
|
126
140
|
/**
|
|
127
141
|
* Authentication helpers namespace
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __require = import.meta.require;
|
|
17
|
+
|
|
18
|
+
export { __export, __require };
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __require = import.meta.require;
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "./chunk-1ahs68ys.js";
|
|
17
5
|
|
|
18
6
|
// src/config.ts
|
|
19
7
|
import process19 from "process";
|
|
@@ -40,7 +28,7 @@ import process2 from "process";
|
|
|
40
28
|
import { existsSync as existsSync22, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
41
29
|
import { dirname, resolve } from "path";
|
|
42
30
|
import process from "process";
|
|
43
|
-
import { Buffer } from "buffer";
|
|
31
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
44
32
|
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
45
33
|
import { closeSync, createReadStream, createWriteStream, existsSync as existsSync222, fsyncSync, openSync, writeFileSync as writeFileSync2 } from "fs";
|
|
46
34
|
import { access, constants, mkdir, readdir, rename, stat, unlink, writeFile } from "fs/promises";
|
|
@@ -50,7 +38,7 @@ import { pipeline } from "stream/promises";
|
|
|
50
38
|
import { createGzip } from "zlib";
|
|
51
39
|
import process4 from "process";
|
|
52
40
|
import process3 from "process";
|
|
53
|
-
import { Buffer as
|
|
41
|
+
import { Buffer as Buffer22 } from "buffer";
|
|
54
42
|
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
|
|
55
43
|
import { closeSync as closeSync2, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync4, fsyncSync as fsyncSync2, openSync as openSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
56
44
|
import { access as access2, constants as constants2, mkdir as mkdir2, readdir as readdir2, rename as rename2, stat as stat2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -323,23 +311,23 @@ class PerformanceMonitor {
|
|
|
323
311
|
}
|
|
324
312
|
var globalCache = new ConfigCache;
|
|
325
313
|
var globalPerformanceMonitor = new PerformanceMonitor;
|
|
326
|
-
var
|
|
327
|
-
var
|
|
328
|
-
function
|
|
329
|
-
this[name] =
|
|
314
|
+
var __defProp = Object.defineProperty;
|
|
315
|
+
var __returnValue = (v) => v;
|
|
316
|
+
function __exportSetter(name, newValue) {
|
|
317
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
330
318
|
}
|
|
331
|
-
var
|
|
319
|
+
var __export = (target, all) => {
|
|
332
320
|
for (var name in all)
|
|
333
|
-
|
|
321
|
+
__defProp(target, name, {
|
|
334
322
|
get: all[name],
|
|
335
323
|
enumerable: true,
|
|
336
324
|
configurable: true,
|
|
337
|
-
set:
|
|
325
|
+
set: __exportSetter.bind(all, name)
|
|
338
326
|
});
|
|
339
327
|
};
|
|
340
328
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
341
329
|
var exports_dist = {};
|
|
342
|
-
|
|
330
|
+
__export(exports_dist, {
|
|
343
331
|
withErrorRecovery: () => withErrorRecovery,
|
|
344
332
|
tryLoadConfig: () => tryLoadConfig3,
|
|
345
333
|
loadConfigWithResult: () => loadConfigWithResult,
|
|
@@ -1028,7 +1016,7 @@ class Logger {
|
|
|
1028
1016
|
}
|
|
1029
1017
|
if (cancelled)
|
|
1030
1018
|
throw new Error("Operation cancelled: Logger was destroyed");
|
|
1031
|
-
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted :
|
|
1019
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer2.from(data);
|
|
1032
1020
|
try {
|
|
1033
1021
|
if (!existsSync222(this.currentLogFile)) {
|
|
1034
1022
|
await writeFile(this.currentLogFile, "", { mode: 420 });
|
|
@@ -1196,13 +1184,13 @@ class Logger {
|
|
|
1196
1184
|
const { key } = this.getCurrentKey();
|
|
1197
1185
|
const iv = randomBytes(16);
|
|
1198
1186
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
1199
|
-
const encrypted =
|
|
1187
|
+
const encrypted = Buffer2.concat([
|
|
1200
1188
|
cipher.update(data, "utf8"),
|
|
1201
1189
|
cipher.final()
|
|
1202
1190
|
]);
|
|
1203
1191
|
const authTag = cipher.getAuthTag();
|
|
1204
1192
|
return {
|
|
1205
|
-
encrypted:
|
|
1193
|
+
encrypted: Buffer2.concat([iv, encrypted, authTag]),
|
|
1206
1194
|
iv
|
|
1207
1195
|
};
|
|
1208
1196
|
}
|
|
@@ -1211,7 +1199,7 @@ class Logger {
|
|
|
1211
1199
|
const gzip = createGzip();
|
|
1212
1200
|
const chunks = [];
|
|
1213
1201
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
1214
|
-
gzip.on("end", () => resolve32(
|
|
1202
|
+
gzip.on("end", () => resolve32(Buffer2.from(Buffer2.concat(chunks))));
|
|
1215
1203
|
gzip.on("error", reject);
|
|
1216
1204
|
gzip.write(data);
|
|
1217
1205
|
gzip.end();
|
|
@@ -1700,13 +1688,13 @@ class Logger {
|
|
|
1700
1688
|
throw new Error("No valid encryption key available");
|
|
1701
1689
|
const key = this.keys.get(this.currentKeyId);
|
|
1702
1690
|
try {
|
|
1703
|
-
const encryptedData =
|
|
1691
|
+
const encryptedData = Buffer2.isBuffer(data) ? data : Buffer2.from(data, "base64");
|
|
1704
1692
|
const iv = encryptedData.slice(0, 16);
|
|
1705
1693
|
const authTag = encryptedData.slice(-16);
|
|
1706
1694
|
const ciphertext = encryptedData.slice(16, -16);
|
|
1707
1695
|
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
1708
1696
|
decipher.setAuthTag(authTag);
|
|
1709
|
-
const decrypted =
|
|
1697
|
+
const decrypted = Buffer2.concat([
|
|
1710
1698
|
decipher.update(ciphertext),
|
|
1711
1699
|
decipher.final()
|
|
1712
1700
|
]);
|
|
@@ -2603,7 +2591,7 @@ class Logger2 {
|
|
|
2603
2591
|
}
|
|
2604
2592
|
if (cancelled)
|
|
2605
2593
|
throw new Error("Operation cancelled: Logger was destroyed");
|
|
2606
|
-
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted :
|
|
2594
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer22.from(data);
|
|
2607
2595
|
try {
|
|
2608
2596
|
if (!existsSync4(this.currentLogFile)) {
|
|
2609
2597
|
await writeFile2(this.currentLogFile, "", { mode: 420 });
|
|
@@ -2773,12 +2761,12 @@ class Logger2 {
|
|
|
2773
2761
|
const { key } = this.getCurrentKey();
|
|
2774
2762
|
const iv = randomBytes2(16);
|
|
2775
2763
|
const cipher = createCipheriv2("aes-256-gcm", key, iv);
|
|
2776
|
-
const input =
|
|
2764
|
+
const input = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "utf8");
|
|
2777
2765
|
const part1 = cipher.update(input);
|
|
2778
2766
|
const part2 = cipher.final();
|
|
2779
2767
|
const totalCipherLen = part1.length + part2.length;
|
|
2780
2768
|
const authTag = cipher.getAuthTag();
|
|
2781
|
-
const out =
|
|
2769
|
+
const out = Buffer22.allocUnsafe(16 + totalCipherLen + 16);
|
|
2782
2770
|
iv.copy(out, 0);
|
|
2783
2771
|
part1.copy(out, 16);
|
|
2784
2772
|
part2.copy(out, 16 + part1.length);
|
|
@@ -2793,7 +2781,7 @@ class Logger2 {
|
|
|
2793
2781
|
const gzip = createGzip2();
|
|
2794
2782
|
const chunks = [];
|
|
2795
2783
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
2796
|
-
gzip.on("end", () => resolve52(
|
|
2784
|
+
gzip.on("end", () => resolve52(Buffer22.from(Buffer22.concat(chunks))));
|
|
2797
2785
|
gzip.on("error", reject);
|
|
2798
2786
|
gzip.write(data);
|
|
2799
2787
|
gzip.end();
|
|
@@ -3388,7 +3376,7 @@ class Logger2 {
|
|
|
3388
3376
|
throw new Error("No valid encryption key available");
|
|
3389
3377
|
const key = this.keys.get(this.currentKeyId);
|
|
3390
3378
|
try {
|
|
3391
|
-
const encryptedData =
|
|
3379
|
+
const encryptedData = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "base64");
|
|
3392
3380
|
const iv = encryptedData.subarray(0, 16);
|
|
3393
3381
|
const authTag = encryptedData.subarray(encryptedData.length - 16);
|
|
3394
3382
|
const ciphertext = encryptedData.subarray(16, encryptedData.length - 16);
|
|
@@ -3397,7 +3385,7 @@ class Logger2 {
|
|
|
3397
3385
|
const d1 = decipher.update(ciphertext);
|
|
3398
3386
|
const d2 = decipher.final();
|
|
3399
3387
|
const totalLen = d1.length + d2.length;
|
|
3400
|
-
const out =
|
|
3388
|
+
const out = Buffer22.allocUnsafe(totalLen);
|
|
3401
3389
|
d1.copy(out, 0);
|
|
3402
3390
|
d2.copy(out, d1.length);
|
|
3403
3391
|
return out.toString("utf8");
|
|
@@ -9138,26 +9126,55 @@ var DEFAULT_CORS_HEADERS = {
|
|
|
9138
9126
|
"Access-Control-Allow-Origin": "*",
|
|
9139
9127
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
9140
9128
|
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Accept, Origin",
|
|
9141
|
-
"Access-Control-Max-Age": "86400"
|
|
9142
|
-
"Access-Control-Allow-Credentials": "true"
|
|
9129
|
+
"Access-Control-Max-Age": "86400"
|
|
9143
9130
|
};
|
|
9144
9131
|
|
|
9145
9132
|
class Cors {
|
|
9146
|
-
|
|
9133
|
+
resolveAllowOrigin(configuredOrigin, credentials, requestOrigin) {
|
|
9134
|
+
if (Array.isArray(configuredOrigin)) {
|
|
9135
|
+
if (requestOrigin && configuredOrigin.includes(requestOrigin)) {
|
|
9136
|
+
return { value: requestOrigin, vary: true };
|
|
9137
|
+
}
|
|
9138
|
+
return { value: null, vary: true };
|
|
9139
|
+
}
|
|
9140
|
+
const origin = configuredOrigin || "*";
|
|
9141
|
+
if (origin === "*") {
|
|
9142
|
+
if (credentials) {
|
|
9143
|
+
if (requestOrigin) {
|
|
9144
|
+
return { value: requestOrigin, vary: true };
|
|
9145
|
+
}
|
|
9146
|
+
return { value: null, vary: true };
|
|
9147
|
+
}
|
|
9148
|
+
return { value: "*", vary: false };
|
|
9149
|
+
}
|
|
9150
|
+
return { value: origin, vary: false };
|
|
9151
|
+
}
|
|
9152
|
+
getCorsHeaders(req) {
|
|
9147
9153
|
const corsConfig = config5.server?.cors;
|
|
9154
|
+
const requestOrigin = req.headers.get("origin");
|
|
9148
9155
|
if (corsConfig && corsConfig.enabled) {
|
|
9149
|
-
|
|
9150
|
-
|
|
9156
|
+
const credentials = Boolean(corsConfig.credentials);
|
|
9157
|
+
const { value: allowOrigin, vary } = this.resolveAllowOrigin(corsConfig.origin, credentials, requestOrigin);
|
|
9158
|
+
const headers = {
|
|
9151
9159
|
"Access-Control-Allow-Methods": Array.isArray(corsConfig.methods) ? corsConfig.methods.join(", ") : DEFAULT_CORS_HEADERS["Access-Control-Allow-Methods"],
|
|
9152
9160
|
"Access-Control-Allow-Headers": Array.isArray(corsConfig.allowedHeaders) ? corsConfig.allowedHeaders.join(", ") : DEFAULT_CORS_HEADERS["Access-Control-Allow-Headers"],
|
|
9153
|
-
"Access-Control-Max-Age": String(corsConfig.maxAge || 86400)
|
|
9154
|
-
"Access-Control-Allow-Credentials": String(corsConfig.credentials || false)
|
|
9161
|
+
"Access-Control-Max-Age": String(corsConfig.maxAge || 86400)
|
|
9155
9162
|
};
|
|
9163
|
+
if (allowOrigin !== null) {
|
|
9164
|
+
headers["Access-Control-Allow-Origin"] = allowOrigin;
|
|
9165
|
+
}
|
|
9166
|
+
if (credentials && allowOrigin !== null && allowOrigin !== "*") {
|
|
9167
|
+
headers["Access-Control-Allow-Credentials"] = "true";
|
|
9168
|
+
}
|
|
9169
|
+
if (vary) {
|
|
9170
|
+
headers.Vary = "Origin";
|
|
9171
|
+
}
|
|
9172
|
+
return headers;
|
|
9156
9173
|
}
|
|
9157
9174
|
return { ...DEFAULT_CORS_HEADERS };
|
|
9158
9175
|
}
|
|
9159
9176
|
async handle(req, next) {
|
|
9160
|
-
const corsHeaders = this.getCorsHeaders();
|
|
9177
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
9161
9178
|
if (req.method === "OPTIONS") {
|
|
9162
9179
|
return new Response(null, { status: 204, headers: corsHeaders });
|
|
9163
9180
|
}
|
|
@@ -9195,15 +9212,14 @@ class Cors {
|
|
|
9195
9212
|
}
|
|
9196
9213
|
|
|
9197
9214
|
// src/middleware/csrf.ts
|
|
9198
|
-
import { createHash, randomBytes as randomBytes4 } from "crypto";
|
|
9215
|
+
import { createHash, randomBytes as randomBytes4, timingSafeEqual } from "crypto";
|
|
9216
|
+
var MAX_TOKENS = 1e4;
|
|
9217
|
+
var TOKEN_TTL_MS = 2 * 60 * 60 * 1000;
|
|
9218
|
+
|
|
9199
9219
|
class Csrf {
|
|
9200
9220
|
static tokens = new Map;
|
|
9201
9221
|
async handle(req, next) {
|
|
9202
|
-
const csrfConfig = config5.server?.security?.csrf
|
|
9203
|
-
if (["GET", "HEAD", "OPTIONS"].includes(req.method)) {
|
|
9204
|
-
const response2 = await next();
|
|
9205
|
-
return response2 || new Response("Not Found", { status: 404 });
|
|
9206
|
-
}
|
|
9222
|
+
const csrfConfig = config5.server?.security?.csrf ?? {};
|
|
9207
9223
|
const method = req.method.toUpperCase();
|
|
9208
9224
|
const ignoredMethods = ["GET", "HEAD", "OPTIONS", ...csrfConfig?.ignoreMethods || []];
|
|
9209
9225
|
if (ignoredMethods.includes(method)) {
|
|
@@ -9227,7 +9243,7 @@ class Csrf {
|
|
|
9227
9243
|
}
|
|
9228
9244
|
const newHeaders = new Headers(response2.headers);
|
|
9229
9245
|
newHeaders.append("Set-Cookie", cookieOptions.join("; "));
|
|
9230
|
-
Csrf.
|
|
9246
|
+
Csrf.storeToken(token2);
|
|
9231
9247
|
return new Response(response2.body, {
|
|
9232
9248
|
status: response2.status,
|
|
9233
9249
|
statusText: response2.statusText,
|
|
@@ -9239,10 +9255,11 @@ class Csrf {
|
|
|
9239
9255
|
const response2 = await next();
|
|
9240
9256
|
return response2 || new Response("Not Found", { status: 404 });
|
|
9241
9257
|
}
|
|
9242
|
-
const
|
|
9258
|
+
const rawToken = req.headers.get("X-CSRF-TOKEN") || req.jsonBody?.csrf_token || req.jsonBody?._token || null;
|
|
9259
|
+
const token = typeof rawToken === "string" ? rawToken : null;
|
|
9243
9260
|
const cookies = this.parseCookies(req);
|
|
9244
9261
|
const cookieToken = cookies[csrfConfig?.cookie?.name || "csrf-token"];
|
|
9245
|
-
if (!token || !cookieToken || token
|
|
9262
|
+
if (!token || !cookieToken || !Csrf.safeCompare(token, cookieToken) || !Csrf.isTokenValid(token)) {
|
|
9246
9263
|
return new Response(JSON.stringify({ error: "CSRF token validation failed" }), {
|
|
9247
9264
|
status: 403,
|
|
9248
9265
|
headers: { "Content-Type": "application/json" }
|
|
@@ -9251,6 +9268,34 @@ class Csrf {
|
|
|
9251
9268
|
const response = await next();
|
|
9252
9269
|
return response || new Response("Not Found", { status: 404 });
|
|
9253
9270
|
}
|
|
9271
|
+
static safeCompare(a, b) {
|
|
9272
|
+
const bufA = Buffer.from(a);
|
|
9273
|
+
const bufB = Buffer.from(b);
|
|
9274
|
+
if (bufA.length !== bufB.length) {
|
|
9275
|
+
return false;
|
|
9276
|
+
}
|
|
9277
|
+
return timingSafeEqual(bufA, bufB);
|
|
9278
|
+
}
|
|
9279
|
+
static storeToken(token) {
|
|
9280
|
+
if (Csrf.tokens.size >= MAX_TOKENS) {
|
|
9281
|
+
const oldest = Csrf.tokens.keys().next().value;
|
|
9282
|
+
if (oldest !== undefined) {
|
|
9283
|
+
Csrf.tokens.delete(oldest);
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
9286
|
+
Csrf.tokens.set(token, Date.now());
|
|
9287
|
+
}
|
|
9288
|
+
static isTokenValid(token) {
|
|
9289
|
+
const issuedAt = Csrf.tokens.get(token);
|
|
9290
|
+
if (issuedAt === undefined) {
|
|
9291
|
+
return false;
|
|
9292
|
+
}
|
|
9293
|
+
if (Date.now() - issuedAt > TOKEN_TTL_MS) {
|
|
9294
|
+
Csrf.tokens.delete(token);
|
|
9295
|
+
return false;
|
|
9296
|
+
}
|
|
9297
|
+
return true;
|
|
9298
|
+
}
|
|
9254
9299
|
generateToken(secret) {
|
|
9255
9300
|
const randomString = randomBytes4(16).toString("hex");
|
|
9256
9301
|
return createHash("sha256").update(`${randomString}${secret}`).digest("hex");
|
|
@@ -9259,11 +9304,17 @@ class Csrf {
|
|
|
9259
9304
|
const cookieHeader = req.headers.get("cookie");
|
|
9260
9305
|
if (!cookieHeader)
|
|
9261
9306
|
return {};
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9307
|
+
const cookies = {};
|
|
9308
|
+
for (const cookie of cookieHeader.split(";")) {
|
|
9309
|
+
const eqIndex = cookie.indexOf("=");
|
|
9310
|
+
if (eqIndex === -1)
|
|
9311
|
+
continue;
|
|
9312
|
+
const name = cookie.slice(0, eqIndex).trim();
|
|
9313
|
+
if (!name)
|
|
9314
|
+
continue;
|
|
9315
|
+
cookies[name] = cookie.slice(eqIndex + 1).trim();
|
|
9316
|
+
}
|
|
9317
|
+
return cookies;
|
|
9267
9318
|
}
|
|
9268
9319
|
}
|
|
9269
9320
|
|
|
@@ -9318,18 +9369,18 @@ import process72 from "process";
|
|
|
9318
9369
|
import { existsSync as existsSync62, readdirSync as readdirSync22 } from "fs";
|
|
9319
9370
|
import { extname as extname2, resolve as resolve62 } from "path";
|
|
9320
9371
|
import process82 from "process";
|
|
9321
|
-
var
|
|
9322
|
-
var
|
|
9323
|
-
function
|
|
9324
|
-
this[name] =
|
|
9372
|
+
var __defProp2 = Object.defineProperty;
|
|
9373
|
+
var __returnValue2 = (v) => v;
|
|
9374
|
+
function __exportSetter2(name, newValue) {
|
|
9375
|
+
this[name] = __returnValue2.bind(null, newValue);
|
|
9325
9376
|
}
|
|
9326
|
-
var
|
|
9377
|
+
var __export2 = (target, all) => {
|
|
9327
9378
|
for (var name in all)
|
|
9328
|
-
|
|
9379
|
+
__defProp2(target, name, {
|
|
9329
9380
|
get: all[name],
|
|
9330
9381
|
enumerable: true,
|
|
9331
9382
|
configurable: true,
|
|
9332
|
-
set:
|
|
9383
|
+
set: __exportSetter2.bind(all, name)
|
|
9333
9384
|
});
|
|
9334
9385
|
};
|
|
9335
9386
|
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -12880,7 +12931,7 @@ export type ConfigOf = Config
|
|
|
12880
12931
|
}
|
|
12881
12932
|
var init_plugin = () => {};
|
|
12882
12933
|
var exports_src = {};
|
|
12883
|
-
|
|
12934
|
+
__export2(exports_src, {
|
|
12884
12935
|
withErrorRecovery: () => withErrorRecovery3,
|
|
12885
12936
|
tryLoadConfig: () => tryLoadConfig4,
|
|
12886
12937
|
loadConfigWithResult: () => loadConfigWithResult2,
|
|
@@ -13748,6 +13799,7 @@ class InMemoryNonceStore {
|
|
|
13748
13799
|
cleanupInterval = null;
|
|
13749
13800
|
constructor(cleanupIntervalMs = 60000) {
|
|
13750
13801
|
this.cleanupInterval = setInterval(() => this.cleanup(), cleanupIntervalMs);
|
|
13802
|
+
this.cleanupInterval.unref?.();
|
|
13751
13803
|
}
|
|
13752
13804
|
has(nonce) {
|
|
13753
13805
|
const expiry = this.nonces.get(nonce);
|
|
@@ -14753,6 +14805,7 @@ class DDoSProtection {
|
|
|
14753
14805
|
}
|
|
14754
14806
|
}
|
|
14755
14807
|
}, this.options.windowSize / 2);
|
|
14808
|
+
this.cleanupInterval.unref?.();
|
|
14756
14809
|
}
|
|
14757
14810
|
isWhitelisted(ip) {
|
|
14758
14811
|
return this.options.whitelistedIPs?.includes(ip) ?? false;
|
|
@@ -14763,6 +14816,15 @@ class DDoSProtection {
|
|
|
14763
14816
|
getRateLimitInfo(key, now) {
|
|
14764
14817
|
let info = this.requestStore.get(key);
|
|
14765
14818
|
if (!info) {
|
|
14819
|
+
if (this.requestStore.size >= 1e5) {
|
|
14820
|
+
let toEvict = Math.ceil(this.requestStore.size / 10);
|
|
14821
|
+
for (const oldestKey of this.requestStore.keys()) {
|
|
14822
|
+
this.requestStore.delete(oldestKey);
|
|
14823
|
+
if (--toEvict <= 0) {
|
|
14824
|
+
break;
|
|
14825
|
+
}
|
|
14826
|
+
}
|
|
14827
|
+
}
|
|
14766
14828
|
info = {
|
|
14767
14829
|
count: 0,
|
|
14768
14830
|
firstRequest: now,
|
|
@@ -15350,6 +15412,9 @@ class Helmet {
|
|
|
15350
15412
|
}
|
|
15351
15413
|
|
|
15352
15414
|
// src/middleware/input_validation.ts
|
|
15415
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
|
|
15416
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
15417
|
+
|
|
15353
15418
|
class InputValidation {
|
|
15354
15419
|
options;
|
|
15355
15420
|
constructor(options = {}) {
|
|
@@ -15446,8 +15511,7 @@ class InputValidation {
|
|
|
15446
15511
|
}
|
|
15447
15512
|
break;
|
|
15448
15513
|
case "email": {
|
|
15449
|
-
|
|
15450
|
-
if (typeof value === "string" && !emailRegex.test(value)) {
|
|
15514
|
+
if (typeof value === "string" && !EMAIL_REGEX.test(value)) {
|
|
15451
15515
|
errors.push({
|
|
15452
15516
|
field: fieldPath,
|
|
15453
15517
|
message: "Must be a valid email address",
|
|
@@ -15470,8 +15534,7 @@ class InputValidation {
|
|
|
15470
15534
|
}
|
|
15471
15535
|
break;
|
|
15472
15536
|
case "uuid": {
|
|
15473
|
-
|
|
15474
|
-
if (typeof value === "string" && !uuidRegex.test(value)) {
|
|
15537
|
+
if (typeof value === "string" && !UUID_REGEX.test(value)) {
|
|
15475
15538
|
errors.push({
|
|
15476
15539
|
field: fieldPath,
|
|
15477
15540
|
message: "Must be a valid UUID",
|
|
@@ -15525,7 +15588,7 @@ class InputValidation {
|
|
|
15525
15588
|
rule: "enum"
|
|
15526
15589
|
});
|
|
15527
15590
|
}
|
|
15528
|
-
if (rule.pattern && typeof value === "string" && !rule.pattern.test(value)) {
|
|
15591
|
+
if (rule.type !== "regex" && rule.pattern && typeof value === "string" && !rule.pattern.test(value)) {
|
|
15529
15592
|
errors.push({
|
|
15530
15593
|
field: fieldPath,
|
|
15531
15594
|
message: "Does not match required pattern",
|
|
@@ -15808,6 +15871,7 @@ class PerformanceAlerting {
|
|
|
15808
15871
|
this.cleanupOldNotifications();
|
|
15809
15872
|
this.autoResolveAlerts();
|
|
15810
15873
|
}, 1e4);
|
|
15874
|
+
this.checkInterval.unref?.();
|
|
15811
15875
|
}
|
|
15812
15876
|
evaluateRules() {
|
|
15813
15877
|
const now = Date.now();
|
|
@@ -16692,6 +16756,7 @@ class PerformanceMonitor5 {
|
|
|
16692
16756
|
this.cleanupInterval = setInterval(() => {
|
|
16693
16757
|
this.cleanupOldMetrics();
|
|
16694
16758
|
}, 60000);
|
|
16759
|
+
this.cleanupInterval.unref?.();
|
|
16695
16760
|
}
|
|
16696
16761
|
cleanupOldMetrics() {
|
|
16697
16762
|
const now = Date.now();
|
|
@@ -17194,6 +17259,7 @@ class RequestTracer {
|
|
|
17194
17259
|
console.warn("Error exporting spans:", error);
|
|
17195
17260
|
}
|
|
17196
17261
|
}, 100);
|
|
17262
|
+
this.exportInterval.unref?.();
|
|
17197
17263
|
}
|
|
17198
17264
|
shouldSample() {
|
|
17199
17265
|
return Math.random() < (this.options.sampleRate || 1);
|
|
@@ -17314,11 +17380,17 @@ class RequestTracer {
|
|
|
17314
17380
|
}
|
|
17315
17381
|
}
|
|
17316
17382
|
async exportSpans() {
|
|
17317
|
-
|
|
17383
|
+
if (this.spans.size > 0) {
|
|
17384
|
+
const cutoff = performance.now() - 60000;
|
|
17385
|
+
for (const [spanId, span] of this.spans) {
|
|
17386
|
+
if (span.endTime === undefined && span.startTime < cutoff) {
|
|
17387
|
+
this.spans.delete(spanId);
|
|
17388
|
+
}
|
|
17389
|
+
}
|
|
17390
|
+
}
|
|
17318
17391
|
if (this.exportQueue.length === 0) {
|
|
17319
17392
|
const finishedSpans = Array.from(this.spans.values()).filter((span) => span.endTime !== undefined);
|
|
17320
17393
|
if (finishedSpans.length > 0) {
|
|
17321
|
-
console.warn(`[RequestTracer] Found ${finishedSpans.length} finished spans to export`);
|
|
17322
17394
|
this.exportQueue.push(...finishedSpans);
|
|
17323
17395
|
for (const span of finishedSpans) {
|
|
17324
17396
|
this.spans.delete(span.spanId);
|
|
@@ -18056,4 +18128,4 @@ var middleware = new Proxy({}, {
|
|
|
18056
18128
|
}
|
|
18057
18129
|
});
|
|
18058
18130
|
|
|
18059
|
-
export {
|
|
18131
|
+
export { defaultConfig4 as defaultConfig, getConfig, config5 as config, extractBasicAuth, extractBearerToken, extractApiKey, basicAuth, bearerAuth, jwtAuth, apiKeyAuth, oauth2Auth, Cors, Csrf, JsonBody, RateLimit, rateLimit, RequestId, SignatureError, InMemoryNonceStore, RequestSigning, requestSigning, signRequest, verifySignature, MemorySessionStore, FileSessionStore, RedisSessionStore, DatabaseSessionStore, createSessionStore, SessionManager, generateSessionId, Session, ContentSecurityPolicy, contentSecurityPolicy, DDoSProtection, ddosProtection, FileSecurity, FileUpload, fileUpload, singleFileUpload, multipleFileUpload, imageUpload, documentUpload, Helmet, InputValidation, inputValidation, PerformanceAlerting, performanceAlerting, PerformanceDashboard, performanceDashboard, PerformanceMonitor5 as PerformanceMonitor, performanceMonitor, RequestTracer, requestTracer, Security, security, SecuritySuite, securitySuite, cors, jsonBody, requestId, session, csrf, auth, middleware };
|