nuxt-security-module 4.2.0 → 4.3.3

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.
Files changed (136) hide show
  1. package/README.md +28 -2
  2. package/dist/index.d.ts +15 -0
  3. package/dist/index.js +104 -0
  4. package/dist/models/AllowedMethodsRestricterOptions.d.ts +3 -0
  5. package/dist/models/BasicAuthOptions.d.ts +7 -0
  6. package/dist/models/ContentSecurityPolicy.d.ts +1 -0
  7. package/dist/models/CorsHandlerOptions.d.ts +11 -0
  8. package/dist/models/CrossOriginEmbedderPolicy.d.ts +6 -0
  9. package/dist/models/CrossOriginEmbedderPolicy.js +7 -0
  10. package/dist/models/CsrfOptions.d.ts +4 -0
  11. package/dist/models/ModuleOptions.d.ts +25 -0
  12. package/dist/models/PermissionsPolicy.d.ts +1 -0
  13. package/dist/models/RateLimitWindow.d.ts +4 -0
  14. package/dist/models/RateLimiterOptions.d.ts +4 -0
  15. package/dist/models/RateLimiterStore.d.ts +4 -0
  16. package/dist/models/RequestSizeLimiterOptions.d.ts +4 -0
  17. package/dist/models/SecurityHeaders.d.ts +20 -0
  18. package/dist/models/SecurityRouteRules.d.ts +2 -0
  19. package/dist/models/SecurityRules.d.ts +2 -0
  20. package/dist/models/SsgOptions.d.ts +7 -0
  21. package/dist/models/StrictTransportSecurity.d.ts +5 -0
  22. package/dist/models/XFrameOptions.d.ts +5 -0
  23. package/dist/models/XFrameOptions.js +6 -0
  24. package/dist/runtime/constants.d.ts +22 -0
  25. package/dist/runtime/constants.js +51 -0
  26. package/dist/runtime/nitro/plugins/hidePoweredBy.d.ts +3 -0
  27. package/dist/runtime/nitro/plugins/hidePoweredBy.js +12 -0
  28. package/dist/runtime/nitro/plugins/nonce.d.ts +3 -0
  29. package/dist/runtime/nitro/plugins/nonce.js +26 -0
  30. package/dist/runtime/nitro/plugins/routeRules.d.ts +3 -0
  31. package/dist/runtime/nitro/plugins/routeRules.js +16 -0
  32. package/dist/runtime/nitro/plugins/securityHeaders.d.ts +3 -0
  33. package/dist/runtime/nitro/plugins/securityHeaders.js +24 -0
  34. package/dist/runtime/nitro/plugins/ssgHashes.d.ts +3 -0
  35. package/dist/runtime/nitro/plugins/ssgHashes.js +26 -0
  36. package/dist/runtime/nitro/plugins/subresourceIntegrity.d.ts +2 -0
  37. package/dist/runtime/nitro/plugins/subresourceIntegrity.js +18 -0
  38. package/dist/runtime/server/middleware/allowedMethodsRestricter.d.ts +3 -0
  39. package/dist/runtime/server/middleware/allowedMethodsRestricter.js +14 -0
  40. package/dist/runtime/server/middleware/basicAuth.d.ts +3 -0
  41. package/dist/runtime/server/middleware/basicAuth.js +22 -0
  42. package/dist/runtime/server/middleware/corsHandler.d.ts +3 -0
  43. package/dist/runtime/server/middleware/corsHandler.js +19 -0
  44. package/dist/runtime/server/middleware/csrf.d.ts +3 -0
  45. package/dist/runtime/server/middleware/csrf.js +27 -0
  46. package/dist/runtime/server/middleware/rateLimiter.d.ts +3 -0
  47. package/dist/runtime/server/middleware/rateLimiter.js +21 -0
  48. package/dist/runtime/server/middleware/requestSizeLimiter.d.ts +3 -0
  49. package/dist/runtime/server/middleware/requestSizeLimiter.js +16 -0
  50. package/dist/runtime/services/checkIsBasicAuthorized.d.ts +2 -0
  51. package/dist/runtime/services/checkIsBasicAuthorized.js +11 -0
  52. package/dist/runtime/services/checkIsChallenged.d.ts +2 -0
  53. package/dist/runtime/services/checkIsChallenged.js +7 -0
  54. package/dist/runtime/services/checkIsCsrfTokenValid.d.ts +1 -0
  55. package/dist/runtime/services/checkIsCsrfTokenValid.js +7 -0
  56. package/dist/runtime/services/checkIsEqualInConstantTime.d.ts +1 -0
  57. package/dist/runtime/services/checkIsEqualInConstantTime.js +10 -0
  58. package/dist/runtime/services/checkIsMethodAllowed.d.ts +1 -0
  59. package/dist/runtime/services/checkIsMethodAllowed.js +3 -0
  60. package/dist/runtime/services/checkIsRecord.d.ts +1 -0
  61. package/dist/runtime/services/checkIsRecord.js +3 -0
  62. package/dist/runtime/services/checkIsRequestTooLarge.d.ts +2 -0
  63. package/dist/runtime/services/checkIsRequestTooLarge.js +8 -0
  64. package/dist/runtime/services/collectSsgHashes.d.ts +3 -0
  65. package/dist/runtime/services/collectSsgHashes.js +40 -0
  66. package/dist/runtime/services/createInMemoryRateLimiterStore.d.ts +2 -0
  67. package/dist/runtime/services/createInMemoryRateLimiterStore.js +21 -0
  68. package/dist/runtime/services/generateHash.d.ts +1 -0
  69. package/dist/runtime/services/generateHash.js +8 -0
  70. package/dist/runtime/services/generateNonce.d.ts +1 -0
  71. package/dist/runtime/services/generateNonce.js +5 -0
  72. package/dist/runtime/services/generateRandomToken.d.ts +1 -0
  73. package/dist/runtime/services/generateRandomToken.js +7 -0
  74. package/dist/runtime/services/getBasicAuthCredentials.d.ts +4 -0
  75. package/dist/runtime/services/getBasicAuthCredentials.js +18 -0
  76. package/dist/runtime/services/getBasicAuthOptions.d.ts +2 -0
  77. package/dist/runtime/services/getBasicAuthOptions.js +16 -0
  78. package/dist/runtime/services/getRetryAfterSeconds.d.ts +3 -0
  79. package/dist/runtime/services/getRetryAfterSeconds.js +8 -0
  80. package/dist/runtime/services/getSecurityRules.d.ts +3 -0
  81. package/dist/runtime/services/getSecurityRules.js +14 -0
  82. package/dist/runtime/services/getSecurityState.d.ts +9 -0
  83. package/dist/runtime/services/getSecurityState.js +13 -0
  84. package/dist/runtime/services/injectNonce.d.ts +1 -0
  85. package/dist/runtime/services/injectNonce.js +6 -0
  86. package/dist/runtime/services/injectSubresourceIntegrity.d.ts +1 -0
  87. package/dist/runtime/services/injectSubresourceIntegrity.js +21 -0
  88. package/dist/runtime/services/insertContentSecurityPolicyMeta.d.ts +2 -0
  89. package/dist/runtime/services/insertContentSecurityPolicyMeta.js +16 -0
  90. package/dist/runtime/services/mergeSecurityRules.d.ts +2 -0
  91. package/dist/runtime/services/mergeSecurityRules.js +10 -0
  92. package/dist/runtime/services/readHtmlTagAttribute.d.ts +1 -0
  93. package/dist/runtime/services/readHtmlTagAttribute.js +7 -0
  94. package/dist/runtime/services/rewriteHtmlTags.d.ts +1 -0
  95. package/dist/runtime/services/rewriteHtmlTags.js +8 -0
  96. package/dist/runtime/services/securityRouteMatcher.d.ts +3 -0
  97. package/dist/runtime/services/securityRouteMatcher.js +10 -0
  98. package/dist/runtime/services/serializeBasicAuthChallenge.d.ts +1 -0
  99. package/dist/runtime/services/serializeBasicAuthChallenge.js +3 -0
  100. package/dist/runtime/services/serializeContentSecurityPolicy.d.ts +2 -0
  101. package/dist/runtime/services/serializeContentSecurityPolicy.js +6 -0
  102. package/dist/runtime/services/serializePermissionsPolicy.d.ts +2 -0
  103. package/dist/runtime/services/serializePermissionsPolicy.js +3 -0
  104. package/dist/runtime/services/serializeSecurityHeaders.d.ts +2 -0
  105. package/dist/runtime/services/serializeSecurityHeaders.js +18 -0
  106. package/dist/runtime/services/serializeStrictTransportSecurity.d.ts +2 -0
  107. package/dist/runtime/services/serializeStrictTransportSecurity.js +7 -0
  108. package/dist/runtime/services/stripHtmlTagAttribute.d.ts +1 -0
  109. package/dist/runtime/services/stripHtmlTagAttribute.js +4 -0
  110. package/dist/runtime/services/updateContentSecurityPolicy.d.ts +3 -0
  111. package/dist/runtime/services/updateContentSecurityPolicy.js +14 -0
  112. package/dist/schemas/allowedMethodsRestricterOptionsSchema.js +4 -0
  113. package/dist/schemas/basicAuthOptionsSchema.js +11 -0
  114. package/dist/schemas/contentSecurityPolicySchema.js +4 -0
  115. package/dist/schemas/corsHandlerOptionsSchema.js +13 -0
  116. package/dist/schemas/csrfOptionsSchema.js +8 -0
  117. package/dist/schemas/moduleOptionsSchema.js +28 -0
  118. package/dist/schemas/permissionsPolicySchema.js +4 -0
  119. package/dist/schemas/rateLimiterOptionsSchema.js +7 -0
  120. package/dist/schemas/requestSizeLimiterOptionsSchema.js +7 -0
  121. package/dist/schemas/securityHeadersSchema.js +24 -0
  122. package/dist/schemas/ssgOptionsSchema.js +10 -0
  123. package/dist/schemas/strictTransportSecurityOptionsSchema.js +8 -0
  124. package/dist/services/computeSubresourceIntegrityHashes.js +28 -0
  125. package/dist/services/formatZodIssues.js +15 -0
  126. package/dist/services/getDefaultModuleOptions.js +40 -0
  127. package/dist/services/getDefaultSecurityHeaders.js +84 -0
  128. package/dist/services/getPluginTemplate.js +9 -0
  129. package/dist/services/getRemoveLoggersViteConfig.js +3 -0
  130. package/dist/services/getRouteRulesTemplate.js +3 -0
  131. package/dist/services/getRouteSecurityRules.js +16 -0
  132. package/dist/services/getStaticResourceHeaders.js +11 -0
  133. package/dist/services/getTypeTemplate.js +24 -0
  134. package/dist/services/mergeModuleOptions.js +38 -0
  135. package/dist/services/parseModuleOptions.js +9 -0
  136. package/package.json +7 -14
