nuxt-security-module 4.2.0 → 4.3.1

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 +17 -0
  3. package/dist/index.js +105 -0
  4. package/dist/models/AllowedMethodsRestricterOptions.d.ts +5 -0
  5. package/dist/models/BasicAuthOptions.d.ts +9 -0
  6. package/dist/models/ContentSecurityPolicy.d.ts +3 -0
  7. package/dist/models/CorsHandlerOptions.d.ts +13 -0
  8. package/dist/models/CrossOriginEmbedderPolicy.d.ts +8 -0
  9. package/dist/models/CrossOriginEmbedderPolicy.js +8 -0
  10. package/dist/models/CsrfOptions.d.ts +6 -0
  11. package/dist/models/ModuleOptions.d.ts +27 -0
  12. package/dist/models/PermissionsPolicy.d.ts +3 -0
  13. package/dist/models/RateLimitWindow.d.ts +6 -0
  14. package/dist/models/RateLimiterOptions.d.ts +6 -0
  15. package/dist/models/RateLimiterStore.d.ts +6 -0
  16. package/dist/models/RequestSizeLimiterOptions.d.ts +6 -0
  17. package/dist/models/SecurityHeaders.d.ts +22 -0
  18. package/dist/models/SecurityRouteRules.d.ts +4 -0
  19. package/dist/models/SecurityRules.d.ts +4 -0
  20. package/dist/models/SsgOptions.d.ts +9 -0
  21. package/dist/models/StrictTransportSecurity.d.ts +7 -0
  22. package/dist/models/XFrameOptions.d.ts +7 -0
  23. package/dist/models/XFrameOptions.js +7 -0
  24. package/dist/runtime/constants.d.ts +24 -0
  25. package/dist/runtime/constants.js +52 -0
  26. package/dist/runtime/nitro/plugins/hidePoweredBy.d.ts +5 -0
  27. package/dist/runtime/nitro/plugins/hidePoweredBy.js +13 -0
  28. package/dist/runtime/nitro/plugins/nonce.d.ts +5 -0
  29. package/dist/runtime/nitro/plugins/nonce.js +27 -0
  30. package/dist/runtime/nitro/plugins/routeRules.d.ts +5 -0
  31. package/dist/runtime/nitro/plugins/routeRules.js +17 -0
  32. package/dist/runtime/nitro/plugins/securityHeaders.d.ts +5 -0
  33. package/dist/runtime/nitro/plugins/securityHeaders.js +25 -0
  34. package/dist/runtime/nitro/plugins/ssgHashes.d.ts +5 -0
  35. package/dist/runtime/nitro/plugins/ssgHashes.js +27 -0
  36. package/dist/runtime/nitro/plugins/subresourceIntegrity.d.ts +4 -0
  37. package/dist/runtime/nitro/plugins/subresourceIntegrity.js +19 -0
  38. package/dist/runtime/server/middleware/allowedMethodsRestricter.d.ts +5 -0
  39. package/dist/runtime/server/middleware/allowedMethodsRestricter.js +15 -0
  40. package/dist/runtime/server/middleware/basicAuth.d.ts +5 -0
  41. package/dist/runtime/server/middleware/basicAuth.js +23 -0
  42. package/dist/runtime/server/middleware/corsHandler.d.ts +5 -0
  43. package/dist/runtime/server/middleware/corsHandler.js +20 -0
  44. package/dist/runtime/server/middleware/csrf.d.ts +5 -0
  45. package/dist/runtime/server/middleware/csrf.js +28 -0
  46. package/dist/runtime/server/middleware/rateLimiter.d.ts +5 -0
  47. package/dist/runtime/server/middleware/rateLimiter.js +22 -0
  48. package/dist/runtime/server/middleware/requestSizeLimiter.d.ts +5 -0
  49. package/dist/runtime/server/middleware/requestSizeLimiter.js +17 -0
  50. package/dist/runtime/services/checkIsBasicAuthorized.d.ts +4 -0
  51. package/dist/runtime/services/checkIsBasicAuthorized.js +12 -0
  52. package/dist/runtime/services/checkIsChallenged.d.ts +4 -0
  53. package/dist/runtime/services/checkIsChallenged.js +8 -0
  54. package/dist/runtime/services/checkIsCsrfTokenValid.d.ts +3 -0
  55. package/dist/runtime/services/checkIsCsrfTokenValid.js +8 -0
  56. package/dist/runtime/services/checkIsEqualInConstantTime.d.ts +3 -0
  57. package/dist/runtime/services/checkIsEqualInConstantTime.js +11 -0
  58. package/dist/runtime/services/checkIsMethodAllowed.d.ts +3 -0
  59. package/dist/runtime/services/checkIsMethodAllowed.js +4 -0
  60. package/dist/runtime/services/checkIsRecord.d.ts +3 -0
  61. package/dist/runtime/services/checkIsRecord.js +4 -0
  62. package/dist/runtime/services/checkIsRequestTooLarge.d.ts +4 -0
  63. package/dist/runtime/services/checkIsRequestTooLarge.js +9 -0
  64. package/dist/runtime/services/collectSsgHashes.d.ts +5 -0
  65. package/dist/runtime/services/collectSsgHashes.js +41 -0
  66. package/dist/runtime/services/createInMemoryRateLimiterStore.d.ts +4 -0
  67. package/dist/runtime/services/createInMemoryRateLimiterStore.js +22 -0
  68. package/dist/runtime/services/generateHash.d.ts +3 -0
  69. package/dist/runtime/services/generateHash.js +9 -0
  70. package/dist/runtime/services/generateNonce.d.ts +3 -0
  71. package/dist/runtime/services/generateNonce.js +6 -0
  72. package/dist/runtime/services/generateRandomToken.d.ts +3 -0
  73. package/dist/runtime/services/generateRandomToken.js +8 -0
  74. package/dist/runtime/services/getBasicAuthCredentials.d.ts +6 -0
  75. package/dist/runtime/services/getBasicAuthCredentials.js +19 -0
  76. package/dist/runtime/services/getBasicAuthOptions.d.ts +4 -0
  77. package/dist/runtime/services/getBasicAuthOptions.js +17 -0
  78. package/dist/runtime/services/getRetryAfterSeconds.d.ts +5 -0
  79. package/dist/runtime/services/getRetryAfterSeconds.js +9 -0
  80. package/dist/runtime/services/getSecurityRules.d.ts +5 -0
  81. package/dist/runtime/services/getSecurityRules.js +15 -0
  82. package/dist/runtime/services/getSecurityState.d.ts +11 -0
  83. package/dist/runtime/services/getSecurityState.js +14 -0
  84. package/dist/runtime/services/injectNonce.d.ts +3 -0
  85. package/dist/runtime/services/injectNonce.js +7 -0
  86. package/dist/runtime/services/injectSubresourceIntegrity.d.ts +3 -0
  87. package/dist/runtime/services/injectSubresourceIntegrity.js +22 -0
  88. package/dist/runtime/services/insertContentSecurityPolicyMeta.d.ts +4 -0
  89. package/dist/runtime/services/insertContentSecurityPolicyMeta.js +17 -0
  90. package/dist/runtime/services/mergeSecurityRules.d.ts +4 -0
  91. package/dist/runtime/services/mergeSecurityRules.js +11 -0
  92. package/dist/runtime/services/readHtmlTagAttribute.d.ts +3 -0
  93. package/dist/runtime/services/readHtmlTagAttribute.js +8 -0
  94. package/dist/runtime/services/rewriteHtmlTags.d.ts +3 -0
  95. package/dist/runtime/services/rewriteHtmlTags.js +9 -0
  96. package/dist/runtime/services/securityRouteMatcher.d.ts +5 -0
  97. package/dist/runtime/services/securityRouteMatcher.js +11 -0
  98. package/dist/runtime/services/serializeBasicAuthChallenge.d.ts +3 -0
  99. package/dist/runtime/services/serializeBasicAuthChallenge.js +4 -0
  100. package/dist/runtime/services/serializeContentSecurityPolicy.d.ts +4 -0
  101. package/dist/runtime/services/serializeContentSecurityPolicy.js +7 -0
  102. package/dist/runtime/services/serializePermissionsPolicy.d.ts +4 -0
  103. package/dist/runtime/services/serializePermissionsPolicy.js +4 -0
  104. package/dist/runtime/services/serializeSecurityHeaders.d.ts +4 -0
  105. package/dist/runtime/services/serializeSecurityHeaders.js +19 -0
  106. package/dist/runtime/services/serializeStrictTransportSecurity.d.ts +4 -0
  107. package/dist/runtime/services/serializeStrictTransportSecurity.js +8 -0
  108. package/dist/runtime/services/stripHtmlTagAttribute.d.ts +3 -0
  109. package/dist/runtime/services/stripHtmlTagAttribute.js +5 -0
  110. package/dist/runtime/services/updateContentSecurityPolicy.d.ts +5 -0
  111. package/dist/runtime/services/updateContentSecurityPolicy.js +15 -0
  112. package/dist/schemas/allowedMethodsRestricterOptionsSchema.js +5 -0
  113. package/dist/schemas/basicAuthOptionsSchema.js +12 -0
  114. package/dist/schemas/contentSecurityPolicySchema.js +5 -0
  115. package/dist/schemas/corsHandlerOptionsSchema.js +14 -0
  116. package/dist/schemas/csrfOptionsSchema.js +9 -0
  117. package/dist/schemas/moduleOptionsSchema.js +29 -0
  118. package/dist/schemas/permissionsPolicySchema.js +5 -0
  119. package/dist/schemas/rateLimiterOptionsSchema.js +8 -0
  120. package/dist/schemas/requestSizeLimiterOptionsSchema.js +8 -0
  121. package/dist/schemas/securityHeadersSchema.js +25 -0
  122. package/dist/schemas/ssgOptionsSchema.js +11 -0
  123. package/dist/schemas/strictTransportSecurityOptionsSchema.js +9 -0
  124. package/dist/services/computeSubresourceIntegrityHashes.js +29 -0
  125. package/dist/services/formatZodIssues.js +16 -0
  126. package/dist/services/getDefaultModuleOptions.js +41 -0
  127. package/dist/services/getDefaultSecurityHeaders.js +85 -0
  128. package/dist/services/getPluginTemplate.js +10 -0
  129. package/dist/services/getRemoveLoggersViteConfig.js +4 -0
  130. package/dist/services/getRouteRulesTemplate.js +4 -0
  131. package/dist/services/getRouteSecurityRules.js +17 -0
  132. package/dist/services/getStaticResourceHeaders.js +12 -0
  133. package/dist/services/getTypeTemplate.js +25 -0
  134. package/dist/services/mergeModuleOptions.js +39 -0
  135. package/dist/services/parseModuleOptions.js +10 -0
  136. package/package.json +7 -14
