@holo-js/security 0.3.15 → 0.3.17

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.
@@ -0,0 +1,45 @@
1
+ import {
2
+ csrfInternals,
3
+ getSecurityRuntime,
4
+ isSecureRequest,
5
+ token
6
+ } from "./chunk-G3HBF4Q5.mjs";
7
+ import {
8
+ SECURITY_CLIENT_CONFIG_COOKIE,
9
+ createSecurityClientConfig,
10
+ serializeSecurityClientConfig
11
+ } from "./chunk-FUOZWKHK.mjs";
12
+
13
+ // src/cookie-issuance.ts
14
+ async function prepareCsrfCookies(request, getCookie) {
15
+ const { config } = getSecurityRuntime();
16
+ const method = request.method.trim().toUpperCase();
17
+ if (!config.csrf.enabled || method !== "GET" && method !== "HEAD") {
18
+ return [];
19
+ }
20
+ const existingToken = getCookie(config.csrf.cookie);
21
+ const clientConfig = serializeSecurityClientConfig(createSecurityClientConfig(config));
22
+ const shouldIssueToken = !existingToken || !csrfInternals.isValidSignedCsrfToken(existingToken);
23
+ const shouldIssueClientConfig = getCookie(SECURITY_CLIENT_CONFIG_COOKIE) !== clientConfig;
24
+ if (!shouldIssueToken && !shouldIssueClientConfig) {
25
+ return [];
26
+ }
27
+ const options = {
28
+ httpOnly: false,
29
+ path: "/",
30
+ sameSite: "lax",
31
+ secure: isSecureRequest(request)
32
+ };
33
+ const cookies = [];
34
+ if (shouldIssueToken) {
35
+ cookies.push({ name: config.csrf.cookie, value: await token(request), options });
36
+ }
37
+ if (shouldIssueClientConfig) {
38
+ cookies.push({ name: SECURITY_CLIENT_CONFIG_COOKIE, value: clientConfig, options });
39
+ }
40
+ return cookies;
41
+ }
42
+
43
+ export {
44
+ prepareCsrfCookies
45
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  normalizeCorsConfig,
3
3
  normalizeSecurityConfig
4
- } from "./chunk-WDTNP5YU.mjs";
4
+ } from "./chunk-WRMZRO77.mjs";
5
5
 
6
6
  // src/contracts.ts
7
7
  var SecurityCsrfError = class extends Error {
@@ -3,11 +3,11 @@ import {
3
3
  SecurityRateLimitError,
4
4
  ip,
5
5
  limit
6
- } from "./chunk-AGICNQRJ.mjs";
6
+ } from "./chunk-FQ4X7X6L.mjs";
7
7
  import {
8
8
  normalizeCorsConfig,
9
9
  normalizeSecurityConfig
10
- } from "./chunk-WDTNP5YU.mjs";
10
+ } from "./chunk-WRMZRO77.mjs";
11
11
 
12
12
  // src/csrf.ts
13
13
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -3,6 +3,7 @@ import {
3
3
  DEFAULT_REDIS_DB,
4
4
  DEFAULT_REDIS_HOST,
5
5
  DEFAULT_REDIS_PORT,
6
+ normalizeRedisConfig,
6
7
  resolveNormalizedRedisConnection
7
8
  } from "@holo-js/kernel";
8
9
  import { registerConfigNormalizer } from "@holo-js/config/registry";