@@ -0,0 +1,3 @@
1
+ import { ContentSecurityPolicy } from "../../models/ContentSecurityPolicy.js";
2
+ import { SecurityState } from "./getSecurityState.js";
3
+ export declare const updateContentSecurityPolicy: (policy: ContentSecurityPolicy, { hashes, nonce }: SecurityState) => ContentSecurityPolicy;
@@ -0,0 +1,14 @@
1
+ import { NONCE_PLACEHOLDER } from "../constants.js";
2
+ // src/runtime/services/updateContentSecurityPolicy.ts
3
+ const SCRIPT_DIRECTIVES = /* @__PURE__ */ new Set(["script-src", "script-src-elem"]);
4
+ const STYLE_DIRECTIVES = /* @__PURE__ */ new Set(["style-src", "style-src-elem"]);
5
+ const updateContentSecurityPolicy = (policy, { hashes, nonce }) => Object.fromEntries(Object.keys(policy).map((directive) => {
6
+ const value = policy[directive];
7
+ if (!Array.isArray(value)) return [directive, value];
8
+ const sources = value.filter((source) => !checkIsStaticNonce(source)).map((source) => source === "'nonce-{{nonce}}'" && nonce ? `'nonce-${nonce}'` : source).filter((source) => source !== NONCE_PLACEHOLDER);
9
+ if (SCRIPT_DIRECTIVES.has(directive)) sources.push(...hashes.script);
10
+ if (STYLE_DIRECTIVES.has(directive)) sources.push(...hashes.style);
11
+ return [directive, sources];
12
+ }));
13
+ const checkIsStaticNonce = (source) => source.startsWith("'nonce-") && source !== "'nonce-{{nonce}}'";
14
+ export { updateContentSecurityPolicy };
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ // src/schemas/allowedMethodsRestricterOptionsSchema.ts
3
+ const allowedMethodsRestricterOptionsSchema = z.strictObject({ methods: z.array(z.string().min(1)).min(1) });
4
+ export { allowedMethodsRestricterOptionsSchema };
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ // src/schemas/basicAuthOptionsSchema.ts
3
+ const pathSchema = z.string().startsWith("/");
4
+ const basicAuthOptionsSchema = z.strictObject({
5
+ exclude: z.array(pathSchema).optional(),
6
+ include: z.array(pathSchema).optional(),
7
+ message: z.string().optional(),
8
+ name: z.string().min(1),
9
+ pass: z.string().min(1)
10
+ });
11
+ export { basicAuthOptionsSchema };
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ // src/schemas/contentSecurityPolicySchema.ts
3
+ const contentSecurityPolicySchema = z.record(z.string().min(1), z.union([z.boolean(), z.array(z.string().min(1))]).optional());
4
+ export { contentSecurityPolicySchema };
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ // src/schemas/corsHandlerOptionsSchema.ts
3
+ const headerNameSchema = z.string().regex(/^[\w-]+$/u);
4
+ const corsHandlerOptionsSchema = z.strictObject({
5
+ allowHeaders: z.array(headerNameSchema).optional(),
6
+ credentials: z.boolean().optional(),
7
+ exposeHeaders: z.array(headerNameSchema).optional(),
8
+ maxAge: z.string().regex(/^\d+$/u).optional(),
9
+ methods: z.array(z.string().min(1)).min(1),
10
+ origin: z.union([z.url(), z.array(z.url())]),
11
+ preflight: z.strictObject({ statusCode: z.int().min(200).max(299) }).optional()
12
+ });
13
+ export { corsHandlerOptionsSchema };
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ // src/schemas/csrfOptionsSchema.ts
3
+ const tokenNameSchema = z.string().regex(/^[\w-]+$/u, "must be letters, digits, underscores or hyphens");
4
+ const csrfOptionsSchema = z.strictObject({
5
+ cookieName: tokenNameSchema,
6
+ headerName: tokenNameSchema
7
+ });
8
+ export { csrfOptionsSchema };
@@ -0,0 +1,28 @@
1
+ import { allowedMethodsRestricterOptionsSchema } from "./allowedMethodsRestricterOptionsSchema.js";
2
+ import { basicAuthOptionsSchema } from "./basicAuthOptionsSchema.js";
3
+ import { corsHandlerOptionsSchema } from "./corsHandlerOptionsSchema.js";
4
+ import { csrfOptionsSchema } from "./csrfOptionsSchema.js";
5
+ import { rateLimiterOptionsSchema } from "./rateLimiterOptionsSchema.js";
6
+ import { requestSizeLimiterOptionsSchema } from "./requestSizeLimiterOptionsSchema.js";
7
+ import { securityHeadersSchema } from "./securityHeadersSchema.js";
8
+ import { ssgOptionsSchema } from "./ssgOptionsSchema.js";
9
+ import { z } from "zod";
10
+ // src/schemas/moduleOptionsSchema.ts
11
+ const moduleOptionsSchema = z.strictObject({
12
+ allowedMethodsRestricter: z.union([allowedMethodsRestricterOptionsSchema, z.literal(false)]),
13
+ basicAuth: z.union([basicAuthOptionsSchema, z.literal(false)]),
14
+ contentSecurityPolicyReportOnly: z.boolean(),
15
+ corsHandler: z.union([corsHandlerOptionsSchema, z.literal(false)]),
16
+ csrf: z.union([csrfOptionsSchema, z.literal(false)]),
17
+ enabled: z.boolean(),
18
+ headers: z.union([securityHeadersSchema, z.literal(false)]),
19
+ hidePoweredBy: z.boolean(),
20
+ nonce: z.boolean(),
21
+ rateLimiter: z.union([rateLimiterOptionsSchema, z.literal(false)]),
22
+ removeLoggers: z.boolean(),
23
+ requestSizeLimiter: z.union([requestSizeLimiterOptionsSchema, z.literal(false)]),
24
+ sri: z.boolean(),
25
+ ssg: z.union([ssgOptionsSchema, z.literal(false)]),
26
+ strict: z.boolean()
27
+ });
28
+ export { moduleOptionsSchema };
@@ -0,0 +1,4 @@
1
+ import { z } from "zod";
2
+ // src/schemas/permissionsPolicySchema.ts
3
+ const permissionsPolicySchema = z.record(z.string().min(1), z.union([z.literal(false), z.array(z.string().min(1))]).optional());
4
+ export { permissionsPolicySchema };
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ // src/schemas/rateLimiterOptionsSchema.ts
3
+ const rateLimiterOptionsSchema = z.strictObject({
4
+ intervalInMilliseconds: z.int("must be a whole number of milliseconds").positive("must be more than zero milliseconds"),
5
+ tokensPerInterval: z.int("must be a whole number of requests").positive("must allow at least one request")
6
+ });
7
+ export { rateLimiterOptionsSchema };
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ // src/schemas/requestSizeLimiterOptionsSchema.ts
3
+ const requestSizeLimiterOptionsSchema = z.strictObject({
4
+ maxRequestSizeInBytes: z.int("must be a whole number of bytes").positive("must be more than zero bytes"),
5
+ maxUploadFileRequestInBytes: z.int("must be a whole number of bytes").positive("must be more than zero bytes")
6
+ });
7
+ export { requestSizeLimiterOptionsSchema };
@@ -0,0 +1,24 @@
1
+ import { CrossOriginEmbedderPolicy } from "../models/CrossOriginEmbedderPolicy.js";
2
+ import { XFrameOptions } from "../models/XFrameOptions.js";
3
+ import { contentSecurityPolicySchema } from "./contentSecurityPolicySchema.js";
4
+ import { permissionsPolicySchema } from "./permissionsPolicySchema.js";
5
+ import { strictTransportSecurityOptionsSchema } from "./strictTransportSecurityOptionsSchema.js";
6
+ import { z } from "zod";
7
+ // src/schemas/securityHeadersSchema.ts
8
+ const headerValueSchema = z.string().min(1);
9
+ const securityHeadersSchema = z.strictObject({
10
+ contentSecurityPolicy: z.union([contentSecurityPolicySchema, z.literal(false)]).optional(),
11
+ crossOriginEmbedderPolicy: z.union([z.enum(CrossOriginEmbedderPolicy), z.literal(false)]).optional(),
12
+ crossOriginOpenerPolicy: z.union([headerValueSchema, z.literal(false)]).optional(),
13
+ crossOriginResourcePolicy: z.union([headerValueSchema, z.literal(false)]).optional(),
14
+ originAgentCluster: z.union([headerValueSchema, z.literal(false)]).optional(),
15
+ permissionsPolicy: z.union([permissionsPolicySchema, z.literal(false)]).optional(),
16
+ referrerPolicy: z.union([headerValueSchema, z.literal(false)]).optional(),
17
+ strictTransportSecurity: z.union([strictTransportSecurityOptionsSchema, z.literal(false)]).optional(),
18
+ xContentTypeOptions: z.union([headerValueSchema, z.literal(false)]).optional(),
19
+ xDNSPrefetchControl: z.union([headerValueSchema, z.literal(false)]).optional(),
20
+ xDownloadOptions: z.union([headerValueSchema, z.literal(false)]).optional(),
21
+ xFrameOptions: z.union([z.enum(XFrameOptions), z.literal(false)]).optional(),
22
+ xPermittedCrossDomainPolicies: z.union([headerValueSchema, z.literal(false)]).optional()
23
+ });
24
+ export { securityHeadersSchema };
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ // src/schemas/ssgOptionsSchema.ts
3
+ const ssgOptionsSchema = z.strictObject({
4
+ exportToPresets: z.boolean(),
5
+ hashScripts: z.boolean(),
6
+ hashStyles: z.boolean(),
7
+ meta: z.boolean(),
8
+ nitroHeaders: z.boolean()
9
+ });
10
+ export { ssgOptionsSchema };
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ // src/schemas/strictTransportSecurityOptionsSchema.ts
3
+ const strictTransportSecurityOptionsSchema = z.strictObject({
4
+ includeSubdomains: z.boolean(),
5
+ maxAge: z.int().nonnegative(),
6
+ preload: z.boolean()
7
+ });
8
+ export { strictTransportSecurityOptionsSchema };
@@ -0,0 +1,28 @@
1
+ import { SRI_HASH_ALGORITHM } from "../runtime/constants.js";
2
+ import { generateHash } from "../runtime/services/generateHash.js";
3
+ import { existsSync } from "node:fs";
4
+ import { readFile, readdir } from "node:fs/promises";
5
+ import { join, posix, relative, sep } from "node:path";
6
+ // src/services/computeSubresourceIntegrityHashes.ts
7
+ const computeSubresourceIntegrityHashes = async (nitro) => {
8
+ const { baseURL: appBaseUrl, cdnURL: appCdnUrl } = nitro.options.runtimeConfig.app;
9
+ const existingPublicAssets = nitro.options.publicAssets.filter(({ dir: directory }) => existsSync(directory));
10
+ const filesByPublicAsset = await Promise.all(existingPublicAssets.map(async ({ baseURL = "", dir: directory }) => {
11
+ return (await readdir(directory, {
12
+ recursive: true,
13
+ withFileTypes: true
14
+ })).filter((entry) => entry.isFile()).map((entry) => {
15
+ const filePath = join(entry.parentPath, entry.name);
16
+ return {
17
+ filePath,
18
+ fullPath: posix.join(baseURL, ...relative(directory, filePath).split(sep))
19
+ };
20
+ });
21
+ }));
22
+ const hashEntries = await Promise.all(filesByPublicAsset.flat().map(async ({ filePath, fullPath }) => {
23
+ const hash = await generateHash(await readFile(filePath), SRI_HASH_ALGORITHM);
24
+ return [appCdnUrl ? new URL(posix.isAbsolute(fullPath) ? fullPath.slice(1) : fullPath, appCdnUrl.endsWith("/") ? appCdnUrl : `${appCdnUrl}/`).href : posix.join("/", appBaseUrl, fullPath), hash];
25
+ }));
26
+ return Object.fromEntries(hashEntries);
27
+ };
28
+ export { computeSubresourceIntegrityHashes };
@@ -0,0 +1,15 @@
1
+ // src/services/formatZodIssues.ts
2
+ const getDeepestPathLength = (leafIssues) => Math.max(-1, ...leafIssues.map(({ path }) => path.length));
3
+ const getLeafIssues = (issue, parentPath) => {
4
+ const path = [...parentPath, ...issue.path];
5
+ if (issue.code !== "invalid_union") return [{
6
+ issue,
7
+ path
8
+ }];
9
+ return issue.errors.map((branch) => branch.flatMap((branchIssue) => getLeafIssues(branchIssue, path))).reduce((deepest, branch) => getDeepestPathLength(branch) > getDeepestPathLength(deepest) ? branch : deepest);
10
+ };
11
+ const formatLeafIssue = ({ issue, path }) => {
12
+ return `${path.map(String).join(".") || "(options)"}: ${issue.message}`;
13
+ };
14
+ const formatZodIssues = (issues) => issues.flatMap((issue) => getLeafIssues(issue, [])).map((leafIssue) => formatLeafIssue(leafIssue)).join("; ");
15
+ export { formatZodIssues };
@@ -0,0 +1,40 @@
1
+ import { getDefaultSecurityHeaders } from "./getDefaultSecurityHeaders.js";
2
+ // src/services/getDefaultModuleOptions.ts
3
+ const getDefaultModuleOptions = (isDevelopment, devServerUrl, isStrict) => ({
4
+ allowedMethodsRestricter: false,
5
+ basicAuth: false,
6
+ contentSecurityPolicyReportOnly: false,
7
+ corsHandler: {
8
+ methods: [
9
+ "GET",
10
+ "HEAD",
11
+ "PUT",
12
+ "PATCH",
13
+ "POST",
14
+ "DELETE"
15
+ ],
16
+ origin: devServerUrl,
17
+ preflight: { statusCode: 204 }
18
+ },
19
+ csrf: false,
20
+ enabled: true,
21
+ headers: getDefaultSecurityHeaders(isDevelopment, isStrict),
22
+ hidePoweredBy: true,
23
+ nonce: true,
24
+ rateLimiter: false,
25
+ removeLoggers: false,
26
+ requestSizeLimiter: {
27
+ maxRequestSizeInBytes: 2e6,
28
+ maxUploadFileRequestInBytes: 8e6
29
+ },
30
+ sri: true,
31
+ ssg: {
32
+ exportToPresets: true,
33
+ hashScripts: true,
34
+ hashStyles: false,
35
+ meta: true,
36
+ nitroHeaders: true
37
+ },
38
+ strict: isStrict
39
+ });
40
+ export { getDefaultModuleOptions };
@@ -0,0 +1,84 @@
1
+ import { NONCE_PLACEHOLDER } from "../runtime/constants.js";
2
+ import { CrossOriginEmbedderPolicy } from "../models/CrossOriginEmbedderPolicy.js";
3
+ import { XFrameOptions } from "../models/XFrameOptions.js";
4
+ // src/services/getDefaultSecurityHeaders.ts
5
+ const getDefaultSecurityHeaders = (isDevelopment, isStrict) => ({
6
+ contentSecurityPolicy: {
7
+ "base-uri": ["'none'"],
8
+ "default-src": ["'self'"],
9
+ "font-src": [
10
+ "'self'",
11
+ "https:",
12
+ "data:"
13
+ ],
14
+ "form-action": ["'self'"],
15
+ "frame-ancestors": ["'self'"],
16
+ "img-src": ["'self'", "data:"],
17
+ "object-src": ["'none'"],
18
+ "script-src": isStrict ? [
19
+ "'self'",
20
+ "'strict-dynamic'",
21
+ NONCE_PLACEHOLDER
22
+ ] : [
23
+ "'self'",
24
+ "https:",
25
+ "'unsafe-inline'",
26
+ "'strict-dynamic'",
27
+ NONCE_PLACEHOLDER
28
+ ],
29
+ "script-src-attr": ["'none'"],
30
+ "style-src": [
31
+ "'self'",
32
+ "https:",
33
+ "'unsafe-inline'"
34
+ ],
35
+ "upgrade-insecure-requests": true
36
+ },
37
+ crossOriginEmbedderPolicy: isDevelopment ? CrossOriginEmbedderPolicy.UnsafeNone : CrossOriginEmbedderPolicy.Credentialless,
38
+ crossOriginOpenerPolicy: "same-origin",
39
+ crossOriginResourcePolicy: "same-origin",
40
+ originAgentCluster: "?1",
41
+ permissionsPolicy: {
42
+ accelerometer: [],
43
+ autoplay: [],
44
+ camera: [],
45
+ "clipboard-read": [],
46
+ "clipboard-write": [],
47
+ "cross-origin-isolated": [],
48
+ "display-capture": [],
49
+ "encrypted-media": [],
50
+ fullscreen: [],
51
+ gamepad: [],
52
+ geolocation: [],
53
+ gyroscope: [],
54
+ hid: [],
55
+ "idle-detection": [],
56
+ "interest-cohort": [],
57
+ "keyboard-map": [],
58
+ magnetometer: [],
59
+ microphone: [],
60
+ midi: [],
61
+ payment: [],
62
+ "picture-in-picture": [],
63
+ "publickey-credentials-get": [],
64
+ "screen-wake-lock": [],
65
+ serial: [],
66
+ "sync-xhr": ["self"],
67
+ unload: [],
68
+ usb: [],
69
+ "web-share": [],
70
+ "xr-spatial-tracking": []
71
+ },
72
+ referrerPolicy: "no-referrer",
73
+ strictTransportSecurity: {
74
+ includeSubdomains: true,
75
+ maxAge: 63072e3,
76
+ preload: false
77
+ },
78
+ xContentTypeOptions: "nosniff",
79
+ xDNSPrefetchControl: "off",
80
+ xDownloadOptions: "noopen",
81
+ xFrameOptions: XFrameOptions.SameOrigin,
82
+ xPermittedCrossDomainPolicies: "none"
83
+ });
84
+ export { getDefaultSecurityHeaders };
@@ -0,0 +1,9 @@
1
+ // src/services/getPluginTemplate.ts
2
+ const getPluginTemplate = (factoryPath, factoryName, dataPath) => [
3
+ `import { ${factoryName} } from ${JSON.stringify(factoryPath)};`,
4
+ `import data from ${JSON.stringify(dataPath)};`,
5
+ "",
6
+ `export default ${factoryName}(data);`,
7
+ ""
8
+ ].join("\n");
9
+ export { getPluginTemplate };
@@ -0,0 +1,3 @@
1
+ // src/services/getRemoveLoggersViteConfig.ts
2
+ const getRemoveLoggersViteConfig = () => ({ build: { rolldownOptions: { output: { minify: { compress: { dropConsole: true } } } } } });
3
+ export { getRemoveLoggersViteConfig };
@@ -0,0 +1,3 @@
1
+ // src/services/getRouteRulesTemplate.ts
2
+ const getRouteRulesTemplate = (routes) => `export default ${JSON.stringify(routes)}\n`;
3
+ export { getRouteRulesTemplate };
@@ -0,0 +1,16 @@
1
+ import { moduleOptionsSchema } from "../schemas/moduleOptionsSchema.js";
2
+ import { formatZodIssues } from "./formatZodIssues.js";
3
+ import { mergeModuleOptions } from "./mergeModuleOptions.js";
4
+ // src/services/getRouteSecurityRules.ts
5
+ const getRouteSecurityRules = (routeRules, options) => {
6
+ const routeSecurityRules = {};
7
+ for (const [route, { security }] of Object.entries(routeRules)) {
8
+ if (security === void 0) continue;
9
+ if (Object.hasOwn(security, "basicAuth")) throw new TypeError(`nuxt-security-module: route ${route}: basicAuth cannot be set in a route rule`);
10
+ const result = moduleOptionsSchema.safeParse(mergeModuleOptions(options, security));
11
+ if (!result.success) throw new TypeError(`nuxt-security-module: route ${route}: ${formatZodIssues(result.error.issues)}`);
12
+ routeSecurityRules[route] = security;
13
+ }
14
+ return routeSecurityRules;
15
+ };
16
+ export { getRouteSecurityRules };
@@ -0,0 +1,11 @@
1
+ import { serializeSecurityHeaders } from "../runtime/services/serializeSecurityHeaders.js";
2
+ // src/services/getStaticResourceHeaders.ts
3
+ const getStaticResourceHeaders = (headers) => serializeSecurityHeaders({
4
+ referrerPolicy: headers.referrerPolicy,
5
+ strictTransportSecurity: headers.strictTransportSecurity,
6
+ xContentTypeOptions: headers.xContentTypeOptions,
7
+ xDownloadOptions: headers.xDownloadOptions,
8
+ xFrameOptions: headers.xFrameOptions,
9
+ xPermittedCrossDomainPolicies: headers.xPermittedCrossDomainPolicies
10
+ });
11
+ export { getStaticResourceHeaders };
@@ -0,0 +1,24 @@
1
+ import { MODULE_NAME, ROUTE_RULES_HOOK } from "../runtime/constants.js";
2
+ // src/services/getTypeTemplate.ts
3
+ const getTypeTemplate = () => `// Generated by ${MODULE_NAME}
4
+ import type { ModuleOptions } from '${MODULE_NAME}'
5
+
6
+ declare module '#${MODULE_NAME}/route-rules.mjs' {
7
+ const routeRules: Record<string, Partial<ModuleOptions>>
8
+ export default routeRules
9
+ }
10
+
11
+ declare module '#${MODULE_NAME}/sri-hashes.mjs' {
12
+ const sriHashes: Record<string, string>
13
+ export default sriHashes
14
+ }
15
+
16
+ declare module 'nitro/types' {
17
+ interface NitroRuntimeHooks {
18
+ '${ROUTE_RULES_HOOK}': (routeRules: Record<string, Partial<ModuleOptions>>) => void | Promise<void>
19
+ }
20
+ }
21
+
22
+ export {}
23
+ `;
24
+ export { getTypeTemplate };
@@ -0,0 +1,38 @@
1
+ // src/services/mergeModuleOptions.ts
2
+ const mergeModuleOptions = (defaults, overrides) => ({
3
+ ...defaults,
4
+ ...overrides,
5
+ headers: mergeHeaders(defaults.headers, overrides.headers),
6
+ ssg: overrides.ssg === false ? false : {
7
+ ...defaults.ssg,
8
+ ...overrides.ssg
9
+ }
10
+ });
11
+ const mergeHeaders = (defaults, overrides) => {
12
+ if (overrides === false || defaults === false) return overrides ?? defaults;
13
+ if (overrides === void 0) return defaults;
14
+ return {
15
+ ...defaults,
16
+ ...overrides,
17
+ contentSecurityPolicy: mergeDirectives(defaults.contentSecurityPolicy, overrides.contentSecurityPolicy),
18
+ permissionsPolicy: mergeFeatures(defaults.permissionsPolicy, overrides.permissionsPolicy)
19
+ };
20
+ };
21
+ const mergeDirectives = (defaults, overrides) => {
22
+ if (overrides === false || defaults === false) return overrides ?? defaults;
23
+ if (overrides === void 0) return defaults;
24
+ const merged = { ...defaults };
25
+ for (const [directive, value] of Object.entries(overrides)) if (value === false) delete merged[directive];
26
+ else if (Array.isArray(value)) merged[directive] = [.../* @__PURE__ */ new Set([...Array.isArray(merged[directive]) ? merged[directive] : [], ...value])];
27
+ else merged[directive] = value;
28
+ return merged;
29
+ };
30
+ const mergeFeatures = (defaults, overrides) => {
31
+ if (overrides === false || defaults === false) return overrides ?? defaults;
32
+ if (overrides === void 0) return defaults;
33
+ const merged = { ...defaults };
34
+ for (const [feature, sources] of Object.entries(overrides)) if (sources === false) delete merged[feature];
35
+ else merged[feature] = [.../* @__PURE__ */ new Set([...merged[feature] || [], ...sources || []])];
36
+ return merged;
37
+ };
38
+ export { mergeModuleOptions };
@@ -0,0 +1,9 @@
1
+ import { moduleOptionsSchema } from "../schemas/moduleOptionsSchema.js";
2
+ import { formatZodIssues } from "./formatZodIssues.js";
3
+ // src/services/parseModuleOptions.ts
4
+ const parseModuleOptions = (options) => {
5
+ const result = moduleOptionsSchema.safeParse(options);
6
+ if (!result.success) throw new TypeError(`nuxt-security-module: ${formatZodIssues(result.error.issues)}`);
7
+ return result.data;
8
+ };
9
+ export { parseModuleOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-security-module",
3
- "version": "4.2.0",
3
+ "version": "4.3.3",
4
4
  "description": "Nuxt security module for Nitro 3 and h3 2 — security headers, a per-request nonce Content Security Policy, subresource integrity, CORS, allowed methods, basic auth and a request size limit.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Jimmy Chen",
@@ -37,8 +37,6 @@
37
37
  "scripts": {
38
38
  "build": "tsdown",
39
39
  "bench": "vitest bench --run",
40
- "format": "oxfmt",
41
- "format:check": "oxfmt --check",
42
40
  "lint": "eslint .",
43
41
  "lint:fix": "eslint --fix .",
44
42
  "test": "vitest",
@@ -52,21 +50,16 @@
52
50
  "zod": "^4.6.7"
53
51
  },
54
52
  "devDependencies": {
55
- "@arethetypeswrong/core": "^0.18.5",
56
- "@esposter/configuration": "4.2.0",
57
- "@esposter/shared-node": "4.2.0",
53
+ "@esposter/configuration": "4.3.3",
54
+ "@esposter/shared-node": "4.3.3",
58
55
  "@nuxt/nitro-server": "npm:@nuxt/nitro-server-nightly@5x",
59
56
  "@types/node": "^26.6.5",
60
- "fast-check": "^4.10.2",
61
- "h3": "^2.0.2",
62
- "nitro": "npm:nitro-nightly@latest",
63
- "publint": "^0.3.25",
64
- "tsdown": "^0.23.1",
65
- "vitest": "^5.0.4"
57
+ "h3": "^2.0.7",
58
+ "tsdown": "^0.23.1"
66
59
  },
67
60
  "peerDependencies": {
68
- "nitro": "npm:nitro-nightly@latest",
61
+ "nitro": "npm:nitro-nightly@3.0.1-20261018-005730-bb9894fe",
69
62
  "nuxt": "npm:nuxt-nightly@5x"
70
63
  },
71
- "gitHead": "fef4971cf6a621a2c1850f32fcec19ee4ec0de99"
64
+ "gitHead": "955bbc130ad9b28877fd4ae8efc73a045141ce01"
72
65
  }