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/1.0.0 rc.3 #262

Merged
merged 49 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9500006
add test evidencing nonce issue in SSG
vejja Oct 17, 2023
0fe16bb
fix: apply new test to SSG pre-rendered page in fixture
vejja Oct 17, 2023
20b0966
fix nonce issue
vejja Oct 17, 2023
f5dce18
remove temporary code
vejja Oct 17, 2023
ca3169a
feat: upgrade nuxt Typescript config
vejja Oct 17, 2023
2df5a5f
remove unecessary workarounds
vejja Oct 17, 2023
a19b3f3
use autoImports in playground and fixtures
vejja Oct 17, 2023
5e714a7
use defineNitroPlugin helper in nitro plugins
vejja Oct 17, 2023
adf39e9
provide typing on runtimeConfig
vejja Oct 17, 2023
50c8ffe
fix type for unsupportedPolicies in cspSsg
vejja Oct 17, 2023
e751f52
improve rateLimiter storage mounting
vejja Oct 17, 2023
5bf020c
Revert "improve rateLimiter storage mounting"
vejja Oct 19, 2023
883de56
Merge branch 'main' into fix/typescript-config
vejja Oct 19, 2023
96d831a
eslint update
vejja Oct 19, 2023
b08c198
improve CSP compliance
vejja Oct 20, 2023
ee366c7
RFC-compliant crypto
vejja Oct 21, 2023
6d70fcb
Update 3.crossOriginEmbedderPolicy.md
espensgr Oct 24, 2023
2ad809a
Update headers.ts
espensgr Oct 24, 2023
909221b
Merge pull request #261 from espensgr/patch-1
Baroshem Oct 25, 2023
9406b97
Merge branch 'chore/1.0.0-rc.3' into fix/nonce-ssg
Baroshem Oct 25, 2023
92bddbe
Merge pull request #245 from vejja/fix/nonce-ssg
Baroshem Oct 25, 2023
2b00e01
Ensure all types are exported
Tristan971 Oct 25, 2023
53c6bbe
Merge pull request #264 from mangadex-pub/export-types
Baroshem Oct 26, 2023
f58f992
update package
vejja Oct 26, 2023
a9e151e
Merge remote-tracking branch 'upstream/chore/1.0.0-rc.3' into fix/typ…
vejja Oct 26, 2023
8f17dd4
Fix Basic Auth Configuration for Multiple Paths
Oct 26, 2023
be2df08
small correction to doc
vejja Oct 26, 2023
063d6d5
Merge remote-tracking branch 'upstream/chore/1.0.0-rc.3' into fix/csp…
vejja Oct 26, 2023
97c37c1
fix nonce test randomly failing
vejja Oct 26, 2023
f0f1357
docs: clarify interval
Baroshem Oct 27, 2023
f57ed56
Merge pull request #257 from vejja/fix/csp-nonce-cookie
Baroshem Oct 27, 2023
0fa8ace
Merge branch 'chore/1.0.0-rc.3' into fix/typescript-config
vejja Oct 28, 2023
3e596bc
fix vercel build step
vejja Oct 28, 2023
20d6c4c
Remove pnpm-lock.yaml
Oct 28, 2023
9d26803
modify execution order of nitro plugins
vejja Oct 28, 2023
076f819
also reorders plugins in SSG mode
vejja Oct 29, 2023
a667406
Merge pull request #248 from vejja/fix/typescript-config
Baroshem Oct 30, 2023
e8b3651
Merge pull request #271 from vejja/ensure-plugins-last
Baroshem Oct 30, 2023
16f169d
feat: extend CSP support in SSG mode
vejja Oct 29, 2023
1f83cb2
Merge pull request #272 from vejja/extend-ssg-csp
Baroshem Oct 30, 2023
3f66f51
Rectified the condition to respond with a 200 status code when the pa…
Oct 31, 2023
c89ae55
Updated the test case
Oct 31, 2023
d8df6ed
Ensure that when exclusion is not configured, access to the entire gl…
Oct 31, 2023
32a2b43
add hashStyles option to Interface
vejja Oct 31, 2023
a999dca
use cheerio to parse HTML
vejja Oct 31, 2023
442993b
Revert "add hashStyles option to Interface"
vejja Nov 1, 2023
76841ce
Merge pull request #267 from rathahin/fix-basic-auth
Baroshem Nov 1, 2023
04e0c45
Merge pull request #275 from vejja/html-parser
Baroshem Nov 1, 2023
b2c0b2a
chore: 1.0.0-rc.3
Baroshem Nov 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"extends": ["@nuxtjs/eslint-config-typescript"],
"rules": {
"@typescript-eslint/no-unused-vars": ["off"]
}
"extends": ["@nuxt/eslint-config"]
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ coverage
Network Trash Folder
Temporary Items
.apdisk

