From 503f8016d507b6bf7525f33648ba6dd77d9c985c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Raffray?= Date: Thu, 14 Nov 2024 15:51:39 +0100 Subject: [PATCH 1/4] make crypto available in Node 18 --- package.json | 2 +- src/module.ts | 2 +- src/runtime/nitro/plugins/30-cspSsgHashes.ts | 2 +- src/runtime/nitro/plugins/40-cspSsrNonce.ts | 5 ++--- src/utils/{hash.ts => crypto.ts} | 12 ++++++++++++ src/utils/merge.ts | 8 ++++---- 6 files changed, 21 insertions(+), 10 deletions(-) rename src/utils/{hash.ts => crypto.ts} (52%) diff --git a/package.json b/package.json index a573918f..444632f0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MIT", "type": "module", "engines": { - "node": ">=20.0.0" + "node": ">=18.0.0" }, "homepage": "https://nuxt-security.vercel.app", "description": "🛡️ Security Module for Nuxt based on HTTP Headers and Middleware", diff --git a/src/module.ts b/src/module.ts index 74beddcb..e50d667c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -5,7 +5,7 @@ import { join, isAbsolute } from 'pathe' import { defu } from 'defu' import viteRemove from 'unplugin-remove/vite' import { getHeadersApplicableToAllResources } from './utils/headers' -import { generateHash } from './utils/hash' +import { generateHash } from './utils/crypto' import { defuReplaceArray } from './utils/merge' import { defaultSecurityConfig } from './defaultConfig' import type { Nuxt } from '@nuxt/schema' diff --git a/src/runtime/nitro/plugins/30-cspSsgHashes.ts b/src/runtime/nitro/plugins/30-cspSsgHashes.ts index 6ad31443..e71138e2 100644 --- a/src/runtime/nitro/plugins/30-cspSsgHashes.ts +++ b/src/runtime/nitro/plugins/30-cspSsgHashes.ts @@ -1,6 +1,6 @@ import { defineNitroPlugin } from '#imports' import { resolveSecurityRules } from '../context' -import { generateHash } from '../../../utils/hash' +import { generateHash } from '../../../utils/crypto' import type { Section } from '../../../types/module' diff --git a/src/runtime/nitro/plugins/40-cspSsrNonce.ts b/src/runtime/nitro/plugins/40-cspSsrNonce.ts index 822693be..aa7ee8e8 100644 --- a/src/runtime/nitro/plugins/40-cspSsrNonce.ts +++ b/src/runtime/nitro/plugins/40-cspSsrNonce.ts @@ -1,5 +1,6 @@ import { defineNitroPlugin } from '#imports' import { resolveSecurityRules } from '../context' +import { generateRandomNonce } from '../../../utils/crypto' const LINK_RE = /]*?>)/gi const SCRIPT_RE = /]*?>)/gi @@ -27,9 +28,7 @@ export default defineNitroPlugin((nitroApp) => { const rules = resolveSecurityRules(event) if (rules.enabled && rules.nonce && !import.meta.prerender) { - const array = new Uint8Array(18); - crypto.getRandomValues(array) - const nonce = btoa(String.fromCharCode(...array)) + const nonce = generateRandomNonce() event.context.security!.nonce = nonce } }) diff --git a/src/utils/hash.ts b/src/utils/crypto.ts similarity index 52% rename from src/utils/hash.ts rename to src/utils/crypto.ts index 2c86db7d..f9b12d7a 100644 --- a/src/utils/hash.ts +++ b/src/utils/crypto.ts @@ -1,3 +1,8 @@ +// These two lines are required only to maintain compatibility with Node 18 +// - In Node 19 and above, crypto is available in the global scope +// - In Workers environments, crypto is available in the global scope +import { webcrypto } from 'node:crypto' +globalThis.crypto ??= webcrypto as Crypto export async function generateHash(content: Buffer | string, hashAlgorithm: 'SHA-256' | 'SHA-384' | 'SHA-512') { let buffer: Uint8Array @@ -11,3 +16,10 @@ export async function generateHash(content: Buffer | string, hashAlgorithm: 'SHA const prefix = hashAlgorithm.replace('-', '').toLowerCase() return `${prefix}-${base64}`; } + +export function generateRandomNonce() { + const array = new Uint8Array(18); + crypto.getRandomValues(array) + const nonce = btoa(String.fromCharCode(...array)) + return nonce +} diff --git a/src/utils/merge.ts b/src/utils/merge.ts index ca99a9a7..10e1558f 100644 --- a/src/utils/merge.ts +++ b/src/utils/merge.ts @@ -1,9 +1,9 @@ -import { createDefu } from 'defu'; +import { createDefu } from 'defu' export const defuReplaceArray = createDefu((obj, key, value) => { if (Array.isArray(obj[key]) || Array.isArray(value)) { - obj[key] = value; - return true; + obj[key] = value + return true } -}); +}) From ae4efb68db7f5ece88ce3ed35e9a57441f46f132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Raffray?= Date: Thu, 14 Nov 2024 15:56:29 +0100 Subject: [PATCH 2/4] adapt CI to check Node 18 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b7f9dc2..c1c33416 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [20] + node: [18] steps: - uses: actions/setup-node@v3 From 44c3f70341a4a46fc923f4809ffe73a08e7f2821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Raffray?= Date: Thu, 14 Nov 2024 15:58:15 +0100 Subject: [PATCH 3/4] modify unbuild entries for new util name 'crypto' --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 444632f0..44686e9c 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ }, "unbuild": { "entries": [ - "./src/utils/hash.ts", + "./src/utils/crypto.ts", "./src/utils/headers.ts", "./src/utils/merge.ts", "./src/defaultConfig.ts" From 577b2dd68f601cd1462e98ccd1f6d924b16cf2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Raffray?= Date: Thu, 14 Nov 2024 16:29:45 +0100 Subject: [PATCH 4/4] 2.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44686e9c..dac85376 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuxt-security", - "version": "2.1.0", + "version": "2.1.1", "license": "MIT", "type": "module", "engines": {