@@ -149,7 +150,10 @@ function normalizeSecurityRateLimitConfig(config, redisConfig) {
149
150
  redisConfig,
150
151
  connectionName,
151
152
  "Security rate-limit Redis connection"
152
- ) : driver === "redis" ? (() => {
153
+ ) : typeof redis.host === "string" && typeof redis.port === "number" && typeof redis.db === "number" ? resolveNormalizedRedisConnection(normalizeRedisConfig({
154
+ default: connectionName,
155
+ connections: { [connectionName]: redis }
156
+ }), connectionName, "Security rate-limit Redis connection") : driver === "redis" ? (() => {
153
157
  throw new Error(
154
158
  `[@holo-js/security] Rate-limit Redis config references shared connection "${connectionName}" without top-level Redis config.`
155
159
  );
package/dist/config.mjs CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  holoSecurityDefaults,
17
17
  normalizeCorsConfig,
18
18
  normalizeSecurityConfig
19
- } from "./chunk-WDTNP5YU.mjs";
19
+ } from "./chunk-WRMZRO77.mjs";
20
20
  export {
21
21
  DEFAULT_CORS_HEADERS,
22
22
  DEFAULT_CORS_MAX_AGE,
@@ -9,8 +9,8 @@ import {
9
9
  ip,
10
10
  limit,
11
11
  securityInternals
12
- } from "./chunk-AGICNQRJ.mjs";
13
- import "./chunk-WDTNP5YU.mjs";
12
+ } from "./chunk-FQ4X7X6L.mjs";
13
+ import "./chunk-WRMZRO77.mjs";
14
14
  export {
15
15
  SecurityCsrfError,
16
16
  SecurityRateLimitError,
package/dist/index.mjs CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  token,
35
35
  verify,
36
36
  verifySignedToken
37
- } from "./chunk-YIBZOYSR.mjs";
37
+ } from "./chunk-G3HBF4Q5.mjs";
38
38
  import {
39
39
  SecurityCsrfError,
40
40
  SecurityRateLimitError,
@@ -46,7 +46,7 @@ import {
46
46
  ip,
47
47
  limit,
48
48
  securityInternals
49
- } from "./chunk-AGICNQRJ.mjs";
49
+ } from "./chunk-FQ4X7X6L.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-WDTNP5YU.mjs";
57
+ } from "./chunk-WRMZRO77.mjs";
58
58
  export {
59
59
  SecurityCsrfError,
60
60
  SecurityRateLimitError,
@@ -1,25 +1,16 @@
1
1
  import {
2
- csrf,
3
- csrfInternals,
4
- getSecurityRuntime,
5
- isSecureRequest,
6
- protect
7
- } from "../chunk-YIBZOYSR.mjs";
2
+ prepareCsrfCookies
3
+ } from "../chunk-5M3RHQIG.mjs";
8
4
  import {
9
- SECURITY_CLIENT_CONFIG_COOKIE,
10
- createSecurityClientConfig,
11
- serializeSecurityClientConfig
12
- } from "../chunk-FUOZWKHK.mjs";
5
+ protect
6
+ } from "../chunk-G3HBF4Q5.mjs";
7
+ import "../chunk-FUOZWKHK.mjs";
13
8
  import {
14
9
  SecurityCsrfError
15
- } from "../chunk-AGICNQRJ.mjs";
16
- import "../chunk-WDTNP5YU.mjs";
10
+ } from "../chunk-FQ4X7X6L.mjs";
11
+ import "../chunk-WRMZRO77.mjs";
17
12
 
18
13
  // src/next/server.ts
