Skip to content

Add default configurable Grails security response headers#15967

Open
jamesfredley wants to merge 1 commit into
8.0.xfrom
feat/default-security-headers
Open

Add default configurable Grails security response headers#15967
jamesfredley wants to merge 1 commit into
8.0.xfrom
feat/default-security-headers

Conversation

@jamesfredley

Copy link
Copy Markdown
Contributor

Description

What was found

Problem Impact
Grails apps do not set common security headers by default Missing X-Content-Type-Options, X-Frame-Options, Referrer-Policy, etc.
Google Doc 2.1 listed framework default security headers as not covered No existing open PR for this
Apps that use Spring Security still benefit from baseline headers when plugin is absent Framework should provide safe defaults with opt-out

What changed

Area Change
Filter GrailsSecurityHeadersFilter (OncePerRequestFilter)
Auto-config GrailsSecurityHeadersAutoConfiguration with @ConditionalOnMissingBean
Defaults X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy: strict-origin-when-cross-origin, X-XSS-Protection: 0
HSTS Only applied on secure requests when configured
CSP Disabled by default; set via config when desired
Config grails.security.headers.* enable/disable and per-header values
Docs / tests Security guide, upgrade notes, auto-config specs

Out of scope / follow-up

Topic Status
Full CSP policy templates Follow-up
Permissions-Policy defaults Follow-up
Spring Security header interaction matrix Document if conflicts appear

Related MD topics

Source Topic
Google Doc 2.1 Default framework security headers
Status map Not covered

Contributor Checklist

Issue and Scope

  • Background explains the security-headers gap.
  • Scoped to default response headers only.
  • Single focused feature seed.
  • Targets 8.0.x.

Code Quality

  • Tests cover enable/disable and defaults.
  • Focused controllers tests intended for CI.
  • No mass reformatting.
  • AI starting point labeled.

Licensing and Attribution

  • Apache License 2.0.
  • Contributor rights confirmed.
  • ai-generated-starting-point label applied.

Documentation

  • User-facing docs updated.
  • PR description explains what changed and why.

Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]

Register a OncePerRequestFilter for X-Content-Type-Options,
X-Frame-Options, Referrer-Policy, optional HSTS on secure requests,
and optional CSP. Configurable via grails.security.headers.* with
opt-out and ConditionalOnMissingBean.

Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]

Copilot AI left a comment

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.

Pull request overview

This PR introduces a default, configurable servlet filter in grails-controllers that applies baseline browser-hardening response headers for Grails 8 servlet web applications, along with auto-configuration, configuration metadata, tests, and documentation updates in the user guide and upgrade notes.

Changes:

  • Added GrailsSecurityHeadersFilter plus GrailsSecurityHeadersProperties and a servlet-only Boot auto-configuration to register it by default (with opt-out via grails.security.headers.*).
  • Added configuration metadata for IDE/property hinting and documented the defaults + customization in the Security guide and Grails 8 upgrade notes.
  • Added a Spock spec verifying auto-config behavior, default headers, per-header disablement, overrides, and secure-request HSTS behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
grails-doc/src/en/guide/upgrading/upgrading80x.adoc Adds Grails 8 upgrade note about the new default security headers filter and how to disable/configure it.
grails-doc/src/en/guide/security.adoc Documents default headers, rationale for HSTS/CSP being opt-in, and example application.yml configuration.
grails-controllers/src/test/groovy/org/grails/plugins/web/controllers/GrailsSecurityHeadersAutoConfigurationSpec.groovy Adds test coverage for auto-configuration and header application behavior.
grails-controllers/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports Registers the new auto-configuration class for Spring Boot discovery.
grails-controllers/src/main/resources/META-INF/additional-spring-configuration-metadata.json Adds Spring configuration metadata entries for grails.security.headers.* properties.
grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/GrailsSecurityHeadersProperties.java Introduces bindable configuration properties and defaults for each supported header.
grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/GrailsSecurityHeadersFilter.java Implements the OncePerRequestFilter that applies headers conditionally based on configuration and existing response headers.
grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/GrailsSecurityHeadersAutoConfiguration.java Adds servlet-only auto-config with conditional registration and a FilterRegistrationBean for ordering.

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

Comment on lines +42 to +50
applyHeader(response, "X-Content-Type-Options", properties.getContentTypeOptions());
applyHeader(response, "X-Frame-Options", properties.getFrameOptions());
applyHeader(response, "Referrer-Policy", properties.getReferrerPolicy());
applyHeader(response, "X-XSS-Protection", properties.getXssProtection());
if (request.isSecure()) {
applyHeader(response, "Strict-Transport-Security", properties.getHsts());
}
applyHeader(response, "Content-Security-Policy", properties.getContentSecurityPolicy());
filterChain.doFilter(request, response);
@bito-code-review

Copy link
Copy Markdown

The proposed change to move header application into a finally block is technically sound for ensuring that downstream components have the first opportunity to set headers. By applying defaults only if the headers are missing after the filter chain completes, you prevent the filter from prematurely setting values that might conflict with or override specific requirements set by controllers or other filters. This approach effectively balances providing sensible defaults with allowing granular overrides.

grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/GrailsSecurityHeadersFilter.java

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } finally {
            applyHeader(response, "X-Content-Type-Options", properties.getContentTypeOptions());
            // ... apply other headers
        }
    }

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.30769% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.6335%. Comparing base (06f5567) to head (4bc02bf).

Files with missing lines Patch % Lines
...b/controllers/GrailsSecurityHeadersProperties.java 58.5366% 17 Missing ⚠️
...s/web/controllers/GrailsSecurityHeadersFilter.java 93.7500% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##                8.0.x     #15967        +/-   ##
==================================================
+ Coverage     49.6039%   49.6335%   +0.0296%     
- Complexity      16956      16974        +18     
==================================================
  Files            1999       2002         +3     
  Lines           93791      93856        +65     
  Branches        16421      16423         +2     
==================================================
+ Hits            46524      46584        +60     
- Misses          40100      40102         +2     
- Partials         7167       7170         +3     
Files with missing lines Coverage Δ
...ollers/GrailsSecurityHeadersAutoConfiguration.java 100.0000% <100.0000%> (ø)
...s/web/controllers/GrailsSecurityHeadersFilter.java 93.7500% <93.7500%> (ø)
...b/controllers/GrailsSecurityHeadersProperties.java 58.5366% <58.5366%> (ø)

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

testlens-app Bot commented Jul 10, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 4bc02bf
▶️ Tests: 24504 executed
⚪️ Checks: 61/61 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants