@holo-js/security 0.3.16 → 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.
- package/dist/chunk-5M3RHQIG.mjs +45 -0
- package/dist/{chunk-AGICNQRJ.mjs → chunk-FQ4X7X6L.mjs} +1 -1
- package/dist/{chunk-YIBZOYSR.mjs → chunk-G3HBF4Q5.mjs} +2 -2
- package/dist/{chunk-WDTNP5YU.mjs → chunk-WRMZRO77.mjs} +5 -1
- package/dist/config.mjs +1 -1
- package/dist/contracts.mjs +2 -2
- package/dist/index.mjs +3 -3
- package/dist/next/server.mjs +11 -36
- package/dist/nuxt/server.mjs +10 -34
- package/dist/sveltekit/server.mjs +10 -31
- package/package.json +3 -3
|
@@ -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
|
+
};
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
SecurityRateLimitError,
|
|
4
4
|
ip,
|
|
5
5
|
limit
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FQ4X7X6L.mjs";
|
|
7
7
|
import {
|
|
8
8
|
normalizeCorsConfig,
|
|
9
9
|
normalizeSecurityConfig
|
|
10
|
-
} from "./chunk-
|
|
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
|
-
) :
|
|
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
package/dist/contracts.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
token,
|
|
35
35
|
verify,
|
|
36
36
|
verifySignedToken
|
|
37
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
57
|
+
} from "./chunk-WRMZRO77.mjs";
|
|
58
58
|
export {
|
|
59
59
|
SecurityCsrfError,
|
|
60
60
|
SecurityRateLimitError,
|
package/dist/next/server.mjs
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
getSecurityRuntime,
|
|
5
|
-
isSecureRequest,
|
|
6
|
-
protect
|
|
7
|
-
} from "../chunk-YIBZOYSR.mjs";
|
|
2
|
+
prepareCsrfCookies
|
|
3
|
+
} from "../chunk-5M3RHQIG.mjs";
|
|
8
4
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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-
|
|
16
|
-
import "../chunk-
|
|
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
|
|
40
|
-
if (
|
|
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
|
|
53
|
-
|
|
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
|
}
|
package/dist/nuxt/server.mjs
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
getSecurityRuntime,
|
|
5
|
-
isSecureRequest,
|
|
6
|
-
protect
|
|
7
|
-
} from "../chunk-YIBZOYSR.mjs";
|
|
2
|
+
prepareCsrfCookies
|
|
3
|
+
} from "../chunk-5M3RHQIG.mjs";
|
|
8
4
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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-
|
|
16
|
-
import "../chunk-
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
isSecureRequest,
|
|
5
|
-
protect
|
|
6
|
-
} from "../chunk-YIBZOYSR.mjs";
|
|
2
|
+
prepareCsrfCookies
|
|
3
|
+
} from "../chunk-5M3RHQIG.mjs";
|
|
7
4
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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-
|
|
15
|
-
import "../chunk-
|
|
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
|
|
24
|
-
const
|
|
25
|
-
|
|
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.
|
|
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,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.17",
|
|
62
|
+
"@holo-js/kernel": "^0.3.17"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"h3": "^1.15.11",
|