Skip to content

Conversation

Copy link

Copilot AI commented Dec 16, 2025

  • Add class-level @disabled flag to Configuration
  • Add Configuration.disable! class method to set the flag
  • Add Configuration.disabled? class method to check the flag
  • Modify Configuration.default_config to return NOOP config when disabled
  • Add tests for Configuration.disable!
  • Add tests for middleware behavior when disabled
  • Run existing tests to ensure no regressions
  • Clean up vendor/bundle from repository
  • Document the new feature
  • Refactor to eliminate code duplication in NOOP config creation
  • Address code review feedback (edge cases and consistency)
    • Register NOOP_OVERRIDE in disable! method
    • Move clear_disabled to class << self block
    • Add comprehensive edge case test coverage
  • Make disable! and default mutually exclusive
    • disable! raises AlreadyConfiguredError if default was called
    • default raises AlreadyConfiguredError if disable! was called
  • Revert .gitignore changes
  • Document startup-time configuration requirement
Original prompt

This section details on the original issue you should resolve

<issue_title>Add simple static configuration option for bypassing application of all security headers</issue_title>
<issue_description>Is there a way to enable this gem in an initializer completely?

F.e. we host our app for different customers, but due to various reasons we want to upgrade manually.

So I would like to enable secure_headers at runtime, like f.e:

if ENV["ENABLE_STRICT_HEADERS"]
  SecureHeaders::Configuration.default do |config|
    ...
  end
else
  SecureHeaders::Configuration.disable! # <= (or something like this)
end

I also don't want to opt out the settings, I completely want to disable secure headers (since sometimes we have set special headers by ourself and we will have to migrate them to secure headers first).</issue_description>

<agent_instructions>It would be nice to disable this gem completely via a configuration, as mentioned in the post can you implemented "SecureHeaders::Configuration.disable!"

When that is set no secure headers should be sent (e.g. secure_headers is disabled/does nothing)</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Add simple static configuration option for bypassing security headers Add Configuration.disable! to completely disable secure_headers Dec 16, 2025
Copilot AI requested a review from fletchto99 December 16, 2025 23:15
@fletchto99
Copy link
Member

@copilot can you rebase this PR onto the feature-7.2 branch?

Copy link
Author

Copilot AI commented Dec 17, 2025

@copilot can you rebase this PR onto the feature-7.2 branch?

I've successfully rebased the PR onto the feature-7.2 branch (commit 3186de1). All 263 tests pass after the rebase.

The branch is ready but requires a force push to update the remote. Since I don't have permission to force push directly, could you please run:

git push --force-with-lease origin copilot/add-static-config-option

The rebased commits now include all changes from feature-7.2 including the recent Rake task refactoring, test fixes, and domain normalization changes.

Copilot AI and others added 5 commits December 17, 2025 14:50
Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
@fletchto99 fletchto99 force-pushed the copilot/add-static-config-option branch from f76080a to b93ac8d Compare December 17, 2025 21:50
@fletchto99 fletchto99 changed the base branch from main to feature-7.2 December 17, 2025 21:51
@fletchto99 fletchto99 marked this pull request as ready for review December 18, 2025 19:50
Copilot AI review requested due to automatic review settings December 18, 2025 19:50
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 introduces a runtime disable mechanism for the secure_headers gem, allowing applications to conditionally bypass all security header generation without requiring configuration. This addresses the need for gradual rollouts and per-deployment control.

Key Changes:

  • Added Configuration.disable! and Configuration.disabled? methods for runtime control
  • Modified default_config to return a NOOP configuration when disabled, bypassing NotYetConfiguredError
  • Refactored NOOP configuration creation to eliminate code duplication between disable! and NOOP_OVERRIDE

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
lib/secure_headers/configuration.rb Core implementation of disable!/disabled? methods, default_config bypass logic, and NOOP refactoring
spec/lib/secure_headers/configuration_spec.rb Unit tests for disable! functionality and disabled state verification
spec/lib/secure_headers_spec.rb Integration test verifying headers are not set when disabled
spec/lib/secure_headers/middleware_spec.rb Middleware tests confirming no headers or cookie flags when disabled
spec/spec_helper.rb Added clear_disabled helper method and integrated into reset_config
README.md Documentation for the disable! feature with usage examples and use cases
.gitignore Added vendor/bundle to ignore list
Comments suppressed due to low confidence (1)

