diff --git a/kork-plugins/src/test/kotlin/com/netflix/spinnaker/kork/plugins/SpringPluginStatusProviderTest.kt b/kork-plugins/src/test/kotlin/com/netflix/spinnaker/kork/plugins/SpringPluginStatusProviderTest.kt index d792f5789..062c978fc 100644 --- a/kork-plugins/src/test/kotlin/com/netflix/spinnaker/kork/plugins/SpringPluginStatusProviderTest.kt +++ b/kork-plugins/src/test/kotlin/com/netflix/spinnaker/kork/plugins/SpringPluginStatusProviderTest.kt @@ -46,6 +46,7 @@ class SpringPluginStatusProviderTest : JUnit5Minutests { subject.onApplicationEvent( ApplicationEnvironmentPreparedEvent( + mockk(relaxed = true), mockk(relaxed = true), arrayOf(), environment diff --git a/kork-swagger/kork-swagger.gradle b/kork-swagger/kork-swagger.gradle index 5d57e7d5c..8d5118bed 100644 --- a/kork-swagger/kork-swagger.gradle +++ b/kork-swagger/kork-swagger.gradle @@ -8,5 +8,6 @@ dependencies { implementation "com.google.guava:guava" implementation "org.springframework.boot:spring-boot-autoconfigure" implementation "io.springfox:springfox-boot-starter" + implementation "org.springframework:spring-webmvc" } diff --git a/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java b/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java new file mode 100644 index 000000000..de624674e --- /dev/null +++ b/kork-swagger/src/main/java/com/netflix/spinnaker/config/SpringfoxHandlerProviderBeanPostProcessor.java @@ -0,0 +1,77 @@ +/* + * Copyright 2024 OpsMx, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.config; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; +import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; + +/** + * With spring boot 2.6.x, default strategy for matching request paths against registered Spring MVC + * handler mappings has changed from AntPathMatcher to PathPatternParser. The actuator endpoints + * also use PathPattern based URL matching and path matching strategy cannot be configured for + * actuator endpoints via a configuration property. Using Actuator and Springfox may result in + * application failing to start. {@see https://github.com/springfox/springfox/issues/3462} + */ +@Component +public class SpringfoxHandlerProviderBeanPostProcessor implements BeanPostProcessor { + + /** + * Overrides BeanPostProcessor method postProcessAfterInitialization() to filter the + * springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider beans for request + * mapping. + * + * @param bean – the new bean instance + * @param beanName – the name of the bean + * @return the bean instance to use, either the original or a wrapped one; if null, no subsequent + * BeanPostProcessors will be invoked + */ + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof WebMvcRequestHandlerProvider) { + customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); + } + return bean; + } + + private void customizeSpringfoxHandlerMappings( + List mappings) { + List copy = + mappings.stream() + .filter(mapping -> mapping.getPatternParser() == null) + .collect(Collectors.toList()); + mappings.clear(); + mappings.addAll(copy); + } + + @SuppressWarnings("unchecked") + private List getHandlerMappings(Object bean) { + try { + Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); + field.setAccessible(true); + return (List) field.get(bean); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/kork-swagger/src/main/resources/META-INF/spring.factories b/kork-swagger/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..29d91a604 --- /dev/null +++ b/kork-swagger/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.netflix.spinnaker.config.SpringfoxHandlerProviderBeanPostProcessor diff --git a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/DefaultTomcatConnectorCustomizer.java b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/DefaultTomcatConnectorCustomizer.java index 1d19b8bf6..4158a5d11 100644 --- a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/DefaultTomcatConnectorCustomizer.java +++ b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/DefaultTomcatConnectorCustomizer.java @@ -52,10 +52,6 @@ class DefaultTomcatConnectorCustomizer implements TomcatConnectorCustomizer { public void customize(Connector connector) { this.applySSLSettings(connector); this.applyRelaxedURIProperties(connector); - if (tomcatConfigurationProperties.getRejectIllegalHeader() != null) { - ((AbstractHttp11Protocol) connector.getProtocolHandler()) - .setRejectIllegalHeader(tomcatConfigurationProperties.getRejectIllegalHeader()); - } } Ssl copySslConfigurationWithClientAuth(TomcatServletWebServerFactory tomcat) { diff --git a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfiguration.java b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfiguration.java index e17b560f7..3ced63259 100644 --- a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfiguration.java +++ b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfiguration.java @@ -46,6 +46,11 @@ class TomcatConfiguration { TomcatConnectorCustomizer defaultTomcatConnectorCustomizer( TomcatConfigurationProperties tomcatConfigurationProperties, SslExtensionConfigurationProperties sslExtensionConfigurationProperties) { + if (tomcatConfigurationProperties.getRejectIllegalHeader() != null) { + System.setProperty( + "server.tomcat.reject-illegal-header", + tomcatConfigurationProperties.getRejectIllegalHeader().toString()); + } return new DefaultTomcatConnectorCustomizer( tomcatConfigurationProperties, sslExtensionConfigurationProperties); } diff --git a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfigurationProperties.java b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfigurationProperties.java index 1f890711a..2cf4ef499 100644 --- a/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfigurationProperties.java +++ b/kork-tomcat/src/main/java/com/netflix/spinnaker/kork/tomcat/TomcatConfigurationProperties.java @@ -36,6 +36,8 @@ public class TomcatConfigurationProperties { private List cipherSuites = CipherSuites.getRecommendedCiphers(); + // This property maps to spring boot property server.tomcat.reject-illegal-header, + // which is true by default. private Boolean rejectIllegalHeader; public int getLegacyServerPort() { diff --git a/spinnaker-dependencies/spinnaker-dependencies.gradle b/spinnaker-dependencies/spinnaker-dependencies.gradle index 6576440ae..8955d2d8e 100644 --- a/spinnaker-dependencies/spinnaker-dependencies.gradle +++ b/spinnaker-dependencies/spinnaker-dependencies.gradle @@ -14,7 +14,7 @@ ext { gcp : "25.3.0", jsch : "0.1.54", jschAgentProxy : "0.0.9", - // spring boot 2.5.15 specifies logback 1.2.12. Pin to 1.2.13 to resolve + // spring boot 2.6.15 specifies logback 1.2.12. Pin to 1.2.13 to resolve // CVE-2023-6378 and CVE-2023-6481 until spring boot 3.1.7 which brings in // 1.4.14. See https://logback.qos.ch/news.html#1.3.12. logback : "1.2.13", @@ -28,12 +28,12 @@ ext { spectator : "1.0.6", spek : "1.1.5", spek2 : "2.0.9", - springBoot : "2.5.15", - springCloud : "2020.0.6", + springBoot : "2.6.15", + springCloud : "2021.0.8", springfoxSwagger : "3.0.0", swagger : "1.5.20", //this should stay in sync with what springfoxSwagger expects. - // Spring boot 2.5.15 and 2.6.15 bring in 9.0.75. 2.7.18 + // Spring boot 2.6.15 bring in 9.0.75. 2.7.18 // brings in 9.0.83, which fixes all CVEs to date (20-feb-24). // // See https://tomcat.apache.org/security-9.html for latest security fixes. @@ -181,7 +181,7 @@ dependencies { // pf4j:3.10.0 brings in slf4j-api:2.0.6 which is not compatible with logback 1.2.x. // And the upgraded logback version(1.3.8) is becoming incompatible with SpringBoot's LogbackLoggingSystem: // java.lang.NoClassDefFoundError at LogbackLoggingSystem.java:293 - // Hence pinning slf4j-api at 1.7.36 which spring boot 2.5.15 brings in. + // Hence pinning slf4j-api at 1.7.36 which spring boot 2.6.15 brings in. api("org.slf4j:slf4j-api"){ version { strictly("1.7.36")