19
- function isSafeMethod(method) {
20
- const normalized = method.trim().toUpperCase();
21
- return normalized === "GET" || normalized === "HEAD";
22
- }
23
14
  function createCsrfErrorResponse(error) {
24
15
  return new Response(error.message, {
25
16
  status: error.status,
@@ -36,30 +27,14 @@ function getRequestCookie(request, name) {
36
27
  return typeof cookie?.value === "string" ? cookie.value : void 0;
37
28
  }
38
29
  async function issueCsrfCookie(request) {
39
- const { config } = getSecurityRuntime();
40
- if (!config.csrf.enabled || !isSafeMethod(request.method)) {
41
- return void 0;
42
- }
43
- const existingCsrfToken = getRequestCookie(request, config.csrf.cookie);
44
- const shouldIssueCsrfToken = !existingCsrfToken || !csrfInternals.isValidSignedCsrfToken(existingCsrfToken);
45
- const clientConfig = serializeSecurityClientConfig(createSecurityClientConfig(config));
46
- const shouldIssueClientConfig = getRequestCookie(request, SECURITY_CLIENT_CONFIG_COOKIE) !== clientConfig;
47
- if (!shouldIssueCsrfToken && !shouldIssueClientConfig) {
30
+ const cookies = await prepareCsrfCookies(request, (name) => getRequestCookie(request, name));
31
+ if (cookies.length === 0) {
48
32
  return void 0;
49
33
  }
50
34
  const { NextResponse } = await import("next/server.js");
51
35
  const response = NextResponse.next();
52
- const cookieOptions = {
53
- httpOnly: false,
54
- path: "/",
55
- sameSite: "lax",
56
- secure: isSecureRequest(request)
57
- };
58
- if (shouldIssueCsrfToken) {
59
- response.cookies.set(config.csrf.cookie, await csrf.token(request), cookieOptions);
60
- }
61
- if (shouldIssueClientConfig) {
62
- response.cookies.set(SECURITY_CLIENT_CONFIG_COOKIE, clientConfig, cookieOptions);
36
+ for (const cookie of cookies) {
37
+ response.cookies.set(cookie.name, cookie.value, cookie.options);
63
38
  }
64
39
  return response;
65
40
  }
@@ -1,19 +1,14 @@
1
1
  import {
2
- csrf,
3
- csrfInternals,
4
- getSecurityRuntime,
5
- isSecureRequest,
6
- protect
7
- } from "../chunk-YIBZOYSR.mjs";
2
+ prepareCsrfCookies
3
+ } from "../chunk-5M3RHQIG.mjs";
8
4
  import {
9
- SECURITY_CLIENT_CONFIG_COOKIE,
10
- createSecurityClientConfig,
11
- serializeSecurityClientConfig
12
- } from "../chunk-FUOZWKHK.mjs";
5
+ protect
6
+ } from "../chunk-G3HBF4Q5.mjs";
7
+ import "../chunk-FUOZWKHK.mjs";
13
8
  import {
14
9
  SecurityCsrfError
15
- } from "../chunk-AGICNQRJ.mjs";
16
- import "../chunk-WDTNP5YU.mjs";
10
+ } from "../chunk-FQ4X7X6L.mjs";
11
+ import "../chunk-WRMZRO77.mjs";
17
12
 
18
13
  // src/nuxt/server.ts
19
14
  import {
@@ -58,28 +53,9 @@ async function createRequest(event) {
58
53
  });
59
54
  }
60
55
  async function issueCsrfCookie(event, request) {
61
- const { config } = getSecurityRuntime();
62
- if (!config.csrf.enabled || !isSafeMethod(request.method)) {
63
- return;
64
- }
65
- const existingCsrfToken = getCookie(event, config.csrf.cookie);
66
- const shouldIssueCsrfToken = !existingCsrfToken || !csrfInternals.isValidSignedCsrfToken(existingCsrfToken);
67
- const clientConfig = serializeSecurityClientConfig(createSecurityClientConfig(config));
68
- const shouldIssueClientConfig = getCookie(event, SECURITY_CLIENT_CONFIG_COOKIE) !== clientConfig;
69
- if (!shouldIssueCsrfToken && !shouldIssueClientConfig) {
70
- return;
71
- }
72
- const cookieOptions = {
73
- httpOnly: false,
74
- path: "/",
75
- sameSite: "lax",
76
- secure: isSecureRequest(request)
77
- };
78
- if (shouldIssueCsrfToken) {
79
- setCookie(event, config.csrf.cookie, await csrf.token(request), cookieOptions);
80
- }
81
- if (shouldIssueClientConfig) {
82
- setCookie(event, SECURITY_CLIENT_CONFIG_COOKIE, clientConfig, cookieOptions);
56
+ const cookies = await prepareCsrfCookies(request, (name) => getCookie(event, name));
57
+ for (const cookie of cookies) {
58
+ setCookie(event, cookie.name, cookie.value, cookie.options);
83
59
  }
84
60
  }
85
61
  function csrfProtection() {
@@ -1,42 +1,21 @@
1
1
  import {
2
- csrf,
3
- getSecurityRuntime,
4
- isSecureRequest,
5
- protect
6
- } from "../chunk-YIBZOYSR.mjs";
2
+ prepareCsrfCookies
3
+ } from "../chunk-5M3RHQIG.mjs";
7
4
  import {
8
- SECURITY_CLIENT_CONFIG_COOKIE,
9
- createSecurityClientConfig,
10
- serializeSecurityClientConfig
11
- } from "../chunk-FUOZWKHK.mjs";
5
+ protect
6
+ } from "../chunk-G3HBF4Q5.mjs";
7
+ import "../chunk-FUOZWKHK.mjs";
12
8
  import {
13
9
  SecurityCsrfError
14
- } from "../chunk-AGICNQRJ.mjs";
15
- import "../chunk-WDTNP5YU.mjs";
10
+ } from "../chunk-FQ4X7X6L.mjs";
11
+ import "../chunk-WRMZRO77.mjs";
16
12
 
