tezx 1.0.13 → 1.0.15

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 (92) hide show
  1. package/README.md +8 -3
  2. package/{dist/cjs → cjs}/index.js +1 -1
  3. package/{dist/index.js → index.js} +1 -1
  4. package/package.json +18 -19
  5. package/dist/MiddlewareConfigure.d.ts +0 -17
  6. package/dist/MiddlewareConfigure.js +0 -63
  7. package/dist/adapter.d.ts +0 -10
  8. package/dist/adapter.js +0 -162
  9. package/dist/cjs/MiddlewareConfigure.js +0 -68
  10. package/dist/cjs/adapter.js +0 -166
  11. package/dist/cjs/common.js +0 -15
  12. package/dist/cjs/config/config.js +0 -35
  13. package/dist/cjs/context.js +0 -428
  14. package/dist/cjs/environment.js +0 -34
  15. package/dist/cjs/header.js +0 -85
  16. package/dist/cjs/helper/common.js +0 -11
  17. package/dist/cjs/helper/env-parser.js +0 -69
  18. package/dist/cjs/helper/index.js +0 -20
  19. package/dist/cjs/middleware/cors.js +0 -49
  20. package/dist/cjs/middleware/index.js +0 -26
  21. package/dist/cjs/middleware/logger.js +0 -21
  22. package/dist/cjs/middleware/powered-by.js +0 -10
  23. package/dist/cjs/middleware/rateLimiter.js +0 -40
  24. package/dist/cjs/middleware/request-id.js +0 -15
  25. package/dist/cjs/middleware/sanitizeHeader.js +0 -51
  26. package/dist/cjs/middleware/secureHeaders.js +0 -42
  27. package/dist/cjs/middleware/xssProtection.js +0 -26
  28. package/dist/cjs/request.js +0 -80
  29. package/dist/cjs/router.js +0 -377
  30. package/dist/cjs/server.js +0 -170
  31. package/dist/cjs/utils/colors.js +0 -24
  32. package/dist/cjs/utils/debugging.js +0 -17
  33. package/dist/cjs/utils/formData.js +0 -219
  34. package/dist/cjs/utils/params.js +0 -94
  35. package/dist/cjs/utils/state.js +0 -34
  36. package/dist/cjs/utils/staticFile.js +0 -158
  37. package/dist/cjs/utils/url.js +0 -59
  38. package/dist/common.d.ts +0 -21
  39. package/dist/common.js +0 -11
  40. package/dist/config/config.d.ts +0 -17
  41. package/dist/config/config.js +0 -31
  42. package/dist/context.d.ts +0 -195
  43. package/dist/context.js +0 -424
  44. package/dist/environment.d.ts +0 -6
  45. package/dist/environment.js +0 -30
  46. package/dist/header.d.ts +0 -71
  47. package/dist/header.js +0 -81
  48. package/dist/helper/common.d.ts +0 -5
  49. package/dist/helper/common.js +0 -8
  50. package/dist/helper/env-parser.d.ts +0 -5
  51. package/dist/helper/env-parser.js +0 -66
  52. package/dist/helper/index.d.ts +0 -2
  53. package/dist/helper/index.js +0 -2
  54. package/dist/middleware/cors.d.ts +0 -10
  55. package/dist/middleware/cors.js +0 -46
  56. package/dist/middleware/index.d.ts +0 -9
  57. package/dist/middleware/index.js +0 -8
  58. package/dist/middleware/logger.d.ts +0 -15
  59. package/dist/middleware/logger.js +0 -18
  60. package/dist/middleware/powered-by.d.ts +0 -16
  61. package/dist/middleware/powered-by.js +0 -6
  62. package/dist/middleware/rateLimiter.d.ts +0 -61
  63. package/dist/middleware/rateLimiter.js +0 -36
  64. package/dist/middleware/request-id.d.ts +0 -16
  65. package/dist/middleware/request-id.js +0 -11
  66. package/dist/middleware/sanitizeHeader.d.ts +0 -53
  67. package/dist/middleware/sanitizeHeader.js +0 -47
  68. package/dist/middleware/secureHeaders.d.ts +0 -78
  69. package/dist/middleware/secureHeaders.js +0 -38
  70. package/dist/middleware/xssProtection.d.ts +0 -43
  71. package/dist/middleware/xssProtection.js +0 -22
  72. package/dist/request.d.ts +0 -82
  73. package/dist/request.js +0 -76
  74. package/dist/router.d.ts +0 -191
  75. package/dist/router.js +0 -373
  76. package/dist/server.d.ts +0 -54
  77. package/dist/server.js +0 -166
  78. package/dist/utils/colors.d.ts +0 -21
  79. package/dist/utils/colors.js +0 -21
  80. package/dist/utils/debugging.d.ts +0 -7
  81. package/dist/utils/debugging.js +0 -13
  82. package/dist/utils/formData.d.ts +0 -5
  83. package/dist/utils/formData.js +0 -213
  84. package/dist/utils/params.d.ts +0 -7
  85. package/dist/utils/params.js +0 -91
  86. package/dist/utils/state.d.ts +0 -50
  87. package/dist/utils/state.js +0 -30
  88. package/dist/utils/staticFile.d.ts +0 -9
  89. package/dist/utils/staticFile.js +0 -154
  90. package/dist/utils/url.d.ts +0 -16
  91. package/dist/utils/url.js +0 -55
  92. /package/{dist/index.d.ts → index.d.ts} +0 -0
