@holo-js/security 0.3.28 → 0.4.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/dist/.holo-build-fingerprint +1 -0
- package/dist/{chunk-WRMZRO77.mjs → chunk-D3ZN5MLJ.mjs} +112 -6
- package/dist/{chunk-PC2YCWDH.mjs → chunk-EINZXLC2.mjs} +7 -5
- package/dist/{chunk-PHPA3C6L.mjs → chunk-EN2H3TN3.mjs} +14 -15
- package/dist/{chunk-26IYURJG.mjs → chunk-RYQLS2OO.mjs} +428 -211
- package/dist/{chunk-FQ4X7X6L.mjs → chunk-U25HSHLJ.mjs} +4 -16
- package/dist/client.d.ts +1 -1
- package/dist/client.mjs +2 -2
- package/dist/config-BK6q6Yh9.d.ts +145 -0
- package/dist/config.d.ts +2 -136
- package/dist/config.mjs +1 -1
- package/dist/contracts.d.ts +15 -4
- package/dist/contracts.mjs +2 -4
- package/dist/drivers/redis-adapter.d.ts +3 -8
- package/dist/drivers/redis-adapter.mjs +13 -29
- package/dist/index.d.ts +24 -74
- package/dist/index.mjs +5 -6
- package/dist/next/server.d.ts +2 -1
- package/dist/next/server.mjs +28 -10
- package/dist/nuxt/server.mjs +50 -14
- package/dist/sveltekit/server.d.ts +2 -1
- package/dist/sveltekit/server.mjs +12 -6
- package/package.json +19 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { NormalizedHoloCorsConfig, NormalizedSecurityLimiterConfig, HoloSecurityConfig, NormalizedHoloSecurityConfig, SecurityRateLimitKeyResolver, SecurityLimiterConfig } from './config.js';
|
|
2
|
-
export { HoloCorsConfig, HoloSecurityCsrfConfig, HoloSecurityRateLimitConfig, NormalizedHoloSecurityCsrfConfig, NormalizedHoloSecurityRateLimitConfig, SecurityRateLimitContext, SecurityRateLimitDriver, SecurityRateLimitFileConfig, SecurityRateLimitMemoryConfig, SecurityRateLimitRedisConfig, defineCorsConfig, defineSecurityConfig, holoCorsDefaults, holoSecurityDefaults, normalizeCorsConfig, normalizeSecurityConfig } from './config.js';
|
|
3
|
-
import { SecurityCsrfField, SecurityCsrfInput, SecurityProtectOptions, SecurityClearRateLimitOptions, SecurityRateLimitCallOptions, SecurityRateLimitHitResult, SecurityRateLimitStore, SecurityRateLimitStoreFactoryOptions, SecurityRateLimitRedisDriverAdapter, SecurityRuntimeBindings, SecurityRuntimeFacade
|
|
4
|
-
export { SecurityClientConfig, SecurityCorsFacade, SecurityCsrfError, SecurityCsrfFacade, SecurityDefaultRateLimitKeyResolver, SecurityRateLimitBucketSnapshot, SecurityRateLimitError, createFileRateLimitStoreConfig, createMemoryRateLimitStoreConfig, createRedisRateLimitStoreConfig, defineRateLimiter, defineSecurityRuntimeBindings, limit, securityInternals } from './contracts.js';
|
|
1
|
+
import { a as NormalizedHoloCorsConfig, b as NormalizedSecurityLimiterConfig, H as HoloSecurityConfig, c as NormalizedHoloSecurityConfig, S as SecurityRateLimitKeyResolver, d as SecurityLimiterConfig } from './config-BK6q6Yh9.js';
|
|
2
|
+
export { e as HoloCorsConfig, f as HoloSecurityCsrfConfig, g as HoloSecurityRateLimitConfig, h as NormalizedHoloSecurityCsrfConfig, i as NormalizedHoloSecurityRateLimitConfig, j as SecurityRateLimitContext, k as SecurityRateLimitDriver, l as SecurityRateLimitFileConfig, m as SecurityRateLimitMemoryConfig, n as SecurityRateLimitRedisConfig, o as SecurityTrustedProxies, p as defineCorsConfig, q as defineSecurityConfig, r as holoCorsDefaults, s as holoSecurityDefaults, t as normalizeCorsConfig, u as normalizeSecurityConfig } from './config-BK6q6Yh9.js';
|
|
3
|
+
import { SecurityCsrfField, SecurityCsrfInput, SecurityProtectOptions, SecurityClearRateLimitOptions, SecurityRateLimitCallOptions, SecurityRateLimitHitResult, SecurityRateLimitStore, SecurityRateLimitStoreFactoryOptions, SecurityRateLimitRedisDriverAdapter, SecurityRuntimeBindings, SecurityRuntimeFacade } from './contracts.js';
|
|
4
|
+
export { SecurityClientConfig, SecurityCorsFacade, SecurityCsrfError, SecurityCsrfFacade, SecurityCsrfSessionAccessor, SecurityDefaultRateLimitKeyResolver, SecurityRateLimitBucketSnapshot, SecurityRateLimitError, createFileRateLimitStoreConfig, createMemoryRateLimitStoreConfig, createRedisRateLimitStoreConfig, defineRateLimiter, defineSecurityRuntimeBindings, limit, securityInternals } from './contracts.js';
|
|
5
5
|
import '@holo-js/kernel';
|
|
6
6
|
|
|
7
|
-
declare function
|
|
7
|
+
declare function ip(request: Request): string;
|
|
8
|
+
|
|
8
9
|
declare function serializeCookie(name: string, value: string, options?: {
|
|
9
10
|
readonly secure?: boolean;
|
|
10
11
|
}): string;
|
|
@@ -13,16 +14,15 @@ declare function matchesPathPattern$1(pathname: string, pattern: string): boolea
|
|
|
13
14
|
declare function isExcludedPath(request: Request): boolean;
|
|
14
15
|
declare function isSecureRequest(request: Request): boolean;
|
|
15
16
|
declare function createCsrfToken(): string;
|
|
16
|
-
declare function
|
|
17
|
-
declare function
|
|
18
|
-
declare function decodeCsrfToken(token: string): {
|
|
19
|
-
readonly nonce: string;
|
|
20
|
-
readonly signature: string;
|
|
21
|
-
} | null;
|
|
22
|
-
declare function isValidSignedCsrfToken(token: string): boolean;
|
|
23
|
-
declare function getCookieToken(request: Request): string | undefined;
|
|
17
|
+
declare function encryptCsrfToken(token: string): string;
|
|
18
|
+
declare function decryptCsrfToken(value: string): string | undefined;
|
|
24
19
|
declare function getHeaderToken(request: Request): string | undefined;
|
|
25
|
-
declare function
|
|
20
|
+
declare function getXsrfHeaderToken(request: Request): string | undefined;
|
|
21
|
+
declare function readInputToken(request: Request): Promise<string | undefined>;
|
|
22
|
+
declare function ensureSessionToken(request: Request): Promise<{
|
|
23
|
+
readonly token: string;
|
|
24
|
+
readonly sessionCookies: readonly string[];
|
|
25
|
+
}>;
|
|
26
26
|
declare function token(request: Request): Promise<string>;
|
|
27
27
|
declare function field(request: Request): Promise<SecurityCsrfField>;
|
|
28
28
|
declare function input(request: Request): Promise<SecurityCsrfInput>;
|
|
@@ -36,22 +36,22 @@ declare const csrf: Readonly<{
|
|
|
36
36
|
cookie: typeof cookie;
|
|
37
37
|
verify: typeof verify;
|
|
38
38
|
}>;
|
|
39
|
+
declare function rememberSessionToken(request: Request, token: string): void;
|
|
39
40
|
declare const csrfInternals: {
|
|
40
41
|
createCsrfToken: typeof createCsrfToken;
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
rememberSessionToken: typeof rememberSessionToken;
|
|
43
|
+
ensureSessionToken: typeof ensureSessionToken;
|
|
44
|
+
requestTokenCache: WeakMap<Request, string>;
|
|
43
45
|
getHeaderToken: typeof getHeaderToken;
|
|
46
|
+
getXsrfHeaderToken: typeof getXsrfHeaderToken;
|
|
44
47
|
isSecureRequest: typeof isSecureRequest;
|
|
45
|
-
|
|
48
|
+
readInputToken: typeof readInputToken;
|
|
46
49
|
isExcludedPath: typeof isExcludedPath;
|
|
47
50
|
isSafeMethod: typeof isSafeMethod;
|
|
48
51
|
matchesPathPattern: typeof matchesPathPattern$1;
|
|
49
|
-
parseCookieHeader: typeof parseCookieHeader;
|
|
50
52
|
serializeCookie: typeof serializeCookie;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
getCsrfSigningKey: typeof getCsrfSigningKey;
|
|
54
|
-
isValidSignedCsrfToken: typeof isValidSignedCsrfToken;
|
|
53
|
+
decryptCsrfToken: typeof decryptCsrfToken;
|
|
54
|
+
encryptCsrfToken: typeof encryptCsrfToken;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
declare function matchesPathPattern(pathname: string, pattern: string): boolean;
|
|
@@ -104,63 +104,12 @@ declare const securityStoreInternals: {
|
|
|
104
104
|
normalizeStoreConfig: typeof normalizeStoreConfig;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
type FileRateLimitBucket = {
|
|
108
|
-
namespaceHash: string;
|
|
109
|
-
keyHash: string;
|
|
110
|
-
prefixHashes: readonly string[];
|
|
111
|
-
attempts: number;
|
|
112
|
-
expiresAt: Date;
|
|
113
|
-
};
|
|
114
|
-
type FileBucketLock = {
|
|
115
|
-
readonly ownerId: string;
|
|
116
|
-
readonly path: string;
|
|
117
|
-
};
|
|
118
107
|
interface FileRateLimitStoreOptions {
|
|
119
108
|
readonly now?: () => Date;
|
|
120
109
|
readonly lockRetryDelayMs?: number;
|
|
121
110
|
readonly lockTimeoutMs?: number;
|
|
122
111
|
}
|
|
123
|
-
declare function createBucketHash(key: string): string;
|
|
124
|
-
declare function getBucketPath(root: string, key: string): string;
|
|
125
|
-
declare function serializeBucket(bucket: FileRateLimitBucket): string;
|
|
126
|
-
declare function deserializeBucket(raw: string): FileRateLimitBucket;
|
|
127
|
-
declare function readBucket(path: string): Promise<FileRateLimitBucket | null>;
|
|
128
|
-
declare function isExpired$1(bucket: FileRateLimitBucket, now: Date): boolean;
|
|
129
|
-
declare function writeBucket(path: string, bucket: FileRateLimitBucket): Promise<void>;
|
|
130
|
-
declare function deleteBucket(path: string): Promise<void>;
|
|
131
|
-
declare function getBucketLockPath(path: string): string;
|
|
132
|
-
declare function sleep(delayMs: number): Promise<void>;
|
|
133
|
-
declare function removeOwnedBucketLock(lock: FileBucketLock): Promise<void>;
|
|
134
|
-
declare function reclaimStaleBucketLock(lockPath: string, timeoutMs: number): Promise<boolean>;
|
|
135
|
-
declare function acquireBucketLock(lockPath: string, options: {
|
|
136
|
-
readonly retryDelayMs: number;
|
|
137
|
-
readonly timeoutMs: number;
|
|
138
|
-
}): Promise<FileBucketLock>;
|
|
139
|
-
declare function withBucketLock<TValue>(path: string, options: {
|
|
140
|
-
readonly retryDelayMs: number;
|
|
141
|
-
readonly timeoutMs: number;
|
|
142
|
-
}, operation: () => Promise<TValue>): Promise<TValue>;
|
|
143
|
-
declare function listBucketPaths(root: string): Promise<string[]>;
|
|
144
|
-
declare function createSnapshot$2(key: string, bucket: FileRateLimitBucket, maxAttempts: number): SecurityRateLimitHitResult['snapshot'];
|
|
145
112
|
declare function createFileRateLimitStore(root: string, options?: FileRateLimitStoreOptions): SecurityRateLimitStore;
|
|
146
|
-
declare const fileRateLimitDriverInternals: {
|
|
147
|
-
createBucketHash: typeof createBucketHash;
|
|
148
|
-
createSnapshot: typeof createSnapshot$2;
|
|
149
|
-
deleteBucket: typeof deleteBucket;
|
|
150
|
-
deserializeBucket: typeof deserializeBucket;
|
|
151
|
-
getBucketPath: typeof getBucketPath;
|
|
152
|
-
isExpired: typeof isExpired$1;
|
|
153
|
-
listBucketPaths: typeof listBucketPaths;
|
|
154
|
-
readBucket: typeof readBucket;
|
|
155
|
-
serializeBucket: typeof serializeBucket;
|
|
156
|
-
sleep: typeof sleep;
|
|
157
|
-
getBucketLockPath: typeof getBucketLockPath;
|
|
158
|
-
acquireBucketLock: typeof acquireBucketLock;
|
|
159
|
-
reclaimStaleBucketLock: typeof reclaimStaleBucketLock;
|
|
160
|
-
removeOwnedBucketLock: typeof removeOwnedBucketLock;
|
|
161
|
-
withBucketLock: typeof withBucketLock;
|
|
162
|
-
writeBucket: typeof writeBucket;
|
|
163
|
-
};
|
|
164
113
|
|
|
165
114
|
type MemoryRateLimitBucket = {
|
|
166
115
|
key: string;
|
|
@@ -261,4 +210,5 @@ declare const security: Readonly<{
|
|
|
261
210
|
verifySignedToken: typeof verifySignedToken;
|
|
262
211
|
}>;
|
|
263
212
|
|
|
264
|
-
export { HoloSecurityConfig, NormalizedHoloCorsConfig, NormalizedHoloSecurityConfig, NormalizedSecurityLimiterConfig, SecurityClearRateLimitOptions, SecurityCsrfField, SecurityCsrfInput, SecurityLimiterConfig, SecurityProtectOptions, SecurityRateLimitCallOptions, SecurityRateLimitHitResult, SecurityRateLimitKeyResolver, SecurityRateLimitRedisDriverAdapter, SecurityRateLimitStore, SecurityRateLimitStoreFactoryOptions, SecurityRuntimeBindings, SecurityRuntimeFacade, SecurityRuntimeNotConfiguredError,
|
|
213
|
+
export { HoloSecurityConfig, NormalizedHoloCorsConfig, NormalizedHoloSecurityConfig, NormalizedSecurityLimiterConfig, SecurityClearRateLimitOptions, SecurityCsrfField, SecurityCsrfInput, SecurityLimiterConfig, SecurityProtectOptions, SecurityRateLimitCallOptions, SecurityRateLimitHitResult, SecurityRateLimitKeyResolver, SecurityRateLimitRedisDriverAdapter, SecurityRateLimitStore, SecurityRateLimitStoreFactoryOptions, SecurityRuntimeBindings, SecurityRuntimeFacade, SecurityRuntimeNotConfiguredError, apply as applyCors, clearRateLimit, configureSecurityRuntime, cors, corsInternals, headers as createCorsHeaders, preflight as createCorsPreflightResponse, cookie as createCsrfCookie, field as createCsrfField, input as createCsrfInput, token as createCsrfToken, createFileRateLimitStore, createMemoryRateLimitStore, createRateLimitStoreFromConfig, createRedisRateLimitStore, createSignedToken, csrf, csrfInternals, security as default, defaultRateLimitKey, getSecurityRuntime, getSecurityRuntimeBindings, ip, isSecureRequest, memoryRateLimitDriverInternals, protect, rateLimit, rateLimitInternals, redisRateLimitDriverInternals, resetSecurityRuntime, securityRuntimeInternals, securityStoreInternals, verify as verifyCsrfRequest, verifySignedToken };
|
|
214
|
+
export type { SecuritySignedTokenOptions, SecuritySignedTokenPayload, SecuritySignedTokenPrimitive, SecuritySignedTokenValue };
|
package/dist/index.mjs
CHANGED
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
csrfInternals,
|
|
16
16
|
defaultRateLimitKey,
|
|
17
17
|
field,
|
|
18
|
-
fileRateLimitDriverInternals,
|
|
19
18
|
getSecurityRuntime,
|
|
20
19
|
getSecurityRuntimeBindings,
|
|
21
20
|
headers,
|
|
22
21
|
input,
|
|
22
|
+
ip,
|
|
23
23
|
isSecureRequest,
|
|
24
24
|
memoryRateLimitDriverInternals,
|
|
25
25
|
preflight,
|
|
@@ -34,7 +34,8 @@ import {
|
|
|
34
34
|
token,
|
|
35
35
|
verify,
|
|
36
36
|
verifySignedToken
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-RYQLS2OO.mjs";
|
|
38
|
+
import "./chunk-EINZXLC2.mjs";
|
|
38
39
|
import {
|
|
39
40
|
SecurityCsrfError,
|
|
40
41
|
SecurityRateLimitError,
|
|
@@ -43,10 +44,9 @@ import {
|
|
|
43
44
|
createRedisRateLimitStoreConfig,
|
|
44
45
|
defineRateLimiter,
|
|
45
46
|
defineSecurityRuntimeBindings,
|
|
46
|
-
ip,
|
|
47
47
|
limit,
|
|
48
48
|
securityInternals
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-U25HSHLJ.mjs";
|
|
50
50
|
import {
|
|
51
51
|
defineCorsConfig,
|
|
52
52
|
defineSecurityConfig,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
holoSecurityDefaults,
|
|
55
55
|
normalizeCorsConfig,
|
|
56
56
|
normalizeSecurityConfig
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-D3ZN5MLJ.mjs";
|
|
58
58
|
export {
|
|
59
59
|
SecurityCsrfError,
|
|
60
60
|
SecurityRateLimitError,
|
|
@@ -86,7 +86,6 @@ export {
|
|
|
86
86
|
defineRateLimiter,
|
|
87
87
|
defineSecurityConfig,
|
|
88
88
|
defineSecurityRuntimeBindings,
|
|
89
|
-
fileRateLimitDriverInternals,
|
|
90
89
|
getSecurityRuntime,
|
|
91
90
|
getSecurityRuntimeBindings,
|
|
92
91
|
holoCorsDefaults,
|
package/dist/next/server.d.ts
CHANGED
package/dist/next/server.mjs
CHANGED
|
@@ -2,17 +2,18 @@ import {
|
|
|
2
2
|
createSecurityRejectionResponse,
|
|
3
3
|
isSecurityRejection,
|
|
4
4
|
prepareCsrfCookies
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-EN2H3TN3.mjs";
|
|
6
6
|
import {
|
|
7
7
|
cors,
|
|
8
8
|
getSecurityRuntime,
|
|
9
9
|
protect
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-RYQLS2OO.mjs";
|
|
11
|
+
import "../chunk-EINZXLC2.mjs";
|
|
12
|
+
import "../chunk-U25HSHLJ.mjs";
|
|
13
|
+
import "../chunk-D3ZN5MLJ.mjs";
|
|
14
14
|
|
|
15
15
|
// src/next/server.ts
|
|
16
|
+
import { HoloRequestBodyTooLargeError, readBoundedRequestFormData } from "@holo-js/kernel";
|
|
16
17
|
function getRequestCookie(request, name) {
|
|
17
18
|
const cookie = request.cookies?.get(name);
|
|
18
19
|
if (typeof cookie === "string") {
|
|
@@ -21,22 +22,38 @@ function getRequestCookie(request, name) {
|
|
|
21
22
|
return typeof cookie?.value === "string" ? cookie.value : void 0;
|
|
22
23
|
}
|
|
23
24
|
async function issueCsrfCookie(request) {
|
|
24
|
-
const cookies = await prepareCsrfCookies(request, (name) => getRequestCookie(request, name));
|
|
25
|
-
if (cookies.length === 0) {
|
|
25
|
+
const { cookies, sessionCookies } = await prepareCsrfCookies(request, (name) => getRequestCookie(request, name));
|
|
26
|
+
if (cookies.length === 0 && sessionCookies.length === 0) {
|
|
26
27
|
return void 0;
|
|
27
28
|
}
|
|
28
29
|
const { NextResponse } = await import("next/server.js");
|
|
29
|
-
|
|
30
|
+
let requestHeaders;
|
|
31
|
+
if (sessionCookies.length > 0) {
|
|
32
|
+
requestHeaders = new Headers(request.headers);
|
|
33
|
+
const forwardedCookies = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const cookie of [
|
|
35
|
+
...(request.headers.get("cookie") ?? "").split(";"),
|
|
36
|
+
...sessionCookies.map((cookie2) => cookie2.split(";", 1)[0] ?? "")
|
|
37
|
+
]) {
|
|
38
|
+
const separator = cookie.indexOf("=");
|
|
39
|
+
if (separator > 0) forwardedCookies.set(cookie.slice(0, separator).trim(), cookie.slice(separator + 1).trim());
|
|
40
|
+
}
|
|
41
|
+
requestHeaders.set("cookie", [...forwardedCookies].map(([name, value]) => `${name}=${value}`).join("; "));
|
|
42
|
+
}
|
|
43
|
+
const response = NextResponse.next(requestHeaders ? { request: { headers: requestHeaders } } : void 0);
|
|
30
44
|
for (const cookie of cookies) {
|
|
31
45
|
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
32
46
|
}
|
|
47
|
+
for (const sessionCookie of sessionCookies) {
|
|
48
|
+
response.headers.append("Set-Cookie", sessionCookie);
|
|
49
|
+
}
|
|
33
50
|
return response;
|
|
34
51
|
}
|
|
35
52
|
async function actionRequest(request) {
|
|
36
53
|
const { field, header } = getSecurityRuntime().config.csrf;
|
|
37
54
|
if (!request.headers.has("next-action") || request.headers.get(header)?.trim() || !request.headers.get("content-type")?.startsWith("multipart/form-data")) return request;
|
|
38
55
|
try {
|
|
39
|
-
const data = await request.clone()
|
|
56
|
+
const data = await readBoundedRequestFormData(request.clone());
|
|
40
57
|
const root = data.get("0");
|
|
41
58
|
if (typeof root !== "string") return request;
|
|
42
59
|
const args = JSON.parse(root);
|
|
@@ -51,7 +68,8 @@ async function actionRequest(request) {
|
|
|
51
68
|
headers.set(header, token);
|
|
52
69
|
return new Request(request.clone(), { headers });
|
|
53
70
|
}
|
|
54
|
-
} catch {
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error instanceof HoloRequestBodyTooLargeError) throw error;
|
|
55
73
|
return request;
|
|
56
74
|
}
|
|
57
75
|
return request;
|
package/dist/nuxt/server.mjs
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isSecurityRejection,
|
|
3
3
|
prepareCsrfCookies
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-EN2H3TN3.mjs";
|
|
5
5
|
import {
|
|
6
6
|
cors,
|
|
7
|
+
csrfInternals,
|
|
7
8
|
protect
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-RYQLS2OO.mjs";
|
|
10
|
+
import "../chunk-EINZXLC2.mjs";
|
|
11
|
+
import "../chunk-U25HSHLJ.mjs";
|
|
12
|
+
import "../chunk-D3ZN5MLJ.mjs";
|
|
12
13
|
|
|
13
14
|
// src/nuxt/server.ts
|
|
15
|
+
import { readBoundedBufferedBody, readBoundedNodeRequestBody, requestBodyLimitFor } from "@holo-js/kernel";
|
|
14
16
|
import {
|
|
15
17
|
createError,
|
|
16
18
|
defineEventHandler,
|
|
19
|
+
appendResponseHeader,
|
|
17
20
|
getCookie,
|
|
18
21
|
getMethod,
|
|
22
|
+
getRequestHeader,
|
|
19
23
|
getRequestHeaders,
|
|
20
24
|
getRequestURL,
|
|
21
|
-
readRawBody,
|
|
22
25
|
setCookie,
|
|
23
26
|
setResponseHeader
|
|
24
27
|
} from "h3";
|
|
@@ -43,6 +46,22 @@ function createRequestBody(body) {
|
|
|
43
46
|
copy.set(body);
|
|
44
47
|
return copy.buffer;
|
|
45
48
|
}
|
|
49
|
+
var h3RawBodySymbol = /* @__PURE__ */ Symbol.for("h3RawBody");
|
|
50
|
+
async function readBoundedEventBody(event) {
|
|
51
|
+
const limit = requestBodyLimitFor(getRequestHeader(event, "content-type"));
|
|
52
|
+
const node = event.node.req;
|
|
53
|
+
const buffered = await readBoundedBufferedBody(
|
|
54
|
+
event._requestBody || event.web?.request?.body || node[h3RawBodySymbol] || node.rawBody || node.body,
|
|
55
|
+
limit
|
|
56
|
+
);
|
|
57
|
+
const bytes = buffered?.bytes ?? await readBoundedNodeRequestBody(event.node.req, limit, getRequestHeader(event, "content-length"));
|
|
58
|
+
event._requestBody = bytes;
|
|
59
|
+
Object.assign(event.node.req, { [h3RawBodySymbol]: bytes });
|
|
60
|
+
if (buffered?.contentType) {
|
|
61
|
+
event.node.req.headers["content-type"] = buffered.contentType;
|
|
62
|
+
}
|
|
63
|
+
return { bytes, contentType: buffered?.contentType };
|
|
64
|
+
}
|
|
46
65
|
async function createRequest(event) {
|
|
47
66
|
if (event.web?.request) {
|
|
48
67
|
const request = event.web.request.clone();
|
|
@@ -51,18 +70,35 @@ async function createRequest(event) {
|
|
|
51
70
|
}
|
|
52
71
|
const method = getMethod(event);
|
|
53
72
|
const headers = createHeaders(event);
|
|
54
|
-
const
|
|
55
|
-
|
|
73
|
+
const buffered = isSafeMethod(method) ? void 0 : await readBoundedEventBody(event);
|
|
74
|
+
if (buffered?.contentType) {
|
|
75
|
+
headers.set("content-type", buffered.contentType);
|
|
76
|
+
}
|
|
77
|
+
return new Request(getRequestURL(event, { xForwardedProto: false }), {
|
|
56
78
|
method,
|
|
57
79
|
headers,
|
|
58
|
-
body: createRequestBody(
|
|
80
|
+
body: createRequestBody(buffered?.bytes)
|
|
59
81
|
});
|
|
60
82
|
}
|
|
83
|
+
function shareSessionWithHandlers(event, token, sessionCookies) {
|
|
84
|
+
const cookie = [event.headers.get("cookie"), ...sessionCookies.map((sessionCookie) => sessionCookie.split(";", 1)[0])].filter(Boolean).join("; ");
|
|
85
|
+
event.headers.set("cookie", cookie);
|
|
86
|
+
event.node.req.headers.cookie = cookie;
|
|
87
|
+
if (event.web?.request) {
|
|
88
|
+
csrfInternals.rememberSessionToken(event.web.request, token);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
61
91
|
async function issueCsrfCookie(event, request) {
|
|
62
|
-
const cookies = await prepareCsrfCookies(request, (name) => getCookie(event, name));
|
|
92
|
+
const { token, cookies, sessionCookies } = await prepareCsrfCookies(request, (name) => getCookie(event, name));
|
|
63
93
|
for (const cookie of cookies) {
|
|
64
94
|
setCookie(event, cookie.name, cookie.value, cookie.options);
|
|
65
95
|
}
|
|
96
|
+
for (const sessionCookie of sessionCookies) {
|
|
97
|
+
appendResponseHeader(event, "set-cookie", sessionCookie);
|
|
98
|
+
}
|
|
99
|
+
if (token && sessionCookies.length > 0) {
|
|
100
|
+
shareSessionWithHandlers(event, token, sessionCookies);
|
|
101
|
+
}
|
|
66
102
|
}
|
|
67
103
|
function csrfProtection() {
|
|
68
104
|
return defineEventHandler(async (event) => {
|
|
@@ -72,11 +108,11 @@ function csrfProtection() {
|
|
|
72
108
|
headers: { Allow: "GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE" }
|
|
73
109
|
});
|
|
74
110
|
}
|
|
75
|
-
const request = await createRequest(event);
|
|
76
|
-
const preflight = cors.preflight(request);
|
|
77
|
-
if (preflight) return preflight;
|
|
78
|
-
cors.headers(request).forEach((value, name) => setResponseHeader(event, name, value));
|
|
79
111
|
try {
|
|
112
|
+
const request = await createRequest(event);
|
|
113
|
+
const preflight = cors.preflight(request);
|
|
114
|
+
if (preflight) return preflight;
|
|
115
|
+
cors.headers(request).forEach((value, name) => setResponseHeader(event, name, value));
|
|
80
116
|
await protect(request);
|
|
81
117
|
await issueCsrfCookie(event, request);
|
|
82
118
|
} catch (error) {
|
|
@@ -34,4 +34,5 @@ declare const svelteKitSecurityInternals: {
|
|
|
34
34
|
issueCsrfCookie: typeof issueCsrfCookie;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export {
|
|
37
|
+
export { csrfProtection, svelteKitSecurityInternals };
|
|
38
|
+
export type { SvelteKitCsrfHandle, SvelteKitCsrfHandleInput };
|
|
@@ -2,21 +2,27 @@ import {
|
|
|
2
2
|
createSecurityRejectionResponse,
|
|
3
3
|
isSecurityRejection,
|
|
4
4
|
prepareCsrfCookies
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-EN2H3TN3.mjs";
|
|
6
6
|
import {
|
|
7
7
|
cors,
|
|
8
8
|
protect
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-RYQLS2OO.mjs";
|
|
10
|
+
import "../chunk-EINZXLC2.mjs";
|
|
11
|
+
import "../chunk-U25HSHLJ.mjs";
|
|
12
|
+
import "../chunk-D3ZN5MLJ.mjs";
|
|
13
13
|
|
|
14
14
|
// src/sveltekit/server.ts
|
|
15
|
+
import { responseCookieInternals } from "@holo-js/kernel";
|
|
15
16
|
async function issueCsrfCookie(event) {
|
|
16
|
-
const cookies = await prepareCsrfCookies(event.request, (name) => event.cookies.get(name));
|
|
17
|
+
const { cookies, sessionCookies } = await prepareCsrfCookies(event.request, (name) => event.cookies.get(name));
|
|
17
18
|
for (const cookie of cookies) {
|
|
18
19
|
event.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
19
20
|
}
|
|
21
|
+
for (const sessionCookie of sessionCookies) {
|
|
22
|
+
const parsed = responseCookieInternals.parseResponseCookie(sessionCookie);
|
|
23
|
+
if (!parsed) throw new Error("[@holo-js/security] The CSRF session cookie could not be parsed.");
|
|
24
|
+
event.cookies.set(parsed.name, parsed.value, parsed.options);
|
|
25
|
+
}
|
|
20
26
|
}
|
|
21
27
|
function csrfProtection() {
|
|
22
28
|
return async ({ event, resolve }) => {
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/security",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Holo-JS Framework - CSRF and rate-limit contracts, config, and runtime seams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cobraprojects/holo-js.git",
|
|
10
|
+
"directory": "packages/security"
|
|
11
|
+
},
|
|
7
12
|
"exports": {
|
|
8
13
|
".": {
|
|
9
14
|
"types": "./dist/index.d.ts",
|
|
@@ -49,7 +54,8 @@
|
|
|
49
54
|
"main": "./dist/index.mjs",
|
|
50
55
|
"types": "./dist/index.d.ts",
|
|
51
56
|
"files": [
|
|
52
|
-
"dist"
|
|
57
|
+
"dist",
|
|
58
|
+
"LICENSE"
|
|
53
59
|
],
|
|
54
60
|
"scripts": {
|
|
55
61
|
"build": "tsup",
|
|
@@ -58,13 +64,13 @@
|
|
|
58
64
|
"test": "vitest --run --coverage"
|
|
59
65
|
},
|
|
60
66
|
"dependencies": {
|
|
61
|
-
"@holo-js/config": "^0.
|
|
62
|
-
"@holo-js/kernel": "^0.
|
|
67
|
+
"@holo-js/config": "^0.4.1",
|
|
68
|
+
"@holo-js/kernel": "^0.4.1"
|
|
63
69
|
},
|
|
64
70
|
"peerDependencies": {
|
|
65
71
|
"h3": "^1.15.11",
|
|
66
|
-
"next": "^16.3
|
|
67
|
-
"ioredis": "^
|
|
72
|
+
"next": "^16.4.3",
|
|
73
|
+
"ioredis": "^6.0.0"
|
|
68
74
|
},
|
|
69
75
|
"peerDependenciesMeta": {
|
|
70
76
|
"h3": {
|
|
@@ -78,15 +84,15 @@
|
|
|
78
84
|
}
|
|
79
85
|
},
|
|
80
86
|
"devDependencies": {
|
|
81
|
-
"@types/node": "^22.
|
|
87
|
+
"@types/node": "^22.20.5",
|
|
82
88
|
"h3": "^1.15.11",
|
|
83
|
-
"ioredis": "^
|
|
84
|
-
"next": "^16.3
|
|
85
|
-
"tsup": "^
|
|
86
|
-
"typescript": "^
|
|
87
|
-
"vitest": "^
|
|
89
|
+
"ioredis": "^6.0.0",
|
|
90
|
+
"next": "^16.4.3",
|
|
91
|
+
"tsup": "^9.0.0",
|
|
92
|
+
"typescript": "^6.0.3",
|
|
93
|
+
"vitest": "^5.0.5"
|
|
88
94
|
},
|
|
89
95
|
"engines": {
|
|
90
|
-
"node": "^22.
|
|
96
|
+
"node": "^22.18.0 || ^24.0.0 || ^26.0.0"
|
|
91
97
|
}
|
|
92
98
|
}
|