Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/2.0.0 rc.1 #448

Merged
merged 98 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
f75d01f
perf: avoid cheerio in favor of regex
GalacticHypernova Mar 23, 2024
68a0de5
perf: avoid unnecessary loop
GalacticHypernova Mar 23, 2024
ca027ce
wip: add external script and link regexes
GalacticHypernova Mar 26, 2024
1aa070b
fix: more specific regex patterns
GalacticHypernova Mar 26, 2024
b7b3a65
wip: rework SRI to regex
GalacticHypernova Mar 26, 2024
0b09dd6
wip: 04-cspSsgHashes.ts
GalacticHypernova Mar 26, 2024
5524431
wip: provide a weakmap for both links and scripts
GalacticHypernova Mar 27, 2024
0540a9b
wip: refactor to Map due to no native support for strings
GalacticHypernova Mar 27, 2024
659b39d
perf: set cache and refactor to 1 map
GalacticHypernova Mar 27, 2024
631951a
fix: parenthesis
GalacticHypernova Mar 27, 2024
6123f65
wip: more work
GalacticHypernova Mar 27, 2024
6b8a9b6
wip: csp hashes
GalacticHypernova Mar 29, 2024
67905dd
wip: rework cspssg to regex
GalacticHypernova Mar 31, 2024
34497c9
fix: return match
GalacticHypernova Mar 31, 2024
08638ae
wip: refactor cspssr to regex
GalacticHypernova Mar 31, 2024
6b80492
fix: check for word boundary
GalacticHypernova Mar 31, 2024
06c9533
Update 04-cspSsgHashes.ts
GalacticHypernova Mar 31, 2024
70b707c
fix: return $
GalacticHypernova Mar 31, 2024
83e36c2
fix: don't manipulate the strings
GalacticHypernova Mar 31, 2024
e8bee97
chore: retrying with loop
GalacticHypernova Mar 31, 2024
49e1db4
fix: typo
GalacticHypernova Mar 31, 2024
9b5bf56
fix: regex range
GalacticHypernova Apr 2, 2024
548e74a
fix: regex range
GalacticHypernova Apr 2, 2024
a646f67
fix: provide proper integrity pattern
GalacticHypernova Apr 2, 2024
85c029c
fix: provide proper integrity pattern
GalacticHypernova Apr 2, 2024
3fcf350
fix: no escape plus
GalacticHypernova Apr 2, 2024
ceabd95
fix: no escape plus
GalacticHypernova Apr 2, 2024
fe2386f
fix: capture whole substr
GalacticHypernova Apr 2, 2024
b46bb8a
Merge branch 'main' into patch-2
GalacticHypernova Apr 4, 2024
e8d705a
Merge branch 'main' into patch-2
GalacticHypernova Apr 12, 2024
b1ef1e7
wip: refactor inclusive pattern
GalacticHypernova Apr 12, 2024
0361b4d
wip: refactor inclusive pattern
GalacticHypernova Apr 12, 2024
5bfe98e
Merge remote-tracking branch 'origin/main' into pr/404
GalacticHypernova Apr 26, 2024
b7497df
fix: use string replace
GalacticHypernova Apr 26, 2024
a24e9e2
SSG pre-rendered headers
vejja Apr 25, 2024
1d8db8a
add hook feature
vejja Apr 29, 2024
c77c3c0
doc update
vejja Apr 29, 2024
75f5dcd
minor fixes
vejja Apr 29, 2024
49bf562
Merge branch 'feat/virtual-sri' into feat/ssg-headers
vejja Apr 29, 2024
bd27138
Merge branch 'main' into feat/ssg-headers
vejja Apr 29, 2024
47d38b3
add security headers on non-html resources
vejja May 3, 2024
ec1428c
put allResources mode under config option
vejja May 5, 2024
284a1e4
fix tests and remove options guard
vejja May 5, 2024
8fbc9e7
doc update
vejja May 5, 2024
a9f0574
minor corrections to docs
vejja May 5, 2024
b4504aa
modifications as per code review
vejja May 6, 2024
1712786
2.0.0-beta.0
vejja May 6, 2024
34c2fc0
move unbuild config to package.json
vejja May 7, 2024
fdfbc9f
auto-import defuReplaceArray
vejja May 8, 2024
47ed844
Merge pull request #441 from Baroshem/feat/ssg-headers
Baroshem May 10, 2024
5ab25ae
Merge branch 'main' into patch-2
GalacticHypernova May 10, 2024
90a46d3
insert csp meta after charset meta
vejja May 10, 2024
a41b3fe
Merge branch 'chore/2.0.0-rc.1' into GalacticHypernova-patch-2
vejja May 10, 2024
80f4516
update recombine
vejja May 10, 2024
ae43392
fix types
vejja May 10, 2024
dc276a3
remove cheerio and unused cache
vejja May 10, 2024
c8d8dbc
make 'as' capturing group optional in LINK_RE
vejja May 10, 2024
7e1ee61
remove unused pre-processing step
vejja May 10, 2024
b11755d
use readonly loop for scanning hashes
vejja May 10, 2024
fd760b1
no Section type redefinition, skip adding CSP meta if no pre-render
vejja May 10, 2024
fbc39a7
add security to docs
vejja May 11, 2024
d99b6f3
fix package latest specifier
vejja May 11, 2024
86911bf
deactivate exportToPresets
vejja May 12, 2024
32e45ff
temp fix: delete array entry for nitro-prerender
vejja May 12, 2024
a2c7366
2.0.0-beta.1
vejja May 12, 2024
24c1ca3
fix array values in prerendered headers
vejja May 12, 2024
59f2347
2.0.0-beta.2
vejja May 12, 2024
98c9b1c
2.0.0-beta.3
vejja May 12, 2024
2e2a2a1
log faulty headers to build output
vejja May 12, 2024
13bbc38
2.0.0-beta.4
vejja May 12, 2024
5ee94cb
update nuxt-security version for docs
vejja May 12, 2024
386ab77
remove headers logs from build output
vejja May 12, 2024
b5fddb7
2.0.0-beta.5
vejja May 12, 2024
99d6dee
Merge pull request #451 from Baroshem/feat/score-banner
vejja May 17, 2024
60ddf61
Merge pull request #404 from GalacticHypernova/patch-2
vejja May 17, 2024
6c8d01f
Merge pull request #449 from Baroshem/fix/csp-meta-charset-v2
vejja May 17, 2024
738ce9b
adapt filtering regexp to conform to CodeQL recommendation
vejja May 21, 2024
944acbc
update default headers values
vejja May 11, 2024
6831dda
spread options for storage drivers
vejja May 13, 2024
71ba82a
fix rateLimiter driver types
vejja May 21, 2024
6cee48f
update doc for --host
vejja May 21, 2024
3584775
Merge pull request #456 from Baroshem/447-not-working-on-dev-on-netwo…
Baroshem May 22, 2024
5466dda
Merge pull request #450 from Baroshem/feat/owasp-defaults
Baroshem May 22, 2024
d40ced0
Merge pull request #452 from Baroshem/fix/storage-driver-options
Baroshem May 22, 2024
71fa772
fix: remove navigate-to csp directive
GalacticHypernova May 25, 2024
b1d3853
fix: remove navigate-to csp directive from docs
GalacticHypernova May 25, 2024
f117c33
fix(types): allow request size limiters to be optional
GalacticHypernova May 25, 2024
ff49301
fix(types): allow rate limiter props to be optional
GalacticHypernova May 25, 2024
ada9409
fix(types): force route specific config to use all props as required
GalacticHypernova May 27, 2024
7b3f70f
fix: |
GalacticHypernova May 27, 2024
caa5a1c
fix: add missing closing bracket
GalacticHypernova May 27, 2024
f537b32
fix: use the correct branch
GalacticHypernova May 27, 2024
0963bfd
fix: use the correct branch
GalacticHypernova May 27, 2024
49339c0
Merge branch 'chore/2.0.0-rc.1' into patch-4
GalacticHypernova May 27, 2024
bc00b3a
fix(types): allow throwError to stay optional
GalacticHypernova May 27, 2024
1346bc3
Merge pull request #457 from GalacticHypernova/patch-3
vejja May 28, 2024
6f6ecc1
Merge pull request #458 from GalacticHypernova/patch-4
vejja May 28, 2024
1bdb71d
fix required types not respected
vejja May 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ security: {
ssg: {
meta: true,
hashScripts: true,
hashStyles: false
hashStyles: false,
nitroHeaders: true,
exportToPresets: true,
},
sri: true
}
Expand Down
96 changes: 83 additions & 13 deletions docs/content/1.documentation/1.getting-started/3.usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export default defineNitroPlugin((nitroApp) => {
})
```

::alert{type="warning"}
Runtime-hook configuration only applies to headers delivered on HTML pages.
<br>
Headers delivered on other resources (e.g. images, js and css files, api routes etc.) are not modifiable via runtime hooks.
::

## Configuration priority order

Nuxt-Security applies your rules in the following prority order:
Expand Down Expand Up @@ -256,31 +262,95 @@ export default defineNuxtConfig({
```


