nuxt-security-module 4.4.0 → 4.5.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/README.md +13 -1
- package/dist/constants.js +7 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +101 -0
- package/dist/models/AllowedMethodsRestricterOptions.d.ts +3 -0
- package/dist/models/BasicAuthOptions.d.ts +7 -0
- package/dist/models/ContentSecurityPolicy.d.ts +1 -0
- package/dist/models/CorsHandlerOptions.d.ts +11 -0
- package/dist/models/CrossOriginEmbedderPolicy.d.ts +6 -0
- package/dist/models/CrossOriginEmbedderPolicy.js +7 -0
- package/dist/models/CsrfOptions.d.ts +4 -0
- package/dist/models/ModuleOptions.d.ts +25 -0
- package/dist/models/PermissionsPolicy.d.ts +1 -0
- package/dist/models/RateLimitWindow.d.ts +4 -0
- package/dist/models/RateLimiterOptions.d.ts +4 -0
- package/dist/models/RateLimiterStore.d.ts +4 -0
- package/dist/models/RequestSizeLimiterOptions.d.ts +3 -0
- package/dist/models/SecurityHeaders.d.ts +20 -0
- package/dist/models/SecurityRouteRules.d.ts +2 -0
- package/dist/models/SecurityRules.d.ts +2 -0
- package/dist/models/SsgElement.d.ts +1 -0
- package/dist/models/SsgHashSource.d.ts +2 -0
- package/dist/models/SsgOptions.d.ts +5 -0
- package/dist/models/StrictTransportSecurity.d.ts +5 -0
- package/dist/models/XFrameOptions.d.ts +5 -0
- package/dist/models/XFrameOptions.js +6 -0
- package/dist/runtime/constants.d.ts +20 -0
- package/dist/runtime/constants.js +51 -0
- package/dist/runtime/nitro/plugins/hidePoweredBy.d.ts +3 -0
- package/dist/runtime/nitro/plugins/hidePoweredBy.js +12 -0
- package/dist/runtime/nitro/plugins/nonce.d.ts +3 -0
- package/dist/runtime/nitro/plugins/nonce.js +21 -0
- package/dist/runtime/nitro/plugins/routeRules.d.ts +3 -0
- package/dist/runtime/nitro/plugins/routeRules.js +20 -0
- package/dist/runtime/nitro/plugins/securityHeaders.d.ts +3 -0
- package/dist/runtime/nitro/plugins/securityHeaders.js +24 -0
- package/dist/runtime/nitro/plugins/ssgHashes.d.ts +3 -0
- package/dist/runtime/nitro/plugins/ssgHashes.js +21 -0
- package/dist/runtime/nitro/plugins/subresourceIntegrity.d.ts +2 -0
- package/dist/runtime/nitro/plugins/subresourceIntegrity.js +13 -0
- package/dist/runtime/server/middleware/allowedMethodsRestricter.d.ts +3 -0
- package/dist/runtime/server/middleware/allowedMethodsRestricter.js +14 -0
- package/dist/runtime/server/middleware/basicAuth.d.ts +3 -0
- package/dist/runtime/server/middleware/basicAuth.js +22 -0
- package/dist/runtime/server/middleware/corsHandler.d.ts +3 -0
- package/dist/runtime/server/middleware/corsHandler.js +20 -0
- package/dist/runtime/server/middleware/csrf.d.ts +3 -0
- package/dist/runtime/server/middleware/csrf.js +27 -0
- package/dist/runtime/server/middleware/rateLimiter.d.ts +3 -0
- package/dist/runtime/server/middleware/rateLimiter.js +21 -0
- package/dist/runtime/server/middleware/requestSizeLimiter.d.ts +3 -0
- package/dist/runtime/server/middleware/requestSizeLimiter.js +18 -0
- package/dist/runtime/services/checkIsBase64.d.ts +1 -0
- package/dist/runtime/services/checkIsBase64.js +5 -0
- package/dist/runtime/services/checkIsBasicAuthorized.d.ts +2 -0
- package/dist/runtime/services/checkIsBasicAuthorized.js +12 -0
- package/dist/runtime/services/checkIsBodyCounted.d.ts +1 -0
- package/dist/runtime/services/checkIsBodyCounted.js +4 -0
- package/dist/runtime/services/checkIsChallenged.d.ts +2 -0
- package/dist/runtime/services/checkIsChallenged.js +7 -0
- package/dist/runtime/services/checkIsCsrfTokenValid.d.ts +1 -0
- package/dist/runtime/services/checkIsCsrfTokenValid.js +7 -0
- package/dist/runtime/services/checkIsEqualInConstantTime.d.ts +1 -0
- package/dist/runtime/services/checkIsEqualInConstantTime.js +10 -0
- package/dist/runtime/services/checkIsMethodAllowed.d.ts +1 -0
- package/dist/runtime/services/checkIsMethodAllowed.js +3 -0
- package/dist/runtime/services/checkIsRecord.d.ts +1 -0
- package/dist/runtime/services/checkIsRecord.js +3 -0
- package/dist/runtime/services/checkIsRequestTooLarge.d.ts +2 -0
- package/dist/runtime/services/checkIsRequestTooLarge.js +7 -0
- package/dist/runtime/services/checkIsSecurityHeaderKey.d.ts +2 -0
- package/dist/runtime/services/checkIsSecurityHeaderKey.js +4 -0
- package/dist/runtime/services/checkIsStaticNonce.d.ts +1 -0
- package/dist/runtime/services/checkIsStaticNonce.js +4 -0
- package/dist/runtime/services/checkIsStringList.d.ts +1 -0
- package/dist/runtime/services/checkIsStringList.js +3 -0
- package/dist/runtime/services/collectSsgHashes.d.ts +3 -0
- package/dist/runtime/services/collectSsgHashes.js +18 -0
- package/dist/runtime/services/createInMemoryRateLimiterStore.d.ts +2 -0
- package/dist/runtime/services/createInMemoryRateLimiterStore.js +21 -0
- package/dist/runtime/services/generateHash.d.ts +1 -0
- package/dist/runtime/services/generateHash.js +8 -0
- package/dist/runtime/services/generateNonce.d.ts +1 -0
- package/dist/runtime/services/generateNonce.js +5 -0
- package/dist/runtime/services/generateRandomToken.d.ts +1 -0
- package/dist/runtime/services/generateRandomToken.js +7 -0
- package/dist/runtime/services/getBasicAuthCredentials.d.ts +4 -0
- package/dist/runtime/services/getBasicAuthCredentials.js +16 -0
- package/dist/runtime/services/getBasicAuthOptions.d.ts +2 -0
- package/dist/runtime/services/getBasicAuthOptions.js +16 -0
- package/dist/runtime/services/getRetryAfterSeconds.d.ts +3 -0
- package/dist/runtime/services/getRetryAfterSeconds.js +8 -0
- package/dist/runtime/services/getSecurityRules.d.ts +3 -0
- package/dist/runtime/services/getSecurityRules.js +14 -0
- package/dist/runtime/services/getSecurityState.d.ts +9 -0
- package/dist/runtime/services/getSecurityState.js +13 -0
- package/dist/runtime/services/getSsgHashSource.d.ts +4 -0
- package/dist/runtime/services/getSsgHashSource.js +26 -0
- package/dist/runtime/services/injectNonce.d.ts +1 -0
- package/dist/runtime/services/injectNonce.js +6 -0
- package/dist/runtime/services/injectSubresourceIntegrity.d.ts +1 -0
- package/dist/runtime/services/injectSubresourceIntegrity.js +21 -0
- package/dist/runtime/services/insertContentSecurityPolicyMeta.d.ts +2 -0
- package/dist/runtime/services/insertContentSecurityPolicyMeta.js +16 -0
- package/dist/runtime/services/mergeSecurityRules.d.ts +2 -0
- package/dist/runtime/services/mergeSecurityRules.js +10 -0
- package/dist/runtime/services/readHtmlTagAttribute.d.ts +1 -0
- package/dist/runtime/services/readHtmlTagAttribute.js +7 -0
- package/dist/runtime/services/rewriteHtmlTags.d.ts +1 -0
- package/dist/runtime/services/rewriteHtmlTags.js +8 -0
- package/dist/runtime/services/securityRouteMatcher.d.ts +4 -0
- package/dist/runtime/services/securityRouteMatcher.js +17 -0
- package/dist/runtime/services/serializeBasicAuthChallenge.d.ts +1 -0
- package/dist/runtime/services/serializeBasicAuthChallenge.js +3 -0
- package/dist/runtime/services/serializeContentSecurityPolicy.d.ts +2 -0
- package/dist/runtime/services/serializeContentSecurityPolicy.js +6 -0
- package/dist/runtime/services/serializePermissionsPolicy.d.ts +2 -0
- package/dist/runtime/services/serializePermissionsPolicy.js +3 -0
- package/dist/runtime/services/serializeSecurityHeaders.d.ts +2 -0
- package/dist/runtime/services/serializeSecurityHeaders.js +10 -0
- package/dist/runtime/services/serializeStrictTransportSecurity.d.ts +2 -0
- package/dist/runtime/services/serializeStrictTransportSecurity.js +7 -0
- package/dist/runtime/services/serializeValue.d.ts +1 -0
- package/dist/runtime/services/serializeValue.js +11 -0
- package/dist/runtime/services/stripHtmlTagAttribute.d.ts +1 -0
- package/dist/runtime/services/stripHtmlTagAttribute.js +4 -0
- package/dist/runtime/services/unquote.d.ts +1 -0
- package/dist/runtime/services/unquote.js +3 -0
- package/dist/runtime/services/updateContentSecurityPolicy.d.ts +3 -0
- package/dist/runtime/services/updateContentSecurityPolicy.js +14 -0
- package/dist/schemas/allowedMethodsRestricterOptionsSchema.js +4 -0
- package/dist/schemas/basicAuthOptionsSchema.js +11 -0
- package/dist/schemas/contentSecurityPolicySchema.js +4 -0
- package/dist/schemas/corsHandlerOptionsSchema.js +13 -0
- package/dist/schemas/csrfOptionsSchema.js +8 -0
- package/dist/schemas/moduleOptionsSchema.js +28 -0
- package/dist/schemas/permissionsPolicySchema.js +4 -0
- package/dist/schemas/rateLimiterOptionsSchema.js +7 -0
- package/dist/schemas/requestSizeLimiterOptionsSchema.js +4 -0
- package/dist/schemas/securityHeadersSchema.js +24 -0
- package/dist/schemas/ssgOptionsSchema.js +8 -0
- package/dist/schemas/strictTransportSecurityOptionsSchema.js +8 -0
- package/dist/services/computeSubresourceIntegrityHashes.js +23 -0
- package/dist/services/constants.js +3 -0
- package/dist/services/formatLeafIssue.js +5 -0
- package/dist/services/formatZodIssues.js +5 -0
- package/dist/services/getDeepestPathLength.js +3 -0
- package/dist/services/getDefaultModuleOptions.js +35 -0
- package/dist/services/getDefaultSecurityHeaders.js +84 -0
- package/dist/services/getLeafIssues.js +11 -0
- package/dist/services/getPluginTemplate.js +9 -0
- package/dist/services/getRemoveLoggersViteConfig.js +3 -0
- package/dist/services/getRouteRulesTemplate.js +3 -0
- package/dist/services/getRouteSecurityRules.js +16 -0
- package/dist/services/getStaticResourceHeaders.js +11 -0
- package/dist/services/getTypeTemplate.js +24 -0
- package/dist/services/mergeDirectives.js +12 -0
- package/dist/services/mergeFeatures.js +10 -0
- package/dist/services/mergeHeaders.js +14 -0
- package/dist/services/mergeModuleOptions.js +12 -0
- package/dist/services/mergeSources.js +9 -0
- package/dist/services/parseModuleOptions.js +9 -0
- package/package.json +5 -5
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CSRF_UNSAFE_METHODS } from "../../constants.js";
|
|
2
|
+
import { checkIsCsrfTokenValid } from "../../services/checkIsCsrfTokenValid.js";
|
|
3
|
+
import { generateRandomToken } from "../../services/generateRandomToken.js";
|
|
4
|
+
import { getSecurityRules } from "../../services/getSecurityRules.js";
|
|
5
|
+
import { defineMiddleware } from "nitro";
|
|
6
|
+
import { HTTPError, getCookie, setCookie } from "nitro/h3";
|
|
7
|
+
// src/runtime/server/middleware/csrf.ts
|
|
8
|
+
const csrf = defineMiddleware((event) => {
|
|
9
|
+
const rules = getSecurityRules(event);
|
|
10
|
+
if (!rules?.enabled || !rules.csrf) return;
|
|
11
|
+
const { cookieName, headerName } = rules.csrf;
|
|
12
|
+
const cookieToken = getCookie(event, cookieName);
|
|
13
|
+
if (!CSRF_UNSAFE_METHODS.includes(event.req.method)) {
|
|
14
|
+
if (cookieToken === void 0) setCookie(event, cookieName, generateRandomToken(32), {
|
|
15
|
+
httpOnly: false,
|
|
16
|
+
path: "/",
|
|
17
|
+
sameSite: "strict",
|
|
18
|
+
secure: event.url.protocol === "https:"
|
|
19
|
+
});
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (!checkIsCsrfTokenValid(cookieToken, event.req.headers.get(headerName))) throw new HTTPError({
|
|
23
|
+
status: 403,
|
|
24
|
+
statusText: "Forbidden"
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
export { csrf as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MAX_RATE_LIMIT_ENTRIES } from "../../constants.js";
|
|
2
|
+
import { createInMemoryRateLimiterStore } from "../../services/createInMemoryRateLimiterStore.js";
|
|
3
|
+
import { getRetryAfterSeconds } from "../../services/getRetryAfterSeconds.js";
|
|
4
|
+
import { getSecurityRules } from "../../services/getSecurityRules.js";
|
|
5
|
+
import { defineMiddleware } from "nitro";
|
|
6
|
+
import { HTTPError, getRequestIP } from "nitro/h3";
|
|
7
|
+
// src/runtime/server/middleware/rateLimiter.ts
|
|
8
|
+
const rateLimiterStore = createInMemoryRateLimiterStore(MAX_RATE_LIMIT_ENTRIES);
|
|
9
|
+
const rateLimiter = defineMiddleware(async (event) => {
|
|
10
|
+
const rules = getSecurityRules(event);
|
|
11
|
+
if (!rules?.enabled || !rules.rateLimiter) return;
|
|
12
|
+
const key = getRequestIP(event, { xForwardedFor: false }) ?? "unknown";
|
|
13
|
+
const retryAfterSeconds = await getRetryAfterSeconds(rateLimiterStore, key, rules.rateLimiter);
|
|
14
|
+
if (retryAfterSeconds === void 0) return;
|
|
15
|
+
throw new HTTPError({
|
|
16
|
+
headers: { "Retry-After": String(retryAfterSeconds) },
|
|
17
|
+
status: 429,
|
|
18
|
+
statusText: "Too Many Requests"
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
export { rateLimiter as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { checkIsBodyCounted } from "../../services/checkIsBodyCounted.js";
|
|
2
|
+
import { checkIsRequestTooLarge } from "../../services/checkIsRequestTooLarge.js";
|
|
3
|
+
import { getSecurityRules } from "../../services/getSecurityRules.js";
|
|
4
|
+
import { defineMiddleware } from "nitro";
|
|
5
|
+
import { HTTPError, assertBodySize } from "nitro/h3";
|
|
6
|
+
// src/runtime/server/middleware/requestSizeLimiter.ts
|
|
7
|
+
const requestSizeLimiter = defineMiddleware((event) => {
|
|
8
|
+
const rules = getSecurityRules(event);
|
|
9
|
+
if (!rules?.enabled || !rules.requestSizeLimiter) return;
|
|
10
|
+
const { method } = event.req;
|
|
11
|
+
const contentLength = event.req.headers.get("content-length");
|
|
12
|
+
if (checkIsRequestTooLarge(method, contentLength, rules.requestSizeLimiter)) throw new HTTPError({
|
|
13
|
+
status: 413,
|
|
14
|
+
statusText: "Payload Too Large"
|
|
15
|
+
});
|
|
16
|
+
if (checkIsBodyCounted(method, contentLength)) assertBodySize(event, rules.requestSizeLimiter.maxRequestSizeInBytes);
|
|
17
|
+
});
|
|
18
|
+
export { requestSizeLimiter as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsBase64: (value: string) => boolean;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// src/runtime/services/checkIsBase64.ts
|
|
2
|
+
const BASE64_REGEX = /^[\d+/A-Za-z]*={0,2}$/u;
|
|
3
|
+
const BASE64_QUANTUM_LENGTH = 4;
|
|
4
|
+
const checkIsBase64 = (value) => BASE64_REGEX.test(value) && (value.endsWith("=") ? value.length % BASE64_QUANTUM_LENGTH === 0 : value.length % BASE64_QUANTUM_LENGTH !== 1);
|
|
5
|
+
export { checkIsBase64 };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { checkIsEqualInConstantTime } from "./checkIsEqualInConstantTime.js";
|
|
2
|
+
import { getBasicAuthCredentials } from "./getBasicAuthCredentials.js";
|
|
3
|
+
// src/runtime/services/checkIsBasicAuthorized.ts
|
|
4
|
+
const checkIsBasicAuthorized = (authorization, { name, pass }) => {
|
|
5
|
+
if (!name || !pass) return false;
|
|
6
|
+
const credentials = getBasicAuthCredentials(authorization);
|
|
7
|
+
if (!credentials) return false;
|
|
8
|
+
const isNameEqual = checkIsEqualInConstantTime(credentials.name, name);
|
|
9
|
+
const isPassEqual = checkIsEqualInConstantTime(credentials.pass, pass);
|
|
10
|
+
return isNameEqual && isPassEqual;
|
|
11
|
+
};
|
|
12
|
+
export { checkIsBasicAuthorized };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsBodyCounted: (method: string, contentLength: null | string) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// src/runtime/services/checkIsChallenged.ts
|
|
2
|
+
const checkIsChallenged = (path, { exclude = [], include = [] }) => {
|
|
3
|
+
const isIncluded = include.some((prefix) => path.startsWith(prefix));
|
|
4
|
+
const isExcluded = exclude.some((prefix) => path.startsWith(prefix));
|
|
5
|
+
return isIncluded || !isExcluded;
|
|
6
|
+
};
|
|
7
|
+
export { checkIsChallenged };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsCsrfTokenValid: (cookieToken: string | undefined, headerToken: null | string) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { checkIsEqualInConstantTime } from "./checkIsEqualInConstantTime.js";
|
|
2
|
+
// src/runtime/services/checkIsCsrfTokenValid.ts
|
|
3
|
+
const checkIsCsrfTokenValid = (cookieToken, headerToken) => {
|
|
4
|
+
if (!cookieToken || !headerToken) return false;
|
|
5
|
+
return checkIsEqualInConstantTime(cookieToken, headerToken);
|
|
6
|
+
};
|
|
7
|
+
export { checkIsCsrfTokenValid };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsEqualInConstantTime: (first: string, second: string) => boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/runtime/services/checkIsEqualInConstantTime.ts
|
|
2
|
+
const checkIsEqualInConstantTime = (first, second) => {
|
|
3
|
+
const firstBytes = new TextEncoder().encode(first);
|
|
4
|
+
const secondBytes = new TextEncoder().encode(second);
|
|
5
|
+
if (firstBytes.length !== secondBytes.length) return false;
|
|
6
|
+
let difference = 0;
|
|
7
|
+
for (const [index, byte] of firstBytes.entries()) difference |= byte ^ (secondBytes[index] ?? 0);
|
|
8
|
+
return difference === 0;
|
|
9
|
+
};
|
|
10
|
+
export { checkIsEqualInConstantTime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsMethodAllowed: (method: string, methods: string[]) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsRecord: (value: unknown) => value is Record<string, unknown>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SIZE_LIMITED_METHODS } from "../constants.js";
|
|
2
|
+
// src/runtime/services/checkIsRequestTooLarge.ts
|
|
3
|
+
const checkIsRequestTooLarge = (method, contentLength, { maxRequestSizeInBytes }) => {
|
|
4
|
+
if (!SIZE_LIMITED_METHODS.includes(method) || contentLength === null) return false;
|
|
5
|
+
return Math.trunc(Number(contentLength)) > maxRequestSizeInBytes;
|
|
6
|
+
};
|
|
7
|
+
export { checkIsRequestTooLarge };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsStaticNonce: (source: string) => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const checkIsStringList: (value: unknown) => value is string[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HTML_TAG_REGEX } from "../constants.js";
|
|
2
|
+
import { getSsgHashSource } from "./getSsgHashSource.js";
|
|
3
|
+
// src/runtime/services/collectSsgHashes.ts
|
|
4
|
+
const collectSsgHashes = async (html, options) => {
|
|
5
|
+
const elements = Array.from(html.matchAll(HTML_TAG_REGEX), ([, elementName, elementAttributes, , body, tagName, tagAttributes]) => [
|
|
6
|
+
(elementName ?? tagName ?? "").toLowerCase(),
|
|
7
|
+
elementAttributes ?? tagAttributes ?? "",
|
|
8
|
+
body
|
|
9
|
+
]);
|
|
10
|
+
const hashSources = await Promise.all(elements.map((element) => getSsgHashSource(element, options)));
|
|
11
|
+
const hashes = {
|
|
12
|
+
script: [],
|
|
13
|
+
style: []
|
|
14
|
+
};
|
|
15
|
+
for (const hashSource of hashSources) if (hashSource) hashes[hashSource[0]].push(hashSource[1]);
|
|
16
|
+
return hashes;
|
|
17
|
+
};
|
|
18
|
+
export { collectSsgHashes };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/runtime/services/createInMemoryRateLimiterStore.ts
|
|
2
|
+
const createInMemoryRateLimiterStore = (maxEntries) => {
|
|
3
|
+
const windows = /* @__PURE__ */ new Map();
|
|
4
|
+
return { increment: (key, intervalInMilliseconds) => {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
const current = windows.get(key);
|
|
7
|
+
const window = current && current.resetAt > now ? {
|
|
8
|
+
count: current.count + 1,
|
|
9
|
+
resetAt: current.resetAt
|
|
10
|
+
} : {
|
|
11
|
+
count: 1,
|
|
12
|
+
resetAt: now + intervalInMilliseconds
|
|
13
|
+
};
|
|
14
|
+
windows.delete(key);
|
|
15
|
+
windows.set(key, window);
|
|
16
|
+
const [oldestKey] = windows.keys();
|
|
17
|
+
if (windows.size > maxEntries && oldestKey !== void 0) windows.delete(oldestKey);
|
|
18
|
+
return Promise.resolve(window);
|
|
19
|
+
} };
|
|
20
|
+
};
|
|
21
|
+
export { createInMemoryRateLimiterStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateHash: (content: string | Uint8Array, hashAlgorithm: string) => Promise<string>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/runtime/services/generateHash.ts
|
|
2
|
+
const generateHash = async (content, hashAlgorithm) => {
|
|
3
|
+
const bytes = typeof content === "string" ? new TextEncoder().encode(content) : new Uint8Array(content);
|
|
4
|
+
const hashBuffer = await globalThis.crypto.subtle.digest(hashAlgorithm, bytes);
|
|
5
|
+
const base64 = btoa(String.fromCodePoint(...new Uint8Array(hashBuffer)));
|
|
6
|
+
return `${hashAlgorithm.replace("-", "").toLowerCase()}-${base64}`;
|
|
7
|
+
};
|
|
8
|
+
export { generateHash };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateNonce: () => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateRandomToken: (byteLength: number) => string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// src/runtime/services/generateRandomToken.ts
|
|
2
|
+
const generateRandomToken = (byteLength) => {
|
|
3
|
+
const bytes = new Uint8Array(byteLength);
|
|
4
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
5
|
+
return btoa(String.fromCodePoint(...bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/[=]+$/u, "");
|
|
6
|
+
};
|
|
7
|
+
export { generateRandomToken };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BASIC_AUTHORIZATION_SCHEME } from "../constants.js";
|
|
2
|
+
import { checkIsBase64 } from "./checkIsBase64.js";
|
|
3
|
+
// src/runtime/services/getBasicAuthCredentials.ts
|
|
4
|
+
const getBasicAuthCredentials = (authorization) => {
|
|
5
|
+
if (!authorization?.startsWith(`Basic `)) return void 0;
|
|
6
|
+
const encoded = authorization.slice(BASIC_AUTHORIZATION_SCHEME.length + 1).trim();
|
|
7
|
+
if (!checkIsBase64(encoded)) return void 0;
|
|
8
|
+
const decoded = new TextDecoder().decode(Uint8Array.fromBase64(encoded));
|
|
9
|
+
const separatorIndex = decoded.indexOf(":");
|
|
10
|
+
if (separatorIndex === -1) return void 0;
|
|
11
|
+
return {
|
|
12
|
+
name: decoded.slice(0, separatorIndex),
|
|
13
|
+
pass: decoded.slice(separatorIndex + 1)
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export { getBasicAuthCredentials };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { checkIsRecord } from "./checkIsRecord.js";
|
|
2
|
+
import { checkIsStringList } from "./checkIsStringList.js";
|
|
3
|
+
// src/runtime/services/getBasicAuthOptions.ts
|
|
4
|
+
const getBasicAuthOptions = (privateRuntimeConfig) => {
|
|
5
|
+
if (!checkIsRecord(privateRuntimeConfig) || !checkIsRecord(privateRuntimeConfig.basicAuth)) return void 0;
|
|
6
|
+
const { exclude, include, message, name, pass } = privateRuntimeConfig.basicAuth;
|
|
7
|
+
if (typeof name !== "string" || typeof pass !== "string") return void 0;
|
|
8
|
+
return {
|
|
9
|
+
...checkIsStringList(exclude) && { exclude },
|
|
10
|
+
...checkIsStringList(include) && { include },
|
|
11
|
+
...typeof message === "string" && { message },
|
|
12
|
+
name,
|
|
13
|
+
pass
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export { getBasicAuthOptions };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { RateLimiterOptions } from "../../models/RateLimiterOptions.js";
|
|
2
|
+
import { RateLimiterStore } from "../../models/RateLimiterStore.js";
|
|
3
|
+
export declare const getRetryAfterSeconds: (store: RateLimiterStore, key: string, { intervalInMilliseconds, tokensPerInterval }: RateLimiterOptions) => Promise<number | undefined>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/runtime/services/getRetryAfterSeconds.ts
|
|
2
|
+
const MILLISECONDS_PER_SECOND = 1e3;
|
|
3
|
+
const getRetryAfterSeconds = async (store, key, { intervalInMilliseconds, tokensPerInterval }) => {
|
|
4
|
+
const { count, resetAt } = await store.increment(key, intervalInMilliseconds);
|
|
5
|
+
if (count <= tokensPerInterval) return void 0;
|
|
6
|
+
return Math.max(1, Math.ceil((resetAt - Date.now()) / MILLISECONDS_PER_SECOND));
|
|
7
|
+
};
|
|
8
|
+
export { getRetryAfterSeconds };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mergeSecurityRules } from "./mergeSecurityRules.js";
|
|
2
|
+
import { findSecurityRules } from "./securityRouteMatcher.js";
|
|
3
|
+
// src/runtime/services/getSecurityRules.ts
|
|
4
|
+
const securityRulesByEvent = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
const getSecurityRules = (event) => {
|
|
6
|
+
const cachedRules = securityRulesByEvent.get(event);
|
|
7
|
+
if (cachedRules) return cachedRules;
|
|
8
|
+
const matchedRules = findSecurityRules(event.url.pathname);
|
|
9
|
+
if (!matchedRules) return void 0;
|
|
10
|
+
const rules = mergeSecurityRules(...matchedRules.toReversed());
|
|
11
|
+
securityRulesByEvent.set(event, rules);
|
|
12
|
+
return rules;
|
|
13
|
+
};
|
|
14
|
+
export { getSecurityRules };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/runtime/services/getSecurityState.ts
|
|
2
|
+
const securityStateByEvent = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
const getSecurityState = (event) => {
|
|
4
|
+
const cachedState = securityStateByEvent.get(event);
|
|
5
|
+
if (cachedState) return cachedState;
|
|
6
|
+
const state = { hashes: {
|
|
7
|
+
script: [],
|
|
8
|
+
style: []
|
|
9
|
+
} };
|
|
10
|
+
securityStateByEvent.set(event, state);
|
|
11
|
+
return state;
|
|
12
|
+
};
|
|
13
|
+
export { getSecurityState };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SsgElement } from "../../models/SsgElement.js";
|
|
2
|
+
import { SsgHashSource } from "../../models/SsgHashSource.js";
|
|
3
|
+
import { SsgOptions } from "../../models/SsgOptions.js";
|
|
4
|
+
export declare const getSsgHashSource: ([name, attributes, body]: SsgElement, { hashScripts, hashStyles }: SsgOptions) => Promise<SsgHashSource | undefined>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CSP_HASH_ALGORITHM } from "../constants.js";
|
|
2
|
+
import { generateHash } from "./generateHash.js";
|
|
3
|
+
import { readHtmlTagAttribute } from "./readHtmlTagAttribute.js";
|
|
4
|
+
// src/runtime/services/getSsgHashSource.ts
|
|
5
|
+
const SCRIPT_PRELOAD_TYPES = /* @__PURE__ */ new Set([
|
|
6
|
+
"audioworklet",
|
|
7
|
+
"paintworklet",
|
|
8
|
+
"script",
|
|
9
|
+
"xslt"
|
|
10
|
+
]);
|
|
11
|
+
const getSsgHashSource = async ([name, attributes, body], { hashScripts, hashStyles }) => {
|
|
12
|
+
const integrity = readHtmlTagAttribute(attributes, "integrity");
|
|
13
|
+
if (name === "script" && hashScripts) {
|
|
14
|
+
const source = readHtmlTagAttribute(attributes, "src");
|
|
15
|
+
if (source !== void 0 && integrity !== void 0) return ["script", `'${integrity}'`];
|
|
16
|
+
if (source === void 0 && body) return ["script", `'${await generateHash(body, CSP_HASH_ALGORITHM)}'`];
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (name === "style" && hashStyles && body) return ["style", `'${await generateHash(body, CSP_HASH_ALGORITHM)}'`];
|
|
20
|
+
if (name !== "link" || integrity === void 0) return void 0;
|
|
21
|
+
const rel = readHtmlTagAttribute(attributes, "rel");
|
|
22
|
+
const as = readHtmlTagAttribute(attributes, "as") ?? "";
|
|
23
|
+
if (rel === "stylesheet" && hashStyles) return ["style", `'${integrity}'`];
|
|
24
|
+
if (hashScripts && (rel === "modulepreload" || rel === "preload" && SCRIPT_PRELOAD_TYPES.has(as))) return ["script", `'${integrity}'`];
|
|
25
|
+
};
|
|
26
|
+
export { getSsgHashSource };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const injectNonce: (html: string, nonce: string) => string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { NONCE_TAG_NAMES } from "../constants.js";
|
|
2
|
+
import { rewriteHtmlTags } from "./rewriteHtmlTags.js";
|
|
3
|
+
import { stripHtmlTagAttribute } from "./stripHtmlTagAttribute.js";
|
|
4
|
+
// src/runtime/services/injectNonce.ts
|
|
5
|
+
const injectNonce = (html, nonce) => rewriteHtmlTags(html, (tagName, attributes) => NONCE_TAG_NAMES.includes(tagName.toLowerCase()) ? ` nonce="${nonce}"${stripHtmlTagAttribute(attributes, "nonce")}` : void 0);
|
|
6
|
+
export { injectNonce };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const injectSubresourceIntegrity: (html: string, hashes: Record<string, string>) => string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readHtmlTagAttribute } from "./readHtmlTagAttribute.js";
|
|
2
|
+
import { rewriteHtmlTags } from "./rewriteHtmlTags.js";
|
|
3
|
+
// src/runtime/services/injectSubresourceIntegrity.ts
|
|
4
|
+
const RESOURCE_ATTRIBUTE_NAMES = {
|
|
5
|
+
link: "href",
|
|
6
|
+
script: "src"
|
|
7
|
+
};
|
|
8
|
+
const INTEGRITY_LINK_RELS = /* @__PURE__ */ new Set([
|
|
9
|
+
"modulepreload",
|
|
10
|
+
"preload",
|
|
11
|
+
"stylesheet"
|
|
12
|
+
]);
|
|
13
|
+
const injectSubresourceIntegrity = (html, hashes) => rewriteHtmlTags(html, (tagName, attributes) => {
|
|
14
|
+
const resourceAttributeName = RESOURCE_ATTRIBUTE_NAMES[tagName.toLowerCase()];
|
|
15
|
+
if (!resourceAttributeName || readHtmlTagAttribute(attributes, "integrity") !== void 0) return void 0;
|
|
16
|
+
const resource = readHtmlTagAttribute(attributes, resourceAttributeName);
|
|
17
|
+
if (resource === void 0 || !Object.hasOwn(hashes, resource)) return void 0;
|
|
18
|
+
if (tagName.toLowerCase() === "link" && !INTEGRITY_LINK_RELS.has(readHtmlTagAttribute(attributes, "rel") ?? "")) return void 0;
|
|
19
|
+
return ` integrity="${hashes[resource]}"${attributes}`;
|
|
20
|
+
});
|
|
21
|
+
export { injectSubresourceIntegrity };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { serializeContentSecurityPolicy } from "./serializeContentSecurityPolicy.js";
|
|
2
|
+
// src/runtime/services/insertContentSecurityPolicyMeta.ts
|
|
3
|
+
const META_CONTENT_SECURITY_POLICY_REGEX = /<meta\s+http-equiv=["']Content-Security-Policy["'][^>]*>/iu;
|
|
4
|
+
const HEAD_CHARSET_REGEX = /<meta\s+charset=["'][^"']*["'][^>]*>/iu;
|
|
5
|
+
const HEAD_OPEN_REGEX = /<head\b[^>]*>/iu;
|
|
6
|
+
const insertContentSecurityPolicyMeta = (html, policy) => {
|
|
7
|
+
const metaTag = `<meta http-equiv="Content-Security-Policy" content="${serializeContentSecurityPolicy({
|
|
8
|
+
...policy,
|
|
9
|
+
"frame-ancestors": false
|
|
10
|
+
})}">`;
|
|
11
|
+
if (META_CONTENT_SECURITY_POLICY_REGEX.test(html)) return html.replace(META_CONTENT_SECURITY_POLICY_REGEX, metaTag);
|
|
12
|
+
const insertion = HEAD_CHARSET_REGEX.exec(html) ?? HEAD_OPEN_REGEX.exec(html);
|
|
13
|
+
const insertAt = insertion ? insertion.index + insertion[0].length : 0;
|
|
14
|
+
return html.slice(0, insertAt) + metaTag + html.slice(insertAt);
|
|
15
|
+
};
|
|
16
|
+
export { insertContentSecurityPolicyMeta };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createDefu } from "defu";
|
|
2
|
+
// src/runtime/services/mergeSecurityRules.ts
|
|
3
|
+
const mergeRulesWithReplacedArrays = createDefu((object, key, value) => {
|
|
4
|
+
if (Array.isArray(object[key]) || Array.isArray(value)) {
|
|
5
|
+
object[key] = value;
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
const mergeSecurityRules = (...rules) => mergeRulesWithReplacedArrays({}, ...rules);
|
|
10
|
+
export { mergeSecurityRules };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readHtmlTagAttribute: (attributes: string, attributeName: string) => string | undefined;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HTML_ATTRIBUTE_REGEX } from "../constants.js";
|
|
2
|
+
import { unquote } from "./unquote.js";
|
|
3
|
+
// src/runtime/services/readHtmlTagAttribute.ts
|
|
4
|
+
const readHtmlTagAttribute = (attributes, attributeName) => {
|
|
5
|
+
for (const [, name, , value] of attributes.matchAll(HTML_ATTRIBUTE_REGEX)) if (name?.toLowerCase() === attributeName) return unquote(value ?? "");
|
|
6
|
+
};
|
|
7
|
+
export { readHtmlTagAttribute };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const rewriteHtmlTags: (html: string, rewriteAttributes: (tagName: string, attributes: string) => string | undefined) => string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { HTML_TAG_REGEX } from "../constants.js";
|
|
2
|
+
// src/runtime/services/rewriteHtmlTags.ts
|
|
3
|
+
const rewriteHtmlTags = (html, rewriteAttributes) => html.replace(HTML_TAG_REGEX, (match, elementName, elementAttributes, elementTrailer, body, tagName, tagAttributes, tagTrailer) => {
|
|
4
|
+
if (elementName) return `<${elementName}${rewriteAttributes(elementName, elementAttributes ?? "") ?? elementAttributes}${elementTrailer}>${body}</${elementName}>`;
|
|
5
|
+
const attributes = rewriteAttributes(tagName ?? "", tagAttributes ?? "");
|
|
6
|
+
return attributes === void 0 ? match : `<${tagName}${attributes}${tagTrailer}>`;
|
|
7
|
+
});
|
|
8
|
+
export { rewriteHtmlTags };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SecurityRules } from "../../models/SecurityRules.js";
|
|
2
|
+
export declare const setSecurityRouteMatcher: (routes: Record<string, SecurityRules>) => void;
|
|
3
|
+
export declare const setSecurityRouteMatcherFailure: (cause: unknown) => void;
|
|
4
|
+
export declare const findSecurityRules: (pathname: string) => SecurityRules[] | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { addRoute, createRouter, findAllRoutes } from "rou3";
|
|
2
|
+
// src/runtime/services/securityRouteMatcher.ts
|
|
3
|
+
let securityRouter;
|
|
4
|
+
let securityRouteFailure;
|
|
5
|
+
const setSecurityRouteMatcher = (routes) => {
|
|
6
|
+
const router = createRouter();
|
|
7
|
+
for (const [pattern, rules] of Object.entries(routes)) addRoute(router, "", pattern, rules);
|
|
8
|
+
securityRouter = router;
|
|
9
|
+
};
|
|
10
|
+
const setSecurityRouteMatcherFailure = (cause) => {
|
|
11
|
+
securityRouteFailure = new TypeError("nuxt-security-module: the security route rules did not resolve", { cause });
|
|
12
|
+
};
|
|
13
|
+
const findSecurityRules = (pathname) => {
|
|
14
|
+
if (securityRouteFailure) throw securityRouteFailure;
|
|
15
|
+
return securityRouter && findAllRoutes(securityRouter, "", pathname).flatMap(({ data }) => data ? [data] : []);
|
|
16
|
+
};
|
|
17
|
+
export { findSecurityRules, setSecurityRouteMatcher, setSecurityRouteMatcherFailure };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const serializeBasicAuthChallenge: (realm: string) => string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// src/runtime/services/serializeContentSecurityPolicy.ts
|
|
2
|
+
const serializeContentSecurityPolicy = (policy) => Object.entries(policy).filter(([, value]) => value !== false).map(([directive, value]) => {
|
|
3
|
+
if (directive === "upgrade-insecure-requests") return "upgrade-insecure-requests;";
|
|
4
|
+
return `${directive} ${Array.isArray(value) ? value.map((source) => source.trim()).join(" ") : ""};`;
|
|
5
|
+
}).join(" ");
|
|
6
|
+
export { serializeContentSecurityPolicy };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// src/runtime/services/serializePermissionsPolicy.ts
|
|
2
|
+
const serializePermissionsPolicy = (policy) => Object.entries(policy).flatMap(([feature, sources]) => sources === false ? [] : [`${feature}=(${(sources ?? []).join(" ")})`]).join(", ");
|
|
3
|
+
export { serializePermissionsPolicy };
|