From efae1583d681b8428d8690ce26d0ffebf7c7b11a Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:10:09 +0100 Subject: [PATCH 01/42] Update index.ts --- src/types/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/index.ts b/src/types/index.ts index c2572416..381a497e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -4,6 +4,7 @@ import type { SecurityHeaders } from './headers' import type { AllowedHTTPMethods, BasicAuth, RateLimiter, RequestSizeLimiter, XssValidator, CorsOptions } from './middlewares' export type Ssg = { + enabled?: boolean; hashScripts?: boolean; hashStyles?: boolean; }; From 40b3d596a0b485eaff5bc71993f37b2a9b8aa423 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:11:02 +0100 Subject: [PATCH 02/42] Update defaultConfig.ts --- src/defaultConfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts index 339bbb91..47e107d3 100644 --- a/src/defaultConfig.ts +++ b/src/defaultConfig.ts @@ -82,6 +82,7 @@ export const defaultSecurityConfig = (serverlUrl: string): Partial Date: Fri, 12 Jan 2024 09:13:27 +0100 Subject: [PATCH 03/42] Update 04-cspSsgHashes.ts --- src/runtime/nitro/plugins/04-cspSsgHashes.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/runtime/nitro/plugins/04-cspSsgHashes.ts b/src/runtime/nitro/plugins/04-cspSsgHashes.ts index b0fe439c..c5259e43 100644 --- a/src/runtime/nitro/plugins/04-cspSsgHashes.ts +++ b/src/runtime/nitro/plugins/04-cspSsgHashes.ts @@ -25,7 +25,7 @@ export default defineNitroPlugin((nitroApp) => { // Parse HTML if SSG is enabled for this route if (security.ssg) { - const { hashScripts, hashStyles } = security.ssg + const { enabled, hashScripts, hashStyles } = security.ssg // Scan all relevant sections of the NuxtRenderHtmlContext type Section = 'body' | 'bodyAppend' | 'bodyPrepend' | 'head' @@ -102,8 +102,10 @@ export default defineNitroPlugin((nitroApp) => { // Generate CSP rules const csp = security.headers.contentSecurityPolicy const headerValue = generateCspRules(csp, scriptHashes, styleHashes) - // Insert CSP in the http meta tag - html.head.push(``) + // Insert CSP in the http meta tag if enabled + if (enabled) { + html.head.push(``) + } // Update rules in HTTP header setResponseHeader(event, 'Content-Security-Policy', headerValue) From 77d720473440c1f05f27cf608282e06a8a70978c Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:18:21 +0100 Subject: [PATCH 04/42] Update 1.csp.md --- docs/content/1.documentation/2.headers/1.csp.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/content/1.documentation/2.headers/1.csp.md b/docs/content/1.documentation/2.headers/1.csp.md index e2d60166..9e45773c 100644 --- a/docs/content/1.documentation/2.headers/1.csp.md +++ b/docs/content/1.documentation/2.headers/1.csp.md @@ -155,6 +155,7 @@ export default defineNuxtConfig({ security: { nonce: true, // Enables HTML nonce support in SSR mode ssg: { + enabled: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, @@ -263,6 +264,7 @@ export default defineNuxtConfig({ // Global security: { ssg: { + enabled: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, From b944f83f6e8baa98f6a290991367e8adfc4d72d5 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:20:04 +0100 Subject: [PATCH 05/42] Update 3.strict-csp.md --- docs/content/1.documentation/5.advanced/3.strict-csp.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/1.documentation/5.advanced/3.strict-csp.md b/docs/content/1.documentation/5.advanced/3.strict-csp.md index a9392a80..0a544fc4 100644 --- a/docs/content/1.documentation/5.advanced/3.strict-csp.md +++ b/docs/content/1.documentation/5.advanced/3.strict-csp.md @@ -608,6 +608,7 @@ export default defineNuxtConfig({ security: { nonce: true, // Enables HTML nonce support in SSR mode ssg: { + enabled: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, From 1ab6fb035e19eac50a43f2f64e202a0da4be815f Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:20:46 +0100 Subject: [PATCH 06/42] Update 2.configuration.md --- .../content/1.documentation/1.getting-started/2.configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/1.documentation/1.getting-started/2.configuration.md b/docs/content/1.documentation/1.getting-started/2.configuration.md index c2a994c2..6d8067e7 100644 --- a/docs/content/1.documentation/1.getting-started/2.configuration.md +++ b/docs/content/1.documentation/1.getting-started/2.configuration.md @@ -116,6 +116,7 @@ security: { exclude: [/node_modules/, /\.git/] }, ssg: { + enabled: true, hashScripts: true, hashStyles: false }, From ecd6f79c8c2a6e22524996a12dc8485459f5c63c Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:23:19 +0100 Subject: [PATCH 07/42] Update nuxt.config.ts --- test/fixtures/ssgHashes/nuxt.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index 5bbce627..dc6ce2e2 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -32,6 +32,7 @@ export default defineNuxtConfig({ rateLimiter: false, sri: true, ssg: { + enabled: true, hashScripts: true, hashStyles: true } From 27e2d9beef4576d107546505aa6f49f87dbf58a8 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:29:01 +0100 Subject: [PATCH 08/42] Update nuxt.config.ts --- test/fixtures/perRoute/nuxt.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/fixtures/perRoute/nuxt.config.ts b/test/fixtures/perRoute/nuxt.config.ts index 757dba3c..9dd4fecb 100644 --- a/test/fixtures/perRoute/nuxt.config.ts +++ b/test/fixtures/perRoute/nuxt.config.ts @@ -220,6 +220,7 @@ export default defineNuxtConfig({ prerender: true, security: { ssg: { + enabled: true, hashScripts: true } } From cee1a1eccd4b6c086aea1f13cdd7dd5533527a8a Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:30:23 +0100 Subject: [PATCH 09/42] Create no-meta-tag.vue --- test/fixtures/ssgHashes/pages/no-meta-tag.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/ssgHashes/pages/no-meta-tag.vue diff --git a/test/fixtures/ssgHashes/pages/no-meta-tag.vue b/test/fixtures/ssgHashes/pages/no-meta-tag.vue new file mode 100644 index 00000000..434562b0 --- /dev/null +++ b/test/fixtures/ssgHashes/pages/no-meta-tag.vue @@ -0,0 +1,5 @@ + From 4a4a5d5977949650b74bfa863eb4a2cb24f7e6e0 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:36:11 +0100 Subject: [PATCH 10/42] Update ssgHashes.test.ts --- test/ssgHashes.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/ssgHashes.test.ts b/test/ssgHashes.test.ts index 50f34dbe..eab6bb5e 100644 --- a/test/ssgHashes.test.ts +++ b/test/ssgHashes.test.ts @@ -158,3 +158,22 @@ describe('[nuxt-security] SSG support of CSP', async () => { expect(externalStyleHashes).toBe(0) }) }) + +it('does not set CSP via meta when disabled', async () => { + const res = await fetch('/no-meta-tag') + + const body = await res.text() + const { metaTag, csp, elementsWithIntegrity, inlineScriptHashes, externalScriptHashes, inlineStyleHashes, externalStyleHashes } = extractDataFromBody(body) + + expect(res).toBeDefined() + expect(res).toBeTruthy() + expect(body).toBeDefined() + expect(metaTag).toBeNull() + expect(csp).toBeUndefined() + expect(elementsWithIntegrity).toBe(expectedIntegrityAttributes - 1) // No vue on no-meta-tag page + expect(inlineScriptHashes).toBe(expectedInlineScriptHashes) + expect(externalScriptHashes).toBe(expectedExternalScriptHashes) + expect(inlineStyleHashes).toBe(expectedInlineStyleHashes) + expect(externalStyleHashes).toBe(expectedExternalStyleHashes) + }) +}) From 7c3e741248c42f99d7ce46a1f2e43c134150a37b Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:37:23 +0100 Subject: [PATCH 11/42] Update nuxt.config.ts --- test/fixtures/ssgHashes/nuxt.config.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index dc6ce2e2..534f125f 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -24,7 +24,13 @@ export default defineNuxtConfig({ }, '/not-ssg': { prerender: false - } + }, + '/no-meta-tag': { + prerender: true, + ssg: { + enabled: false, + }, + }, }, // Global configuration From bdcd9c3e4159f8c2b4487b57912667f55c25dba5 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:02:18 +0100 Subject: [PATCH 12/42] Update 3.strict-csp.md --- docs/content/1.documentation/5.advanced/3.strict-csp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/1.documentation/5.advanced/3.strict-csp.md b/docs/content/1.documentation/5.advanced/3.strict-csp.md index 0a544fc4..903db947 100644 --- a/docs/content/1.documentation/5.advanced/3.strict-csp.md +++ b/docs/content/1.documentation/5.advanced/3.strict-csp.md @@ -569,7 +569,7 @@ Nuxt Security supports CSP via HTTP headers for Nitro Presets that output HTTP h ::alert{type="info"} If you deploy your SSG site on Vercel or Netlify, you will benefit automatically from CSP Headers.
-CSP will be delivered via HTTP headers, in addition to the standard `` approach. +CSP will be delivered via HTTP headers, in addition to the standard `` approach. If you want to disabled the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: enabled` option. :: ### Per Route CSP From c767309077017dfae16b00666fdc47c98d54892c Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:02:50 +0100 Subject: [PATCH 13/42] Update 3.strict-csp.md --- docs/content/1.documentation/5.advanced/3.strict-csp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/1.documentation/5.advanced/3.strict-csp.md b/docs/content/1.documentation/5.advanced/3.strict-csp.md index 903db947..6adcc79a 100644 --- a/docs/content/1.documentation/5.advanced/3.strict-csp.md +++ b/docs/content/1.documentation/5.advanced/3.strict-csp.md @@ -569,7 +569,7 @@ Nuxt Security supports CSP via HTTP headers for Nitro Presets that output HTTP h ::alert{type="info"} If you deploy your SSG site on Vercel or Netlify, you will benefit automatically from CSP Headers.
-CSP will be delivered via HTTP headers, in addition to the standard `` approach. If you want to disabled the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: enabled` option. +CSP will be delivered via HTTP headers, in addition to the standard `` approach. If you want to disable the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: enabled` option. :: ### Per Route CSP From 86d652d87d24d5d785d8bd68bee733c238086f4f Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:18:32 +0100 Subject: [PATCH 14/42] Update ssgHashes.test.ts --- test/ssgHashes.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/ssgHashes.test.ts b/test/ssgHashes.test.ts index eab6bb5e..4b5052ad 100644 --- a/test/ssgHashes.test.ts +++ b/test/ssgHashes.test.ts @@ -157,9 +157,8 @@ describe('[nuxt-security] SSG support of CSP', async () => { expect(inlineStyleHashes).toBe(0) expect(externalStyleHashes).toBe(0) }) -}) -it('does not set CSP via meta when disabled', async () => { + it('does not set CSP via meta when disabled', async () => { const res = await fetch('/no-meta-tag') const body = await res.text() From fa7c965f88acaaa3cf2330bb8493840bb9bca687 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Fri, 12 Jan 2024 12:27:27 +0100 Subject: [PATCH 15/42] refactor: rename ssg.enabled to ssg.meta --- .../1.documentation/1.getting-started/2.configuration.md | 2 +- docs/content/1.documentation/2.headers/1.csp.md | 4 ++-- docs/content/1.documentation/5.advanced/3.strict-csp.md | 4 ++-- src/defaultConfig.ts | 2 +- src/runtime/nitro/plugins/04-cspSsgHashes.ts | 6 +++--- src/types/index.ts | 2 +- test/fixtures/perRoute/nuxt.config.ts | 4 ++-- test/fixtures/ssgHashes/nuxt.config.ts | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/content/1.documentation/1.getting-started/2.configuration.md b/docs/content/1.documentation/1.getting-started/2.configuration.md index 6d8067e7..a4ec4bf1 100644 --- a/docs/content/1.documentation/1.getting-started/2.configuration.md +++ b/docs/content/1.documentation/1.getting-started/2.configuration.md @@ -116,7 +116,7 @@ security: { exclude: [/node_modules/, /\.git/] }, ssg: { - enabled: true, + meta: true, hashScripts: true, hashStyles: false }, diff --git a/docs/content/1.documentation/2.headers/1.csp.md b/docs/content/1.documentation/2.headers/1.csp.md index 9e45773c..95a900ac 100644 --- a/docs/content/1.documentation/2.headers/1.csp.md +++ b/docs/content/1.documentation/2.headers/1.csp.md @@ -155,7 +155,7 @@ export default defineNuxtConfig({ security: { nonce: true, // Enables HTML nonce support in SSR mode ssg: { - enabled: true, // Enables CSP as a meta tag in SSG mode + meta: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, @@ -264,7 +264,7 @@ export default defineNuxtConfig({ // Global security: { ssg: { - enabled: true, // Enables CSP as a meta tag in SSG mode + meta: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, diff --git a/docs/content/1.documentation/5.advanced/3.strict-csp.md b/docs/content/1.documentation/5.advanced/3.strict-csp.md index 6adcc79a..b046dbc1 100644 --- a/docs/content/1.documentation/5.advanced/3.strict-csp.md +++ b/docs/content/1.documentation/5.advanced/3.strict-csp.md @@ -569,7 +569,7 @@ Nuxt Security supports CSP via HTTP headers for Nitro Presets that output HTTP h ::alert{type="info"} If you deploy your SSG site on Vercel or Netlify, you will benefit automatically from CSP Headers.
-CSP will be delivered via HTTP headers, in addition to the standard `` approach. If you want to disable the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: enabled` option. +CSP will be delivered via HTTP headers, in addition to the standard `` approach. If you want to disable the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: meta` option. :: ### Per Route CSP @@ -608,7 +608,7 @@ export default defineNuxtConfig({ security: { nonce: true, // Enables HTML nonce support in SSR mode ssg: { - enabled: true, // Enables CSP as a meta tag in SSG mode + meta: true, // Enables CSP as a meta tag in SSG mode hashScripts: true, // Enables CSP hash support for scripts in SSG mode hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended) }, diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts index 47e107d3..44fb5459 100644 --- a/src/defaultConfig.ts +++ b/src/defaultConfig.ts @@ -82,7 +82,7 @@ export const defaultSecurityConfig = (serverlUrl: string): Partial { // Parse HTML if SSG is enabled for this route if (security.ssg) { - const { enabled, hashScripts, hashStyles } = security.ssg + const { hashScripts, hashStyles } = security.ssg // Scan all relevant sections of the NuxtRenderHtmlContext type Section = 'body' | 'bodyAppend' | 'bodyPrepend' | 'head' @@ -102,8 +102,8 @@ export default defineNitroPlugin((nitroApp) => { // Generate CSP rules const csp = security.headers.contentSecurityPolicy const headerValue = generateCspRules(csp, scriptHashes, styleHashes) - // Insert CSP in the http meta tag if enabled - if (enabled) { + // Insert CSP in the http meta tag if meta is true + if (security.ssg?.meta) { html.head.push(``) } // Update rules in HTTP header diff --git a/src/types/index.ts b/src/types/index.ts index 381a497e..eb647515 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -4,7 +4,7 @@ import type { SecurityHeaders } from './headers' import type { AllowedHTTPMethods, BasicAuth, RateLimiter, RequestSizeLimiter, XssValidator, CorsOptions } from './middlewares' export type Ssg = { - enabled?: boolean; + meta?: boolean; hashScripts?: boolean; hashStyles?: boolean; }; diff --git a/test/fixtures/perRoute/nuxt.config.ts b/test/fixtures/perRoute/nuxt.config.ts index 9dd4fecb..4edfd4bb 100644 --- a/test/fixtures/perRoute/nuxt.config.ts +++ b/test/fixtures/perRoute/nuxt.config.ts @@ -220,7 +220,7 @@ export default defineNuxtConfig({ prerender: true, security: { ssg: { - enabled: true, + meta: true, hashScripts: true } } @@ -258,7 +258,7 @@ export default defineNuxtConfig({ contentSecurityPolicy: false, } } - }, + }, }, security: { headers: { diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index 534f125f..14f031ed 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -28,7 +28,7 @@ export default defineNuxtConfig({ '/no-meta-tag': { prerender: true, ssg: { - enabled: false, + meta: false, }, }, }, @@ -38,7 +38,7 @@ export default defineNuxtConfig({ rateLimiter: false, sri: true, ssg: { - enabled: true, + meta: true, hashScripts: true, hashStyles: true } From 2d41f6de8972292291f9903d9e9ad0738f2b91fb Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:30:20 +0100 Subject: [PATCH 16/42] Update nuxt.config.ts --- test/fixtures/ssgHashes/nuxt.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index 14f031ed..4610722f 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -29,8 +29,8 @@ export default defineNuxtConfig({ prerender: true, ssg: { meta: false, - }, - }, + } + } }, // Global configuration From ba58d75a52b8500e1c29f6e510c5f57451c9187d Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <46671786+mtdvlpr@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:31:05 +0100 Subject: [PATCH 17/42] Update nuxt.config.ts --- test/fixtures/ssgHashes/nuxt.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index 4610722f..0cfd53e9 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -28,7 +28,7 @@ export default defineNuxtConfig({ '/no-meta-tag': { prerender: true, ssg: { - meta: false, + meta: false } } }, From adcaab34e652487c3edb863209fdc644f3335f87 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 21:33:04 +0200 Subject: [PATCH 18/42] feat: allow users to configure methods for XSSValidator --- src/types/middlewares.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index 2a859237..a7df30e0 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -16,6 +16,7 @@ export type RateLimiter = { }; export type XssValidator = { + methods?: Array whiteList?: Record; stripIgnoreTag?: boolean; stripIgnoreTagBody?: boolean; From b498e34c78d57b5298e3fd8a49b39cfaf58ca641 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 21:34:45 +0200 Subject: [PATCH 19/42] fix: semicolon --- src/types/middlewares.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index a7df30e0..0a58ce5e 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -16,7 +16,7 @@ export type RateLimiter = { }; export type XssValidator = { - methods?: Array + methods?: Array; whiteList?: Record; stripIgnoreTag?: boolean; stripIgnoreTagBody?: boolean; From 48c1dc39875fe896b23d357011c13ff843f9deef Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 21:56:39 +0200 Subject: [PATCH 20/42] add file --- src/runtime/utils/methods.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/runtime/utils/methods.ts diff --git a/src/runtime/utils/methods.ts b/src/runtime/utils/methods.ts new file mode 100644 index 00000000..42b64d7b --- /dev/null +++ b/src/runtime/utils/methods.ts @@ -0,0 +1 @@ +export const xssMethods = ['GET', 'POST'] From 9de30aaf7ef3823dd4c55e7f06d84dd5797af670 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 21:58:04 +0200 Subject: [PATCH 21/42] type: adhere to h3's method naming convention --- src/types/middlewares.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index 0a58ce5e..59c22ba1 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -16,7 +16,7 @@ export type RateLimiter = { }; export type XssValidator = { - methods?: Array; + methods?: Array>; whiteList?: Record; stripIgnoreTag?: boolean; stripIgnoreTagBody?: boolean; From 4539f2d82950a4589581bebd517e3f7c52273bf4 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 21:59:50 +0200 Subject: [PATCH 22/42] feat: allow method override in module setup --- src/module.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/module.ts b/src/module.ts index d23f251b..9a9b145c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -110,6 +110,9 @@ export default defineNuxtModule({ } if (nuxt.options.security.xssValidator) { + if (nuxt.options.security.xssValidator.methods) { + xssMethods.push(...nuxt.options.security.xssValidator.methods) + } addServerHandler({ handler: normalize( resolve(runtimeDir, 'server/middleware/xssValidator') From 868bf0f64050cbe87d635f3ee8a0376e90030e7d Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 22:00:44 +0200 Subject: [PATCH 23/42] fix: add import --- src/runtime/server/middleware/xssValidator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/server/middleware/xssValidator.ts b/src/runtime/server/middleware/xssValidator.ts index b189870a..31d2cadb 100644 --- a/src/runtime/server/middleware/xssValidator.ts +++ b/src/runtime/server/middleware/xssValidator.ts @@ -1,6 +1,6 @@ import { FilterXSS } from 'xss' import { defineEventHandler, createError, getQuery, readBody, getRouteRules } from '#imports' - +import { xssMethods } from "../../utils/methods" export default defineEventHandler(async (event) => { const { security } = getRouteRules(event) @@ -8,7 +8,7 @@ export default defineEventHandler(async (event) => { const xssValidator = new FilterXSS(security.xssValidator) if (event.node.req.socket.readyState !== 'readOnly') { - if (['POST', 'GET'].includes(event.node.req.method!)) { + if (xssMethods.includes(event.node.req.method!)) { const valueToFilter = event.node.req.method === 'GET' ? getQuery(event) From 17a7a5d22b0ec66bf6a88a6820a09696106fa5af Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 13 Feb 2024 22:01:40 +0200 Subject: [PATCH 24/42] fix: add import --- src/module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/module.ts b/src/module.ts index 9a9b145c..e2ce328e 100644 --- a/src/module.ts +++ b/src/module.ts @@ -5,6 +5,7 @@ import { defu } from 'defu' import type { Nuxt } from '@nuxt/schema' import viteRemove from 'unplugin-remove/vite' import { defuReplaceArray } from './utils' +import { xssMethods } from "./runtime/utils/methods" import type { ModuleOptions, NuxtSecurityRouteRules From d30eb077fdd41ee95995506dcb0e7c29a19f3b8d Mon Sep 17 00:00:00 2001 From: Morgbn Date: Thu, 15 Feb 2024 20:28:23 +0800 Subject: [PATCH 25/42] feat: add escapeHtml option to XssValidator resolves #206 --- .../1.documentation/3.middleware/3.xss-validator.md | 1 + src/runtime/server/middleware/xssValidator.ts | 8 ++++++-- src/types/middlewares.ts | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/content/1.documentation/3.middleware/3.xss-validator.md b/docs/content/1.documentation/3.middleware/3.xss-validator.md index 61b45f3f..ba96fb6a 100644 --- a/docs/content/1.documentation/3.middleware/3.xss-validator.md +++ b/docs/content/1.documentation/3.middleware/3.xss-validator.md @@ -48,6 +48,7 @@ XSS validator accepts following configuration options: ```ts type XssValidator = { whiteList: Record; + escapeHtml: boolean; stripIgnoreTag: boolean; stripIgnoreTagBody: boolean; css: Record | boolean; diff --git a/src/runtime/server/middleware/xssValidator.ts b/src/runtime/server/middleware/xssValidator.ts index b189870a..7389efc4 100644 --- a/src/runtime/server/middleware/xssValidator.ts +++ b/src/runtime/server/middleware/xssValidator.ts @@ -1,11 +1,15 @@ -import { FilterXSS } from 'xss' +import { FilterXSS, IFilterXSSOptions } from 'xss' import { defineEventHandler, createError, getQuery, readBody, getRouteRules } from '#imports' export default defineEventHandler(async (event) => { const { security } = getRouteRules(event) if (security?.xssValidator) { - const xssValidator = new FilterXSS(security.xssValidator) + const filterOpt: IFilterXSSOptions = { ...security.xssValidator, escapeHtml: undefined } + if (security.xssValidator.escapeHtml === false) { // No html escaping (by default "<" is replaced by "<" and ">" by ">") + filterOpt.escapeHtml = (value: string) => value + } + const xssValidator = new FilterXSS(filterOpt) if (event.node.req.socket.readyState !== 'readOnly') { if (['POST', 'GET'].includes(event.node.req.method!)) { diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index 2a859237..d4936dc6 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -17,6 +17,7 @@ export type RateLimiter = { export type XssValidator = { whiteList?: Record; + escapeHtml?: boolean; stripIgnoreTag?: boolean; stripIgnoreTagBody?: boolean; css?: Record | boolean; From 089d73cc264f7722eb21956ed0a073b49b805061 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 15 Feb 2024 17:30:36 +0200 Subject: [PATCH 26/42] refactor: use config --- src/runtime/server/middleware/xssValidator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runtime/server/middleware/xssValidator.ts b/src/runtime/server/middleware/xssValidator.ts index 31d2cadb..3df9cb1b 100644 --- a/src/runtime/server/middleware/xssValidator.ts +++ b/src/runtime/server/middleware/xssValidator.ts @@ -1,6 +1,6 @@ import { FilterXSS } from 'xss' import { defineEventHandler, createError, getQuery, readBody, getRouteRules } from '#imports' -import { xssMethods } from "../../utils/methods" + export default defineEventHandler(async (event) => { const { security } = getRouteRules(event) @@ -8,7 +8,7 @@ export default defineEventHandler(async (event) => { const xssValidator = new FilterXSS(security.xssValidator) if (event.node.req.socket.readyState !== 'readOnly') { - if (xssMethods.includes(event.node.req.method!)) { + if (security.xssValidator.methods.includes(event.node.req.method!)) { const valueToFilter = event.node.req.method === 'GET' ? getQuery(event) @@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => { ) { return } const stringifiedValue = JSON.stringify(valueToFilter) const processedValue = xssValidator.process( - JSON.stringify(valueToFilter) + stringifiedValue ) if (processedValue !== stringifiedValue) { const badRequestError = { From eee67feab07a5a150e5820fd09f40f4f12dcec5c Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 15 Feb 2024 17:34:58 +0200 Subject: [PATCH 27/42] refactor: use config --- src/module.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/module.ts b/src/module.ts index e2ce328e..d23f251b 100644 --- a/src/module.ts +++ b/src/module.ts @@ -5,7 +5,6 @@ import { defu } from 'defu' import type { Nuxt } from '@nuxt/schema' import viteRemove from 'unplugin-remove/vite' import { defuReplaceArray } from './utils' -import { xssMethods } from "./runtime/utils/methods" import type { ModuleOptions, NuxtSecurityRouteRules @@ -111,9 +110,6 @@ export default defineNuxtModule({ } if (nuxt.options.security.xssValidator) { - if (nuxt.options.security.xssValidator.methods) { - xssMethods.push(...nuxt.options.security.xssValidator.methods) - } addServerHandler({ handler: normalize( resolve(runtimeDir, 'server/middleware/xssValidator') From 544d08f9748228d7152235569472b78d9c673ead Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Sat, 17 Feb 2024 16:01:10 +0100 Subject: [PATCH 28/42] fix: tests --- .prettierrc | 5 +++++ test/fixtures/ssgHashes/nuxt.config.ts | 12 ++++++------ test/perRoute.test.ts | 10 ++++------ 3 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..81c51f98 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "trailingComma": "none", + "semi": false +} diff --git a/test/fixtures/ssgHashes/nuxt.config.ts b/test/fixtures/ssgHashes/nuxt.config.ts index 0cfd53e9..557646d5 100644 --- a/test/fixtures/ssgHashes/nuxt.config.ts +++ b/test/fixtures/ssgHashes/nuxt.config.ts @@ -1,5 +1,4 @@ export default defineNuxtConfig({ - modules: ['../../../src/module'], // Per route configuration @@ -8,7 +7,7 @@ export default defineNuxtConfig({ prerender: true }, '/inline-script': { - prerender: true, + prerender: true }, '/inline-style': { prerender: true @@ -27,8 +26,10 @@ export default defineNuxtConfig({ }, '/no-meta-tag': { prerender: true, - ssg: { - meta: false + security: { + ssg: { + meta: false + } } } }, @@ -42,6 +43,5 @@ export default defineNuxtConfig({ hashScripts: true, hashStyles: true } - }, - + } }) diff --git a/test/perRoute.test.ts b/test/perRoute.test.ts index a2cd6cae..f0898ecf 100644 --- a/test/perRoute.test.ts +++ b/test/perRoute.test.ts @@ -845,9 +845,7 @@ describe('[nuxt-security] Per-route Configuration', async () => { expect(head).toBeDefined() const content = head!.match(//)?.[1] - expect(content).toBeDefined() - const contentHashes = content!.match(/'sha256-(.*?)'/) - expect(contentHashes).toBeNull() + expect(content).toBeUndefined() }) it('injects CSP hashes on a deeply-enabled route', async () => { @@ -892,7 +890,7 @@ describe('[nuxt-security] Per-route Configuration', async () => { it ('does not overwrite middleware headers when false', async () => { const res = await fetch('/preserve-middleware') expect(res.status).toBe(200) - + const { headers } = res const csp = headers.get('content-security-policy') expect(csp).toBeDefined() @@ -905,7 +903,7 @@ describe('[nuxt-security] Per-route Configuration', async () => { it ('overwrites middleware headers when not false', async () => { const res = await fetch('/preserve-middleware/deep/page') expect(res.status).toBe(200) - + const { headers } = res const csp = headers.get('content-security-policy') expect(csp).toBeDefined() @@ -918,7 +916,7 @@ describe('[nuxt-security] Per-route Configuration', async () => { it ('removes deprecated standard headers when false', async () => { const res = await fetch('/remove-deprecated') expect(res.status).toBe(200) - + const { headers } = res const csp = headers.get('content-security-policy') expect(csp).toBeDefined() From 4821bc9cf3ed558142c780e4c7221626ca35823a Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Sat, 17 Feb 2024 16:14:09 +0100 Subject: [PATCH 29/42] fix: tests --- test/fixtures/perRoute/nuxt.config.ts | 71 +++++++++++++++++++-------- test/perRoute.test.ts | 40 ++++++++++++++- 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/test/fixtures/perRoute/nuxt.config.ts b/test/fixtures/perRoute/nuxt.config.ts index 4edfd4bb..a72b74f3 100644 --- a/test/fixtures/perRoute/nuxt.config.ts +++ b/test/fixtures/perRoute/nuxt.config.ts @@ -1,11 +1,9 @@ export default defineNuxtConfig({ - modules: [ - '../../../src/module' - ], + modules: ['../../../src/module'], routeRules: { '/**': { headers: { - 'foo': 'bar', + foo: 'bar', 'Referrer-Policy': 'no-referrer-when-downgrade' } }, @@ -19,7 +17,7 @@ export default defineNuxtConfig({ headers: { crossOriginOpenerPolicy: 'same-origin-allow-popups' } - }, + } }, '/ignore-specific/**': { security: { @@ -34,7 +32,7 @@ export default defineNuxtConfig({ headers: { crossOriginResourcePolicy: false, crossOriginOpenerPolicy: undefined, - crossOriginEmbedderPolicy: 'credentialless', + crossOriginEmbedderPolicy: 'credentialless' } } }, @@ -42,7 +40,7 @@ export default defineNuxtConfig({ security: { headers: { crossOriginResourcePolicy: 'same-site', - crossOriginOpenerPolicy: false, + crossOriginOpenerPolicy: false } } }, @@ -51,9 +49,10 @@ export default defineNuxtConfig({ 'Cross-Origin-Resource-Policy': 'cross-origin', 'Strict-Transport-Security': 'max-age=1; preload;', 'Permissions-Policy': 'fullscreen=*, camera=(self)', - 'Content-Security-Policy': "script-src 'self' https:; media-src 'none';", - 'foo': 'baz', - 'foo2': 'baz2' + 'Content-Security-Policy': + "script-src 'self' https:; media-src 'none';", + foo: 'baz', + foo2: 'baz2' } }, '/resolve-conflict/**': { @@ -63,8 +62,8 @@ export default defineNuxtConfig({ 'Cross-Origin-Embedder-Policy': 'unsafe-none', 'Strict-Transport-Security': 'max-age=1; preload;', 'Permissions-Policy': 'fullscreen=*', - 'foo': 'baz', - 'foo2': 'baz2' + foo: 'baz', + foo2: 'baz2' }, security: { headers: { @@ -118,9 +117,9 @@ export default defineNuxtConfig({ }, //@ts-ignore - Intentional as we test backwards compatibility with a deprecated syntax 'Content-Security-Policy': { - "base-uri": false, - "script-src": "'self'", - "img-src": ['https:'] + 'base-uri': false, + 'script-src': "'self'", + 'img-src': ['https:'] } } }, @@ -198,7 +197,7 @@ export default defineNuxtConfig({ nonce: false, headers: { contentSecurityPolicy: { - "script-src": ["'nonce-{{nonce}}'"] + 'script-src': ["'nonce-{{nonce}}'"] } } } @@ -214,7 +213,13 @@ export default defineNuxtConfig({ } }, '/csp-hash/deep/disabled': { - prerender: true + prerender: true, + security: { + ssg: { + meta: true, + hashScripts: false + } + } }, '/csp-hash/deep/enabled': { prerender: true, @@ -225,6 +230,27 @@ export default defineNuxtConfig({ } } }, + '/csp-meta/**': { + security: { + ssg: false + } + }, + '/csp-meta/deep/disabled': { + prerender: true, + security: { + ssg: { + meta: false, + } + } + }, + '/csp-meta/deep/enabled': { + prerender: true, + security: { + ssg: { + meta: true, + } + } + }, '/sri-attribute/**': { security: { sri: false @@ -255,15 +281,20 @@ export default defineNuxtConfig({ }, security: { headers: { - contentSecurityPolicy: false, + contentSecurityPolicy: false } } - }, + } }, security: { headers: { contentSecurityPolicy: { - "script-src": ["'self'", 'https:', "'unsafe-inline'", "'strict-dynamic'"] + 'script-src': [ + "'self'", + 'https:', + "'unsafe-inline'", + "'strict-dynamic'" + ] } } } diff --git a/test/perRoute.test.ts b/test/perRoute.test.ts index f0898ecf..f818e672 100644 --- a/test/perRoute.test.ts +++ b/test/perRoute.test.ts @@ -845,7 +845,9 @@ describe('[nuxt-security] Per-route Configuration', async () => { expect(head).toBeDefined() const content = head!.match(//)?.[1] - expect(content).toBeUndefined() + expect(content).toBeDefined() + const contentHashes = content!.match(/'sha256-(.*?)'/) + expect(contentHashes).toBeNull() }) it('injects CSP hashes on a deeply-enabled route', async () => { @@ -869,6 +871,42 @@ describe('[nuxt-security] Per-route Configuration', async () => { expect(hashes).toHaveLength(2) }) + it('does not inject CSP meta on a deeply-disabled route', async () => { + const res = await fetch('/csp-meta/deep/disabled') + // DISABLING THIS PART OF THE TEST AFTER PATCH #348 THAT REMOVES CSP SSG PRESETS + /* + const cspHeaderValue = res.headers.get('content-security-policy') + expect(cspHeaderValue).toBeDefined() + */ + + const text = await res.text() + const head = text.match(/(.*?)<\/head>/s)?.[1] + expect(head).toBeDefined() + + const content = head!.match( + // + )?.[1] + expect(content).toBeUndefined() + }) + + it('injects CSP meta on a deeply-enabled route', async () => { + const res = await fetch('/csp-hash/deep/enabled') + // DISABLING THIS PART OF THE TEST AFTER PATCH #348 THAT REMOVES CSP SSG PRESETS + /* + const cspHeaderValue = res.headers.get('content-security-policy') + expect(cspHeaderValue).toBeDefined() + */ + + const text = await res.text() + const head = text.match(/(.*?)<\/head>/s)?.[1] + expect(head).toBeDefined() + + const content = head!.match( + // + )?.[1] + expect(content).toBeDefined() + }) + it('does not inject SRI attributes on a deeply-disabled route', async () => { const res = await fetch('/sri-attribute/deep/disabled') From c99f2744264a7d6509bce2af1cb88709d81d752d Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Sat, 17 Feb 2024 16:15:50 +0100 Subject: [PATCH 30/42] fix: test --- test/perRoute.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/perRoute.test.ts b/test/perRoute.test.ts index f818e672..59e26da8 100644 --- a/test/perRoute.test.ts +++ b/test/perRoute.test.ts @@ -890,7 +890,7 @@ describe('[nuxt-security] Per-route Configuration', async () => { }) it('injects CSP meta on a deeply-enabled route', async () => { - const res = await fetch('/csp-hash/deep/enabled') + const res = await fetch('/csp-meta/deep/enabled') // DISABLING THIS PART OF THE TEST AFTER PATCH #348 THAT REMOVES CSP SSG PRESETS /* const cspHeaderValue = res.headers.get('content-security-policy') From fc16473496b3487db33a3302e19043c52f9c2235 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Sat, 17 Feb 2024 16:17:17 +0100 Subject: [PATCH 31/42] fix: test --- test/fixtures/perRoute/pages/csp-meta/deep/disabled.vue | 3 +++ test/fixtures/perRoute/pages/csp-meta/deep/enabled.vue | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 test/fixtures/perRoute/pages/csp-meta/deep/disabled.vue create mode 100644 test/fixtures/perRoute/pages/csp-meta/deep/enabled.vue diff --git a/test/fixtures/perRoute/pages/csp-meta/deep/disabled.vue b/test/fixtures/perRoute/pages/csp-meta/deep/disabled.vue new file mode 100644 index 00000000..f8d2c744 --- /dev/null +++ b/test/fixtures/perRoute/pages/csp-meta/deep/disabled.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/fixtures/perRoute/pages/csp-meta/deep/enabled.vue b/test/fixtures/perRoute/pages/csp-meta/deep/enabled.vue new file mode 100644 index 00000000..f8d2c744 --- /dev/null +++ b/test/fixtures/perRoute/pages/csp-meta/deep/enabled.vue @@ -0,0 +1,3 @@ + \ No newline at end of file From 7738023b760815286c3ef0d9479a8545dd2e68b0 Mon Sep 17 00:00:00 2001 From: Manoah Tervoort <149895ja@gmail.com> Date: Sat, 17 Feb 2024 16:59:55 +0100 Subject: [PATCH 32/42] fix: test --- test/ssgHashes.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/ssgHashes.test.ts b/test/ssgHashes.test.ts index 4b5052ad..2a7362c5 100644 --- a/test/ssgHashes.test.ts +++ b/test/ssgHashes.test.ts @@ -114,7 +114,7 @@ describe('[nuxt-security] SSG support of CSP', async () => { expect(body).toBeDefined() expect(metaTag).toBeDefined() expect(csp).toBeDefined() - expect(elementsWithIntegrity).toBe(expectedIntegrityAttributes + 1) // + 1 External style + expect(elementsWithIntegrity).toBe(expectedIntegrityAttributes + 1) // + 1 External style expect(inlineScriptHashes).toBe(expectedInlineScriptHashes) expect(externalScriptHashes).toBe(expectedExternalScriptHashes + 1) // + 1 vue modulepreload expect(inlineStyleHashes).toBe(expectedInlineStyleHashes) @@ -170,9 +170,9 @@ describe('[nuxt-security] SSG support of CSP', async () => { expect(metaTag).toBeNull() expect(csp).toBeUndefined() expect(elementsWithIntegrity).toBe(expectedIntegrityAttributes - 1) // No vue on no-meta-tag page - expect(inlineScriptHashes).toBe(expectedInlineScriptHashes) - expect(externalScriptHashes).toBe(expectedExternalScriptHashes) - expect(inlineStyleHashes).toBe(expectedInlineStyleHashes) - expect(externalStyleHashes).toBe(expectedExternalStyleHashes) + expect(inlineScriptHashes).toBe(0) + expect(externalScriptHashes).toBe(0) + expect(inlineStyleHashes).toBe(0) + expect(externalStyleHashes).toBe(0) }) }) From a55532c03ae4bb6c4b9e7e2d23698685077fc313 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 18:50:13 +0200 Subject: [PATCH 33/42] chore: remove xssMethods --- src/runtime/utils/methods.ts | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/runtime/utils/methods.ts diff --git a/src/runtime/utils/methods.ts b/src/runtime/utils/methods.ts deleted file mode 100644 index 42b64d7b..00000000 --- a/src/runtime/utils/methods.ts +++ /dev/null @@ -1 +0,0 @@ -export const xssMethods = ['GET', 'POST'] From 7a0b5b5827254132f10c224d84c1834a59968032 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 18:51:11 +0200 Subject: [PATCH 34/42] add methods to xssValidator --- src/defaultConfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts index 339bbb91..5dbfac0e 100644 --- a/src/defaultConfig.ts +++ b/src/defaultConfig.ts @@ -55,6 +55,7 @@ export const defaultSecurityConfig = (serverlUrl: string): Partial Date: Sun, 18 Feb 2024 18:52:43 +0200 Subject: [PATCH 35/42] chore: revert out-of-scope change --- src/runtime/server/middleware/xssValidator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/server/middleware/xssValidator.ts b/src/runtime/server/middleware/xssValidator.ts index 3df9cb1b..4866fc73 100644 --- a/src/runtime/server/middleware/xssValidator.ts +++ b/src/runtime/server/middleware/xssValidator.ts @@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => { ) { return } const stringifiedValue = JSON.stringify(valueToFilter) const processedValue = xssValidator.process( - stringifiedValue + JSON.stringify(valueToFilter) ) if (processedValue !== stringifiedValue) { const badRequestError = { From d82c659cfd5a77981c7f412d8b95af7caea406c6 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 18:58:17 +0200 Subject: [PATCH 36/42] docs: add `methods` documentation --- .../1.documentation/3.middleware/3.xss-validator.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/content/1.documentation/3.middleware/3.xss-validator.md b/docs/content/1.documentation/3.middleware/3.xss-validator.md index 61b45f3f..3f8aa390 100644 --- a/docs/content/1.documentation/3.middleware/3.xss-validator.md +++ b/docs/content/1.documentation/3.middleware/3.xss-validator.md @@ -6,7 +6,7 @@ :ellipsis{right=0px width=75% blur=150px} -This middleware works for both `GET`, `POST` methods and will throw an `400 Bad Request` error when the either body or query params will contain unsecure code. Based on +This middleware works by default for both `GET`, `POST` methods and will throw an `400 Bad Request` error when the either body or query params contain unsecure code. Based on ::alert{type="info"} ℹ Read more about performing output escaping [here](https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#perform-output-escaping). @@ -47,6 +47,7 @@ XSS validator accepts following configuration options: ```ts type XssValidator = { + methods: Array>; whiteList: Record; stripIgnoreTag: boolean; stripIgnoreTagBody: boolean; @@ -55,6 +56,11 @@ type XssValidator = { } | {}; ``` +### `methods` +- Default: `['GET', 'POST']` + +List of methods for which the validator will be invoked. You can extend it by adding additional methods (must be uppercase). + ### `whiteList` - Default: `-` From 5eb1533db7e5ddd61b4d5a9f5cd78e0da8334a5a Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 19:00:12 +0200 Subject: [PATCH 37/42] types: add jsdoc --- src/types/middlewares.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index 59c22ba1..2a0374fc 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -16,6 +16,9 @@ export type RateLimiter = { }; export type XssValidator = { + /** Array of uppercase methods for which the validator will be invoked. + @default ['GET', 'POST'] + */ methods?: Array>; whiteList?: Record; stripIgnoreTag?: boolean; From 4a61fd825166cefc3da62d5bff075aaa1169e907 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 19:04:32 +0200 Subject: [PATCH 38/42] types: fix methods types --- src/types/middlewares.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/middlewares.ts b/src/types/middlewares.ts index 2a0374fc..bab3d377 100644 --- a/src/types/middlewares.ts +++ b/src/types/middlewares.ts @@ -16,10 +16,10 @@ export type RateLimiter = { }; export type XssValidator = { - /** Array of uppercase methods for which the validator will be invoked. + /** Array of methods for which the validator will be invoked. @default ['GET', 'POST'] */ - methods?: Array>; + methods?: Array; whiteList?: Record; stripIgnoreTag?: boolean; stripIgnoreTagBody?: boolean; @@ -36,7 +36,7 @@ export type BasicAuth = { message: string; } -export type HTTPMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'POST' | string; +export type HTTPMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'PUT' | 'TRACE' | 'OPTIONS' | 'CONNECT' | 'HEAD'; // Cannot use the H3CorsOptions from `h3` as it breaks the build process for some reason :( export type CorsOptions = { From 639ebc48d1a2e1b5fe942f96d221d3bca032720a Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 19:08:50 +0200 Subject: [PATCH 39/42] fix: `Set` the methods to avoid duplication --- src/module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/module.ts b/src/module.ts index d23f251b..c5692a59 100644 --- a/src/module.ts +++ b/src/module.ts @@ -110,6 +110,8 @@ export default defineNuxtModule({ } if (nuxt.options.security.xssValidator) { + // Remove potential duplicates + nuxt.options.security.xssValidator.methods = Array.from(new Set(nuxt.options.security.xssValidator.methods)) addServerHandler({ handler: normalize( resolve(runtimeDir, 'server/middleware/xssValidator') From f5360d2786d68888b4cc76172d4ca950dcf4fe25 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 18 Feb 2024 19:11:33 +0200 Subject: [PATCH 40/42] docs: fix wording a little --- .../content/1.documentation/3.middleware/3.xss-validator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/1.documentation/3.middleware/3.xss-validator.md b/docs/content/1.documentation/3.middleware/3.xss-validator.md index 3f8aa390..8e9800ca 100644 --- a/docs/content/1.documentation/3.middleware/3.xss-validator.md +++ b/docs/content/1.documentation/3.middleware/3.xss-validator.md @@ -6,7 +6,7 @@ :ellipsis{right=0px width=75% blur=150px} -This middleware works by default for both `GET`, `POST` methods and will throw an `400 Bad Request` error when the either body or query params contain unsecure code. Based on +This middleware works by default for both `GET` and `POST` methods, and will throw a `400 Bad Request` error when either the body or the query params contain unsafe code. Based on ::alert{type="info"} ℹ Read more about performing output escaping [here](https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#perform-output-escaping). @@ -43,7 +43,7 @@ You can also disable the middleware globally or per route by setting `xssValidat ## Options -XSS validator accepts following configuration options: +XSS validator accepts the following configuration options: ```ts type XssValidator = { @@ -59,7 +59,7 @@ type XssValidator = { ### `methods` - Default: `['GET', 'POST']` -List of methods for which the validator will be invoked. You can extend it by adding additional methods (must be uppercase). +List of methods for which the validator will be invoked. ### `whiteList` From e5f328a71b3baf3862a93b9d6abbf1ac88817b8d Mon Sep 17 00:00:00 2001 From: Baroshem Date: Thu, 22 Feb 2024 07:56:58 +0100 Subject: [PATCH 41/42] feat: escape html docs and types --- .../content/1.documentation/3.middleware/3.xss-validator.md | 6 ++++++ playground/nuxt.config.ts | 2 +- src/runtime/server/middleware/xssValidator.ts | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/content/1.documentation/3.middleware/3.xss-validator.md b/docs/content/1.documentation/3.middleware/3.xss-validator.md index 933f22ba..52b8ea24 100644 --- a/docs/content/1.documentation/3.middleware/3.xss-validator.md +++ b/docs/content/1.documentation/3.middleware/3.xss-validator.md @@ -80,6 +80,12 @@ Filter out tags not in the whitelist Filter out tags and tag bodies not in the whitelist +### `escapeHtml` + +- Default: `-` + +Disable html escaping (by default `<` is replaced by `<` and `>` by `>`) + ### `css` - Default: `-` diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 7e277481..a9b29864 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -26,4 +26,4 @@ export default defineNuxtConfig({ }, runtimeHooks: true } -}) \ No newline at end of file +}) diff --git a/src/runtime/server/middleware/xssValidator.ts b/src/runtime/server/middleware/xssValidator.ts index 976d90aa..2833ffd4 100644 --- a/src/runtime/server/middleware/xssValidator.ts +++ b/src/runtime/server/middleware/xssValidator.ts @@ -1,5 +1,6 @@ import { FilterXSS, IFilterXSSOptions } from 'xss' import { defineEventHandler, createError, getQuery, readBody, getRouteRules } from '#imports' +import { HTTPMethod } from '~/src/module' export default defineEventHandler(async (event) => { const { security } = getRouteRules(event) @@ -12,7 +13,7 @@ export default defineEventHandler(async (event) => { const xssValidator = new FilterXSS(filterOpt) if (event.node.req.socket.readyState !== 'readOnly') { - if (security.xssValidator.methods.includes(event.node.req.method!)) { + if (security.xssValidator.methods && security.xssValidator.methods.includes(event.node.req.method! as HTTPMethod)) { const valueToFilter = event.node.req.method === 'GET' ? getQuery(event) From ca69e00c6566423b50e59a0a57b8ff1417c23a14 Mon Sep 17 00:00:00 2001 From: Baroshem Date: Thu, 22 Feb 2024 08:14:49 +0100 Subject: [PATCH 42/42] chore: bump 1.2.0 --- .stackblitz/package.json | 2 +- .stackblitz/yarn.lock | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.stackblitz/package.json b/.stackblitz/package.json index 17f812c9..56ee1253 100644 --- a/.stackblitz/package.json +++ b/.stackblitz/package.json @@ -11,6 +11,6 @@ "nuxt": "3.9.3" }, "dependencies": { - "nuxt-security": "^1.1.2" + "nuxt-security": "^1.2.0" } } diff --git a/.stackblitz/yarn.lock b/.stackblitz/yarn.lock index a14d1088..d90c5104 100644 --- a/.stackblitz/yarn.lock +++ b/.stackblitz/yarn.lock @@ -4101,10 +4101,10 @@ nuxt-csurf@^1.3.1: defu "^6.1.1" uncsrf "^1.1.1" -nuxt-security@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/nuxt-security/-/nuxt-security-1.1.2.tgz#08079f5cf55dc3f479be664c93195cd9c3b68c9f" - integrity sha512-Cdn8Cg5gJy+QO/QjlJgb1Gv03mtSZ6NP8Fb2LEjtsRqmWci6DDYI0D5TJYoj9SpVCYOiw9YVsawGX8HPK/YRMg== +nuxt-security@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/nuxt-security/-/nuxt-security-1.2.0.tgz#7b98bca275f45cb652466849413eee5bdc74657c" + integrity sha512-pAXW1vqMeMC6PnwzgE/Gf75BMOSQpSLbrgnZh9qmNf1Ytj7YI+Z180KZbkSXu944cFt6TGO0BDwlzc5Ij8uxPw== dependencies: "@nuxt/kit" "^3.8.0" basic-auth "^2.0.1" diff --git a/package.json b/package.json index cf5a67e2..1efb9e79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuxt-security", - "version": "1.1.2", + "version": "1.2.0", "license": "MIT", "type": "module", "homepage": "https://nuxt-security.vercel.app",