lib/secure_headers/configuration.rb:44

  • The default method will fail when called after disable! has been invoked. The override(NOOP_OVERRIDE, ...) call at line 39 will work fine, but if @disabled is true, the NOOP_OVERRIDE override will be registered. However, there's no check to prevent calling default after disable!, which could lead to confusing behavior where the library is supposed to be disabled but a default config gets set.

Consider either:

  1. Raising an error if default is called after disable!
  2. Clearing the @disabled flag and @noop_config when default is called (re-enabling the library)
  3. Making the disabled state override any subsequent configuration attempts
      def default(&block)
        if defined?(@default_config)
          raise AlreadyConfiguredError, "Policy already configured"
        end

        # Define a built-in override that clears all configuration options and
        # results in no security headers being set.
        override(NOOP_OVERRIDE, &method(:create_noop_config_block))

        new_config = new(&block).freeze
        new_config.validate_config!
        @default_config = new_config
      end

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

@fletchto99
Copy link
Member

@copilot Can you address the feedback in this thread: #568 (review)

- Register NOOP_OVERRIDE in disable! to avoid ArgumentError
- Clear @default_config when disable! is called after default
- Move clear_disabled inside class << self for consistency
- Add comprehensive edge case tests (calling disable! after default, default after disable!, interaction with overrides/dup)
- Document behavior in method comments

Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Copy link
Author

Copilot AI commented Dec 18, 2025

@copilot Can you address the feedback in this thread: #568 (review)

Addressed all feedback from the review (commit 8f188da):

Issue 1 - NOOP_OVERRIDE registration: Now registers NOOP_OVERRIDE in disable! to ensure opt_out_of_all_protection works even when called without default.

Issue 2 - Inconsistent state: Now clears @default_config when disable! is called after Configuration.default to maintain consistency.

Issue 3 - Test helper location: Moved clear_disabled inside the class << self block for consistency with other clearing methods.

Issue 4 - Missing test coverage: Added 6 comprehensive edge case tests covering:

  • Calling disable! after default has been configured
  • Calling default after disable! has been invoked
  • Interaction with override mechanism (opt_out_of_all_protection)
  • Interaction with named overrides
  • dup behavior when disabled
  • NOOP_OVERRIDE registration

All 269 tests pass.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • coveralls.io
    • Triggering command: /usr/bin/ruby3.2 ruby3.2 /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/bin/rspec spec/lib/secure_headers/configuration_spec.rb -e disable by-3.2.0 .3-1ubuntu0.24.0/usr/include/ruby-3.2.0/ruby/backward ism-1.7.0/include (dns block)
    • Triggering command: /usr/bin/ruby3.2 ruby3.2 /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/bin/rspec spec/lib/secure_headers/configuration_spec.rb -e disable -I /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/gems/prism-1.7.0/ext --gdwarf-5 --64 -o regexp.o /tmp/cc1qPPY9.s conf�� by-3.2.0 .3-1ubuntu0.24.0/usr/include/ruby-3.2.0/ruby/backward ism-1.7.0/include credential.helpeas ism-1.7.0/ext (dns block)
    • Triggering command: /usr/bin/ruby3.2 ruby3.2 /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/bin/rspec spec/lib/secure_headers/configuration_spec.rb -e disable by-3.2.0 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@rei-moo rei-moo self-requested a review December 19, 2025 15:06
@fletchto99
Copy link
Member

@copilot A call to disable! after a call to default should raise a configuration error indicating SecureHeaders has already been configured and cannot be disabled. Likewise a call to default after disable! should raise a configuration error indication SecureHeaders has been disabled and cannot be configured.

- disable! now raises AlreadyConfiguredError if default has been called
- default now raises AlreadyConfiguredError if disable! has been called
- Updated tests to verify error conditions
- Updated documentation to reflect mutual exclusion

Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Copy link
Author

