Skip to content

Commit

Permalink
Merge branch 'chore/1.0.0-rc.3' into fix/nonce-ssg
Browse files Browse the repository at this point in the history
  • Loading branch information
Baroshem authored Oct 25, 2023
2 parents f5dce18 + 909221b commit 9406b97
Show file tree
Hide file tree
Showing 10 changed files with 2,223 additions and 2,264 deletions.
4 changes: 2 additions & 2 deletions .stackblitz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"postinstall": "nuxt prepare"
},
"devDependencies": {
"nuxt": "3.2.3"
"nuxt": "3.7.3"
},
"dependencies": {
"nuxt-security": "^0.14.4"
"nuxt-security": "^1.0.0-rc.2"
}
}
4,399 changes: 2,182 additions & 2,217 deletions .stackblitz/yarn.lock

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[![nuxt-security](https://nuxt-security.vercel.app/preview.png)](https://nuxt-security.vercel.app)


[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![Github Actions CI][github-actions-ci-src]][github-actions-ci-href]
Expand All @@ -14,8 +13,7 @@ Automatically configure your app to follow OWASP security patterns and principle
> This module works with Nuxt 3 only
- [📖  Read the documentation](https://nuxt-security.vercel.app)
- [👾  Playground](https://stackblitz.com/github/baroshem/nuxt-security?file=.stackblitz%2Fnuxt.config.ts)
- [ Intro video](https://www.youtube.com/watch?v=8ac30Py8Ses)
- [👾  Playground](https://nuxt-security.vercel.app/playground)

## Features

Expand Down
14 changes: 4 additions & 10 deletions docs/content/1.documentation/2.headers/1.csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export default defineNuxtConfig({
})
```

## `Nonce support`
## Nonce

To further increase CSP security, you can use a [nonce-based strict csp](https://web.dev/strict-csp/#what-is-a-strict-content-security-policy).
This can be configured as follows:
Expand Down Expand Up @@ -203,13 +203,9 @@ export default defineNuxtConfig({
})
```

## Using `nonce` in your application code
There are two ways to use `nonce` in your application. Check out both of them and decide which one suits your needs best:

There are two ways to use `nonce` in your application. Check out both of them and decide which one suits your needs best.

### With the `useHead` composable

If you are dynamically adding script or link tags in your application using the `useHead` composable, all nonce values will be automatically added.
1. **`useHead` composable** - If you are dynamically adding script or link tags in your application using the `useHead` composable, all nonce values will be automatically added.
However, take note that due to [a current bug in unjs/unhead](https://github.com/unjs/unhead/issues/136), you'll need to add a workaround **when using ssr** to prevent double loading and executing of your scripts when using nonce.

```ts
Expand All @@ -219,9 +215,7 @@ However, take note that due to [a current bug in unjs/unhead](https://github.com
useHead({ script: [{ src: 'https://example.com/script.js' }] }, { mode: 'server' })
```

### Directly inserting tags into DOM

If you are unable or unwilling to use `useHead` and are inserting directly into the DOM (e.g. `document.createElement`), you can get the current valid nonce value using the `useNonce` composable:
2. **Directly inserting tags into DOM** - If you are unable or unwilling to use `useHead` and are inserting directly into the DOM (e.g. `document.createElement`), you can get the current valid nonce value using the `useNonce` composable:

```ts
const nonce = useNonce()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Cross-Origin-Embedder-Policy: require-corp
The `crossOriginEmbedderPolicy` header can be configured with following values.

```ts
crossOriginEmbedderPolicy: 'unsafe-none' | 'require-corp' | false;
crossOriginEmbedderPolicy: 'unsafe-none' | 'require-corp' | 'credentialless' | false;
```

### `unsafe-none`
Expand All @@ -64,6 +64,10 @@ This is the default value. Allows the document to fetch cross-origin resources w

A document can only load resources from the same origin, or resources explicitly marked as loadable from another origin. If a cross origin resource supports CORS, the crossorigin attribute or the Cross-Origin-Resource-Policy header must be used to load it without being blocked by COEP.

### `credentialless`

no-cors cross-origin requests are sent without credentials. In particular, it means Cookies are omitted from the request, and ignored from the response. The responses are allowed **without** an explicit permission via the Cross-Origin-Resource-Policy header. Navigate responses behave similarly as the require-corp mode: They require Cross-Origin-Resource-Policy response header.

::alert{type="warning"}
⚠️ Read more about `Avoiding blockage with CORS` [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#avoiding_coep_blockage_with_cors).
::
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nuxt-security",
"version": "1.0.0-rc.1",
"version": "1.0.0-rc.2",
"license": "MIT",
"type": "module",
"homepage": "https://nuxt-security.vercel.app",
Expand Down
6 changes: 3 additions & 3 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default defineNuxtConfig({
xXSSProtection: '0'
},
rateLimiter: {
tokensPerInterval: 10
},
nonce: true
tokensPerInterval: 10,
interval: 10000
}
}
})
33 changes: 19 additions & 14 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { resolve, normalize } from 'pathe'
import { defineNuxtModule, addServerHandler, installModule, addVitePlugin } from '@nuxt/kit'
import { defu } from 'defu'
import { Nuxt, RuntimeConfig } from '@nuxt/schema'
import { builtinDrivers } from 'unstorage'
import viteRemove from 'unplugin-remove/vite'
import { defuReplaceArray } from './utils'
import {
ModuleOptions,
Expand All @@ -13,7 +13,7 @@ import {
SecurityHeaders
} from './types/headers'
import {
BasicAuth, RateLimiter
BasicAuth
} from './types/middlewares'
import {
defaultSecurityConfig
Expand Down Expand Up @@ -55,8 +55,6 @@ export default defineNuxtModule<ModuleOptions>({
if (!securityOptions.enabled) { return }

if (securityOptions.removeLoggers) {
// ViteRemove does not come with a proper TS declaration
const viteRemove = await import('unplugin-remove/vite') as unknown as (options: any) => any
addVitePlugin(viteRemove(securityOptions.removeLoggers))
}

Expand Down Expand Up @@ -98,16 +96,6 @@ export default defineNuxtModule<ModuleOptions>({
}

if (nuxt.options.security.rateLimiter) {
// setup unstorage
const driverName = (securityOptions.rateLimiter as RateLimiter).driver?.name
if (driverName) {
nuxt.options.nitro.virtual = defu(nuxt.options.nitro.virtual, {
'#storage-driver': `export { default } from '${
builtinDrivers[driverName as keyof typeof builtinDrivers]
}'`
})
}

addServerHandler({
handler: normalize(
resolve(runtimeDir, 'server/middleware/rateLimiter')
Expand Down Expand Up @@ -231,6 +219,23 @@ const registerSecurityNitroPlugins = (
nuxt.hook('nitro:config', (config) => {
config.plugins = config.plugins || []

if (securityOptions.rateLimiter) {
// setup unstorage
const driver = (securityOptions.rateLimiter).driver
if (driver) {
const { name, options } = driver
config.storage = defu(
config.storage,
{
'#storage-driver': {
driver: name,
options
}
}
)
}
}

// Register nitro plugin to replace default 'X-Powered-By' header with custom one that does not indicate what is the framework underneath the app.
if (securityOptions.hidePoweredBy) {
config.externals = config.externals || {}
Expand Down
17 changes: 5 additions & 12 deletions src/runtime/server/middleware/rateLimiter.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import { defineEventHandler, getRequestHeader, createError, H3Event, setHeader } from 'h3'
import { createStorage } from 'unstorage'
// @ts-ignore
import { getRouteRules, useRuntimeConfig } from '#imports'
// @ts-ignore
import storageDriver from '#storage-driver'
import type { H3Event } from 'h3'
import { defineEventHandler, getRequestHeader, createError, setHeader, getRouteRules, useStorage } from '#imports'

type StorageItem = {
value: number,
date: number
}

const driverConfig = useRuntimeConfig().security.rateLimiter.driver

const driver = storageDriver(driverConfig.options)
const storage = createStorage({ driver }).mount('', driver)
const storage = useStorage<StorageItem>('#storage-driver')

export default defineEventHandler(async (event) => {
const routeRules = getRouteRules(event)
Expand Down Expand Up @@ -62,7 +55,7 @@ export default defineEventHandler(async (event) => {

const newStorageItem: StorageItem = { value: storageItem.value - 1, date: newItemDate }

await storage.setItem(ip, JSON.stringify(newStorageItem))
await storage.setItem(ip, newStorageItem)
const currentItem = await storage.getItem(ip)as StorageItem

if (currentItem && rateLimiterConfig.headers) {
Expand All @@ -76,7 +69,7 @@ export default defineEventHandler(async (event) => {

async function setStorageItem (rateLimiterConfig: any, ip: string) {
const rateLimitedObject: StorageItem = { value: rateLimiterConfig?.tokensPerInterval, date: Date.now() }
await storage.setItem(ip, JSON.stringify(rateLimitedObject))
await storage.setItem(ip, rateLimitedObject)
}

// Taken and modified from https://github.com/timb-103/nuxt-rate-limit/blob/8a37846469c2f32f0e2ca6893a31baeec944d56c/src/runtime/server/utils/rate-limit.ts#L78
Expand Down
2 changes: 1 addition & 1 deletion src/types/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type CrossOriginResourcePolicyValue = 'same-site' | 'same-origin' | 'cros

export type CrossOriginOpenerPolicyValue = 'unsafe-none' | 'same-origin-allow-popups' | 'same-origin';

export type CrossOriginEmbedderPolicyValue = 'unsafe-none' | 'require-corp';
export type CrossOriginEmbedderPolicyValue = 'unsafe-none' | 'require-corp' | 'credentialless';

export type ReferrerPolicyValue =
| 'no-referrer'
Expand Down

0 comments on commit 9406b97

Please sign in to comment.