@holo-js/security 0.3.28 → 0.4.0

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/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, ip } from './contracts.js';
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 parseCookieHeader(header: string | null | undefined): Readonly<Record<string, string>>;
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 getCsrfSigningKey(): string;
17
- declare function encodeCsrfToken(nonce: string): string;
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 readFormToken(request: Request): Promise<string | undefined>;
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
- generatedTokenCache: WeakMap<Request, string>;
42
- getCookieToken: typeof getCookieToken;
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
- readFormToken: typeof readFormToken;
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
- decodeCsrfToken: typeof decodeCsrfToken;
52
- encodeCsrfToken: typeof encodeCsrfToken;
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, type SecuritySignedTokenOptions, type SecuritySignedTokenPayload, type SecuritySignedTokenPrimitive, type SecuritySignedTokenValue, 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, fileRateLimitDriverInternals, getSecurityRuntime, getSecurityRuntimeBindings, ip, isSecureRequest, memoryRateLimitDriverInternals, protect, rateLimit, rateLimitInternals, redisRateLimitDriverInternals, resetSecurityRuntime, securityRuntimeInternals, securityStoreInternals, verify as verifyCsrfRequest, verifySignedToken };
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-26IYURJG.mjs";
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-FQ4X7X6L.mjs";
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-WRMZRO77.mjs";
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,
@@ -13,4 +13,5 @@ declare const nextSecurityInternals: {
13
13
  issueCsrfCookie: typeof issueCsrfCookie;
14
14
  };
15
15
 
16
- export { type NextCsrfMiddleware, csrfProtection, nextSecurityInternals };
16
+ export { csrfProtection, nextSecurityInternals };
17
+ export type { NextCsrfMiddleware };
@@ -2,17 +2,18 @@ import {
2
2
  createSecurityRejectionResponse,
3
3
  isSecurityRejection,
4
4
  prepareCsrfCookies
5
- } from "../chunk-PHPA3C6L.mjs";
5
+ } from "../chunk-Q3PK4AGL.mjs";
6
6
  import {
7
7
  cors,
8
8
  getSecurityRuntime,
9
9
  protect
10
- } from "../chunk-26IYURJG.mjs";
11
- import "../chunk-PC2YCWDH.mjs";
12
- import "../chunk-FQ4X7X6L.mjs";
13
- import "../chunk-WRMZRO77.mjs";
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
- const response = NextResponse.next();
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().formData();
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;
@@ -1,24 +1,27 @@
1
1
  import {
2
2
  isSecurityRejection,
3
3
  prepareCsrfCookies
4
- } from "../chunk-PHPA3C6L.mjs";
4
+ } from "../chunk-Q3PK4AGL.mjs";
5
5
  import {
6
6
  cors,
7
+ csrfInternals,
7
8
  protect
8
- } from "../chunk-26IYURJG.mjs";
9
- import "../chunk-PC2YCWDH.mjs";
10
- import "../chunk-FQ4X7X6L.mjs";
11
- import "../chunk-WRMZRO77.mjs";
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 body = isSafeMethod(method) ? void 0 : await readRawBody(event, false);
55
- return new Request(getRequestURL(event), {
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(body)
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) {
@@ -28,10 +28,11 @@ type SvelteKitCsrfHandleInput<TEvent extends SvelteKitCsrfEvent = SvelteKitCsrfE
28
28
  readonly resolve: (event: TEvent, options?: SvelteKitResolveOptions) => Response | Promise<Response>;
29
29
  };
30
30
  type SvelteKitCsrfHandle = <TEvent extends SvelteKitCsrfEvent>(input: SvelteKitCsrfHandleInput<TEvent>) => Response | Promise<Response>;
31
- declare function issueCsrfCookie(event: SvelteKitCsrfEvent): Promise<void>;
31
+ declare function issueCsrfCookie(event: SvelteKitCsrfEvent): Promise<readonly string[]>;
32
32
  declare function csrfProtection(): SvelteKitCsrfHandle;
33
33
  declare const svelteKitSecurityInternals: {
34
34
  issueCsrfCookie: typeof issueCsrfCookie;
35
35
  };
36
36
 
37
- export { type SvelteKitCsrfHandle, type SvelteKitCsrfHandleInput, csrfProtection, svelteKitSecurityInternals };
37
+ export { csrfProtection, svelteKitSecurityInternals };
38
+ export type { SvelteKitCsrfHandle, SvelteKitCsrfHandleInput };
@@ -1,22 +1,24 @@
1
1
  import {
2
+ appendSetCookies,
2
3
  createSecurityRejectionResponse,
3
4
  isSecurityRejection,
4
5
  prepareCsrfCookies
5
- } from "../chunk-PHPA3C6L.mjs";
6
+ } from "../chunk-Q3PK4AGL.mjs";
6
7
  import {
7
8
  cors,
8
9
  protect
9
- } from "../chunk-26IYURJG.mjs";
10
- import "../chunk-PC2YCWDH.mjs";
11
- import "../chunk-FQ4X7X6L.mjs";
12
- import "../chunk-WRMZRO77.mjs";
10
+ } from "../chunk-RYQLS2OO.mjs";
11
+ import "../chunk-EINZXLC2.mjs";
12
+ import "../chunk-U25HSHLJ.mjs";
13
+ import "../chunk-D3ZN5MLJ.mjs";
13
14
 
