Skip to content

Commit

Permalink
Merge pull request #435 from Baroshem/feat/virtual-sri
Browse files Browse the repository at this point in the history
feat(core): use virtual file system for SRI
  • Loading branch information
Baroshem committed Apr 29, 2024
2 parents 2128967 + 9db8674 commit 57ea940
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 32 deletions.
2 changes: 2 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineNuxtConfig } from 'nuxt/config'

export default defineNuxtConfig({
modules: ['../src/module'],
devtools: { enabled: true },

// Per route configuration
routeRules: {
Expand Down Expand Up @@ -46,6 +47,7 @@ export default defineNuxtConfig({
// Global configuration
security: {
headers: {
crossOriginEmbedderPolicy: false,
xXSSProtection: '0'
},
rateLimiter: {
Expand Down
11 changes: 9 additions & 2 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,15 @@ export default defineNuxtModule<ModuleOptions>({
// 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<string, string> = {}
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)
})
}
})

Expand Down
21 changes: 4 additions & 17 deletions src/runtime/nitro/plugins/50-subresourceIntegrity.ts
Original file line number Diff line number Diff line change
@@ -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<Record<string, string>>('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
Expand Down
15 changes: 2 additions & 13 deletions src/runtime/utils/hashes.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 57ea940

Please sign in to comment.