# Yarn is the package manager, do not commit npm lock file
package-lock.json
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false
3 changes: 2 additions & 1 deletion .nuxtrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
imports.autoImport=true
imports.autoImport=false
typescript.includeWorkspace=true
1 change: 1 addition & 0 deletions .stackblitz/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules
.output
.env
dist
.vercel
1 change: 1 addition & 0 deletions docs/.nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
imports.autoImport=true
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface ModuleOptions {
basicAuth: BasicAuth | false;
enabled: boolean;
csrf: CsrfOptions | false;
nonce: NonceOptions | false;
nonce: boolean;
removeLoggers?: RemoveOptions | false;
ssg?: Ssg;
}
Expand Down
9 changes: 4 additions & 5 deletions docs/content/1.documentation/2.headers/1.csp.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ export default defineNuxtConfig({
? [
"'self'", // backwards compatibility for older browsers that don't support strict-dynamic
"'nonce-{{nonce}}'",
"'strict-dynamic'",
]
: // In dev mode, we allow unsafe-inline so that hot reloading keeps working
["'self'", "'unsafe-inline'"],
Expand Down Expand Up @@ -193,11 +192,11 @@ The `nonce` value is generated per request and is added to the CSP header. This
```ts
export default defineNuxtConfig({
routeRules: {
'/api/custom-route': {
nonce: false // do not check nonce for this route (1)
'/custom-route': {
nonce: false // do not generate nonce for this route (1)
},
'/api/other-route': {
nonce: { mode: 'check' } // do not generate a new nonce for this route, but check it against the existing one (2)
'/other-route': {
nonce: true // generate a new nonce for this route (2)
}
}
})
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).
::
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The amount of requests that reach the application before rate limiting will bloc

- Default: `300000`

The time after which the rate limiting will be reset.
The time value in miliseconds after which the rate limiting will be reset. For example, if you set it to `10000` and `tokensPerInterval: 3` it will allow three requests from one IP address in 10 seconds and the next one in this interval will be banned. After 10 seconds however, user will be able to send requests again.

### `headers`

Expand Down
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@
"dist"
],
"scripts": {
"prepack": "nuxt-module-build",
"dev": "nuxt-module-build --stub && nuxi prepare playground && nuxi dev playground",
"prepack": "nuxt-module-build build",
"dev": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:start": "nuxi start playground",
"dev:generate": "nuxi generate playground",
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"dev:preview": "nuxi preview playground",
"dev:docs": "cd docs && yarn dev",
"lint": "eslint --ext .js,.ts,.vue",
"lint": "eslint .",
"test": "vitest run --silent",
"test:watch": "vitest watch",
"stackblitz": "cd .stackblitz && yarn && yarn dev"
},
"packageManager": "[email protected]",
"dependencies": {
"@nuxt/kit": "^3.7.3",
"@nuxt/kit": "^3.8.0",
"basic-auth": "^2.0.1",
"defu": "^6.1.1",
"nuxt-csurf": "^1.3.1",
Expand All @@ -59,15 +59,15 @@
"xss": "^1.0.14"
},
"devDependencies": {
"@nuxt/module-builder": "latest",
"@nuxt/schema": "^3.7.3",
"@nuxt/test-utils": "^3.7.3",
"@nuxtjs/eslint-config-typescript": "latest",
"@types/node": "^18.14.4",
"eslint": "latest",
"nuxt": "^3.7.3",
"typescript": "5.2.2",
"vitest": "^0.28.5"
"@nuxt/module-builder": "^0.5.2",
"@nuxt/schema": "^3.8.0",
"@nuxt/test-utils": "^3.8.0",
"@nuxt/eslint-config": "^0.2.0",
"@types/node": "^18.18.1",
"eslint": "^8.50.0",
"nuxt": "^3.8.0",
"typescript": "^5.2.2",
"vitest": "^0.33.0"
},
"stackblitz": {
"installDependencies": false,
Expand Down
1 change: 1 addition & 0 deletions playground/.nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
imports.autoImport=true
4 changes: 3 additions & 1 deletion playground/pages/secret.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<template>
Secret Route
<div>
Secret Route
</div>
</template>
3 changes: 0 additions & 3 deletions playground/server/api/test.post.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { defineEventHandler } from 'h3'


export default defineEventHandler((event) => {
console.log('test')
})
3 changes: 3 additions & 0 deletions playground/server/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}
3 changes: 3 additions & 0 deletions playground/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
87 changes: 77 additions & 10 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@ import { fileURLToPath } from 'node:url'
import { resolve, normalize } from 'pathe'
import { defineNuxtModule, addServerHandler, installModule, addVitePlugin } from '@nuxt/kit'
import { defu } from 'defu'
import { Nuxt, RuntimeConfig } from '@nuxt/schema'
import type { Nuxt, RuntimeConfig } from '@nuxt/schema'
import viteRemove from 'unplugin-remove/vite'
import { defuReplaceArray } from './utils'
import {
import type {
ModuleOptions,
NuxtSecurityRouteRules
} from './types/index'
import {
import type {
SecurityHeaders
} from './types/headers'
import {
import type {
BasicAuth
} from './types/middlewares'
import {
defaultSecurityConfig
} from './defaultConfig'
import { SECURITY_MIDDLEWARE_NAMES } from './middlewares'
import { HeaderMapper, SECURITY_HEADER_NAMES, getHeaderValueFromOptions } from './headers'
import { type HeaderMapper, SECURITY_HEADER_NAMES, getHeaderValueFromOptions } from './headers'

declare module '@nuxt/schema' {
declare module 'nuxt/schema' {
interface NuxtOptions {
security: ModuleOptions;
security: ModuleOptions
}
interface RuntimeConfig {
security: ModuleOptions,
private: { basicAuth: BasicAuth | false, [key: string]: any }
}
}

Expand All @@ -36,6 +40,10 @@ declare module 'nitropack' {
}
}

export * from './types/index'
export * from './types/headers'
export * from './types/middlewares'

export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'nuxt-security',
Expand Down Expand Up @@ -63,7 +71,7 @@ export default defineNuxtModule<ModuleOptions>({
nuxt.options.runtimeConfig.private = defu(
nuxt.options.runtimeConfig.private,
{
basicAuth: securityOptions.basicAuth as BasicAuth | boolean
basicAuth: securityOptions.basicAuth
}
)

Expand All @@ -72,7 +80,7 @@ export default defineNuxtModule<ModuleOptions>({
nuxt.options.runtimeConfig.security = defu(
nuxt.options.runtimeConfig.security,
{
...(securityOptions as unknown as RuntimeConfig['security'])
...securityOptions
}
)

Expand All @@ -82,6 +90,11 @@ export default defineNuxtModule<ModuleOptions>({

setSecurityRouteRules(nuxt, securityOptions)

// Remove Content-Security-Policy header in pre-rendered routes
// When pre-rendered, the CSP is provided via html <meta> instead
// If kept, this would block the site from rendering
removeCspHeaderForPrerenderedRoutes(nuxt)

if (nuxt.options.security.requestSizeLimiter) {
addServerHandler({
handler: normalize(
Expand Down Expand Up @@ -113,7 +126,6 @@ export default defineNuxtModule<ModuleOptions>({
)
})
}

if (nuxt.options.security.nonce) {
addServerHandler({
handler: normalize(
Expand Down Expand Up @@ -197,6 +209,17 @@ const setSecurityRouteRules = (nuxt: Nuxt, securityOptions: ModuleOptions) => {
}
}

const removeCspHeaderForPrerenderedRoutes = (nuxt: Nuxt) => {
const nitroRouteRules = nuxt.options.nitro.routeRules
for (const route in nitroRouteRules) {
const routeRules = nitroRouteRules[route]
if (routeRules.prerender || nuxt.options.nitro.static) {
routeRules.headers = routeRules.headers || {}
routeRules.headers['Content-Security-Policy'] = ''
}
}
}

const registerSecurityNitroPlugins = (
nuxt: Nuxt,
securityOptions: ModuleOptions
Expand Down Expand Up @@ -262,4 +285,48 @@ const registerSecurityNitroPlugins = (
)
}
})

// Make sure our nitro plugins will be applied last
// After all other third-party modules that might have loaded their own nitro plugins
nuxt.hook('nitro:init', nitro => {
const securityPluginsPrefix = normalize(
fileURLToPath(
new URL('./runtime/nitro/plugins', import.meta.url)
)
)
// SSR: Reorder plugins in Nitro options
nitro.options.plugins.sort((a, b) => {
if (a.startsWith(securityPluginsPrefix)) {
if (b.startsWith(securityPluginsPrefix)) {
return 0
} else {
return 1
}
} else {
if (b.startsWith(securityPluginsPrefix)) {
return -1
} else {
return 0
}
}
})
// SSG: Reorder plugins in Nitro hook
nitro.hooks.hook('prerender:config', config => {
config.plugins?.sort((a, b) => {
if (a?.startsWith(securityPluginsPrefix)) {
if (b?.startsWith(securityPluginsPrefix)) {
return 0
} else {
return 1
}
} else {
if (b?.startsWith(securityPluginsPrefix)) {
return -1
} else {
return 0
}
}
})
})
})
}
4 changes: 2 additions & 2 deletions src/runtime/composables/nonce.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNuxtApp, useCookie } from '#imports'
import { useNuxtApp } from '#imports'

export function useNonce () {
return useNuxtApp().ssrContext?.event?.context.nonce ?? useCookie('nonce').value
return useNuxtApp().ssrContext?.event?.context.nonce
}
10 changes: 5 additions & 5 deletions src/runtime/nitro/plugins/01-hidePoweredBy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { NitroAppPlugin, RenderResponse } from 'nitropack'
import { defineNitroPlugin } from '#imports'

export default <NitroAppPlugin> function (nitro) {
nitro.hooks.hook('render:response', (response: RenderResponse) => {
if (response.headers['x-powered-by']) {
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('render:response', (response) => {
if (response?.headers?.['x-powered-by']) {
delete response.headers['x-powered-by']
}
})
}
})
Loading
Loading