Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ fastify.get('/here-we-use-helmet-reply-decorator', async (request, reply) => {
} else {
// we apply customized options
await reply.helmet({ frameguard: false })
// You can also pass a function returning customized options.
// This is useful for modifying previously set options.
// await reply.helmet((opts) => {
// // Here we're adding a new option to a directive without having to
// // replace the entire `contentSecurityPolicy` object.
// opts.contentSecurityPolicy.directives['script-src'].push('"nonce-123abc"');
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example assumes opts.contentSecurityPolicy and its nested properties exist, but users may receive configuration where these are undefined. Consider adding a comment noting that this is a simplified example and proper validation should be performed in production code.

Copilot uses AI. Check for mistakes.
// return opts
// })
}

return {
Expand Down
11 changes: 8 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,13 @@ async function replyDecorators (request, reply, configuration, enableCSP) {
}

reply.helmet = function (opts) {
const helmetConfiguration = opts
? Object.assign(Object.create(null), configuration, opts)
: configuration
let helmetConfiguration = configuration

if (typeof opts === 'function') {
helmetConfiguration = opts(helmetConfiguration)
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When opts is a function, the configuration is passed directly without creating a new object via Object.create(null). This inconsistency means the function could mutate the original configuration object. Consider passing a copy: helmetConfiguration = opts(Object.assign(Object.create(null), configuration))

Suggested change
helmetConfiguration = opts(helmetConfiguration)
helmetConfiguration = opts(Object.assign(Object.create(null), configuration))

Copilot uses AI. Check for mistakes.
} else if (opts) {
helmetConfiguration = Object.assign(Object.create(null), configuration, opts)
}

return helmet(helmetConfiguration)(request.raw, reply.raw, done)
}
Expand Down Expand Up @@ -135,6 +139,7 @@ async function buildHelmetOnRoutes (request, reply, configuration, enableCSP) {

// Helmet forward a typeof Error object so we just need to throw it as is.
function done (error) {
/* c8 ignore next */
Copy link
Copy Markdown
Member

@jean-michelet jean-michelet May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you plz document why this is necessary, we should have very good reasons to ignore code coverage.
I see this package has a few ignore comments without any explanation, so it's not a feedback specific to you.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jean-michelet For whatever reason this condition isn't covered by the existing tests. I added the ignore as a work-around because I saw it was used elsewhere in the code (and it was the only way I could commit without --force). I've tried adding a test to cover it but was unsuccessful, so any help or advice is appreciated!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: I’ve opened a PR to add comments:
#292

if (error) throw error
}

Expand Down
49 changes: 49 additions & 0 deletions test/global.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,55 @@ test('It should be able to pass custom options to the `helmet` reply decorator',
t.assert.deepStrictEqual(actualResponseHeaders, expected)
})

test('It should be able to pass custom options as a function to the `helmet` reply decorator', async (t) => {
t.plan(4)

const fastify = Fastify()
await fastify.register(helmet, { global: false })

fastify.get('/', async (_request, reply) => {
t.assert.ok(reply.helmet)
t.assert.notDeepStrictEqual(reply.helmet, null)

await reply.helmet((opts) => {
opts.frameguard = false

return opts
})
return { message: 'ok' }
})

const response = await fastify.inject({
method: 'GET',
path: '/'
})

const expected = {
'x-dns-prefetch-control': 'off',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '0'
}

const notExpected = {
'x-frame-options': 'SAMEORIGIN'
}

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']
}

const actualNotExpectedHeaders = {
'x-frame-options': response.headers['x-frame-options']
}

t.assert.notDeepStrictEqual(actualNotExpectedHeaders, notExpected)
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)

Expand Down
2 changes: 1 addition & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ declare module 'fastify' {
script: string;
style: string;
},
helmet: (opts?: HelmetOptions) => typeof helmet
helmet: (opts?: HelmetOptions | ((opts: HelmetOptions) => HelmetOptions)) => typeof helmet
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a test for the type? We use tsd.

}

export interface RouteOptions extends fastifyHelmet.FastifyHelmetRouteOptions { }
Expand Down