Files
simple-mail-cleaner/backend/node_modules/@fastify/helmet/test/routes.test.js
2026-01-22 15:49:12 +01:00

500 lines
13 KiB
JavaScript

'use strict'
const { test } = require('node:test')
const Fastify = require('fastify')
const helmet = require('..')
test('It should apply route specific helmet options over the global options', async (t) => {
t.plan(2)
const fastify = Fastify()
await fastify.register(helmet, { global: true })
fastify.get('/', { helmet: { frameguard: false } }, (_request, reply) => {
reply.send({ hello: 'world' })
})
const response = await fastify.inject({
method: 'GET',
path: '/'
})
const notExpected = {
'x-frame-options': 'SAMEORIGIN'
}
const expected = {
'x-dns-prefetch-control': 'off',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '0'
}
const actualResponseHeaders = {
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.notDeepStrictEqual(
response.headers['x-frame-options'],
notExpected['x-frame-options']
)
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})
test('It should disable helmet on specific route when route `helmet` option is set to `false`', async (t) => {
t.plan(2)
const fastify = Fastify()
await fastify.register(helmet, { global: true })
fastify.get('/disabled', { helmet: false }, (_request, reply) => {
reply.send({ hello: 'disabled' })
})
fastify.get('/enabled', (_request, reply) => {
reply.send({ hello: 'enabled' })
})
const helmetHeaders = {
'x-frame-options': 'SAMEORIGIN',
'x-dns-prefetch-control': 'off',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '0'
}
await fastify
.inject({
method: 'GET',
path: '/disabled'
})
.then((response) => {
const actualResponseHeaders = {
'x-frame-options': response.headers['x-frame-options'],
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.notDeepStrictEqual(actualResponseHeaders, helmetHeaders)
})
.catch((err) => {
t.assert.fail(err)
})
await fastify
.inject({
method: 'GET',
path: '/enabled'
})
.then((response) => {
const actualResponseHeaders = {
'x-frame-options': response.headers['x-frame-options'],
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.deepStrictEqual(actualResponseHeaders, helmetHeaders)
})
.catch((err) => {
t.assert.fail(err)
})
})
test('It should add CSPNonce decorator and hooks when route `enableCSPNonces` option is set to `true`', async (t) => {
t.plan(4)
const fastify = Fastify()
await fastify.register(helmet, {
global: false,
enableCSPNonces: false,
contentSecurityPolicy: {
directives: {
'script-src': ["'self'", "'unsafe-eval'", "'unsafe-inline'"],
'style-src': ["'self'", "'unsafe-inline'"]
}
}
})
fastify.get(
'/',
{
helmet: {
enableCSPNonces: true
}
},
(_request, reply) => {
t.assert.ok(reply.cspNonce)
reply.send(reply.cspNonce)
}
)
const response = await fastify.inject({ method: 'GET', path: '/' })
const cspCache = response.json()
const expected = {
'content-security-policy': `script-src 'self' 'unsafe-eval' 'unsafe-inline' 'nonce-${cspCache.script}';style-src 'self' 'unsafe-inline' 'nonce-${cspCache.style}';default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src-attr 'none';upgrade-insecure-requests`
}
const actualResponseHeaders = {
'content-security-policy': response.headers['content-security-policy']
}
t.assert.ok(cspCache.script)
t.assert.ok(cspCache.style)
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})
test('It should add CSPNonce decorator and hooks with default options when route `enableCSPNonces` option is set to `true`', async (t) => {
t.plan(8)
const fastify = Fastify()
await fastify.register(helmet, {
global: false,
enableCSPNonces: false
})
fastify.get('/no-csp', (_request, reply) => {
t.assert.equal(reply.cspNonce, null)
reply.send({ message: 'no csp' })
})
fastify.get(
'/with-csp',
{
helmet: {
enableCSPNonces: true
}
},
(_request, reply) => {
t.assert.ok(reply.cspNonce)
reply.send(reply.cspNonce)
}
)
fastify.inject({
method: 'GET',
path: '/no-csp'
})
let response
response = await fastify.inject({ method: 'GET', path: '/with-csp' })
const cspCache = response.json()
t.assert.ok(cspCache.script)
t.assert.ok(cspCache.style)
response = await fastify.inject({ method: 'GET', path: '/with-csp' })
const newCsp = response.json()
t.assert.notEqual(cspCache, newCsp)
t.assert.ok(cspCache.script)
t.assert.ok(cspCache.style)
})
test('It should not add CSPNonce decorator when route `enableCSPNonces` option is set to `false`', async (t) => {
t.plan(8)
const fastify = Fastify()
await fastify.register(helmet, {
global: true,
enableCSPNonces: true
})
fastify.get('/with-csp', (_request, reply) => {
t.assert.ok(reply.cspNonce)
reply.send(reply.cspNonce)
})
fastify.get(
'/no-csp',
{ helmet: { enableCSPNonces: false } },
(_request, reply) => {
t.assert.equal(reply.cspNonce, null)
reply.send({ message: 'no csp' })
}
)
fastify.inject({
method: 'GET',
path: '/no-csp'
})
let response
response = await fastify.inject({ method: 'GET', path: '/with-csp' })
const cspCache = response.json()
t.assert.ok(cspCache.script)
t.assert.ok(cspCache.style)
response = await fastify.inject({ method: 'GET', path: '/with-csp' })
const newCsp = response.json()
t.assert.notEqual(cspCache, newCsp)
t.assert.ok(cspCache.script)
t.assert.ok(cspCache.style)
})
test('It should not set default directives when route useDefaults is set to `false`', async (t) => {
t.plan(1)
const fastify = Fastify()
await fastify.register(helmet, {
global: false,
enableCSPNonces: false,
contentSecurityPolicy: {
directives: {}
}
})
fastify.get(
'/',
{
helmet: {
contentSecurityPolicy: {
useDefaults: false,
directives: {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-eval'", "'unsafe-inline'"],
'style-src': ["'self'", "'unsafe-inline'"]
}
}
}
},
(_request, reply) => {
reply.send({ hello: 'world' })
}
)
const response = await fastify.inject({ method: 'GET', path: '/' })
const expected = {
'content-security-policy':
"default-src 'self';script-src 'self' 'unsafe-eval' 'unsafe-inline';style-src 'self' 'unsafe-inline'"
}
const actualResponseHeaders = {
'content-security-policy': response.headers['content-security-policy']
}
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})
test('It should not set `content-security-policy` header, if route contentSecurityPolicy is false', async (t) => {
t.plan(1)
const fastify = Fastify()
await fastify.register(helmet, {
global: false,
enableCSPNonces: false,
contentSecurityPolicy: {
directives: {}
}
})
fastify.get(
'/',
{
helmet: {
contentSecurityPolicy: false
}
},
(_request, reply) => {
reply.send({ hello: 'world' })
}
)
const response = await fastify.inject({ method: 'GET', path: '/' })
const expected = {
'content-security-policy': undefined
}
const actualResponseHeaders = {
'content-security-policy': response.headers['content-security-policy']
}
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})
test('It should be able to conditionally apply the middlewares through the `helmet` reply decorator', async (t) => {
t.plan(10)
const fastify = Fastify()
await fastify.register(helmet, { global: false })
fastify.get('/:condition', async (request, reply) => {
const { condition } = request.params
t.assert.ok(reply.helmet)
t.assert.notEqual(reply.helmet, null)
if (condition !== 'frameguard') {
await reply.helmet({ frameguard: false })
} else {
await reply.helmet({ frameguard: true })
}
return { message: 'ok' }
})
const expected = {
'x-dns-prefetch-control': 'off',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '0'
}
const maybeExpected = {
'x-frame-options': 'SAMEORIGIN'
}
{
const response = await fastify.inject({
method: 'GET',
path: '/no-frameguard'
})
const actualResponseHeaders = {
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.equal(response.statusCode, 200)
t.assert.notDeepStrictEqual(
response.headers['x-frame-options'],
maybeExpected['x-frame-options']
)
t.assert.deepStrictEqual(actualResponseHeaders, expected)
}
const response = await fastify.inject({
method: 'GET',
path: '/frameguard'
})
const actualResponseHeaders = {
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.equal(response.statusCode, 200)
t.assert.deepStrictEqual(
response.headers['x-frame-options'],
maybeExpected['x-frame-options']
)
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})
test('It should throw an error when route specific helmet options are of an invalid type', async (t) => {
t.plan(2)
const fastify = Fastify()
await fastify.register(helmet)
try {
fastify.get('/', { helmet: 'invalid_options' }, () => {
return { message: 'ok' }
})
} catch (error) {
t.assert.ok(error)
t.assert.equal(
error.message,
'Unknown value for route helmet configuration'
)
}
})
test('It should forward `helmet` reply decorator and route specific errors to `fastify-helmet`', async (t) => {
t.plan(6)
const fastify = Fastify()
await fastify.register(helmet, { global: false })
fastify.get('/helmet-reply-decorator-error', async (_request, reply) => {
await reply.helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'", () => 'bad;value']
}
}
})
return { message: 'ok' }
})
fastify.get(
'/helmet-route-configuration-error',
{
helmet: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'", () => 'bad;value']
}
}
}
},
async () => {
return { message: 'ok' }
}
)
const notExpected = {
'x-dns-prefetch-control': 'off',
'x-frame-options': 'SAMEORIGIN',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '0'
}
{
const response = await fastify.inject({
method: 'GET',
path: '/helmet-reply-decorator-error'
})
const actualResponseHeaders = {
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.equal(response.statusCode, 500)
t.assert.equal(
JSON.parse(response.payload).message,
'Content-Security-Policy received an invalid directive value for "default-src"'
)
t.assert.notDeepStrictEqual(actualResponseHeaders, notExpected)
}
const response = await fastify.inject({
method: 'GET',
path: '/helmet-route-configuration-error'
})
const actualResponseHeaders = {
'x-dns-prefetch-control': response.headers['x-dns-prefetch-control'],
'x-download-options': response.headers['x-download-options'],
'x-content-type-options': response.headers['x-content-type-options'],
'x-xss-protection': response.headers['x-xss-protection']
}
t.assert.equal(response.statusCode, 500)
t.assert.equal(
JSON.parse(response.payload).message,
'Content-Security-Policy received an invalid directive value for "default-src"'
)
t.assert.notDeepStrictEqual(actualResponseHeaders, notExpected)
})