## Overwriting or modifying existing values
## Modifying security options

Within your runtime hooks, you can either overwrite or modify the existing values for any security option.
One of the easiest way to merge existing rules with your own is to use `defu`:
Within your runtime hooks, you can either modify or overwrite the existing values for any security option.

### Merging with replacement

One of the easiest way to merge existing rules with your own is to use `defuReplaceArray`:

```ts{}[server/plugins/filename.ts]
import defu from 'defu'
// You don't need to import defuReplaceArray as it is auto-imported by Nuxt Security

export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(routeRules) => {
// You can fetch configuration data asynchronously from an external source
const validDomain = await $fetch('https://some-site.com/rules')
// You can then override the security options of any route
routeRules['/some/route'] = defuReplaceArray(
{
headers: {
contentSecurityPolicy: {
"script-src": ["'self'", "..."]
// The script-src directive will be replaced with "'self' ..."
}
}
},
routeRules['/some/route'] // The other existing rules for /some/route will be preserved
)
})
})
```

In the example above,
- All existing security options for `/some/route` will be maintained, and only the `script-src` CSP directive will be modified.
- The existing content of the `script-src` directive will be erased and replaced by your values

Read more about [`defuReplaceArray`](/documentation/advanced/auto-imports/#defuReplaceArray)

::alert{type="info"}
`defuReplaceArray` is auto-imported by Nuxt Security. You can use this utility anywhere in your /server folder.
::

### Merging with addition

If you want to add additional values to the existing settings, you can use the standard `defu` utility to merge your rules.

```ts{}[server/plugins/filename.ts]
// You will need to import defu
import { defu } from 'defu'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(routeRules) => {
routeRules['/some/route'] = defu(
{
headers: {
contentSecurityPolicy: {
"connect-src": ["'self'", validDomain]
},
xFrameOptions: false
},
hidePoweredBy: false
"script-src": ["'self'", "..."]
// The values "'self' ..." will be added to the existing values
}
}
},
routeRules['/some/route']
routeRules['/some/route'] // The other existing rules for /some/route will be preserved
)
})
})
```

In the example above,
- All existing security options for `/some/route` will be maintained, and only the `script-src` CSP directive will be modified.
- The existing content of the `script-src` directive will be preserved, and your values will be added to the existing values.

Read more about [`defu`](https://github.com/unjs/defu)


### Overwriting rules

If you want to erase the existing settings, don't use defu and overwrite the values:

```ts{}[server/plugins/filename.ts]
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(routeRules) => {
routeRules['/some/route'] = {
headers: {
contentSecurityPolicy: {
"script-src": ["'self'", "..."]
}
}
}
// Any existing rules for /some/route will be erased
})
})
```

In the example above,
- All existing security options for `/some/route` will be erased.
- The `script-src` directive will contain your values.

1 change: 1 addition & 0 deletions docs/content/1.documentation/2.headers/1.csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export default defineNuxtConfig({
meta: true, // Enables CSP as a meta tag in SSG mode
hashScripts: true, // Enables CSP hash support for scripts in SSG mode
hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended)
exportToPresets: true // Export security headers to Nitro presets
},
sri: true,
headers: {
Expand Down
30 changes: 29 additions & 1 deletion docs/content/1.documentation/5.advanced/3.strict-csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -669,16 +669,42 @@ Nuxt Security uses a different approach, depending on whether SSR or SSG is used

**CSP Headers for SSG via Nitro Presets**

Nuxt Security supports CSP via HTTP headers for Nitro Presets that generate HTTP headers.

When using the SSG mode, some static hosting services such as Vercel or Netlify provide the ability to specify a configuration file that governs the value of the headers that will be generated. When these hosting services benefit from a [Nitro Preset](https://nitro.unjs.io/deploy/#overview), it is possible for Nuxt Security to predict the value of the CSP headers for each page and write the value to the configuration file.

Nuxt Security supports CSP via HTTP headers for Nitro Presets that output HTTP headers.
This feature is enabled by default with the `ssg: exportToPresets` option.

::alert{type="info"}
If you deploy your SSG site on Vercel or Netlify, you will benefit automatically from CSP Headers.
<br>
CSP will be delivered via HTTP headers, in addition to the standard `<meta http-equiv>` approach. If you want to disable the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: meta` option.
::

**CSP Headers for SSG via `prerenderedHeaders` hook**

Nuxt Security allows you to generate your own headers rules with the `nuxt-security:prerenderedHeaders` buildtime hook.

If you do not deploy with a Nitro preset, or if you have specific requirements that are not met by the `ssg: exportToPresets` default, you can use this hook to generate your headers configuration file yourself.

See our documentation on the [prerenderedPages hook](/documentation/advanced/hooks/#prerendered-headers-hook)

::alert{type="info"}
This will allow you to deliver CSP via HTTP headers, in addition to the standard `<meta http-equiv>` approach.
::

**CSP Headers for Hybrid Pre-Rendered Pages**

Nuxt Security supports CSP via HTTP headers for pre-rendered pages of Hybrid applications.

This feature is enabled by default with the `ssg: nitroHeaders` option.

::alert{type="info"}
In Hybrid applications, CSP of pre-rendered pages will be delivered via HTTP headers, in addition to the standard `<meta http-equiv>` approach.
<br>
If you want to disable the meta tag, so that only the HTTP headers are used, you can do so with the `ssg: meta` option.
::

### Per Route CSP

Nuxt Security gives you the ability to define per-route CSP. For instance, you can have Strict CSP on the admin section of your application, and a more relaxed policy on the blog section.
Expand Down Expand Up @@ -718,6 +744,8 @@ export default defineNuxtConfig({
meta: true, // Enables CSP as a meta tag in SSG mode
hashScripts: true, // Enables CSP hash support for scripts in SSG mode
hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended)
nitroHeaders: true // Allow Nitro to serve security headers for pre-rendered routes
exportToPresets: true // Export pre-rendered security headers to Nitro presets
},
// You can use nonce and ssg simultaneously
// Nuxt Security will take care of choosing the adequate parameters when you build for either SSR or SSG
Expand Down
190 changes: 190 additions & 0 deletions docs/content/1.documentation/5.advanced/4.hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Nuxt Security Hooks

Use hooks to further customize Nuxt Security

---

:ellipsis{right=0px width=75% blur=150px}

## Available hooks

Nuxt Security provides two custom hooks:

1. **The routeRules hook** (Nitro runtime hook): to modify the settings of Nuxt-Security at runtime.
2. **The prerenderedHeaders hook** (Nuxt buildtime hook): to create a headers configuration file for your static server.

## Route Rules Hook

The `nuxt-security:routeRules` hook is convenient when you don't know the applicable security options at build time.

This happens most frequently in the following cases:
- Your Nuxt application is designed to be deployed on multiple websites, with a different configuration for each website.
- Your security options are kept in a third-party vault system (e.g. Google Secret Manager), and your build system does not have access to the secrets.

::alert{type="info"}
Your `nuxt-security:routeRules` hook will be invoked each time your server is restarted.
::

### How to use

In order to use this hook, you will need to write a Nitro plugin

```ts{}[/server/plugins/my-plugin.ts]
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', (appSecurityOptions) => {
// Your code here
})
})
```

- The `appSecurityOptions` variable contains all your application security options in the form of router definitions.

These router definitions are provided in the form of h3's radix router rules.
Please see [radix3](https://github.com/unjs/radix3) for further details.

- The anonymous function `(appSecurityOptions) => {}` will be called asynchronously

If you need to fetch your security data from an external secrets' manager API, you can use `async/await` in your code.

- Your code can modify any security option inside this hook

For each route, you can modify the rules exactly as you would do it with the `routeRules` option of `nuxt.config.ts`.


### Examples

You can apply custom settings to your whole application by modifying the base `/**` route :

```ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(appSecurityOptions) => {
const cspConnectSrc = await $fetch('https://secret-manager-api.com/api-route')
// This example replaces only the connect-src CSP directive
appSecurityOptions['/**'] = defuReplaceArray(
{
headers: {
contentSecurityPolicy: {
"connect-src": [cspConnectSrc]
}
}
},
appSecurityOptions['/**']
)
})
})
```

You can also apply your settings to selected sub-routes of your application :

```ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(appSecurityOptions) => {
const cspConnectSrc = await $fetch('https://secret-manager-api.com/api-route')
// This example modifies the CSP only for `/admin/**` routes
appSecurityOptions['/admin/**'] = defuReplaceArray(
{
headers: {
contentSecurityPolicy: {
"connect-src": [cspConnectSrc]
}
}
},
appSecurityOptions['/admin/**']
)
})
})
```

You are not constrained to CSP options, you can modify any security option with this hook :

```ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxt-security:routeRules', async(appSecurityOptions) => {
const tokenLimit = await $fetch('https://secret-manager-api.com/api-route')
// This example modifies the Rate Limiter only for API routes
// It also modifies the X-Powered-By setting for these routes
appSecurityOptions['/api/**'] = defuReplaceArray(
{
rateLimiter: {
tokensPerInterval: tokenLimit
},
hidePoweredBy: false
},
appSecurityOptions['/api/**']
)
})
})
```

## Prerendered Headers Hook

The `nuxt-security:prerenderedHeaders` hook is convenient when you want to know the security headers that should be delivered by your static server.

This happens most frequently when you deploy your website statically on a CDN. In that case, your server has the HTML pages, but it doesn't know which security headers should be delivered.

You may want to configure your hosting provider so that the correct headers are delivered for each static page.


::alert{type="info"}
Your `nuxt-security:prerenderedHeaders` hook will be invoked each time your build your application.
::

### How to use

In order to use this hook, you will need write your code in `defineNuxtConfig`

```ts{}[nuxt.config.ts]
export default defineNuxtConfig({
hooks: {
'nuxt-security:prerenderedHeaders': (prerenderedHeaders) => {
// Your code here
}
}
})
```

- The `prerenderedHeaders` variable contains all calculated headers for each page.

```js
{
'/page1': {
header1: value1,
header2: value2
},
'/page2': {
header3: value3,
...
}
}
```

- The anonymous function `(prerenderedHeaders) => {}` will be called asynchronously

If you need to write to files asynchronously in your code, you can use `async/await` in your code.


### Examples

You can generate nginx-compatible header rules within your CI/CD pipeline and save them to a file on disk :

```ts{}[nuxt.config.ts]
import { writeFile } from 'node:fs/promises'
defineNuxtConfig({
hooks: {
'nuxt-security:prerenderedHeaders': async(prerenderedHeaders) => {
// Don't take this snippet for granted, this is just provided as a basic example
let nginxText = ''
for (const path in prerenderedHeaders) {
nginxText += 'location ' + path + ' {\n'
const headersForPath = prerenderedHeaders[path]
for (const headerName in headersForPath) {
const headerValue = headersForPath[headerName]
nginxText += ` add_header ${headerName} "${headerValue}";\n`
}
nginxText += '}\n\n'
}
await writeFile('./.nuxt/server.headers', nginxText)
}
}
})
```