package/dist/header.d.ts DELETED
@@ -1,71 +0,0 @@
1
- export declare class HeadersParser {
2
- private headers;
3
- constructor(init?: [string, string | string[]][] | Record<string, string>);
4
- /**
5
- * Adds multiple headers to the parser.
6
- * @param headers - Headers as an array of tuples or a record object.
7
- */
8
- add(headers: [string, string | string[]][] | Record<string, string>): this;
9
- /**
10
- * Sets a header value.
11
- * @param key - Header name.
12
- * @param value - Header value(s).
13
- */
14
- set(key: string, value: string | string[]): this;
15
- clear(): this;
16
- /**
17
- * Retrieves the first value of a header.
18
- * @param key - Header name.
19
- * @returns The first header value or undefined if not found.
20
- */
21
- get(key: string): string | undefined;
22
- /**
23
- * Retrieves all values of a header.
24
- * @param key - Header name.
25
- * @returns An array of header values.
26
- */
27
- getAll(key: string): string[];
28
- /**
29
- * Checks if a header exists.
30
- * @param key - Header name.
31
- * @returns True if the header exists, false otherwise.
32
- */
33
- has(key: string): boolean;
34
- /**
35
- * Deletes a header.
36
- * @param key - Header name.
37
- * @returns True if deleted successfully, false otherwise.
38
- */
39
- delete(key: string): boolean;
40
- /**
41
- * Appends a value to an existing header or creates a new one.
42
- * @param key - Header name.
43
- * @param value - Value to append.
44
- */
45
- append(key: string, value: string): this;
46
- /**
47
- * Returns an iterator over header entries.
48
- * @returns IterableIterator of header key-value pairs.
49
- */
50
- entries(): IterableIterator<[string, string[]]>;
51
- /**
52
- * Returns an iterator over header keys.
53
- * @returns IterableIterator of header names.
54
- */
55
- keys(): IterableIterator<string>;
56
- /**
57
- * Returns an iterator over header values.
58
- * @returns IterableIterator of header values arrays.
59
- */
60
- values(): IterableIterator<string[]>;
61
- /**
62
- * Iterates over headers and executes a callback function.
63
- * @param callback - Function to execute for each header.
64
- */
65
- forEach(callback: (value: string[], key: string) => void): void;
66
- /**
67
- * Converts headers into a plain object.
68
- * @returns A record of headers where single-value headers are returned as a string.
69
- */
70
- toObject(): Record<string, string | string[]>;
71
- }
package/dist/header.js DELETED
@@ -1,81 +0,0 @@
1
- export class HeadersParser {
2
- headers = new Map();
3
- constructor(init) {
4
- if (init) {
5
- this.add(init);
6
- }
7
- }
8
- add(headers) {
9
- if (Array.isArray(headers)) {
10
- for (const [key, value] of headers) {
11
- this.set(key, value);
12
- }
13
- }
14
- else if (typeof Headers !== "undefined" && headers instanceof Headers) {
15
- for (const [key, value] of headers.entries()) {
16
- this.set(key, value);
17
- }
18
- }
19
- else if (typeof headers === "object") {
20
- for (const key in headers) {
21
- if (Object.prototype.hasOwnProperty.call(headers, key)) {
22
- this.set(key, headers[key]);
23
- }
24
- }
25
- }
26
- return this;
27
- }
28
- set(key, value) {
29
- this.headers.set(key.toLowerCase(), Array.isArray(value) ? value : [value]);
30
- return this;
31
- }
32
- clear() {
33
- this.headers.clear();
34
- return this;
35
- }
36
- get(key) {
37
- const values = this.headers.get(key.toLowerCase());
38
- return values ? values[0] : undefined;
39
- }
40
- getAll(key) {
41
- return this.headers.get(key.toLowerCase()) || [];
42
- }
43
- has(key) {
44
- return this.headers.has(key.toLowerCase());
45
- }
46
- delete(key) {
47
- return this.headers.delete(key.toLowerCase());
48
- }
49
- append(key, value) {
50
- const lowerKey = key.toLowerCase();
51
- if (this.headers.has(lowerKey)) {
52
- this.headers.get(lowerKey).push(value);
53
- }
54
- else {
55
- this.headers.set(lowerKey, [value]);
56
- }
57
- return this;
58
- }
59
- entries() {
60
- return this.headers.entries();
61
- }
62
- keys() {
63
- return this.headers.keys();
64
- }
65
- values() {
66
- return this.headers.values();
67
- }
68
- forEach(callback) {
69
- for (const [key, value] of this.headers) {
70
- callback(value, key);
71
- }
72
- }
73
- toObject() {
74
- const obj = {};
75
- for (const [key, value] of this.headers.entries()) {
76
- obj[key] = value.length > 1 ? value : value[0];
77
- }
78
- return obj;
79
- }
80
- }
81
- Object.defineProperty(HeadersParser, "name", { value: "Headers" });
@@ -1,5 +0,0 @@
1
- /**
2
- * Generate a unique request ID
3
- * @returns {string} - A unique request identifier
4
- */
5
- export declare function generateID(): string;
@@ -1,8 +0,0 @@
1
- export function generateID() {
2
- const timestamp = Date.now().toString(16);
3
- const random = Math.floor(Math.random() * 0xffffffffffff)
4
- .toString(16)
5
- .padStart(12, "0");
6
- const pid = (process.pid % 0x10000).toString(16).padStart(4, "0");
7
- return `${timestamp}-${random}-${pid}`;
8
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Loads environment variables from .env files.
3
- * @param basePath - The base directory where .env files are located.
4
- */
5
- export declare function loadEnv(basePath?: string): Record<string, string>;
@@ -1,66 +0,0 @@
1
- import { EnvironmentDetector } from "../environment";
2
- function parseEnvFile(filePath, result) {
3
- try {
4
- let fileExists = false;
5
- let runtime = EnvironmentDetector.getEnvironment;
6
- if (runtime === "node" || runtime === "bun") {
7
- const { existsSync } = require("fs");
8
- fileExists = existsSync(filePath);
9
- }
10
- else if (runtime === "deno") {
11
- try {
12
- Deno.statSync(filePath);
13
- fileExists = true;
14
- }
15
- catch {
16
- fileExists = false;
17
- }
18
- }
19
- if (!fileExists) {
20
- return;
21
- }
22
- let fileContent = "";
23
- if (runtime === "node" || runtime === "bun") {
24
- const { readFileSync } = require("fs");
25
- fileContent = readFileSync(filePath, "utf8");
26
- }
27
- else if (runtime === "deno") {
28
- fileContent = new TextDecoder("utf-8").decode(Deno.readFileSync(filePath));
29
- }
30
- const lines = fileContent.split("\n");
31
- for (const line of lines) {
32
- const trimmedLine = line.trim();
33
- if (!trimmedLine || trimmedLine.startsWith("#"))
34
- continue;
35
- const [key, value] = trimmedLine.split("=", 2).map((part) => part.trim());
36
- if (key && value) {
37
- const parsedValue = value
38
- .replace(/^"(.*)"$/, "$1")
39
- .replace(/^'(.*)'$/, "$1");
40
- result[key] = parsedValue;
41
- if (runtime === "node" || runtime === "bun") {
42
- process.env[key] = parsedValue;
43
- }
44
- else if (runtime === "deno") {
45
- Deno.env.set(key, parsedValue);
46
- }
47
- }
48
- }
49
- }
50
- catch (error) {
51
- console.error(`[dotenv] Error parsing file: ${filePath}`, error);
52
- }
53
- }
54
- export function loadEnv(basePath = "./") {
55
- const result = {};
56
- const envFiles = [
57
- ".env",
58
- ".env.local",
59
- `.env.${process?.env?.NODE_ENV || "development"}`,
60
- `.env.${process?.env?.NODE_ENV || "development"}.local`,
61
- ];
62
- for (const envFile of envFiles) {
63
- parseEnvFile(`${basePath}${envFile}`, result);
64
- }
65
- return result;
66
- }
@@ -1,2 +0,0 @@
1
- export * from "./common";
2
- export { loadEnv } from "./env-parser";
@@ -1,2 +0,0 @@
1
- export * from "./common";
2
- export { loadEnv } from "./env-parser";
@@ -1,10 +0,0 @@
1
- import { ctx as Context } from "../router";
2
- export type CorsOptions = {
3
- origin?: string | RegExp | (string | RegExp)[] | ((reqOrigin: string) => boolean);
4
- methods?: string[];
5
- allowedHeaders?: string[];
6
- exposedHeaders?: string[];
7
- credentials?: boolean;
8
- maxAge?: number;
9
- };
10
- export declare function cors(option?: CorsOptions): (ctx: Context, next: () => Promise<any>) => Promise<any>;
@@ -1,46 +0,0 @@
1
- export function cors(option = {}) {
2
- const { methods, allowedHeaders, credentials, exposedHeaders, maxAge, origin, } = option;
3
- return async (ctx, next) => {
4
- const reqOrigin = ctx.req.headers.get("origin") || "";
5
- let allowOrigin = "*";
6
- if (typeof origin === "string") {
7
- allowOrigin = origin;
8
- }
9
- else if (origin instanceof RegExp) {
10
- allowOrigin = origin.test(reqOrigin) ? reqOrigin : "";
11
- }
12
- else if (Array.isArray(origin)) {
13
- const isAllowed = origin.some((item) => {
14
- if (typeof item === "string") {
15
- return item === reqOrigin;
16
- }
17
- else if (item instanceof RegExp) {
18
- return item.test(reqOrigin);
19
- }
20
- });
21
- allowOrigin = isAllowed ? reqOrigin : "";
22
- }
23
- else if (typeof origin === "function") {
24
- allowOrigin = origin(reqOrigin) ? reqOrigin : "";
25
- }
26
- ctx.headers.set("Access-Control-Allow-Origin", allowOrigin);
27
- ctx.headers.set("Access-Control-Allow-Methods", (methods || ["GET", "POST", "PUT", "DELETE"]).join(", "));
28
- ctx.headers.set("Access-Control-Allow-Headers", (allowedHeaders || ["Content-Type", "Authorization"]).join(", "));
29
- if (exposedHeaders) {
30
- ctx.headers.set("Access-Control-Expose-Headers", exposedHeaders.join(", "));
31
- }
32
- if (credentials) {
33
- ctx.headers.set("Access-Control-Allow-Credentials", "true");
34
- }
35
- if (maxAge) {
36
- ctx.headers.set("Access-Control-Max-Age", maxAge.toString());
37
- }
38
- if (ctx.req.method === "OPTIONS") {
39
- return new Response(null, {
40
- status: 204,
41
- headers: ctx.headers.toObject(),
42
- });
43
- }
44
- return await next();
45
- };
46
- }
@@ -1,9 +0,0 @@
1
- export { cors } from "./cors";
2
- export type { CorsOptions } from "./cors";
3
- export * from "./logger";
4
- export * from "./powered-by";
5
- export * from "./request-id";
6
- export * from "./secureHeaders";
7
- export * from "./xssProtection";
8
- export * from "./sanitizeHeader";
9
- export * from "./rateLimiter";
@@ -1,8 +0,0 @@
1
- export { cors } from "./cors";
2
- export * from "./logger";
3
- export * from "./powered-by";
4
- export * from "./request-id";
5
- export * from "./secureHeaders";
6
- export * from "./xssProtection";
7
- export * from "./sanitizeHeader";
8
- export * from "./rateLimiter";
@@ -1,15 +0,0 @@
1
- import { Middleware } from "../router";
2
- /**
3
- * Logger Middleware
4
- * Logs incoming requests with method, pathname, status, and execution time.
5
- *
6
- * @returns {Middleware} - A middleware function for logging HTTP requests.
7
- *
8
- * @example
9
- * ```ts
10
- * import { logger } from 'tezx';
11
- *
12
- * app.use(logger());
13
- * ```
14
- */
15
- export declare function logger(): Middleware;
@@ -1,18 +0,0 @@
1
- import { COLORS } from "../utils/colors";
2
- export function logger() {
3
- return async (ctx, next) => {
4
- try {
5
- console.log(`${COLORS.bold}<-- ${COLORS.reset}${COLORS.bgMagenta} ${ctx.method} ${COLORS.reset} ${ctx.pathname}`);
6
- const startTime = performance.now();
7
- let n = await next();
8
- const elapsed = performance.now() - startTime;
9
- console.log(`${COLORS.bold}--> ${COLORS.reset}${COLORS.bgBlue} ${ctx.method} ${COLORS.reset} ${ctx.pathname} ` +
10
- `${COLORS.yellow}${ctx.getStatus}${COLORS.reset} ${COLORS.magenta}${elapsed.toFixed(2)}ms${COLORS.reset}`);
11
- return n;
12
- }
13
- catch (err) {
14
- console.error(`${COLORS.red}Error:${COLORS.reset}`, err.stack);
15
- throw new Error(err.stack);
16
- }
17
- };
18
- }
@@ -1,16 +0,0 @@
1
- import { Middleware } from "../router";
2
- /**
3
- * PoweredBy Middleware
4
- * Adds an "X-Powered-By" header to responses.
5
- *
6
- * @param {string} [serverName] - Optional custom server name; defaults to "TezX".
7
- * @returns {Middleware} - A middleware function for setting the "X-Powered-By" header.
8
- *
9
- * @example
10
- * ```ts
11
- * import { poweredBy } from 'tezx';
12
- *
13
- * app.use(poweredBy("MyServer"));
14
- * ```
15
- */
16
- export declare const poweredBy: (serverName?: string) => Middleware;
@@ -1,6 +0,0 @@
1
- export const poweredBy = (serverName) => {
2
- return (ctx, next) => {
3
- ctx.header("X-Powered-By", serverName || "TezX");
4
- return next();
5
- };
6
- };
@@ -1,61 +0,0 @@
1
- import { Context } from "../context";
2
- import { Middleware } from "../router";
3
- export type RateLimiterOptions = {
4
- /**
5
- * 🔴 Maximum allowed requests in the time window
6
- * @example
7
- * maxRequests: 100 // Allow 100 requests per window
8
- */
9
- maxRequests: number;
10
- /**
11
- * 🕒 Time window in milliseconds
12
- * @example
13
- * windowMs: 60_000 // 1 minute window
14
- */
15
- windowMs: number;
16
- /**
17
- * 🔑 Client identifier generator function
18
- * @default (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`
19
- * @example
20
- * keyGenerator: (ctx) => ctx.user?.id || ctx.ip // Use user ID if authenticated
21
- */
22
- keyGenerator?: (ctx: Context) => string;
23
- /**
24
- // * ⚠️ (Future) Storage backend - currently memory only
25
- // * @todo Implement Redis storage
26
- // */
27
- /**
28
- * 🛑 Custom rate limit exceeded handler
29
- * @default Sends 429 status with Retry-After header
30
- * @example
31
- * onError: (ctx, retryAfter) => {
32
- * ctx.status = 429;
33
- * ctx.body = { error: `Try again in ${retryAfter} seconds` };
34
- * }
35
- */
36
- onError?: (ctx: Context, retryAfter: number, error: Error) => void;
37
- };
38
- /**
39
- * 🚦 Rate limiting middleware for request throttling
40
- *
41
- * Enforces maximum request limits per client with sliding window.
42
- * Currently supports in-memory storage only (Redis coming soon).
43
- *
44
- * @param {RateLimiterOptions} options - Configuration
45
- * @returns {Middleware} Middleware function
46
- *
47
- * @example
48
- * // Basic rate limiting (100 requests/minute)
49
- * app.use(rateLimiter({
50
- * maxRequests: 100,
51
- * windowMs: 60_000
52
- * }));
53
- *
54
- * // Custom client identification
55
- * app.use(rateLimiter({
56
- * maxRequests: 10,
57
- * windowMs: 10_000,
58
- * keyGenerator: (ctx) => ctx.user?.id || ctx.ip
59
- * }));
60
- */
61
- export declare const rateLimiter: (options: RateLimiterOptions) => Middleware;
@@ -1,36 +0,0 @@
1
- export const rateLimiter = (options) => {
2
- const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, onError = (ctx, retryAfter, error) => {
3
- ctx.setStatus = 429;
4
- throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
5
- }, } = options;
6
- const memoryStore = new Map();
7
- return async (ctx, next) => {
8
- const key = keyGenerator(ctx);
9
- let requestCount;
10
- let resetTime;
11
- for (const [key, entry] of memoryStore.entries()) {
12
- if (Date.now() >= entry.resetTime) {
13
- memoryStore.delete(key);
14
- }
15
- }
16
- const entry = memoryStore.get(key);
17
- if (entry && Date.now() < entry.resetTime) {
18
- requestCount = entry.count + 1;
19
- resetTime = entry.resetTime;
20
- }
21
- else {
22
- requestCount = 1;
23
- resetTime = Date.now() + windowMs;
24
- memoryStore.set(key, { count: requestCount, resetTime });
25
- }
26
- if (requestCount > maxRequests) {
27
- const retryAfter = Math.ceil((resetTime - Date.now()) / 1000);
28
- ctx.headers.set("Retry-After", retryAfter.toString());
29
- return onError(ctx, retryAfter, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
30
- }
31
- ctx.headers.set("X-RateLimit-Limit", maxRequests.toString());
32
- ctx.headers.set("X-RateLimit-Remaining", (maxRequests - requestCount).toString());
33
- ctx.headers.set("X-RateLimit-Reset", resetTime.toString());
34
- return await next();
35
- };
36
- };
@@ -1,16 +0,0 @@
1
- import { Middleware } from "../router";
2
- /**
3
- * Request ID Middleware
4
- * Assigns a unique request ID to each incoming request.
5
- *
6
- * @param {string} [headerName="X-Request-ID"] - Header name to use for request ID.
7
- * @returns {Middleware} - A middleware function for tracking requests.
8
- *
9
- * @example
10
- * ```ts
11
- * import { requestID } from 'tezx';
12
- *
13
- * app.use(requestID());
14
- * ```
15
- */
16
- export declare const requestID: (headerName?: string) => Middleware;
@@ -1,11 +0,0 @@
1
- import { generateID } from "../helper";
2
- export const requestID = (headerName = "X-Request-ID") => {
3
- return (ctx, next) => {
4
- const existingID = ctx.headers?.get(headerName.toLowerCase()) ||
5
- ctx.headers?.get(headerName);
6
- const requestId = existingID || `req-${generateID()}`;
7
- ctx.state.set("requestID", requestId);
8
- ctx.header(headerName, requestId);
9
- return next();
10
- };
11
- };
@@ -1,53 +0,0 @@
1
- import { Middleware } from "../router";
2
- type SanitizeHeadersOptions = {
3
- /**
4
- * 🟢 Whitelist of allowed headers (case-insensitive)
5
- * @default [] (allow all if empty)
6
- * @example
7
- * whitelist: ['content-type', 'authorization'] // Only allow these headers
8
- */
9
- whitelist?: string[];
10
- /**
11
- * 🔴 Blacklist of disallowed headers (case-insensitive)
12
- * @default [] (block none if empty)
13
- * @example
14
- * blacklist: ['x-powered-by', 'server'] // Block server info headers
15
- */
16
- blacklist?: string[];
17
- /**
18
- * 🔵 Normalize header keys to lowercase
19
- * @default true
20
- * @example
21
- * normalizeKeys: false // Preserve original header case
22
- */
23
- normalizeKeys?: boolean;
24
- /**
25
- * 🟠 Allow potentially unsafe characters in header values
26
- * @default false
27
- * @warning Enabling this may reduce security
28
- * @example
29
- * allowUnsafeCharacters: true // Allow CR/LF in headers
30
- */
31
- allowUnsafeCharacters?: boolean;
32
- };
33
- /**
34
- * 🧼 Middleware to sanitize HTTP headers for security and compliance
35
- *
36
- * Removes dangerous headers, enforces allow/block lists, and normalizes headers.
37
- * Protects against header injection and information leakage.
38
- *
39
- * @param {SanitizeHeadersOptions} [options={}] - Configuration options
40
- * @returns {Middleware} Middleware function
41
- *
42
- * @example
43
- * // Basic usage with defaults
44
- * app.use(sanitizeHeaders());
45
- *
46
- * // Strict configuration
47
- * app.use(sanitizeHeaders({
48
- * whitelist: ['accept', 'content-type'],
49
- * normalizeKeys: true
50
- * }));
51
- */
52
- export declare const sanitizeHeaders: (options?: SanitizeHeadersOptions) => Middleware;
53
- export {};
@@ -1,47 +0,0 @@
1
- import { GlobalConfig } from "../config/config";
2
- export const sanitizeHeaders = (options = {}) => {
3
- const { whitelist = [], blacklist = [], normalizeKeys = true, allowUnsafeCharacters = false, } = options;
4
- return async (ctx, next) => {
5
- const sanitizedHeaders = new Map();
6
- for (const [key, values] of ctx.headers.entries()) {
7
- if (!Array.isArray(values) || values.length === 0) {
8
- continue;
9
- }
10
- const normalizedKey = normalizeKeys ? key.toLowerCase() : key;
11
- if (whitelist.length > 0 &&
12
- !whitelist.some((r) => r?.toLowerCase() === normalizedKey)) {
13
- GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" not in whitelist - removed`);
14
- continue;
15
- }
16
- if (blacklist.some((r) => r.toLowerCase() === normalizedKey)) {
17
- GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" in blacklist - removed`);
18
- continue;
19
- }
20
- if (!isValidHeaderName(normalizedKey)) {
21
- GlobalConfig.debugging.warn(`⚠️ Invalid header name: "${normalizedKey}" - removed`);
22
- continue;
23
- }
24
- const sanitizedValues = values
25
- .map((value) => sanitizeHeaderValue(value, allowUnsafeCharacters))
26
- .filter(Boolean);
27
- if (sanitizedValues.length === 0) {
28
- GlobalConfig.debugging.warn(`⚠️ All values for "${normalizedKey}" invalid - removed`);
29
- continue;
30
- }
31
- sanitizedHeaders.set(normalizedKey, sanitizedValues);
32
- }
33
- ctx.headers.clear().add([...sanitizedHeaders.entries()]);
34
- return await next();
35
- };
36
- };
37
- const isValidHeaderName = (name) => {
38
- const HEADER_NAME_REGEX = /^[a-zA-Z0-9\-_]+$/;
39
- return HEADER_NAME_REGEX.test(name);
40
- };
41
- const sanitizeHeaderValue = (value, allowUnsafeCharacters) => {
42
- let sanitized = value.trim();
43
- if (!allowUnsafeCharacters) {
44
- sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, "");
45
- }
46
- return sanitized;
47
- };