SecureHeaders filter at route level is not working for downloadOptions and permissionPolicy with 2025.0.0
Describe the bug
Issue 1: SecureHeaders filter at route level is not working for downloadOptions and permissionPolicy
Works with springCloudVersion: 2024.0.2
spring:
cloud:
gateway:
routes:
- id: api-testing
uri: http://localhost:8081
order: 1
predicates:
- Path=/gateway/api/json/**
filters:
- RewritePath=/gateway/(?<segment>.*), /$\{segment}
- name: SecureHeaders
args:
xssProtectionHeader: route_level
strictTransportSecurity: route_level
contentTypeOptions: route_level
frameOptions: route_level
referrerPolicy: route_level
contentSecurityPolicy: route_level
downloadOptions: route_level
permittedCrossDomainPolicies: route_level
$ curl http://localhost:8080/gateway/api/json/realm -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> GET /gateway/api/json/realm HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: application/json
< Matched-Stub-Id: 7f4f6c07-265a-4a95-bf89-82ea01bd919f
< Vary: Accept-Encoding, User-Agent
< X-Xss-Protection: route_level
< Strict-Transport-Security: route_level
< X-Frame-Options: route_level
< X-Content-Type-Options: route_level
< Referrer-Policy: route_level
< Content-Security-Policy: route_level
< X-Download-Options: route_level
< X-Permitted-Cross-Domain-Policies: route_level
Not Working with springCloudVersion: 2025.0.0
The above route configuration doesn't work, so I migrated it as follows:
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: api-testing
uri: http://localhost:8081
order: 1
predicates:
- Path=/gateway/api/json/**
filters:
- RewritePath=/gateway/(?<segment>.*), /$\{segment}
- name: SecureHeaders
args:
xssProtectionHeaderValue: route_level
strictTransportSecurityHeaderValue: route_level
frameOptionsHeaderValue: route_level
contentTypeOptionsHeaderValue: route_level
referrerPolicyHeaderValue: route_level
contentSecurityPolicyHeaderValue: route_level
downloadOptionsHeaderValue: route_level
permittedCrossDomainPoliciesHeaderValue: route_level
permissionPolicyHeaderValue: route_level
$ curl http://localhost:8080/gateway/api/json/realm -v
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* using HTTP/1.x
> GET /gateway/api/json/realm HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: application/json
< Matched-Stub-Id: 7f4f6c07-265a-4a95-bf89-82ea01bd919f
< Vary: Accept-Encoding, User-Agent
< X-Xss-Protection: route_level
< Strict-Transport-Security: route_level
< X-Frame-Options: route_level
< X-Content-Type-Options: route_level
< Referrer-Policy: route_level
< Content-Security-Policy: route_level
< X-Download-Options: noopen
< X-Permitted-Cross-Domain-Policies: route_level
As observed, the X-Download-Options header is using the default value instead of the one configured at the route level. Additionally, the Permissions-Policy header is missing from the response.
Issue 2: The differing property names between SecureHeadersGatewayFilterFactory.Config and SecureHeadersProperties make the configuration somewhat confusing
SecureHeadersProperties:
private String xssProtectionHeader = X_XSS_PROTECTION_HEADER_DEFAULT;
private String strictTransportSecurity = STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT;
private String frameOptions = X_FRAME_OPTIONS_HEADER_DEFAULT;
private String contentTypeOptions = X_CONTENT_TYPE_OPTIONS_HEADER_DEFAULT;
private String referrerPolicy = REFERRER_POLICY_HEADER_DEFAULT;
private String contentSecurityPolicy = CONTENT_SECURITY_POLICY_HEADER_DEFAULT;
private String downloadOptions = X_DOWNLOAD_OPTIONS_HEADER_DEFAULT;
private String permittedCrossDomainPolicies = X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER_DEFAULT;
private String permissionsPolicy = PERMISSIONS_POLICY_HEADER_OPT_IN_DEFAULT;
SecureHeadersGatewayFilterFactory.Config:
private String routePermissionsPolicyHeaderValue;
private String xssProtectionHeaderValue;
private String strictTransportSecurityHeaderValue;
private String frameOptionsHeaderValue;
private String contentTypeOptionsHeaderValue;
private String referrerPolicyHeaderValue;
private String contentSecurityPolicyHeaderValue;
private String downloadOptionsHeaderValue;
private String permittedCrossDomainPoliciesHeaderValue;
private String permissionPolicyHeaderValue;
Also, it's strange we have two properties for permissionPolicy, routePermissionsPolicyHeaderValue and permissionPolicyHeaderValue, neither of which work.