package/README.md CHANGED
@@ -1,22 +1,48 @@
1
1
  # nuxt-security-module
2
2
 
3
+ [![Apache-2.0 licensed][badge-license]][url-license]
4
+ [![NPM version][badge-npm-version]][url-npm]
5
+ [![NPM downloads][badge-npm-downloads]][url-npm]
6
+ [![NPM Unpacked Size (with version)][badge-npm-unpacked-size]][url-npm]
7
+
3
8
  Nuxt security module for [Nitro 3](https://nitro.build) and h3 2. It sets security headers on rendered pages, a per-request nonce Content Security Policy with the nonce written onto the page's own tags, subresource integrity, a prerender policy as a `<meta>` element, the framework's `X-Powered-By` header removed, CORS, allowed methods, basic auth and a request size limit.
4
9
 
5
- It replaces [nuxt-security](https://github.com/Baroshem/nuxt-security). Its upstream audit, every issue it was held to, and the defaults it ships are on the documentation page: [nuxt-security-module](https://github.com/Esposter/Esposter/blob/main/apps/web/content/docs/nuxt-security-module/index.md).
10
+ It replaces [nuxt-security](https://github.com/Baroshem/nuxt-security). Its upstream audit, every issue it was held to, and the defaults it ships are on the documentation page: [nuxt-security-module](https://github.com/Esposter/Esposter/blob/main/openspec/specs/nuxt-security-module/README.md).
6
11
 
7
12
  ## Table of Contents
8
13
 
14
+ - 🚀 [Getting Started](#getting-started)
9
15
  - 📖 [Documentation](#documentation)
10
16
  - ⚖️ [License](#license)
11
17
 
12
18
  ---
13
19
 
20
+ ## <a name="getting-started">🚀 Getting Started</a>
21
+
22
+ ```bash
23
+ pnpm i nuxt-security-module
24
+ ```
25
+
26
+ ```ts
27
+ // nuxt.config.ts
28
+ export default defineNuxtConfig({ modules: ["nuxt-security-module"] });
29
+ ```
30
+
14
31
  ## <a name="documentation">📖 Documentation</a>
15
32
 
16
- The module's option reference, its upstream audit and its defaults review live in the repository's documentation section, under `apps/web/content/docs/nuxt-security-module`. The package's status and the steps not yet built are on its index page.
33
+ We highly recommend you take a look at the [documentation](https://github.com/Esposter/Esposter/tree/main/openspec/specs/nuxt-security-module) to level up.
34
+
35
+ The module's option reference, its upstream audit and its defaults review live in the repository's [specs](https://github.com/Esposter/Esposter/tree/main/openspec/specs/nuxt-security-module). The package's status and the steps not yet built are in its `README.md` there.
17
36
 
18
37
  ---
19
38
 
20
39
  ## <a name="license">⚖️ License</a>
21
40
 
22
41
  Apache-2.0
42
+
43
+ [badge-license]: https://img.shields.io/github/license/Esposter/Esposter.svg?color=blue
44
+ [url-license]: https://github.com/Esposter/Esposter/blob/main/LICENSE
45
+ [badge-npm-version]: https://img.shields.io/npm/v/nuxt-security-module/latest?color=brightgreen
46
+ [url-npm]: https://www.npmjs.com/package/nuxt-security-module/v/latest
47
+ [badge-npm-unpacked-size]: https://img.shields.io/npm/unpacked-size/nuxt-security-module/latest?label=npm
48
+ [badge-npm-downloads]: https://img.shields.io/npm/dm/nuxt-security-module.svg
@@ -0,0 +1,17 @@
1
+ import { ModuleOptions } from "./models/ModuleOptions.js";
2
+ import { SecurityRouteRules } from "./models/SecurityRouteRules.js";
3
+ import { NuxtModule } from "@nuxt/schema";
4
+ //#region src/module.d.ts
5
+ declare module "@nuxt/schema" {
6
+ interface NuxtConfig {
7
+ security?: Partial<ModuleOptions>;
8
+ }
9
+ }
10
+ declare module "h3/rules" {
11
+ interface RouteRuleConfig {
12
+ security?: SecurityRouteRules;
13
+ }
14
+ }
15
+ declare const securityModule: NuxtModule<Partial<ModuleOptions>>;
16
+ //#endregion
17
+ export { type ModuleOptions, securityModule as default };
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ import { MODULE_NAME } from "./runtime/constants.js";
2
+ import { checkIsRecord } from "./runtime/services/checkIsRecord.js";
3
+ import { computeSubresourceIntegrityHashes } from "./services/computeSubresourceIntegrityHashes.js";
4
+ import { getDefaultModuleOptions } from "./services/getDefaultModuleOptions.js";
5
+ import { getPluginTemplate } from "./services/getPluginTemplate.js";
6
+ import { getRemoveLoggersViteConfig } from "./services/getRemoveLoggersViteConfig.js";
7
+ import { getRouteRulesTemplate } from "./services/getRouteRulesTemplate.js";
8
+ import { mergeModuleOptions } from "./services/mergeModuleOptions.js";
9
+ import { getRouteSecurityRules } from "./services/getRouteSecurityRules.js";
10
+ import { getStaticResourceHeaders } from "./services/getStaticResourceHeaders.js";
11
+ import { getTypeTemplate } from "./services/getTypeTemplate.js";
12
+ import { parseModuleOptions } from "./services/parseModuleOptions.js";
13
+ import { defu } from "defu";
14
+ import { addNitroPlugin, addServerHandler, addServerTemplate, addTypeTemplate, createResolver, defineNuxtModule } from "@nuxt/kit";
15
+ //#region src/module.ts
16
+ const ROUTE_RULES_TEMPLATE_FILENAME = `#${MODULE_NAME}/route-rules.mjs`;
17
+ const SRI_HASHES_TEMPLATE_FILENAME = `#${MODULE_NAME}/sri-hashes.mjs`;
18
+ const ROUTE_RULES_PLUGIN_FILENAME = `#${MODULE_NAME}/route-rules-plugin.mjs`;
19
+ const SRI_PLUGIN_FILENAME = `#${MODULE_NAME}/sri-plugin.mjs`;
20
+ const securityModule = defineNuxtModule({
21
+ meta: {
22
+ compatibility: { nuxt: ">=5.0.0-0" },
23
+ configKey: "security",
24
+ name: MODULE_NAME
25
+ },
26
+ setup(userOptions, nuxt) {
27
+ const options = parseModuleOptions(mergeModuleOptions(getDefaultModuleOptions(nuxt.options.dev, nuxt.options.devServer.url, userOptions.strict === true), userOptions));
28
+ if (!options.enabled) return;
29
+ const resolver = createResolver(import.meta.url);
30
+ const { basicAuth, ...publicOptions } = options;
31
+ if (basicAuth) {
32
+ const { private: privateRuntimeConfig } = nuxt.options.runtimeConfig;
33
+ nuxt.options.runtimeConfig.private = defu(checkIsRecord(privateRuntimeConfig) ? privateRuntimeConfig : {}, { basicAuth });
34
+ }
35
+ const routeSecurityRules = getRouteSecurityRules(nuxt.options.routeRules ?? {}, options);
36
+ addServerTemplate({
37
+ filename: ROUTE_RULES_TEMPLATE_FILENAME,
38
+ getContents: () => getRouteRulesTemplate({
39
+ "/**": publicOptions,
40
+ ...routeSecurityRules
41
+ })
42
+ });
43
+ addServerTemplate({
44
+ filename: ROUTE_RULES_PLUGIN_FILENAME,
45
+ getContents: () => getPluginTemplate(resolver.resolve("./runtime/nitro/plugins/routeRules"), "createRouteRulesPlugin", ROUTE_RULES_TEMPLATE_FILENAME)
46
+ });
47
+ addNitroPlugin(ROUTE_RULES_PLUGIN_FILENAME);
48
+ if (options.removeLoggers) nuxt.options.vite = defu(getRemoveLoggersViteConfig(), nuxt.options.vite);
49
+ if (options.headers) nuxt.options.routeRules = defu({ "/**": { headers: getStaticResourceHeaders(options.headers) } }, nuxt.options.routeRules);
50
+ if (options.nonce) addNitroPlugin(resolver.resolve("./runtime/nitro/plugins/nonce"));
51
+ if (options.sri) {
52
+ addServerTemplate({
53
+ filename: SRI_PLUGIN_FILENAME,
54
+ getContents: () => getPluginTemplate(resolver.resolve("./runtime/nitro/plugins/subresourceIntegrity"), "createSubresourceIntegrityPlugin", SRI_HASHES_TEMPLATE_FILENAME)
55
+ });
56
+ addNitroPlugin(SRI_PLUGIN_FILENAME);
57
+ }
58
+ if (options.ssg) addNitroPlugin(resolver.resolve("./runtime/nitro/plugins/ssgHashes"));
59
+ if (options.headers) addNitroPlugin(resolver.resolve("./runtime/nitro/plugins/securityHeaders"));
60
+ if (options.hidePoweredBy) addNitroPlugin(resolver.resolve("./runtime/nitro/plugins/hidePoweredBy"));
61
+ if (options.csrf) addServerHandler({
62
+ handler: resolver.resolve("./runtime/server/middleware/csrf"),
63
+ middleware: true
64
+ });
65
+ if (options.rateLimiter) addServerHandler({
66
+ handler: resolver.resolve("./runtime/server/middleware/rateLimiter"),
67
+ middleware: true
68
+ });
69
+ if (options.requestSizeLimiter) addServerHandler({
70
+ handler: resolver.resolve("./runtime/server/middleware/requestSizeLimiter"),
71
+ middleware: true
72
+ });
73
+ if (options.corsHandler) addServerHandler({
74
+ handler: resolver.resolve("./runtime/server/middleware/corsHandler"),
75
+ middleware: true
76
+ });
77
+ if (options.allowedMethodsRestricter) addServerHandler({
78
+ handler: resolver.resolve("./runtime/server/middleware/allowedMethodsRestricter"),
79
+ middleware: true
80
+ });
81
+ if (basicAuth) addServerHandler({
82
+ handler: resolver.resolve("./runtime/server/middleware/basicAuth"),
83
+ middleware: true
84
+ });
85
+ let sriHashes = {};
86
+ if (options.sri) {
87
+ addServerTemplate({
88
+ filename: SRI_HASHES_TEMPLATE_FILENAME,
89
+ getContents: () => `export default ${JSON.stringify(sriHashes)}\n`
90
+ });
91
+ nuxt.hook("nitro:build:before", async (nitro) => {
92
+ sriHashes = await computeSubresourceIntegrityHashes(nitro);
93
+ });
94
+ }
95
+ addTypeTemplate({
96
+ filename: `types/${MODULE_NAME}.d.ts`,
97
+ getContents: getTypeTemplate
98
+ }, {
99
+ nitro: true,
100
+ nuxt: true
101
+ });
102
+ }
103
+ });
104
+ //#endregion
105
+ export { securityModule as default };
@@ -0,0 +1,5 @@
1
+ //#region src/models/AllowedMethodsRestricterOptions.d.ts
2
+ export interface AllowedMethodsRestricterOptions {
3
+ methods: string[];
4
+ }
5
+ //#endregion
@@ -0,0 +1,9 @@
1
+ //#region src/models/BasicAuthOptions.d.ts
2
+ export interface BasicAuthOptions {
3
+ exclude?: string[];
4
+ include?: string[];
5
+ message?: string;
6
+ name: string;
7
+ pass: string;
8
+ }
9
+ //#endregion
@@ -0,0 +1,3 @@
1
+ //#region src/models/ContentSecurityPolicy.d.ts
2
+ export type ContentSecurityPolicy = Record<string, boolean | string[] | undefined>;
3
+ //#endregion
@@ -0,0 +1,13 @@
1
+ //#region src/models/CorsHandlerOptions.d.ts
2
+ export interface CorsHandlerOptions {
3
+ allowHeaders?: string[];
4
+ credentials?: boolean;
5
+ exposeHeaders?: string[];
6
+ maxAge?: string;
7
+ methods: string[];
8
+ origin: string | string[];
9
+ preflight?: {
10
+ statusCode: number;
11
+ };
12
+ }
13
+ //#endregion
@@ -0,0 +1,8 @@
1
+ //#region src/models/CrossOriginEmbedderPolicy.d.ts
2
+ export declare const CrossOriginEmbedderPolicy: {
3
+ readonly Credentialless: "credentialless";
4
+ readonly RequireCorp: "require-corp";
5
+ readonly UnsafeNone: "unsafe-none";
6
+ };
7
+ export type CrossOriginEmbedderPolicy = (typeof CrossOriginEmbedderPolicy)[keyof typeof CrossOriginEmbedderPolicy];
8
+ //#endregion
@@ -0,0 +1,8 @@
1
+ //#region src/models/CrossOriginEmbedderPolicy.ts
2
+ const CrossOriginEmbedderPolicy = {
3
+ Credentialless: "credentialless",
4
+ RequireCorp: "require-corp",
5
+ UnsafeNone: "unsafe-none"
6
+ };
7
+ //#endregion
8
+ export { CrossOriginEmbedderPolicy };
@@ -0,0 +1,6 @@
1
+ //#region src/models/CsrfOptions.d.ts
2
+ export interface CsrfOptions {
3
+ cookieName: string;
4
+ headerName: string;
5
+ }
6
+ //#endregion
@@ -0,0 +1,27 @@
1
+ import { AllowedMethodsRestricterOptions } from "./AllowedMethodsRestricterOptions.js";
2
+ import { BasicAuthOptions } from "./BasicAuthOptions.js";
3
+ import { CorsHandlerOptions } from "./CorsHandlerOptions.js";
4
+ import { CsrfOptions } from "./CsrfOptions.js";
5
+ import { RateLimiterOptions } from "./RateLimiterOptions.js";
6
+ import { RequestSizeLimiterOptions } from "./RequestSizeLimiterOptions.js";
7
+ import { SecurityHeaders } from "./SecurityHeaders.js";
8
+ import { SsgOptions } from "./SsgOptions.js";
9
+ //#region src/models/ModuleOptions.d.ts
10
+ export interface ModuleOptions {
11
+ allowedMethodsRestricter: AllowedMethodsRestricterOptions | false;
12
+ basicAuth: BasicAuthOptions | false;
13
+ contentSecurityPolicyReportOnly: boolean;
14
+ corsHandler: CorsHandlerOptions | false;
15
+ csrf: CsrfOptions | false;
16
+ enabled: boolean;
17
+ headers: false | SecurityHeaders;
18
+ hidePoweredBy: boolean;
19
+ nonce: boolean;
20
+ rateLimiter: false | RateLimiterOptions;
21
+ removeLoggers: boolean;
22
+ requestSizeLimiter: false | RequestSizeLimiterOptions;
23
+ sri: boolean;
24
+ ssg: false | SsgOptions;
25
+ strict: boolean;
26
+ }
27
+ //#endregion
@@ -0,0 +1,3 @@
1
+ //#region src/models/PermissionsPolicy.d.ts
2
+ export type PermissionsPolicy = Record<string, false | string[] | undefined>;
3
+ //#endregion
@@ -0,0 +1,6 @@
1
+ //#region src/models/RateLimitWindow.d.ts
2
+ export interface RateLimitWindow {
3
+ count: number;
4
+ resetAt: number;
5
+ }
6
+ //#endregion
@@ -0,0 +1,6 @@
1
+ //#region src/models/RateLimiterOptions.d.ts
2
+ export interface RateLimiterOptions {
3
+ intervalInMilliseconds: number;
4
+ tokensPerInterval: number;
5
+ }
6
+ //#endregion
@@ -0,0 +1,6 @@
1
+ import { RateLimitWindow } from "./RateLimitWindow.js";
2
+ //#region src/models/RateLimiterStore.d.ts
3
+ export interface RateLimiterStore {
4
+ increment: (key: string, intervalInMilliseconds: number) => Promise<RateLimitWindow>;
5
+ }
6
+ //#endregion
@@ -0,0 +1,6 @@
1
+ //#region src/models/RequestSizeLimiterOptions.d.ts
2
+ export interface RequestSizeLimiterOptions {
3
+ maxRequestSizeInBytes: number;
4
+ maxUploadFileRequestInBytes: number;
5
+ }
6
+ //#endregion
@@ -0,0 +1,22 @@
1
+ import { ContentSecurityPolicy } from "./ContentSecurityPolicy.js";
2
+ import { CrossOriginEmbedderPolicy } from "./CrossOriginEmbedderPolicy.js";
3
+ import { PermissionsPolicy } from "./PermissionsPolicy.js";
4
+ import { StrictTransportSecurity } from "./StrictTransportSecurity.js";
5
+ import { XFrameOptions } from "./XFrameOptions.js";
6
+ //#region src/models/SecurityHeaders.d.ts
7
+ export interface SecurityHeaders {
8
+ contentSecurityPolicy?: ContentSecurityPolicy | false;
9
+ crossOriginEmbedderPolicy?: CrossOriginEmbedderPolicy | false;
10
+ crossOriginOpenerPolicy?: false | string;
11
+ crossOriginResourcePolicy?: false | string;
12
+ originAgentCluster?: false | string;
13
+ permissionsPolicy?: false | PermissionsPolicy;
14
+ referrerPolicy?: false | string;
15
+ strictTransportSecurity?: false | StrictTransportSecurity;
16
+ xContentTypeOptions?: false | string;
17
+ xDNSPrefetchControl?: false | string;
18
+ xDownloadOptions?: false | string;
19
+ xFrameOptions?: false | XFrameOptions;
20
+ xPermittedCrossDomainPolicies?: false | string;
21
+ }
22
+ //#endregion
@@ -0,0 +1,4 @@
1
+ import { ModuleOptions } from "./ModuleOptions.js";
2
+ //#region src/models/SecurityRouteRules.d.ts
3
+ export type SecurityRouteRules = Partial<ModuleOptions>;
4
+ //#endregion
@@ -0,0 +1,4 @@
1
+ import { ModuleOptions } from "./ModuleOptions.js";
2
+ //#region src/models/SecurityRules.d.ts
3
+ export type SecurityRules = Partial<ModuleOptions>;
4
+ //#endregion
@@ -0,0 +1,9 @@
1
+ //#region src/models/SsgOptions.d.ts
2
+ export interface SsgOptions {
3
+ exportToPresets: boolean;
4
+ hashScripts: boolean;
5
+ hashStyles: boolean;
6
+ meta: boolean;
7
+ nitroHeaders: boolean;
8
+ }
9
+ //#endregion
@@ -0,0 +1,7 @@
1
+ //#region src/models/StrictTransportSecurity.d.ts
2
+ export interface StrictTransportSecurity {
3
+ includeSubdomains: boolean;
4
+ maxAge: number;
5
+ preload: boolean;
6
+ }
7
+ //#endregion
@@ -0,0 +1,7 @@
1
+ //#region src/models/XFrameOptions.d.ts
2
+ export declare const XFrameOptions: {
3
+ readonly Deny: "DENY";
4
+ readonly SameOrigin: "SAMEORIGIN";
5
+ };
6
+ export type XFrameOptions = (typeof XFrameOptions)[keyof typeof XFrameOptions];
7
+ //#endregion
@@ -0,0 +1,7 @@
1
+ //#region src/models/XFrameOptions.ts
2
+ const XFrameOptions = {
3
+ Deny: "DENY",
4
+ SameOrigin: "SAMEORIGIN"
5
+ };
6
+ //#endregion
7
+ export { XFrameOptions };
@@ -0,0 +1,24 @@
1
+ import { SecurityHeaders } from "../models/SecurityHeaders.js";
2
+ //#region src/runtime/constants.d.ts
3
+ export declare const MODULE_NAME = "nuxt-security-module";
4
+ export declare const ROUTE_RULES_HOOK: `${typeof MODULE_NAME}:routeRules`;
5
+ export declare const SECURITY_HEADER_NAMES: Readonly<Record<keyof SecurityHeaders, string>>;
6
+ export declare const CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME = "Content-Security-Policy-Report-Only";
7
+ export declare const MAX_RATE_LIMIT_ENTRIES = 1e4;
8
+ export declare const UNKNOWN_CLIENT_KEY = "unknown";
9
+ export declare const CSRF_UNSAFE_METHODS: readonly string[];
10
+ export declare const CSRF_TOKEN_BYTE_LENGTH = 32;
11
+ export declare const NONCE_BYTE_LENGTH = 18;
12
+ export declare const NONCE_PLACEHOLDER = "'nonce-{{nonce}}'";
13
+ export declare const SRI_HASH_ALGORITHM = "SHA-384";
14
+ export declare const CSP_HASH_ALGORITHM = "SHA-256";
15
+ export declare const PRERENDERED_HEADERS_DIRECTORY_NAME = "nuxt-security";
16
+ export declare const PRERENDERED_HEADERS_FILE_NAME = "headers.json";
17
+ export declare const HTML_ATTRIBUTE_REGEX: RegExp;
18
+ export declare const HTML_TAG_REGEX: RegExp;
19
+ export declare const NONCE_TAG_NAMES: readonly string[];
20
+ export declare const FILE_UPLOAD_CONTENT_TYPE = "multipart/form-data";
21
+ export declare const SIZE_LIMITED_METHODS: readonly string[];
22
+ export declare const DEFAULT_BASIC_AUTH_REALM = "Please enter username and password";
23
+ export declare const BASIC_AUTHORIZATION_SCHEME = "Basic";
24
+ //#endregion
@@ -0,0 +1,52 @@
1
+ //#region src/runtime/constants.ts
2
+ const MODULE_NAME = "nuxt-security-module";
3
+ const ROUTE_RULES_HOOK = `${MODULE_NAME}:routeRules`;
4
+ const SECURITY_HEADER_NAMES = {
5
+ contentSecurityPolicy: "Content-Security-Policy",
6
+ crossOriginEmbedderPolicy: "Cross-Origin-Embedder-Policy",
7
+ crossOriginOpenerPolicy: "Cross-Origin-Opener-Policy",
8
+ crossOriginResourcePolicy: "Cross-Origin-Resource-Policy",
9
+ originAgentCluster: "Origin-Agent-Cluster",
10
+ permissionsPolicy: "Permissions-Policy",
11
+ referrerPolicy: "Referrer-Policy",
12
+ strictTransportSecurity: "Strict-Transport-Security",
13
+ xContentTypeOptions: "X-Content-Type-Options",
14
+ xDNSPrefetchControl: "X-DNS-Prefetch-Control",
15
+ xDownloadOptions: "X-Download-Options",
16
+ xFrameOptions: "X-Frame-Options",
17
+ xPermittedCrossDomainPolicies: "X-Permitted-Cross-Domain-Policies"
18
+ };
19
+ const CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME = "Content-Security-Policy-Report-Only";
20
+ const MAX_RATE_LIMIT_ENTRIES = 1e4;
21
+ const UNKNOWN_CLIENT_KEY = "unknown";
22
+ const CSRF_UNSAFE_METHODS = [
23
+ "DELETE",
24
+ "PATCH",
25
+ "POST",
26
+ "PUT"
27
+ ];
28
+ const CSRF_TOKEN_BYTE_LENGTH = 32;
29
+ const NONCE_BYTE_LENGTH = 18;
30
+ const NONCE_PLACEHOLDER = "'nonce-{{nonce}}'";
31
+ const SRI_HASH_ALGORITHM = "SHA-384";
32
+ const CSP_HASH_ALGORITHM = "SHA-256";
33
+ const PRERENDERED_HEADERS_DIRECTORY_NAME = "nuxt-security";
34
+ const PRERENDERED_HEADERS_FILE_NAME = "headers.json";
35
+ const HTML_ATTRIBUTE_PATTERN = String.raw`\s+[^\s"'<>/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>=]+))?`;
36
+ const HTML_ATTRIBUTE_REGEX = /\s+(?<name>[^\s"'<>/=]+)(?<assignment>\s*=\s*(?<value>"[^"]*"|'[^']*'|[^\s"'<>=]+))?/gu;
37
+ const HTML_TAG_REGEX = new RegExp(String.raw`<(script|style)\b((?:${HTML_ATTRIBUTE_PATTERN})*)(\s*\/?)>([\s\S]*?)<\/\1\s*>|<([a-z][\w:-]*)((?:${HTML_ATTRIBUTE_PATTERN})*)(\s*\/?)>`, "giu");
38
+ const NONCE_TAG_NAMES = [
39
+ "link",
40
+ "script",
41
+ "style"
42
+ ];
43
+ const FILE_UPLOAD_CONTENT_TYPE = "multipart/form-data";
44
+ const SIZE_LIMITED_METHODS = [
45
+ "DELETE",
46
+ "POST",
47
+ "PUT"
48
+ ];
49
+ const DEFAULT_BASIC_AUTH_REALM = "Please enter username and password";
50
+ const BASIC_AUTHORIZATION_SCHEME = "Basic";
51
+ //#endregion
52
+ export { BASIC_AUTHORIZATION_SCHEME, CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME, CSP_HASH_ALGORITHM, CSRF_TOKEN_BYTE_LENGTH, CSRF_UNSAFE_METHODS, DEFAULT_BASIC_AUTH_REALM, FILE_UPLOAD_CONTENT_TYPE, HTML_ATTRIBUTE_REGEX, HTML_TAG_REGEX, MAX_RATE_LIMIT_ENTRIES, MODULE_NAME, NONCE_BYTE_LENGTH, NONCE_PLACEHOLDER, NONCE_TAG_NAMES, PRERENDERED_HEADERS_DIRECTORY_NAME, PRERENDERED_HEADERS_FILE_NAME, ROUTE_RULES_HOOK, SECURITY_HEADER_NAMES, SIZE_LIMITED_METHODS, SRI_HASH_ALGORITHM, UNKNOWN_CLIENT_KEY };
@@ -0,0 +1,5 @@
1
+ import { NitroAppPlugin } from "nitro/types";
2
+ //#region src/runtime/nitro/plugins/hidePoweredBy.d.ts
3
+ declare const hidePoweredBy: NitroAppPlugin;
4
+ //#endregion
5
+ export { hidePoweredBy as default };
@@ -0,0 +1,13 @@
1
+ import { getSecurityRules } from "../../services/getSecurityRules.js";
2
+ import { definePlugin } from "nitro";
3
+ import { isEvent } from "nitro/h3";
4
+ //#region src/runtime/nitro/plugins/hidePoweredBy.ts
5
+ const hidePoweredBy = definePlugin((nitroApp) => {
6
+ nitroApp.hooks.hook("response", (_response, event) => {
7
+ if (!isEvent(event)) return;
8
+ const rules = getSecurityRules(event);
9
+ if (rules?.enabled && rules.hidePoweredBy) event.res.headers.delete("x-powered-by");
10
+ });
11
+ });
12
+ //#endregion
13
+ export { hidePoweredBy as default };
@@ -0,0 +1,5 @@
1
+ import { NitroAppPlugin } from "nitro/types";
2
+ //#region src/runtime/nitro/plugins/nonce.d.ts
3
+ declare const noncePlugin: NitroAppPlugin;
4
+ //#endregion
5
+ export { noncePlugin as default };
@@ -0,0 +1,27 @@
1
+ import { generateNonce } from "../../services/generateNonce.js";
2
+ import { getSecurityRules } from "../../services/getSecurityRules.js";
3
+ import { getSecurityState } from "../../services/getSecurityState.js";
4
+ import { injectNonce } from "../../services/injectNonce.js";
5
+ import { definePlugin } from "nitro";
6
+ //#region src/runtime/nitro/plugins/nonce.ts
7
+ const HTML_SECTIONS = [
8
+ "body",
9
+ "bodyAppend",
10
+ "bodyPrepend",
11
+ "head"
12
+ ];
13
+ const noncePlugin = definePlugin((nitroApp) => {
14
+ if (import.meta.prerender) return;
15
+ nitroApp.hooks.hook("render:route", (_context, { event }) => {
16
+ const rules = getSecurityRules(event);
17
+ if (rules?.enabled && rules.nonce) getSecurityState(event).nonce = generateNonce();
18
+ });
19
+ nitroApp.hooks.hook("render:html", (html, { event }) => {
20
+ const rules = getSecurityRules(event);
21
+ const nonce = getSecurityState(event).nonce;
22
+ if (!rules?.enabled || !rules.nonce || !rules.headers || !rules.headers.contentSecurityPolicy || !nonce) return;
23
+ for (const section of HTML_SECTIONS) html[section] = html[section].map((element) => injectNonce(element, nonce));
24
+ });
25
+ });
26
+ //#endregion
27
+ export { noncePlugin as default };
@@ -0,0 +1,5 @@
1
+ import { SecurityRules } from "../../../models/SecurityRules.js";
2
+ import { NitroAppPlugin } from "nitro/types";
3
+ //#region src/runtime/nitro/plugins/routeRules.d.ts
4
+ export declare const createRouteRulesPlugin: (routeRules: Record<string, SecurityRules>) => NitroAppPlugin;
5
+ //#endregion
@@ -0,0 +1,17 @@
1
+ import { ROUTE_RULES_HOOK } from "../../constants.js";
2
+ import { setSecurityRouteMatcher } from "../../services/securityRouteMatcher.js";
3
+ import { definePlugin } from "nitro";
4
+ //#region src/runtime/nitro/plugins/routeRules.ts
5
+ const createRouteRulesPlugin = (routeRules) => definePlugin((nitroApp) => {
6
+ let routesResolved;
7
+ nitroApp.hooks.hook("request", () => {
8
+ routesResolved ??= (async () => {
9
+ const routes = structuredClone(routeRules);
10
+ await nitroApp.hooks.callHook(ROUTE_RULES_HOOK, routes);
11
+ setSecurityRouteMatcher(routes);
12
+ })();
13
+ return routesResolved;
14
+ });
15
+ });
16
+ //#endregion
17
+ export { createRouteRulesPlugin };
@@ -0,0 +1,5 @@
1
+ import { NitroAppPlugin } from "nitro/types";
2
+ //#region src/runtime/nitro/plugins/securityHeaders.d.ts
3
+ declare const securityHeaders: NitroAppPlugin;
4
+ //#endregion
5
+ export { securityHeaders as default };
@@ -0,0 +1,25 @@
1
+ import { CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME, SECURITY_HEADER_NAMES } from "../../constants.js";
2
+ import { getSecurityRules } from "../../services/getSecurityRules.js";
3
+ import { getSecurityState } from "../../services/getSecurityState.js";
4
+ import { serializeSecurityHeaders } from "../../services/serializeSecurityHeaders.js";
5
+ import { updateContentSecurityPolicy } from "../../services/updateContentSecurityPolicy.js";
6
+ import { definePlugin } from "nitro";
7
+ //#region src/runtime/nitro/plugins/securityHeaders.ts
8
+ const CONTENT_SECURITY_POLICY_HEADER_NAME = SECURITY_HEADER_NAMES.contentSecurityPolicy;
9
+ const securityHeaders = definePlugin((nitroApp) => {
10
+ nitroApp.hooks.hook("render:route", (_context, { event }) => {
11
+ const rules = getSecurityRules(event);
12
+ if (!rules?.enabled || !rules.headers) return;
13
+ const { contentSecurityPolicy } = rules.headers;
14
+ const serializedHeaders = serializeSecurityHeaders({
15
+ ...rules.headers,
16
+ contentSecurityPolicy: contentSecurityPolicy ? updateContentSecurityPolicy(contentSecurityPolicy, getSecurityState(event)) : false
17
+ });
18
+ for (const [name, value] of Object.entries(serializedHeaders)) {
19
+ const isPolicy = name === CONTENT_SECURITY_POLICY_HEADER_NAME;
20
+ event.res.headers.set(isPolicy && rules.contentSecurityPolicyReportOnly ? CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME : name, value);
21
+ }
22
+ });
23
+ });
24
+ //#endregion
25
+ export { securityHeaders as default };
@@ -0,0 +1,5 @@
1
+ import { NitroAppPlugin } from "nitro/types";
2
+ //#region src/runtime/nitro/plugins/ssgHashes.d.ts
3
+ declare const ssgHashes: NitroAppPlugin;
4
+ //#endregion
5
+ export { ssgHashes as default };
@@ -0,0 +1,27 @@
1
+ import { collectSsgHashes } from "../../services/collectSsgHashes.js";
2
+ import { getSecurityRules } from "../../services/getSecurityRules.js";
3
+ import { getSecurityState } from "../../services/getSecurityState.js";
4
+ import { insertContentSecurityPolicyMeta } from "../../services/insertContentSecurityPolicyMeta.js";
5
+ import { updateContentSecurityPolicy } from "../../services/updateContentSecurityPolicy.js";
6
+ import { definePlugin } from "nitro";
7
+ //#region src/runtime/nitro/plugins/ssgHashes.ts
8
+ const HTML_SECTIONS = [
9
+ "head",
10
+ "bodyPrepend",
11
+ "body",
12
+ "bodyAppend"
13
+ ];
14
+ const ssgHashes = definePlugin((nitroApp) => {
15
+ if (!import.meta.prerender) return;
16
+ nitroApp.hooks.hook("render:html", async (html, { event }) => {
17
+ const rules = getSecurityRules(event);
18
+ if (!rules?.enabled || !rules.ssg || !rules.headers || !rules.headers.contentSecurityPolicy) return;
19
+ const state = getSecurityState(event);
20
+ state.hashes = await collectSsgHashes(HTML_SECTIONS.map((section) => html[section].join("")).join(""), rules.ssg);
21
+ if (!rules.ssg.meta) return;
22
+ const policy = updateContentSecurityPolicy(rules.headers.contentSecurityPolicy, state);
23
+ html.head = [insertContentSecurityPolicyMeta(html.head.join(""), policy)];
24
+ });
25
+ });
26
+ //#endregion
27
+ export { ssgHashes as default };
@@ -0,0 +1,4 @@
1
+ import { NitroAppPlugin } from "nitro/types";
2
+ //#region src/runtime/nitro/plugins/subresourceIntegrity.d.ts
3
+ export declare const createSubresourceIntegrityPlugin: (sriHashes: Record<string, string>) => NitroAppPlugin;
4
+ //#endregion
@@ -0,0 +1,19 @@
1
+ import { getSecurityRules } from "../../services/getSecurityRules.js";
2
+ import { injectSubresourceIntegrity } from "../../services/injectSubresourceIntegrity.js";
3
+ import { definePlugin } from "nitro";
4
+ //#region src/runtime/nitro/plugins/subresourceIntegrity.ts
5
+ const HTML_SECTIONS = [
6
+ "body",
7
+ "bodyAppend",
8
+ "bodyPrepend",
9
+ "head"
10
+ ];
11
+ const createSubresourceIntegrityPlugin = (sriHashes) => definePlugin((nitroApp) => {
12
+ nitroApp.hooks.hook("render:html", (html, { event }) => {
13
+ const rules = getSecurityRules(event);
14
+ if (!rules?.enabled || !rules.sri) return;
15
+ for (const section of HTML_SECTIONS) html[section] = html[section].map((element) => injectSubresourceIntegrity(element, sriHashes));
16
+ });
17
+ });
18
+ //#endregion
19
+ export { createSubresourceIntegrityPlugin };