@zudojs/security 0.0.1
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 +37 -0
- package/dist/body/body.core.d.ts +51 -0
- package/dist/body/body.core.d.ts.map +1 -0
- package/dist/body/body.core.js +120 -0
- package/dist/body/body.core.js.map +1 -0
- package/dist/body/index.d.ts +5 -0
- package/dist/body/index.d.ts.map +1 -0
- package/dist/body/index.js +5 -0
- package/dist/body/index.js.map +1 -0
- package/dist/cookie/cookie.core.d.ts +58 -0
- package/dist/cookie/cookie.core.d.ts.map +1 -0
- package/dist/cookie/cookie.core.js +163 -0
- package/dist/cookie/cookie.core.js.map +1 -0
- package/dist/cookie/index.d.ts +5 -0
- package/dist/cookie/index.d.ts.map +1 -0
- package/dist/cookie/index.js +5 -0
- package/dist/cookie/index.js.map +1 -0
- package/dist/cors/cors.core.d.ts +58 -0
- package/dist/cors/cors.core.d.ts.map +1 -0
- package/dist/cors/cors.core.js +131 -0
- package/dist/cors/cors.core.js.map +1 -0
- package/dist/cors/cors.namespace.d.ts +15 -0
- package/dist/cors/cors.namespace.d.ts.map +1 -0
- package/dist/cors/cors.namespace.js +14 -0
- package/dist/cors/cors.namespace.js.map +1 -0
- package/dist/cors/index.d.ts +6 -0
- package/dist/cors/index.d.ts.map +1 -0
- package/dist/cors/index.js +5 -0
- package/dist/cors/index.js.map +1 -0
- package/dist/csrf/csrf.core.d.ts +56 -0
- package/dist/csrf/csrf.core.d.ts.map +1 -0
- package/dist/csrf/csrf.core.js +149 -0
- package/dist/csrf/csrf.core.js.map +1 -0
- package/dist/csrf/index.d.ts +5 -0
- package/dist/csrf/index.d.ts.map +1 -0
- package/dist/csrf/index.js +5 -0
- package/dist/csrf/index.js.map +1 -0
- package/dist/header/header.core.d.ts +47 -0
- package/dist/header/header.core.d.ts.map +1 -0
- package/dist/header/header.core.js +158 -0
- package/dist/header/header.core.js.map +1 -0
- package/dist/header/index.d.ts +5 -0
- package/dist/header/index.d.ts.map +1 -0
- package/dist/header/index.js +5 -0
- package/dist/header/index.js.map +1 -0
- package/dist/headers/headers.core.d.ts +48 -0
- package/dist/headers/headers.core.d.ts.map +1 -0
- package/dist/headers/headers.core.js +124 -0
- package/dist/headers/headers.core.js.map +1 -0
- package/dist/headers/index.d.ts +6 -0
- package/dist/headers/index.d.ts.map +1 -0
- package/dist/headers/index.js +6 -0
- package/dist/headers/index.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/input/index.d.ts +5 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +5 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/input.core.d.ts +73 -0
- package/dist/input/input.core.d.ts.map +1 -0
- package/dist/input/input.core.js +182 -0
- package/dist/input/input.core.js.map +1 -0
- package/dist/rateLimit/index.d.ts +5 -0
- package/dist/rateLimit/index.d.ts.map +1 -0
- package/dist/rateLimit/index.js +5 -0
- package/dist/rateLimit/index.js.map +1 -0
- package/dist/rateLimit/rateLimit.core.d.ts +51 -0
- package/dist/rateLimit/rateLimit.core.d.ts.map +1 -0
- package/dist/rateLimit/rateLimit.core.js +175 -0
- package/dist/rateLimit/rateLimit.core.js.map +1 -0
- package/dist/rateLimit/rateLimit.namespace.d.ts +13 -0
- package/dist/rateLimit/rateLimit.namespace.d.ts.map +1 -0
- package/dist/rateLimit/rateLimit.namespace.js +13 -0
- package/dist/rateLimit/rateLimit.namespace.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/security.type.d.ts +203 -0
- package/dist/types/security.type.d.ts.map +1 -0
- package/dist/types/security.type.js +31 -0
- package/dist/types/security.type.js.map +1 -0
- package/dist/url/index.d.ts +5 -0
- package/dist/url/index.d.ts.map +1 -0
- package/dist/url/index.js +5 -0
- package/dist/url/index.js.map +1 -0
- package/dist/url/url.core.d.ts +38 -0
- package/dist/url/url.core.d.ts.map +1 -0
- package/dist/url/url.core.js +187 -0
- package/dist/url/url.core.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CORS
|
|
3
|
+
*
|
|
4
|
+
* Validates and generates CORS headers for cross-origin requests.
|
|
5
|
+
*/
|
|
6
|
+
/** Default CORS configuration (restrictive). */
|
|
7
|
+
const DEFAULT_CORS_CONFIG = {
|
|
8
|
+
origin: undefined,
|
|
9
|
+
methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
|
|
10
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
11
|
+
exposedHeaders: [],
|
|
12
|
+
credentials: false,
|
|
13
|
+
maxAge: 86400,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a request origin is allowed.
|
|
17
|
+
*
|
|
18
|
+
* @param origin - The request Origin header value.
|
|
19
|
+
* @param config - CORS configuration.
|
|
20
|
+
* @returns The allowed origin value, or undefined if not allowed.
|
|
21
|
+
*/
|
|
22
|
+
export function isOriginAllowed(origin, config) {
|
|
23
|
+
if (!origin) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const allowedOrigin = config.origin;
|
|
27
|
+
if (allowedOrigin === undefined) {
|
|
28
|
+
// No origin configuration = no CORS headers
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
// Function check
|
|
32
|
+
if (typeof allowedOrigin === "function") {
|
|
33
|
+
return allowedOrigin(origin) ? origin : undefined;
|
|
34
|
+
}
|
|
35
|
+
// Regex check
|
|
36
|
+
if (allowedOrigin instanceof RegExp) {
|
|
37
|
+
return allowedOrigin.test(origin) ? origin : undefined;
|
|
38
|
+
}
|
|
39
|
+
// String check
|
|
40
|
+
if (typeof allowedOrigin === "string") {
|
|
41
|
+
if (allowedOrigin === "*") {
|
|
42
|
+
return "*";
|
|
43
|
+
}
|
|
44
|
+
return allowedOrigin === origin ? origin : undefined;
|
|
45
|
+
}
|
|
46
|
+
// Array check
|
|
47
|
+
if (Array.isArray(allowedOrigin)) {
|
|
48
|
+
if (allowedOrigin.includes(origin)) {
|
|
49
|
+
return origin;
|
|
50
|
+
}
|
|
51
|
+
// Check for wildcard in array
|
|
52
|
+
if (allowedOrigin.includes("*")) {
|
|
53
|
+
return "*";
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Generates CORS headers for a preflight request.
|
|
61
|
+
*
|
|
62
|
+
* @param requestOrigin - The request Origin header.
|
|
63
|
+
* @param config - CORS configuration.
|
|
64
|
+
* @returns CORS headers to set on the response.
|
|
65
|
+
*/
|
|
66
|
+
export function generatePreflightHeaders(requestOrigin, config) {
|
|
67
|
+
const headers = {};
|
|
68
|
+
const allowedOrigin = isOriginAllowed(requestOrigin, config);
|
|
69
|
+
if (allowedOrigin) {
|
|
70
|
+
headers["Access-Control-Allow-Origin"] = allowedOrigin;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// No matching origin — don't set CORS headers
|
|
74
|
+
return headers;
|
|
75
|
+
}
|
|
76
|
+
const methods = config.methods ?? DEFAULT_CORS_CONFIG.methods;
|
|
77
|
+
headers["Access-Control-Allow-Methods"] = methods.join(", ");
|
|
78
|
+
const allowedHeaders = config.allowedHeaders ?? DEFAULT_CORS_CONFIG.allowedHeaders;
|
|
79
|
+
headers["Access-Control-Allow-Headers"] = allowedHeaders.join(", ");
|
|
80
|
+
if (config.credentials) {
|
|
81
|
+
headers["Access-Control-Allow-Credentials"] = "true";
|
|
82
|
+
}
|
|
83
|
+
const maxAge = config.maxAge ?? DEFAULT_CORS_CONFIG.maxAge;
|
|
84
|
+
headers["Access-Control-Max-Age"] = String(maxAge);
|
|
85
|
+
return headers;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generates CORS headers for a simple request.
|
|
89
|
+
*
|
|
90
|
+
* @param requestOrigin - The request Origin header.
|
|
91
|
+
* @param config - CORS configuration.
|
|
92
|
+
* @returns CORS headers to set on the response.
|
|
93
|
+
*/
|
|
94
|
+
export function generateSimpleHeaders(requestOrigin, config) {
|
|
95
|
+
const headers = {};
|
|
96
|
+
const allowedOrigin = isOriginAllowed(requestOrigin, config);
|
|
97
|
+
if (allowedOrigin) {
|
|
98
|
+
headers["Access-Control-Allow-Origin"] = allowedOrigin;
|
|
99
|
+
}
|
|
100
|
+
if (config.credentials) {
|
|
101
|
+
headers["Access-Control-Allow-Credentials"] = "true";
|
|
102
|
+
}
|
|
103
|
+
const exposedHeaders = config.exposedHeaders;
|
|
104
|
+
if (exposedHeaders && exposedHeaders.length > 0) {
|
|
105
|
+
headers["Access-Control-Expose-Headers"] = exposedHeaders.join(", ");
|
|
106
|
+
}
|
|
107
|
+
return headers;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validates that a requested method is allowed.
|
|
111
|
+
*
|
|
112
|
+
* @param method - The HTTP method from Access-Control-Request-Method.
|
|
113
|
+
* @param config - CORS configuration.
|
|
114
|
+
* @returns True if the method is allowed.
|
|
115
|
+
*/
|
|
116
|
+
export function isMethodAllowed(method, config) {
|
|
117
|
+
const allowedMethods = config.methods ?? DEFAULT_CORS_CONFIG.methods;
|
|
118
|
+
return allowedMethods.includes(method.toUpperCase());
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validates that all requested headers are allowed.
|
|
122
|
+
*
|
|
123
|
+
* @param headers - Headers from Access-Control-Request-Headers.
|
|
124
|
+
* @param config - CORS configuration.
|
|
125
|
+
* @returns An array of disallowed headers, or empty array if all allowed.
|
|
126
|
+
*/
|
|
127
|
+
export function getDisallowedHeaders(headers, config) {
|
|
128
|
+
const allowedHeaders = new Set((config.allowedHeaders ?? DEFAULT_CORS_CONFIG.allowedHeaders).map((h) => h.toLowerCase()));
|
|
129
|
+
return headers.filter((h) => !allowedHeaders.has(h.toLowerCase()));
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=cors.core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.core.js","sourceRoot":"","sources":["../../src/cors/cors.core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CORS Namespace
|
|
3
|
+
*
|
|
4
|
+
* Convenience namespace for CORS utilities.
|
|
5
|
+
*/
|
|
6
|
+
import { isOriginAllowed, generatePreflightHeaders, generateSimpleHeaders, isMethodAllowed, getDisallowedHeaders } from "./cors.core.js";
|
|
7
|
+
export type { CorsHeaders } from "./cors.core.js";
|
|
8
|
+
export declare const cors: {
|
|
9
|
+
isOriginAllowed: typeof isOriginAllowed;
|
|
10
|
+
generatePreflightHeaders: typeof generatePreflightHeaders;
|
|
11
|
+
generateSimpleHeaders: typeof generateSimpleHeaders;
|
|
12
|
+
isMethodAllowed: typeof isMethodAllowed;
|
|
13
|
+
getDisallowedHeaders: typeof getDisallowedHeaders;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=cors.namespace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.namespace.d.ts","sourceRoot":"","sources":["../../src/cors/cors.namespace.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,eAAO,MAAM,IAAI;;;;;;CAMhB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CORS Namespace
|
|
3
|
+
*
|
|
4
|
+
* Convenience namespace for CORS utilities.
|
|
5
|
+
*/
|
|
6
|
+
import { isOriginAllowed, generatePreflightHeaders, generateSimpleHeaders, isMethodAllowed, getDisallowedHeaders, } from "./cors.core.js";
|
|
7
|
+
export const cors = {
|
|
8
|
+
isOriginAllowed,
|
|
9
|
+
generatePreflightHeaders,
|
|
10
|
+
generateSimpleHeaders,
|
|
11
|
+
isMethodAllowed,
|
|
12
|
+
getDisallowedHeaders,
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=cors.namespace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.namespace.js","sourceRoot":"","sources":["../../src/cors/cors.namespace.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,eAAe;IACf,wBAAwB;IACxB,qBAAqB;IACrB,eAAe;IACf,oBAAoB;CACrB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CORS Barrel
|
|
3
|
+
*/
|
|
4
|
+
export type { CorsHeaders } from "./cors.core.js";
|
|
5
|
+
export { isOriginAllowed, generatePreflightHeaders, generateSimpleHeaders, isMethodAllowed, getDisallowedHeaders, } from "./cors.core.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* Generates and validates CSRF tokens for state-changing requests.
|
|
5
|
+
*/
|
|
6
|
+
import type { CsrfConfig } from "../types/security.type.js";
|
|
7
|
+
/**
|
|
8
|
+
* Generates a cryptographically secure CSRF token.
|
|
9
|
+
*
|
|
10
|
+
* @param secret - The secret key for HMAC generation.
|
|
11
|
+
* @param expiration - Optional expiration in seconds (default: 3600).
|
|
12
|
+
* @returns The CSRF token string.
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateCsrfToken(secret: string, expiration?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* Validates a CSRF token.
|
|
17
|
+
*
|
|
18
|
+
* @param token - The CSRF token to validate.
|
|
19
|
+
* @param secret - The secret key for verification.
|
|
20
|
+
* @param expiration - Optional expiration in seconds (default: 3600).
|
|
21
|
+
* @returns True if the token is valid and not expired.
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateCsrfToken(token: string, secret: string, expiration?: number): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Checks if a request method requires CSRF protection.
|
|
26
|
+
*
|
|
27
|
+
* @param method - The HTTP method.
|
|
28
|
+
* @param config - Optional CSRF configuration.
|
|
29
|
+
* @returns True if CSRF protection is required.
|
|
30
|
+
*/
|
|
31
|
+
export declare function requiresCsrfProtection(method: string, config?: CsrfConfig): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extracts the CSRF token from request headers.
|
|
34
|
+
*
|
|
35
|
+
* @param headers - Request headers.
|
|
36
|
+
* @param headerName - The header name to look for.
|
|
37
|
+
* @returns The CSRF token, or undefined.
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractCsrfTokenFromHeaders(headers: Record<string, string | string[] | undefined>, headerName?: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Extracts the CSRF token from cookies.
|
|
42
|
+
*
|
|
43
|
+
* @param cookieHeader - The raw Cookie header.
|
|
44
|
+
* @param cookieName - The cookie name to look for.
|
|
45
|
+
* @returns The CSRF token, or undefined.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractCsrfTokenFromCookies(cookieHeader: string, cookieName?: string): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Generates Set-Cookie header for CSRF token.
|
|
50
|
+
*
|
|
51
|
+
* @param token - The CSRF token to store.
|
|
52
|
+
* @param config - Optional CSRF configuration.
|
|
53
|
+
* @returns The Set-Cookie header value.
|
|
54
|
+
*/
|
|
55
|
+
export declare function generateCsrfCookie(token: string, config?: CsrfConfig): string;
|
|
56
|
+
//# sourceMappingURL=csrf.core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.core.d.ts","sourceRoot":"","sources":["../../src/csrf/csrf.core.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAkB5D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAW7E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CA4CT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAOT;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,EACtD,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAapB;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAcpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAW7E"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — CSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* Generates and validates CSRF tokens for state-changing requests.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
7
|
+
/** Default token expiration (1 hour). */
|
|
8
|
+
const DEFAULT_EXPIRATION = 3600;
|
|
9
|
+
/** Default cookie name for CSRF token. */
|
|
10
|
+
const DEFAULT_COOKIE_NAME = "_csrf";
|
|
11
|
+
/** Default header name for CSRF token. */
|
|
12
|
+
const DEFAULT_HEADER_NAME = "x-csrf-token";
|
|
13
|
+
/** Methods that require CSRF protection. */
|
|
14
|
+
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS", "TRACE"];
|
|
15
|
+
/** Default methods that require CSRF protection. */
|
|
16
|
+
const DEFAULT_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
|
17
|
+
/**
|
|
18
|
+
* Generates a cryptographically secure CSRF token.
|
|
19
|
+
*
|
|
20
|
+
* @param secret - The secret key for HMAC generation.
|
|
21
|
+
* @param expiration - Optional expiration in seconds (default: 3600).
|
|
22
|
+
* @returns The CSRF token string.
|
|
23
|
+
*/
|
|
24
|
+
export function generateCsrfToken(secret, expiration) {
|
|
25
|
+
const ttl = expiration ?? DEFAULT_EXPIRATION;
|
|
26
|
+
const expiresAt = Math.floor(Date.now() / 1000) + ttl;
|
|
27
|
+
const random = randomBytes(16).toString("hex");
|
|
28
|
+
const payload = `${expiresAt}:${random}`;
|
|
29
|
+
const signature = createHash("sha256")
|
|
30
|
+
.update(`${payload}:${secret}`)
|
|
31
|
+
.digest("hex")
|
|
32
|
+
.slice(0, 16);
|
|
33
|
+
return `${payload}:${signature}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates a CSRF token.
|
|
37
|
+
*
|
|
38
|
+
* @param token - The CSRF token to validate.
|
|
39
|
+
* @param secret - The secret key for verification.
|
|
40
|
+
* @param expiration - Optional expiration in seconds (default: 3600).
|
|
41
|
+
* @returns True if the token is valid and not expired.
|
|
42
|
+
*/
|
|
43
|
+
export function validateCsrfToken(token, secret, expiration) {
|
|
44
|
+
const ttl = expiration ?? DEFAULT_EXPIRATION;
|
|
45
|
+
const parts = token.split(":");
|
|
46
|
+
if (parts.length !== 3) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const [expiresAtStr, random, providedSignature] = parts;
|
|
50
|
+
if (!expiresAtStr || !random || !providedSignature) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const expiresAt = parseInt(expiresAtStr, 10);
|
|
54
|
+
if (isNaN(expiresAt)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Check expiration
|
|
58
|
+
const now = Math.floor(Date.now() / 1000);
|
|
59
|
+
if (now > expiresAt) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// Verify signature
|
|
63
|
+
const payload = `${expiresAt}:${random}`;
|
|
64
|
+
const expectedSignature = createHash("sha256")
|
|
65
|
+
.update(`${payload}:${secret}`)
|
|
66
|
+
.digest("hex")
|
|
67
|
+
.slice(0, 16);
|
|
68
|
+
// Constant-time comparison
|
|
69
|
+
if (providedSignature.length !== expectedSignature.length) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
let result = 0;
|
|
73
|
+
for (let i = 0; i < providedSignature.length; i++) {
|
|
74
|
+
result |= providedSignature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);
|
|
75
|
+
}
|
|
76
|
+
return result === 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Checks if a request method requires CSRF protection.
|
|
80
|
+
*
|
|
81
|
+
* @param method - The HTTP method.
|
|
82
|
+
* @param config - Optional CSRF configuration.
|
|
83
|
+
* @returns True if CSRF protection is required.
|
|
84
|
+
*/
|
|
85
|
+
export function requiresCsrfProtection(method, config) {
|
|
86
|
+
if (SAFE_METHODS.includes(method.toUpperCase())) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const methods = config?.methods ?? DEFAULT_METHODS;
|
|
90
|
+
return methods.includes(method.toUpperCase());
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extracts the CSRF token from request headers.
|
|
94
|
+
*
|
|
95
|
+
* @param headers - Request headers.
|
|
96
|
+
* @param headerName - The header name to look for.
|
|
97
|
+
* @returns The CSRF token, or undefined.
|
|
98
|
+
*/
|
|
99
|
+
export function extractCsrfTokenFromHeaders(headers, headerName) {
|
|
100
|
+
const name = headerName ?? DEFAULT_HEADER_NAME;
|
|
101
|
+
const value = headers[name];
|
|
102
|
+
if (typeof value === "string") {
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
106
|
+
return value[0];
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extracts the CSRF token from cookies.
|
|
112
|
+
*
|
|
113
|
+
* @param cookieHeader - The raw Cookie header.
|
|
114
|
+
* @param cookieName - The cookie name to look for.
|
|
115
|
+
* @returns The CSRF token, or undefined.
|
|
116
|
+
*/
|
|
117
|
+
export function extractCsrfTokenFromCookies(cookieHeader, cookieName) {
|
|
118
|
+
const name = cookieName ?? DEFAULT_COOKIE_NAME;
|
|
119
|
+
const cookies = cookieHeader.split(";").map((pair) => {
|
|
120
|
+
const eqIndex = pair.indexOf("=");
|
|
121
|
+
if (eqIndex === -1)
|
|
122
|
+
return { name: pair.trim(), value: "" };
|
|
123
|
+
return {
|
|
124
|
+
name: pair.slice(0, eqIndex).trim(),
|
|
125
|
+
value: pair.slice(eqIndex + 1).trim(),
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
const cookie = cookies.find((c) => c.name === name);
|
|
129
|
+
return cookie?.value || undefined;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Generates Set-Cookie header for CSRF token.
|
|
133
|
+
*
|
|
134
|
+
* @param token - The CSRF token to store.
|
|
135
|
+
* @param config - Optional CSRF configuration.
|
|
136
|
+
* @returns The Set-Cookie header value.
|
|
137
|
+
*/
|
|
138
|
+
export function generateCsrfCookie(token, config) {
|
|
139
|
+
const name = config?.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
140
|
+
const ttl = config?.expiration ?? DEFAULT_EXPIRATION;
|
|
141
|
+
return [
|
|
142
|
+
`${name}=${token}`,
|
|
143
|
+
"Path=/",
|
|
144
|
+
"HttpOnly",
|
|
145
|
+
"SameSite=Strict",
|
|
146
|
+
`Max-Age=${ttl}`,
|
|
147
|
+
].join("; ");
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=csrf.core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf.core.js","sourceRoot":"","sources":["../../src/csrf/csrf.core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/csrf/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/csrf/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — Header Security
|
|
3
|
+
*
|
|
4
|
+
* Validates and sanitizes HTTP headers against injection attacks,
|
|
5
|
+
* size limits, and blocked header names.
|
|
6
|
+
*/
|
|
7
|
+
import type { HeaderSecurityConfig, HeaderValidationResult } from "../types/security.type.js";
|
|
8
|
+
/**
|
|
9
|
+
* Validates a single header name.
|
|
10
|
+
*
|
|
11
|
+
* @param name - The header name to validate.
|
|
12
|
+
* @returns An error message if invalid, or undefined.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateHeaderName(name: string): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Validates a single header value.
|
|
17
|
+
*
|
|
18
|
+
* @param name - The header name (for context in error messages).
|
|
19
|
+
* @param value - The header value to validate.
|
|
20
|
+
* @param config - Optional security configuration.
|
|
21
|
+
* @returns An error message if invalid, or undefined.
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateHeaderValue(name: string, value: string, config?: HeaderSecurityConfig): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Validates all headers against security configuration.
|
|
26
|
+
*
|
|
27
|
+
* @param headers - Record of header name-value pairs.
|
|
28
|
+
* @param config - Optional security configuration.
|
|
29
|
+
* @returns Validation result with any errors found.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateHeaders(headers: Record<string, string | string[] | undefined>, config?: HeaderSecurityConfig): HeaderValidationResult;
|
|
32
|
+
/**
|
|
33
|
+
* Sanitizes a header value by stripping dangerous characters.
|
|
34
|
+
* Returns undefined if the value is completely empty after sanitization.
|
|
35
|
+
*
|
|
36
|
+
* @param value - The header value to sanitize.
|
|
37
|
+
* @returns The sanitized value, or undefined if empty.
|
|
38
|
+
*/
|
|
39
|
+
export declare function sanitizeHeaderValue(value: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Checks if a header is a hop-by-hop header that should not be forwarded.
|
|
42
|
+
*
|
|
43
|
+
* @param name - The header name.
|
|
44
|
+
* @returns True if the header is hop-by-hop.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isHopByHopHeader(name: string): boolean;
|
|
47
|
+
//# sourceMappingURL=header.core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.core.d.ts","sourceRoot":"","sources":["../../src/header/header.core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;AA6BnC;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBnE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,oBAAoB,GAC5B,MAAM,GAAG,SAAS,CAoBpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,EACtD,MAAM,CAAC,EAAE,oBAAoB,GAC5B,sBAAsB,CA+DxB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQrE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYtD"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/security — Header Security
|
|
3
|
+
*
|
|
4
|
+
* Validates and sanitizes HTTP headers against injection attacks,
|
|
5
|
+
* size limits, and blocked header names.
|
|
6
|
+
*/
|
|
7
|
+
/** Default maximum header value size (8KB). */
|
|
8
|
+
const DEFAULT_MAX_VALUE_SIZE = 8192;
|
|
9
|
+
/** Default maximum number of headers. */
|
|
10
|
+
const DEFAULT_MAX_HEADERS = 50;
|
|
11
|
+
/** Default maximum total header size (64KB). */
|
|
12
|
+
const DEFAULT_MAX_TOTAL_SIZE = 65536;
|
|
13
|
+
/** Headers that are commonly blocked for security. */
|
|
14
|
+
const DEFAULT_BLOCKED_HEADERS = [
|
|
15
|
+
"x-forwarded-for",
|
|
16
|
+
"x-forwarded-host",
|
|
17
|
+
"x-forwarded-proto",
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* CRLF injection pattern — matches carriage return or line feed.
|
|
21
|
+
* These characters must never appear in header values.
|
|
22
|
+
*/
|
|
23
|
+
const CRLF_PATTERN = /[\r\n]/;
|
|
24
|
+
/**
|
|
25
|
+
* Null byte pattern.
|
|
26
|
+
*/
|
|
27
|
+
const NULL_BYTE_PATTERN = /\x00/;
|
|
28
|
+
/**
|
|
29
|
+
* Validates a single header name.
|
|
30
|
+
*
|
|
31
|
+
* @param name - The header name to validate.
|
|
32
|
+
* @returns An error message if invalid, or undefined.
|
|
33
|
+
*/
|
|
34
|
+
export function validateHeaderName(name) {
|
|
35
|
+
if (name.length === 0) {
|
|
36
|
+
return "Header name cannot be empty";
|
|
37
|
+
}
|
|
38
|
+
if (name.length > 256) {
|
|
39
|
+
return `Header name exceeds maximum length of 256: ${name.length}`;
|
|
40
|
+
}
|
|
41
|
+
// RFC 7230: token = 1*tchar
|
|
42
|
+
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
|
43
|
+
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
|
44
|
+
if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) {
|
|
45
|
+
return `Header name contains invalid characters: ${name}`;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validates a single header value.
|
|
51
|
+
*
|
|
52
|
+
* @param name - The header name (for context in error messages).
|
|
53
|
+
* @param value - The header value to validate.
|
|
54
|
+
* @param config - Optional security configuration.
|
|
55
|
+
* @returns An error message if invalid, or undefined.
|
|
56
|
+
*/
|
|
57
|
+
export function validateHeaderValue(name, value, config) {
|
|
58
|
+
const maxValueSize = config?.maxValueSize ?? DEFAULT_MAX_VALUE_SIZE;
|
|
59
|
+
// Check for CRLF injection
|
|
60
|
+
if (CRLF_PATTERN.test(value)) {
|
|
61
|
+
return `Header "${name}" contains CRLF characters (injection risk)`;
|
|
62
|
+
}
|
|
63
|
+
// Check for null bytes
|
|
64
|
+
if (NULL_BYTE_PATTERN.test(value)) {
|
|
65
|
+
return `Header "${name}" contains null bytes`;
|
|
66
|
+
}
|
|
67
|
+
// Check value size
|
|
68
|
+
const byteSize = Buffer.byteLength(value, "utf8");
|
|
69
|
+
if (byteSize > maxValueSize) {
|
|
70
|
+
return `Header "${name}" value size ${byteSize} exceeds maximum ${maxValueSize}`;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validates all headers against security configuration.
|
|
76
|
+
*
|
|
77
|
+
* @param headers - Record of header name-value pairs.
|
|
78
|
+
* @param config - Optional security configuration.
|
|
79
|
+
* @returns Validation result with any errors found.
|
|
80
|
+
*/
|
|
81
|
+
export function validateHeaders(headers, config) {
|
|
82
|
+
const errors = [];
|
|
83
|
+
const maxHeaders = config?.maxHeaders ?? DEFAULT_MAX_HEADERS;
|
|
84
|
+
const maxTotalSize = config?.maxTotalSize ?? DEFAULT_MAX_TOTAL_SIZE;
|
|
85
|
+
const blockedHeaders = new Set((config?.blockedHeaders ?? DEFAULT_BLOCKED_HEADERS).map((h) => h.toLowerCase()));
|
|
86
|
+
const entries = Object.entries(headers).filter(([, v]) => v !== undefined);
|
|
87
|
+
// Check header count
|
|
88
|
+
if (entries.length > maxHeaders) {
|
|
89
|
+
errors.push(`Too many headers: ${entries.length} exceeds maximum ${maxHeaders}`);
|
|
90
|
+
}
|
|
91
|
+
let totalSize = 0;
|
|
92
|
+
for (const [name, value] of entries) {
|
|
93
|
+
// Check blocked headers
|
|
94
|
+
if (blockedHeaders.has(name.toLowerCase())) {
|
|
95
|
+
errors.push(`Header "${name}" is blocked by security policy`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// Validate name
|
|
99
|
+
const nameError = validateHeaderName(name);
|
|
100
|
+
if (nameError) {
|
|
101
|
+
errors.push(nameError);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// Validate values (may be array for Set-Cookie, etc.)
|
|
105
|
+
const values = Array.isArray(value) ? value : [value];
|
|
106
|
+
for (const v of values) {
|
|
107
|
+
const valueError = validateHeaderValue(name, v, config);
|
|
108
|
+
if (valueError) {
|
|
109
|
+
errors.push(valueError);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
totalSize +=
|
|
113
|
+
Buffer.byteLength(name, "utf8") + Buffer.byteLength(v, "utf8");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check total size
|
|
117
|
+
if (totalSize > maxTotalSize) {
|
|
118
|
+
errors.push(`Total header size ${totalSize} exceeds maximum ${maxTotalSize}`);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
valid: errors.length === 0,
|
|
122
|
+
errors,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Sanitizes a header value by stripping dangerous characters.
|
|
127
|
+
* Returns undefined if the value is completely empty after sanitization.
|
|
128
|
+
*
|
|
129
|
+
* @param value - The header value to sanitize.
|
|
130
|
+
* @returns The sanitized value, or undefined if empty.
|
|
131
|
+
*/
|
|
132
|
+
export function sanitizeHeaderValue(value) {
|
|
133
|
+
// Strip null bytes
|
|
134
|
+
let sanitized = value.replace(NULL_BYTE_PATTERN, "");
|
|
135
|
+
// Strip CRLF (strip both \r and \n)
|
|
136
|
+
sanitized = sanitized.replace(/[\r\n]/g, "");
|
|
137
|
+
return sanitized.length > 0 ? sanitized : undefined;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Checks if a header is a hop-by-hop header that should not be forwarded.
|
|
141
|
+
*
|
|
142
|
+
* @param name - The header name.
|
|
143
|
+
* @returns True if the header is hop-by-hop.
|
|
144
|
+
*/
|
|
145
|
+
export function isHopByHopHeader(name) {
|
|
146
|
+
const hopByHop = new Set([
|
|
147
|
+
"connection",
|
|
148
|
+
"keep-alive",
|
|
149
|
+
"proxy-authenticate",
|
|
150
|
+
"proxy-authorization",
|
|
151
|
+
"te",
|
|
152
|
+
"trailer",
|
|
153
|
+
"transfer-encoding",
|
|
154
|
+
"upgrade",
|
|
155
|
+
]);
|
|
156
|
+
return hopByHop.has(name.toLowerCase());
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=header.core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.core.js","sourceRoot":"","sources":["../../src/header/header.core.ts"],"names":[],"mappings":"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"}
|