17
13
  // src/sveltekit/server.ts
18
- function isSafeMethod(method) {
19
- const normalized = method.trim().toUpperCase();
20
- return normalized === "GET" || normalized === "HEAD";
21
- }
22
14
  async function issueCsrfCookie(event) {
23
- const runtime = getSecurityRuntime();
24
- const { config } = runtime;
25
- if (!config.csrf.enabled || !isSafeMethod(event.request.method)) {
26
- return;
15
+ const cookies = await prepareCsrfCookies(event.request, (name) => event.cookies.get(name));
16
+ for (const cookie of cookies) {
17
+ event.cookies.set(cookie.name, cookie.value, cookie.options);
27
18
  }
28
- event.cookies.set(config.csrf.cookie, await csrf.token(event.request), {
29
- httpOnly: false,
30
- path: "/",
31
- sameSite: "lax",
32
- secure: isSecureRequest(event.request)
33
- });
34
- event.cookies.set(SECURITY_CLIENT_CONFIG_COOKIE, serializeSecurityClientConfig(createSecurityClientConfig(config)), {
35
- httpOnly: false,
36
- path: "/",
37
- sameSite: "lax",
38
- secure: isSecureRequest(event.request)
39
- });
40
19
  }
41
20
  function createCsrfErrorResponse(error) {
42
21
  return new Response(error.message, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/security",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
4
4
  "description": "Holo-JS Framework - CSRF and rate-limit contracts, config, and runtime seams",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,12 +58,12 @@
58
58
  "test": "vitest --run --coverage"
59
59
  },
60
60
  "dependencies": {
61
- "@holo-js/config": "^0.3.15",
62
- "@holo-js/kernel": "^0.3.15"
61
+ "@holo-js/config": "^0.3.17",
62
+ "@holo-js/kernel": "^0.3.17"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "h3": "^1.15.11",
66
- "next": "^16.2.4",
66
+ "next": "^16.3.5",
67
67
  "ioredis": "^5.4.2"
68
68
  },
69
69
  "peerDependenciesMeta": {
@@ -81,12 +81,12 @@
81
81
  "@types/node": "^22.10.2",
82
82
  "h3": "^1.15.11",
83
83
  "ioredis": "^5.4.2",
84
- "next": "^16.2.4",
84
+ "next": "^16.3.5",
85
85
  "tsup": "^8.3.5",
86
86
  "typescript": "^5.7.2",
87
87
  "vitest": "^4.1.5"
88
88
  },
89
89
  "engines": {
90
- "node": ">=22.12.0"
90
+ "node": "^22.12.0 || ^24.0.0 || ^26.0.0"
91
91
  }
92
92
  }