Skip to content

Commit f3706ab

Browse files
authored
Merge pull request #547 from Baroshem/vejja/issue541
feat(core): crypto compatibility for Workers
2 parents 4e2a603 + 88a46a4 commit f3706ab

File tree

7 files changed

+33
-21
lines changed

7 files changed

+33
-21
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
matrix:
1818
os: [ubuntu-latest]
19-
node: [18]
19+
node: [20]
2020

2121
steps:
2222
- uses: actions/setup-node@v3

docs/content/1.documentation/2.headers/4.crossOriginOpenerPolicy.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ Cross-Origin-Opener-Policy: same-origin
5555
The `crossOriginOpenerPolicy` header can be configured with following values.
5656

5757
```ts
58-
crossOriginOpenerPolicy: 'unsafe-none' | 'same-origin-allow-popups' | 'same-origin' | false
58+
crossOriginOpenerPolicy: 'same-origin' | 'same-origin-allow-popups' | 'unsafe-none' | false
5959
```
6060

61-
### `unsafe-none`
61+
### `same-origin`
6262

63-
This is the default value. Allows the document to be added to its opener's browsing context group unless the opener itself has a COOP of same-origin or same-origin-allow-popups.
63+
This is the default value. Isolates the browsing context exclusively to same-origin documents. Cross-origin documents are not loaded in the same browsing context.
6464

6565
### `same-origin-allow-popups`
6666

6767
Retains references to newly opened windows or tabs that either don't set COOP or that opt out of isolation by setting a COOP of unsafe-none.
6868

69-
### `same-origin`
69+
### `unsafe-none`
7070

71-
Isolates the browsing context exclusively to same-origin documents. Cross-origin documents are not loaded in the same browsing context.
71+
Allows the document to be added to its opener's browsing context group unless the opener itself has a COOP of same-origin or same-origin-allow-popups.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"version": "2.0.0",
44
"license": "MIT",
55
"type": "module",
6+
"engines": {
7+
"node": ">=20.0.0"
8+
},
69
"homepage": "https://nuxt-security.vercel.app",
710
"description": "🛡️ Security Module for Nuxt based on HTTP Headers and Middleware",
811
"repository": {

src/module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ function reorderNitroPlugins(nuxt: Nuxt) {
302302

303303

304304
async function hashBundledAssets(nitro: Nitro) {
305-
const hashAlgorithm = 'sha384'
305+
const hashAlgorithm = 'SHA-384'
306306
const sriHashes: Record<string, string> = {}
307307

308308
// Will be later necessary to construct url
@@ -326,7 +326,7 @@ async function hashBundledAssets(nitro: Nitro) {
326326
// const fullPath = join(entry.path, entry.name)
327327
const path = join(dir, entry.name)
328328
const content = await readFile(path)
329-
const hash = generateHash(content, hashAlgorithm)
329+
const hash = await generateHash(content, hashAlgorithm)
330330
// construct the url as it will appear in the head template
331331
const fullPath = join(baseURL, entry.name)
332332
let url: string

src/runtime/nitro/plugins/30-cspSsgHashes.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default defineNitroPlugin((nitroApp) => {
2121
return
2222
}
2323

24-
nitroApp.hooks.hook('render:html', (html, { event }) => {
24+
nitroApp.hooks.hook('render:html', async(html, { event }) => {
2525
// Exit if no CSP defined
2626
const rules = resolveSecurityRules(event)
2727
if (!rules.enabled || !rules.headers || !rules.headers.contentSecurityPolicy) {
@@ -34,7 +34,7 @@ export default defineNitroPlugin((nitroApp) => {
3434
}
3535
const scriptHashes = event.context.security!.hashes.script
3636
const styleHashes = event.context.security!.hashes.style
37-
const hashAlgorithm = 'sha256'
37+
const hashAlgorithm = 'SHA-256'
3838

3939
// Parse HTML if SSG is enabled for this route
4040
if (rules.ssg) {
@@ -43,12 +43,13 @@ export default defineNitroPlugin((nitroApp) => {
4343
// Scan all relevant sections of the NuxtRenderHtmlContext
4444
const sections = ['body', 'bodyAppend', 'bodyPrepend', 'head'] as Section[]
4545
for (const section of sections) {
46-
html[section].forEach(element => {
46+
for (const element of html[section]) {
4747
if (hashScripts) {
4848
// Parse all script tags
4949
const inlineScriptMatches = element.matchAll(INLINE_SCRIPT_RE)
5050
for (const [, scriptText] of inlineScriptMatches) {
51-
scriptHashes.add(`'${generateHash(scriptText, hashAlgorithm)}'`)
51+
const hash = await generateHash(scriptText, hashAlgorithm)
52+
scriptHashes.add(`'${hash}'`)
5253
}
5354
const externalScriptMatches = element.matchAll(SCRIPT_RE)
5455
for (const [, integrity] of externalScriptMatches) {
@@ -60,7 +61,8 @@ export default defineNitroPlugin((nitroApp) => {
6061
if (hashStyles) {
6162
const styleMatches = element.matchAll(STYLE_RE)
6263
for (const [, styleText] of styleMatches) {
63-
styleHashes.add(`'${generateHash(styleText, hashAlgorithm)}'`)
64+
const hash = await generateHash(styleText, hashAlgorithm)
65+
styleHashes.add(`'${hash}'`)
6466
}
6567
}
6668

@@ -94,7 +96,7 @@ export default defineNitroPlugin((nitroApp) => {
9496
}
9597
}
9698
}
97-
})
99+
}
98100
}
99101
}
100102
})

src/runtime/nitro/plugins/40-cspSsrNonce.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { defineNitroPlugin } from '#imports'
2-
import { randomBytes } from 'node:crypto'
32
import { resolveSecurityRules } from '../context'
43

54
const LINK_RE = /<link([^>]*?>)/gi
@@ -28,7 +27,9 @@ export default defineNitroPlugin((nitroApp) => {
2827

2928
const rules = resolveSecurityRules(event)
3029
if (rules.enabled && rules.nonce && !import.meta.prerender) {
31-
const nonce = randomBytes(16).toString('base64')
30+
const array = new Uint8Array(18);
31+
crypto.getRandomValues(array)
32+
const nonce = btoa(String.fromCharCode(...array))
3233
event.context.security!.nonce = nonce
3334
}
3435
})

src/utils/hash.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
import { createHash } from 'node:crypto';
21

3-
export function generateHash(content: Buffer | string, hashAlgorithm: string) {
4-
const hash = createHash(hashAlgorithm);
5-
hash.update(content);
6-
return `${hashAlgorithm}-${hash.digest('base64')}`;
2+
export async function generateHash(content: Buffer | string, hashAlgorithm: 'SHA-256' | 'SHA-384' | 'SHA-512') {
3+
let buffer: Uint8Array
4+
if (typeof content === 'string') {
5+
buffer = new TextEncoder().encode(content);
6+
} else {
7+
buffer = new Uint8Array(content);
8+
}
9+
const hashBuffer = await crypto.subtle.digest(hashAlgorithm, buffer);
10+
const base64 = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
11+
const prefix = hashAlgorithm.replace('-', '').toLowerCase()
12+
return `${prefix}-${base64}`;
713
}

0 commit comments

Comments
 (0)