Skip to content

Conversation

@Tam
Copy link

@Tam Tam commented May 26, 2025

Added support for passing a function to reply.helmet, allowing dynamic modification of Helmet options. Updated type definitions and included tests to validate this functionality. The aim is to make updating the existing options easier, without needing to overwrite the entire object (i.e. when modifying a specific CSP value).

await reply.helmet(opts => {
  opts.contentSecurityPolicy.directives['script-src'].push('nonce-123')
  return opts
})

Checklist

Tam added 2 commits May 26, 2025 15:36
Added support for passing a function to `reply.helmet`, allowing dynamic modification of Helmet options. Updated type definitions and included tests to validate this functionality.
Copy link
Member

@jean-michelet jean-michelet left a comment

Choose a reason for hiding this comment

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

As I'm not familiar with this plugin, I'd rather not comment on the usefulness of this feature.


// Helmet forward a typeof Error object so we just need to throw it as is.
function done (error) {
/* c8 ignore next */
Copy link
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
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
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

README.md Outdated
Comment on lines 103 to 107
// you can also pass a function returning customized options
// await reply.helmet((opts) => {
// opts.frameguard = false
// return opts
// })
Copy link
Member

Choose a reason for hiding this comment

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

Instead, can you share an example that is only achievable with a callback rather than an object?
It would help users to understand why it is a helpful feature.

Copy link
Author

Choose a reason for hiding this comment

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

@jean-michelet I've updated the readme with a clearer example, let me know if that's better. 06d709b

@jean-michelet jean-michelet requested a review from a team May 31, 2025 10:20
@Fdawgs
Copy link
Member

Fdawgs commented May 31, 2025

Thanks for the PR @Tam, i'm interested what your use case is for this?

@Tam
Copy link
Author

Tam commented May 31, 2025

Thanks for the PR @Tam, i'm interested what your use case is for this?

I'm adding some js tags to the twig.js templating library for a side-project and wanted to automatically add integrity hashes and nonces to the CSP header.

@climba03003
Copy link
Member

I'm adding some js tags to the twig.js templating library for a side-project and wanted to automatically add integrity hashes and nonces to the CSP header.

Then, why not using the enableCSPNonces or helmet function generate the nonce?
You can pass the generated nonce to the template library.

https://github.com/fastify/fastify-helmet?tab=readme-ov-file#content-security-policy-nonce

@Tam
Copy link
Author

Tam commented Jun 1, 2025

I'm adding some js tags to the twig.js templating library for a side-project and wanted to automatically add integrity hashes and nonces to the CSP header.

Then, why not using the enableCSPNonces or helmet function generate the nonce? You can pass the generated nonce to the template library.

https://github.com/fastify/fastify-helmet?tab=readme-ov-file#content-security-policy-nonce

That would work for inline scripts, but not for external ones. For example, if I were to add a tag like this:

{% js 'https://unpkg.com/[email protected]' with {
    integrity: 'sha256-4gndpcgjVHnzFm3vx3UOHbzVpcGAi3eS/C5nM3aPtEc=',
    crossorigin: 'anonymous',
} %}

I'd want it to automatically add the domain and integrity hash to the CSP header. It's mainly a developer QoL thing.

style: string;
},
helmet: (opts?: HelmetOptions) => typeof helmet
helmet: (opts?: HelmetOptions | ((opts: HelmetOptions) => HelmetOptions)) => typeof helmet
Copy link
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.

@Fdawgs Fdawgs requested a review from Copilot November 6, 2025 20:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for passing a function to the reply.helmet() decorator, allowing users to modify existing helmet configuration options programmatically rather than replacing them entirely.

Key changes:

  • Enhanced the reply.helmet() decorator to accept a function that receives current options and returns modified options
  • Added comprehensive test coverage for the new function-based API
  • Updated TypeScript type definitions to reflect the new signature

Reviewed Changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
index.js Updated reply.helmet to handle function parameters and added c8 ignore comment for error path
types/index.d.ts Extended helmet decorator type signature to accept function parameter
test/global.test.js Added test case verifying function-based options work correctly
README.md Added documentation example showing how to use function-based options

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.
// 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants