From c66ae24f5edc1012797fb678c5049e56ae92c55c Mon Sep 17 00:00:00 2001 From: Ian Sergeant Date: Fri, 28 Aug 2026 14:21:55 +0200 Subject: [PATCH 1/4] Add policy directives for enhanced security Signed-off-by: Ian Sergeant --- .../org/zowe/apiml/WebSecurityConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java b/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java index 7fe04c734f..732cc25525 100644 --- a/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java +++ b/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java @@ -34,11 +34,13 @@ import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint; import org.springframework.security.web.server.authentication.logout.HttpStatusReturningServerLogoutSuccessHandler; import org.springframework.security.web.server.authorization.AuthorizationContext; +import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter; import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; import org.zowe.apiml.constants.ApimlConstants; import org.zowe.apiml.filter.*; import org.zowe.apiml.gateway.filters.security.AuthExceptionHandlerReactive; @@ -723,4 +725,23 @@ SecurityWebFilterChain gatewayAuthenticatedEndpoints(ServerHttpSecurity http, Au .build(); } + @Bean + @Order(0) + SecurityWebFilterChain apiCatalogUiSecurityFilterChain(ServerHttpSecurity http) { + return http + .securityMatcher(ServerWebExchangeMatchers.pathMatchers( + "/apicatalog/ui/v1/**", + "/apicatalog/ui/v1/index.html" + )) + .csrf(ServerHttpSecurity.CsrfSpec::disable) + .headers(headers -> headers + .hsts(ServerHttpSecurity.HeaderSpec.HstsSpec::disable) + .writer(new CustomHstsServerHttpHeadersWriter()) + .frameOptions(spec -> spec.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)) + .contentSecurityPolicy(csp -> csp + .policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; connect-src 'self'; frame-ancestors 'self';") ) + ) + .authorizeExchange(exchanges -> exchanges.anyExchange().permitAll()) + .build(); + } } From efb785a6c3ede77c8408cb446aa50ac66b0ba1ee Mon Sep 17 00:00:00 2001 From: Ian Sergeant Date: Fri, 28 Aug 2026 16:39:30 +0200 Subject: [PATCH 2/4] Add policy directives for enhanced security for multi-service Signed-off-by: Ian Sergeant --- .../zowe/apiml/apicatalog/config/SecurityConfiguration.java | 6 +++++- apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/SecurityConfiguration.java b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/SecurityConfiguration.java index c515535999..f771ddb137 100644 --- a/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/SecurityConfiguration.java +++ b/api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/config/SecurityConfiguration.java @@ -251,7 +251,11 @@ private ServerHttpSecurity baseConfiguration( .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.hsts(ServerHttpSecurity.HeaderSpec.HstsSpec::disable) .writer(new CustomHstsServerHttpHeadersWriter()) - .frameOptions(spec -> spec.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN))) + .frameOptions(spec -> spec.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)) + .contentSecurityPolicy(csp -> csp + .policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; connect-src 'self'; frame-ancestors 'self';") + ) + ) .exceptionHandling(exceptionHandlingSpec -> exceptionHandlingSpec .authenticationEntryPoint((exchange, exception) -> { String requestedPath = exchange.getRequest().getPath().toString(); diff --git a/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java b/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java index 732cc25525..13b4cc7d77 100644 --- a/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java +++ b/apiml/src/main/java/org/zowe/apiml/WebSecurityConfig.java @@ -739,7 +739,8 @@ SecurityWebFilterChain apiCatalogUiSecurityFilterChain(ServerHttpSecurity http) .writer(new CustomHstsServerHttpHeadersWriter()) .frameOptions(spec -> spec.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)) .contentSecurityPolicy(csp -> csp - .policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; connect-src 'self'; frame-ancestors 'self';") ) + .policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; connect-src 'self'; frame-ancestors 'self';") + ) ) .authorizeExchange(exchanges -> exchanges.anyExchange().permitAll()) .build(); From 3b63638df6634fb8810321d512b52b1fe9e1b9f0 Mon Sep 17 00:00:00 2001 From: Ian Sergeant Date: Mon, 31 Aug 2026 15:53:41 +0200 Subject: [PATCH 3/4] Add test for content security policy Signed-off-by: Ian Sergeant --- .../ApiCatalogUiSecurityHeaderTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java new file mode 100644 index 0000000000..c31c6063f0 --- /dev/null +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java @@ -0,0 +1,48 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ +package org.zowe.apiml.apicatalog; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; + +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@AutoConfigureWebTestClient +class ApiCatalogUiSecurityHeaderTest { + + @Autowired + private WebTestClient webTestClient; + private static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy"; + + @Test + @DisplayName("Should serve Content-Security-Policy header on API Catalog UI index page") + void shouldReturnContentSecurityPolicyHeaderForUiIndex() { + webTestClient.get() + .uri("/apicatalog/ui/v1/index.html") + .exchange() + .expectStatus().isOk() + .expectHeader().valueMatches(CONTENT_SECURITY_POLICY, ".*default-src 'self'.*") + .expectHeader().valueEquals("X-Frame-Options", "SAMEORIGIN") + .expectHeader().valueEquals("X-Content-Type-Options", "nosniff"); + } + + @Test + @DisplayName("Should serve Content-Security-Policy header on API Catalog UI root path") + void shouldReturnContentSecurityPolicyHeaderForUiRootPath() { + webTestClient.get() + .uri("/apicatalog/ui/v1/") + .exchange() + .expectHeader().valueMatches(CONTENT_SECURITY_POLICY, ".*default-src 'self'.*"); + } +} From b52cd3e7cfd9d42cf0df35ec0adde55ff100d15f Mon Sep 17 00:00:00 2001 From: Ian Sergeant Date: Mon, 31 Aug 2026 16:04:06 +0200 Subject: [PATCH 4/4] Corrected licence format Signed-off-by: Ian Sergeant --- .../zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java index c31c6063f0..7cd8cf08f3 100644 --- a/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java +++ b/api-catalog-services/src/test/java/org/zowe/apiml/apicatalog/ApiCatalogUiSecurityHeaderTest.java @@ -7,6 +7,7 @@ * * Copyright Contributors to the Zowe Project. */ + package org.zowe.apiml.apicatalog; import org.junit.jupiter.api.DisplayName;