From 9db8674ae866aed610caf22ee17c524cb3e415fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Raffray?= Date: Thu, 25 Apr 2024 15:03:43 +0200 Subject: [PATCH] use virtual file system for bundle hashes --- playground/nuxt.config.ts | 2 ++ src/module.ts | 11 ++++++++-- .../nitro/plugins/50-subresourceIntegrity.ts | 21 ++++--------------- src/runtime/utils/hashes.ts | 15 ++----------- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 69010738..eeb614f7 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -2,6 +2,7 @@ import { defineNuxtConfig } from 'nuxt/config' export default defineNuxtConfig({ modules: ['../src/module'], + devtools: { enabled: true }, // Per route configuration routeRules: { @@ -46,6 +47,7 @@ export default defineNuxtConfig({ // Global configuration security: { headers: { + crossOriginEmbedderPolicy: false, xXSSProtection: '0' }, rateLimiter: { diff --git a/src/module.ts b/src/module.ts index e139863a..a707660d 100644 --- a/src/module.ts +++ b/src/module.ts @@ -195,8 +195,15 @@ export default defineNuxtModule({ // Import composables addImportsDir(resolver.resolve('./runtime/composables')) - // Calculates SRI hashes at build time - nuxt.hook('nitro:build:before', hashBundledAssets) + // Record SRI Hashes in the Virtual File System at build time + let sriHashes: Record = {} + nuxt.options.nitro.virtual = defu( + { '#sri-hashes': () => `export default ${JSON.stringify(sriHashes)}` }, + nuxt.options.nitro.virtual + ) + nuxt.hook('nitro:build:before', async(nitro) => { + sriHashes = await hashBundledAssets(nitro) + }) } }) diff --git a/src/runtime/nitro/plugins/50-subresourceIntegrity.ts b/src/runtime/nitro/plugins/50-subresourceIntegrity.ts index d33dbb92..7180c3df 100644 --- a/src/runtime/nitro/plugins/50-subresourceIntegrity.ts +++ b/src/runtime/nitro/plugins/50-subresourceIntegrity.ts @@ -1,29 +1,16 @@ -import { useStorage, defineNitroPlugin } from '#imports' +import { defineNitroPlugin } from '#imports' import { resolveSecurityRules } from '../utils' - +//@ts-expect-error : we are importing from the virtual file system +import sriHashes from '#sri-hashes' export default defineNitroPlugin((nitroApp) => { - nitroApp.hooks.hook('render:html', async(html, { event }) => { + nitroApp.hooks.hook('render:html', (html, { event }) => { // Exit if SRI not enabled for this route const rules = resolveSecurityRules(event) if (!rules.enabled || !rules.sri) { return } - // Retrieve the sriHases that we computed at build time - // - // - If we are in a pre-rendering step of nuxi generate - // Then the /integrity directory does not exist in server assets - // But it is still in the .nuxt build directory - // - // - Conversely, if we are in a standalone SSR server pre-built by nuxi build - // Then we don't have a .nuxt build directory anymore - // But we did save the /integrity directory into the server assets - const prerendering = !!import.meta.prerender - const storageBase = prerendering ? 'build' : 'assets' - const sriHashes = await useStorage(storageBase).getItem>('integrity:sriHashes.json') || {} - - // Scan all relevant sections of the NuxtRenderHtmlContext // Note: integrity can only be set on scripts and on links with rel preload, modulepreload and stylesheet // However the SRI standard provides that other elements may be added to that list in the future diff --git a/src/runtime/utils/hashes.ts b/src/runtime/utils/hashes.ts index e5f10e1e..c7bc8311 100644 --- a/src/runtime/utils/hashes.ts +++ b/src/runtime/utils/hashes.ts @@ -1,6 +1,6 @@ import { createHash } from 'node:crypto' import { existsSync } from 'node:fs' -import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises' +import { readdir, readFile } from 'node:fs/promises' import type { Nitro } from 'nitropack' import { join } from 'pathe' @@ -47,18 +47,7 @@ export async function hashBundledAssets(nitro: Nitro) { } } } - - // Save hashes in a /integrity directory within the .nuxt build for later use with SSG - const buildDir = nitro.options.buildDir - const integrityDir = join(buildDir, 'integrity') - if (!existsSync(integrityDir)) { - await mkdir(integrityDir) - } - const hashFilePath = join(integrityDir, 'sriHashes.json') - await writeFile(hashFilePath, JSON.stringify(sriHashes)) - - // Mount the /integrity directory into server assets for later use with SSR - nitro.options.serverAssets.push({ dir: integrityDir, baseName: 'integrity' }) + return sriHashes } export function generateHash (content: Buffer | string, hashAlgorithm: string) {