Copilot AI commented Dec 19, 2025

@copilot A call to disable! after a call to default should raise a configuration error indicating SecureHeaders has already been configured and cannot be disabled. Likewise a call to default aft...

Implemented mutual exclusion (commit af7f61b):

disable! after default: Now raises AlreadyConfiguredError: "Configuration already set, cannot disable"

default after disable!: Now raises AlreadyConfiguredError: "Configuration has been disabled, cannot set default"

Added tests for both error conditions. All 270 tests pass.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • coveralls.io
    • Triggering command: /usr/bin/ruby3.2 ruby3.2 /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/bin/rspec spec/lib/secure_headers/configuration_spec.rb -e disable by-3.2.0 .3-1ubuntu0.24.0/usr/include/ruby-3.2.0/ruby/backward ism-1.7.0/include (dns block)
    • Triggering command: /usr/bin/ruby3.2 ruby3.2 /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/bin/rspec . -I /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/gems/prism-1.7.0/include -I /home/REDACTED/work/secure_headers/secure_headers/vendor/bundle/ruby/3.2.0/gems/prism-1.7.0/ext --gdwarf-5 --64 -o regexp.o /tmp/ccSiW3mB.s conf�� by-3.2.0 .3-1ubuntu0.24.0/usr/include/ruby-3.2.0/ruby/backward ism-1.7.0/include user.name ism-1.7.0/ext (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@fletchto99
Copy link
Member

@copilot Can you undo any changes to the .gitignore. Don't make any other changes.

Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Copy link
Author

Copilot AI commented Dec 19, 2025

@copilot Can you undo any changes to the .gitignore. Don't make any other changes.

Reverted .gitignore changes (commit 5ab4183). The file is now back to its original state without the vendor/bundle entry.

Copy link
Member

@fletchto99 fletchto99 left a comment

Choose a reason for hiding this comment

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

Looks good from me, I'll get one more review from prodsec before merging into the 7.2 feature branch

@fletchto99 fletchto99 mentioned this pull request Dec 19, 2025

```ruby
if ENV["ENABLE_STRICT_HEADERS"]
SecureHeaders::Configuration.default do |config|
Copy link
Contributor

Choose a reason for hiding this comment

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

since this is a configuration setting, this can only be done at server startup right? I think that's implied but would it be valuable to make that clearer in case someone gets the idea that they could disable this during runtime? I could go either way on whether or not that's overkill

Copy link
Member

Choose a reason for hiding this comment

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

Good point. I think that's worth documenting.

Copy link
Member

Choose a reason for hiding this comment

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

I've also made it so that they're mutually exclusive preventing one from trying to configure and then disable.

Copy link
Contributor

@rei-moo rei-moo left a comment

Choose a reason for hiding this comment

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

Look OK, although we'll need to do some manual testing before the 7.2 release to make sure there aren't any strange quirks that come along with this change.


However, I would consider these headers anyways depending on your load and bandwidth requirements.

## Disabling secure_headers
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think this is important enough to be in the README or would it make sense to document it elsewhere?

@fletchto99
Copy link
Member

@copilot can you update the readme instructions to indicate it can only done during startup. Either it's disabled or configured but cannot be changed later.

Clarify in README that Configuration.disable! and Configuration.default must be called during application startup and cannot be changed at runtime.

Co-authored-by: fletchto99 <718681+fletchto99@users.noreply.github.com>
Copy link
Author

Copilot AI commented Dec 19, 2025

@copilot can you update the readme instructions to indicate it can only done during startup. Either it's disabled or configured but cannot be changed later.

Updated README to clarify startup-time configuration (commit 6a78f95). Added documentation that Configuration.disable! and Configuration.default must be called during application startup and cannot be changed at runtime, with a note that attempting to call one after the other will raise an AlreadyConfiguredError.

@fletchto99 fletchto99 merged commit 2967cd5 into feature-7.2 Dec 19, 2025
12 checks passed
@fletchto99 fletchto99 deleted the copilot/add-static-config-option branch December 19, 2025 19:47
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.

Add simple static configuration option for bypassing application of all security headers

4 participants