14
15
  // src/sveltekit/server.ts
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
+ return sessionCookies;
20
22
  }
21
23
  function csrfProtection() {
22
24
  return async ({ event, resolve }) => {
@@ -28,16 +30,17 @@ function csrfProtection() {
28
30
  headers: { Allow: "GET, HEAD, OPTIONS, POST, PUT, PATCH, DELETE" }
29
31
  });
30
32
  }
33
+ let sessionCookies;
31
34
  try {
32
35
  await protect(event.request);
33
- await issueCsrfCookie(event);
36
+ sessionCookies = await issueCsrfCookie(event);
34
37
  } catch (error) {
35
38
  if (isSecurityRejection(error)) {
36
39
  return cors.apply(event.request, createSecurityRejectionResponse(error));
37
40
  }
38
41
  throw error;
39
42
  }
40
- return cors.apply(event.request, await resolve(event));
43
+ return appendSetCookies(cors.apply(event.request, await resolve(event)), sessionCookies);
41
44
  };
42
45
  }
43
46
  var svelteKitSecurityInternals = {
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@holo-js/security",
3
- "version": "0.3.28",
3
+ "version": "0.4.0",
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.3.28",
62
- "@holo-js/kernel": "^0.3.28"
67
+ "@holo-js/config": "^0.4.0",
68
+ "@holo-js/kernel": "^0.4.0"
63
69
  },
64
70
  "peerDependencies": {
65
71
  "h3": "^1.15.11",
66
- "next": "^16.3.5",
67
- "ioredis": "^5.4.2"
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.10.2",
87
+ "@types/node": "^22.20.5",
82
88
  "h3": "^1.15.11",
83
- "ioredis": "^5.4.2",
84
- "next": "^16.3.5",
85
- "tsup": "^8.3.5",
86
- "typescript": "^5.7.2",
87
- "vitest": "^4.1.5"
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.12.0 || ^24.0.0 || ^26.0.0"
96
+ "node": "^22.18.0 || ^24.0.0 || ^26.0.0"
91
97
  }
92
98
  }
@@ -1,64 +0,0 @@
1
- import {
2
- csrfInternals,
3
- getSecurityRuntime,
4
- isSecureRequest,
5
- token
6
- } from "./chunk-26IYURJG.mjs";
7
- import {
8
- SECURITY_CLIENT_CONFIG_COOKIE,
9
- createSecurityClientConfig,
10
- serializeSecurityClientConfig
11
- } from "./chunk-PC2YCWDH.mjs";
12
- import {
13
- SecurityCsrfError
14
- } from "./chunk-FQ4X7X6L.mjs";
15
-
16
- // src/cookie-issuance.ts
17
- async function prepareCsrfCookies(request, getCookie) {
18
- const { config } = getSecurityRuntime();
19
- const method = request.method.trim().toUpperCase();
20
- if (!config.csrf.enabled || method !== "GET" && method !== "HEAD") {
21
- return [];
22
- }
23
- const existingToken = getCookie(config.csrf.cookie);
24
- const clientConfig = serializeSecurityClientConfig(createSecurityClientConfig(config));
25
- const shouldIssueToken = !existingToken || !csrfInternals.isValidSignedCsrfToken(existingToken);
26
- const shouldIssueClientConfig = getCookie(SECURITY_CLIENT_CONFIG_COOKIE) !== clientConfig;
27
- if (!shouldIssueToken && !shouldIssueClientConfig) {
28
- return [];
29
- }
30
- const options = {
31
- httpOnly: false,
32
- path: "/",
33
- sameSite: "lax",
34
- secure: isSecureRequest(request)
35
- };
36
- const cookies = [];
37
- if (shouldIssueToken) {
38
- cookies.push({ name: config.csrf.cookie, value: await token(request), options });
39
- }
40
- if (shouldIssueClientConfig) {
41
- cookies.push({ name: SECURITY_CLIENT_CONFIG_COOKIE, value: clientConfig, options });
42
- }
43
- return cookies;
44
- }
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
-
60
- export {
61
- prepareCsrfCookies,
62
- isSecurityRejection,
63
- createSecurityRejectionResponse
64
- };