@holo-js/security 0.3.26 → 0.3.28
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/{chunk-5SCCUNBO.mjs → chunk-26IYURJG.mjs} +19 -26
- package/dist/{chunk-F32JK536.mjs → chunk-PHPA3C6L.mjs} +21 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.mjs +1 -1
- package/dist/next/server.mjs +9 -16
- package/dist/nuxt/server.mjs +6 -7
- package/dist/sveltekit/server.mjs +8 -16
- package/package.json +3 -3
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-WRMZRO77.mjs";
|
|
11
11
|
|
|
12
12
|
// src/csrf.ts
|
|
13
|
+
import { resolveRequestUrl } from "@holo-js/kernel";
|
|
13
14
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
14
15
|
|
|
15
16
|
// src/runtime.ts
|
|
@@ -50,9 +51,22 @@ var securityRuntimeInternals = {
|
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
// src/proxy.ts
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
import { isIP } from "net";
|
|
55
|
+
import { requestUrlInternals } from "@holo-js/kernel";
|
|
56
|
+
function bareClientAddress(value) {
|
|
57
|
+
if (!value) return void 0;
|
|
58
|
+
if (isIP(value)) return value;
|
|
59
|
+
const bracketed = /^\[([^\]]+)\](?::\d+)?$/u.exec(value)?.[1];
|
|
60
|
+
if (bracketed && isIP(bracketed) === 6) return bracketed;
|
|
61
|
+
const ipv4 = /^([\d.]+):\d+$/u.exec(value)?.[1];
|
|
62
|
+
return ipv4 && isIP(ipv4) === 4 ? ipv4 : void 0;
|
|
63
|
+
}
|
|
64
|
+
function resolveTrustedClientIp(request) {
|
|
65
|
+
const trustedProxyHeaders = requestUrlInternals.resolveTrustedProxyHeaders();
|
|
66
|
+
if (trustedProxyHeaders === "forwarded") {
|
|
67
|
+
return bareClientAddress(requestUrlInternals.readForwardedParameter(request.headers, "for")) ?? "unknown";
|
|
68
|
+
}
|
|
69
|
+
return ip(request, trustedProxyHeaders === "x-forwarded");
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
// src/rate-limit.ts
|
|
@@ -93,7 +107,7 @@ async function defaultRateLimitKey(request) {
|
|
|
93
107
|
if (typeof runtimeDefaultKey !== "undefined" && runtimeDefaultKey !== null) {
|
|
94
108
|
return normalizeResolvedLimiterKey(runtimeDefaultKey, "Default rate limiter key resolver");
|
|
95
109
|
}
|
|
96
|
-
return `ip:${
|
|
110
|
+
return `ip:${resolveTrustedClientIp(request)}`;
|
|
97
111
|
}
|
|
98
112
|
async function resolveLimiterKey(name, limiter, options) {
|
|
99
113
|
if (typeof options.key === "string" && options.key.length > 0) {
|
|
@@ -224,28 +238,8 @@ function isExcludedPath(request) {
|
|
|
224
238
|
const pathname = new URL(request.url).pathname;
|
|
225
239
|
return pathname === "/broadcasting/auth" || except.some((pattern) => matchesPathPattern(pathname, pattern));
|
|
226
240
|
}
|
|
227
|
-
function normalizeForwardedValue(value) {
|
|
228
|
-
return value.trim().replace(/^"|"$/g, "").toLowerCase();
|
|
229
|
-
}
|
|
230
|
-
function getForwardedProto(request) {
|
|
231
|
-
const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",", 1)[0]?.trim();
|
|
232
|
-
if (forwardedProto) {
|
|
233
|
-
return normalizeForwardedValue(forwardedProto);
|
|
234
|
-
}
|
|
235
|
-
const forwarded = request.headers.get("forwarded")?.split(",", 1)[0];
|
|
236
|
-
if (!forwarded) {
|
|
237
|
-
return void 0;
|
|
238
|
-
}
|
|
239
|
-
for (const segment of forwarded.split(";")) {
|
|
240
|
-
const [name, value] = segment.split("=", 2);
|
|
241
|
-
if (name?.trim().toLowerCase() === "proto" && value) {
|
|
242
|
-
return normalizeForwardedValue(value);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return void 0;
|
|
246
|
-
}
|
|
247
241
|
function isSecureRequest(request) {
|
|
248
|
-
return
|
|
242
|
+
return resolveRequestUrl(request).protocol === "https:";
|
|
249
243
|
}
|
|
250
244
|
function createCsrfToken() {
|
|
251
245
|
return randomBytes(32).toString("base64url");
|
|
@@ -403,7 +397,6 @@ var csrf = Object.freeze({
|
|
|
403
397
|
var csrfInternals = {
|
|
404
398
|
createCsrfToken,
|
|
405
399
|
generatedTokenCache,
|
|
406
|
-
getForwardedProto,
|
|
407
400
|
getCookieToken,
|
|
408
401
|
getHeaderToken,
|
|
409
402
|
isSecureRequest,
|
|
@@ -3,12 +3,15 @@ import {
|
|
|
3
3
|
getSecurityRuntime,
|
|
4
4
|
isSecureRequest,
|
|
5
5
|
token
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-26IYURJG.mjs";
|
|
7
7
|
import {
|
|
8
8
|
SECURITY_CLIENT_CONFIG_COOKIE,
|
|
9
9
|
createSecurityClientConfig,
|
|
10
10
|
serializeSecurityClientConfig
|
|
11
11
|
} from "./chunk-PC2YCWDH.mjs";
|
|
12
|
+
import {
|
|
13
|
+
SecurityCsrfError
|
|
14
|
+
} from "./chunk-FQ4X7X6L.mjs";
|
|
12
15
|
|
|
13
16
|
// src/cookie-issuance.ts
|
|
14
17
|
async function prepareCsrfCookies(request, getCookie) {
|
|
@@ -40,6 +43,22 @@ async function prepareCsrfCookies(request, getCookie) {
|
|
|
40
43
|
return cookies;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
// src/rejection-response.ts
|
|
47
|
+
import { HoloRequestUrlError } from "@holo-js/kernel";
|
|
48
|
+
function isSecurityRejection(error) {
|
|
49
|
+
return error instanceof SecurityCsrfError || error instanceof HoloRequestUrlError;
|
|
50
|
+
}
|
|
51
|
+
function createSecurityRejectionResponse(error) {
|
|
52
|
+
return new Response(error.message, {
|
|
53
|
+
status: error.status,
|
|
54
|
+
headers: {
|
|
55
|
+
"content-type": "text/plain; charset=utf-8"
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
export {
|
|
44
|
-
prepareCsrfCookies
|
|
61
|
+
prepareCsrfCookies,
|
|
62
|
+
isSecurityRejection,
|
|
63
|
+
createSecurityRejectionResponse
|
|
45
64
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ declare function serializeCookie(name: string, value: string, options?: {
|
|
|
11
11
|
declare function isSafeMethod(method: string): boolean;
|
|
12
12
|
declare function matchesPathPattern$1(pathname: string, pattern: string): boolean;
|
|
13
13
|
declare function isExcludedPath(request: Request): boolean;
|
|
14
|
-
declare function getForwardedProto(request: Request): string | undefined;
|
|
15
14
|
declare function isSecureRequest(request: Request): boolean;
|
|
16
15
|
declare function createCsrfToken(): string;
|
|
17
16
|
declare function getCsrfSigningKey(): string;
|
|
@@ -40,7 +39,6 @@ declare const csrf: Readonly<{
|
|
|
40
39
|
declare const csrfInternals: {
|
|
41
40
|
createCsrfToken: typeof createCsrfToken;
|
|
42
41
|
generatedTokenCache: WeakMap<Request, string>;
|
|
43
|
-
getForwardedProto: typeof getForwardedProto;
|
|
44
42
|
getCookieToken: typeof getCookieToken;
|
|
45
43
|
getHeaderToken: typeof getHeaderToken;
|
|
46
44
|
isSecureRequest: typeof isSecureRequest;
|
package/dist/index.mjs
CHANGED
package/dist/next/server.mjs
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createSecurityRejectionResponse,
|
|
3
|
+
isSecurityRejection,
|
|
2
4
|
prepareCsrfCookies
|
|
3
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-PHPA3C6L.mjs";
|
|
4
6
|
import {
|
|
5
7
|
cors,
|
|
6
8
|
getSecurityRuntime,
|
|
7
9
|
protect
|
|
8
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-26IYURJG.mjs";
|
|
9
11
|
import "../chunk-PC2YCWDH.mjs";
|
|
10
|
-
import
|
|
11
|
-
SecurityCsrfError
|
|
12
|
-
} from "../chunk-FQ4X7X6L.mjs";
|
|
12
|
+
import "../chunk-FQ4X7X6L.mjs";
|
|
13
13
|
import "../chunk-WRMZRO77.mjs";
|
|
14
14
|
|
|
15
15
|
// src/next/server.ts
|
|
16
|
-
function createCsrfErrorResponse(error) {
|
|
17
|
-
return new Response(error.message, {
|
|
18
|
-
status: error.status,
|
|
19
|
-
headers: {
|
|
20
|
-
"content-type": "text/plain; charset=utf-8"
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
16
|
function getRequestCookie(request, name) {
|
|
25
17
|
const cookie = request.cookies?.get(name);
|
|
26
18
|
if (typeof cookie === "string") {
|
|
@@ -74,15 +66,16 @@ function csrfProtection() {
|
|
|
74
66
|
headers: { Allow: "GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE" }
|
|
75
67
|
});
|
|
76
68
|
}
|
|
69
|
+
let response;
|
|
77
70
|
try {
|
|
78
71
|
await protect(await actionRequest(request));
|
|
72
|
+
response = await issueCsrfCookie(request);
|
|
79
73
|
} catch (error) {
|
|
80
|
-
if (error
|
|
81
|
-
return cors.apply(request,
|
|
74
|
+
if (isSecurityRejection(error)) {
|
|
75
|
+
return cors.apply(request, createSecurityRejectionResponse(error));
|
|
82
76
|
}
|
|
83
77
|
throw error;
|
|
84
78
|
}
|
|
85
|
-
const response = await issueCsrfCookie(request);
|
|
86
79
|
if (response) return cors.apply(request, response);
|
|
87
80
|
if ([...cors.headers(request)].length === 0) return void 0;
|
|
88
81
|
const { NextResponse } = await import("next/server.js");
|
package/dist/nuxt/server.mjs
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isSecurityRejection,
|
|
2
3
|
prepareCsrfCookies
|
|
3
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-PHPA3C6L.mjs";
|
|
4
5
|
import {
|
|
5
6
|
cors,
|
|
6
7
|
protect
|
|
7
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-26IYURJG.mjs";
|
|
8
9
|
import "../chunk-PC2YCWDH.mjs";
|
|
9
|
-
import
|
|
10
|
-
SecurityCsrfError
|
|
11
|
-
} from "../chunk-FQ4X7X6L.mjs";
|
|
10
|
+
import "../chunk-FQ4X7X6L.mjs";
|
|
12
11
|
import "../chunk-WRMZRO77.mjs";
|
|
13
12
|
|
|
14
13
|
// src/nuxt/server.ts
|
|
@@ -79,8 +78,9 @@ function csrfProtection() {
|
|
|
79
78
|
cors.headers(request).forEach((value, name) => setResponseHeader(event, name, value));
|
|
80
79
|
try {
|
|
81
80
|
await protect(request);
|
|
81
|
+
await issueCsrfCookie(event, request);
|
|
82
82
|
} catch (error) {
|
|
83
|
-
if (error
|
|
83
|
+
if (isSecurityRejection(error)) {
|
|
84
84
|
throw createError({
|
|
85
85
|
statusCode: error.status,
|
|
86
86
|
statusMessage: error.message,
|
|
@@ -89,7 +89,6 @@ function csrfProtection() {
|
|
|
89
89
|
}
|
|
90
90
|
throw error;
|
|
91
91
|
}
|
|
92
|
-
await issueCsrfCookie(event, request);
|
|
93
92
|
return void 0;
|
|
94
93
|
});
|
|
95
94
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createSecurityRejectionResponse,
|
|
3
|
+
isSecurityRejection,
|
|
2
4
|
prepareCsrfCookies
|
|
3
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-PHPA3C6L.mjs";
|
|
4
6
|
import {
|
|
5
7
|
cors,
|
|
6
8
|
protect
|
|
7
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-26IYURJG.mjs";
|
|
8
10
|
import "../chunk-PC2YCWDH.mjs";
|
|
9
|
-
import
|
|
10
|
-
SecurityCsrfError
|
|
11
|
-
} from "../chunk-FQ4X7X6L.mjs";
|
|
11
|
+
import "../chunk-FQ4X7X6L.mjs";
|
|
12
12
|
import "../chunk-WRMZRO77.mjs";
|
|
13
13
|
|
|
14
14
|
// src/sveltekit/server.ts
|
|
@@ -18,14 +18,6 @@ async function issueCsrfCookie(event) {
|
|
|
18
18
|
event.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
function createCsrfErrorResponse(error) {
|
|
22
|
-
return new Response(error.message, {
|
|
23
|
-
status: error.status,
|
|
24
|
-
headers: {
|
|
25
|
-
"content-type": "text/plain; charset=utf-8"
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
21
|
function csrfProtection() {
|
|
30
22
|
return async ({ event, resolve }) => {
|
|
31
23
|
const preflight = cors.preflight(event.request);
|
|
@@ -38,13 +30,13 @@ function csrfProtection() {
|
|
|
38
30
|
}
|
|
39
31
|
try {
|
|
40
32
|
await protect(event.request);
|
|
33
|
+
await issueCsrfCookie(event);
|
|
41
34
|
} catch (error) {
|
|
42
|
-
if (error
|
|
43
|
-
return cors.apply(event.request,
|
|
35
|
+
if (isSecurityRejection(error)) {
|
|
36
|
+
return cors.apply(event.request, createSecurityRejectionResponse(error));
|
|
44
37
|
}
|
|
45
38
|
throw error;
|
|
46
39
|
}
|
|
47
|
-
await issueCsrfCookie(event);
|
|
48
40
|
return cors.apply(event.request, await resolve(event));
|
|
49
41
|
};
|
|
50
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/security",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "Holo-JS Framework - CSRF and rate-limit contracts, config, and runtime seams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"test": "vitest --run --coverage"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@holo-js/config": "^0.3.
|
|
62
|
-
"@holo-js/kernel": "^0.3.
|
|
61
|
+
"@holo-js/config": "^0.3.28",
|
|
62
|
+
"@holo-js/kernel": "^0.3.28"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"h3": "^1.15.11",
|