diff --git a/build.gradle.kts b/build.gradle.kts index 61a3bcd74..a4f84239e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -205,6 +205,7 @@ dependencies { testRuntimeOnly("org.springframework:spring-beans:latest.release") testRuntimeOnly("org.springframework:spring-context:latest.release") testRuntimeOnly("org.springframework:spring-web:latest.release") + testRuntimeOnly("org.springframework:spring-webmvc:latest.release") testRuntimeOnly("org.springframework:spring-test:latest.release") testRuntimeOnly("org.springframework.boot:spring-boot-test:latest.release") testRuntimeOnly("org.springframework.boot:spring-boot-test-autoconfigure:latest.release") diff --git a/src/main/java/org/openrewrite/java/spring/cve/Spring4Shell.java b/src/main/java/org/openrewrite/java/spring/cve/Spring4Shell.java new file mode 100644 index 000000000..274b99edc --- /dev/null +++ b/src/main/java/org/openrewrite/java/spring/cve/Spring4Shell.java @@ -0,0 +1,112 @@ +/* + * Copyright 2021 the original author or authors. + *
+ * 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 + *
+ * https://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 org.openrewrite.java.spring.cve;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.JavaVisitor;
+import org.openrewrite.java.search.FindAnnotations;
+import org.openrewrite.java.search.FindTypes;
+import org.openrewrite.java.search.UsesType;
+import org.openrewrite.java.tree.J;
+
+import java.util.Set;
+import java.util.function.Supplier;
+
+public class Spring4Shell extends Recipe {
+ @Override
+ public String getDisplayName() {
+ return "Spring4Shell fix";
+ }
+
+ @Override
+ public String getDescription() {
+ return "See the [blog post](https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement#status) on the issue. This recipe can be further refined as more information becomes available.";
+ }
+
+ @Override
+ protected JavaVisitor
+ * 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
+ *
+ * https://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.
+ */
+@NonNullApi @NonNullFields
+package org.openrewrite.java.spring.cve;
+
+import org.openrewrite.internal.lang.NonNullApi;
+import org.openrewrite.internal.lang.NonNullFields;
diff --git a/src/main/resources/Spring4Shell.java b/src/main/resources/Spring4Shell.java
new file mode 100644
index 000000000..c2ad92736
--- /dev/null
+++ b/src/main/resources/Spring4Shell.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.bind.ServletRequestDataBinder;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
+import org.springframework.web.method.support.InvocableHandlerMethod;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
+import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
+
+class Fix {
+ @Bean
+ public WebMvcRegistrations mvcRegistrations() {
+ return new WebMvcRegistrations() {
+ @Override
+ public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
+ return new ExtendedRequestMappingHandlerAdapter();
+ }
+ };
+ }
+
+private static class ExtendedRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
+ @Override
+ protected InitBinderDataBinderFactory createDataBinderFactory(List
+ * 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
+ *
+ * https://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 org.openrewrite.java.spring.org.openrewrite.java.spring.cve
+
+import org.junit.jupiter.api.Test
+import org.openrewrite.Recipe
+import org.openrewrite.java.JavaParser
+import org.openrewrite.java.JavaRecipeTest
+import org.openrewrite.java.spring.cve.Spring4Shell
+
+class Spring4ShellTest : JavaRecipeTest {
+ override val parser: JavaParser
+ get() = JavaParser.fromJavaVersion()
+ .logCompilationWarningsAndErrors(true)
+ .classpath("spring-beans", "spring-boot", "spring-context")
+ .build()
+
+ override val recipe: Recipe
+ get() = Spring4Shell()
+
+ @Test
+ fun spring4Shell() = assertChanged(
+ before = """
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+ import org.springframework.context.annotation.Bean;
+
+ @SpringBootApplication
+ class Test {
+ @Bean
+ String existingBean() {
+ return "hi";
+ }
+ }
+ """,
+ after = """
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
+ import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
+ import org.springframework.context.annotation.Bean;
+ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
+
+ @SpringBootApplication
+ class Test {
+ @Bean
+ String existingBean() {
+ return "hi";
+ }
+
+ @Bean
+ public WebMvcRegistrations mvcRegistrations() {
+ return new WebMvcRegistrations() {
+ @Override
+ public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
+ return null;
+ }
+ };
+ }
+ }
+ """,
+ )
+}