diff --git a/MODULE.bazel b/MODULE.bazel
index 9313f6cce..d3eabf2ba 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -136,6 +136,9 @@ VULNERABLE_TEST_MAVEN_ARTIFACTS = [
"org.apache.xmlgraphics:batik-xml:1.14",
"org.glassfish:javax.el:3.0.1-b06",
"org.hibernate:hibernate-validator:5.2.4.Final",
+ "org.springframework.cloud:spring-cloud-function-context:3.1.6",
+ "org.springframework.cloud:spring-cloud-function-core:3.1.6",
+ "org.springframework:spring-messaging:6.1.4",
]
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel
index 04eac4178..1ffd0e353 100644
--- a/examples/BUILD.bazel
+++ b/examples/BUILD.bazel
@@ -334,6 +334,26 @@ java_fuzz_target_test(
],
)
+java_fuzz_target_test(
+ name = "SpringCloudFunctionRoutingFuzzer",
+ srcs = [
+ "src/main/java/com/example/SpringCloudFunctionRoutingFuzzer.java",
+ ],
+ allowed_findings = [
+ "com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh",
+ ],
+ fuzzer_args = ["-runs=100000"],
+ tags = ["no-jdk8"],
+ target_class = "com.example.SpringCloudFunctionRoutingFuzzer",
+ verify_crash_reproducer = False,
+ deps = [
+ "@maven//:com_fasterxml_jackson_core_jackson_databind",
+ "@maven//:org_springframework_cloud_spring_cloud_function_context",
+ "@maven//:org_springframework_cloud_spring_cloud_function_core",
+ "@maven//:org_springframework_spring_messaging",
+ ],
+)
+
java_fuzz_target_test(
name = "JacksonCborFuzzer",
srcs = [
diff --git a/examples/src/main/java/com/example/SpringCloudFunctionRoutingFuzzer.java b/examples/src/main/java/com/example/SpringCloudFunctionRoutingFuzzer.java
new file mode 100644
index 000000000..6eb058df0
--- /dev/null
+++ b/examples/src/main/java/com/example/SpringCloudFunctionRoutingFuzzer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 Code Intelligence GmbH
+ *
+ * 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.example;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import org.springframework.cloud.function.context.FunctionProperties;
+import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry;
+import org.springframework.cloud.function.context.config.RoutingFunction;
+import org.springframework.cloud.function.json.JacksonMapper;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.converter.CompositeMessageConverter;
+import org.springframework.messaging.converter.StringMessageConverter;
+import org.springframework.messaging.support.MessageBuilder;
+
+/**
+ * Reproduce CVE-2022-22963 by fuzzing the
+ * routing-expression header in Spring Cloud Function.
+ */
+public class SpringCloudFunctionRoutingFuzzer {
+ private static RoutingFunction router;
+
+ public static void fuzzerInitialize() {
+ LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE);
+ // Empty function registry
+ SimpleFunctionRegistry registry =
+ new SimpleFunctionRegistry(
+ null,
+ new CompositeMessageConverter(Arrays.asList(new StringMessageConverter())),
+ new JacksonMapper(new ObjectMapper()));
+ router = new RoutingFunction(registry, new FunctionProperties());
+ }
+
+ public static void fuzzerTestOneInput(String payload, String expr) {
+ try {
+ Message message =
+ MessageBuilder.withPayload(payload)
+ .setHeader("spring.cloud.function.routing-expression", expr)
+ .build();
+ router.apply(message);
+ } catch (Throwable ignored) {
+ // Most inputs will cause parsing or routing errors; that is fine.
+ }
+ }
+}
diff --git a/maven_install.json b/maven_install.json
index dcd73dc46..b45173996 100755
--- a/maven_install.json
+++ b/maven_install.json
@@ -1,7 +1,7 @@
{
"__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
- "__INPUT_ARTIFACTS_HASH": 22269842,
- "__RESOLVED_ARTIFACTS_HASH": 236949396,
+ "__INPUT_ARTIFACTS_HASH": 1534174847,
+ "__RESOLVED_ARTIFACTS_HASH": 1096484833,
"conflict_resolution": {
"com.google.code.gson:gson:2.8.6": "com.google.code.gson:gson:2.8.9",
"com.google.j2objc:j2objc-annotations:2.8": "com.google.j2objc:j2objc-annotations:3.1",
@@ -188,6 +188,12 @@
},
"version": "1.12.3"
},
+ "io.projectreactor:reactor-core": {
+ "shasums": {
+ "jar": "12ff3eae092bf07f6651a93b9292a87fbee7f5b9ad4c9cc01189b1a4f0979973"
+ },
+ "version": "3.4.12"
+ },
"jakarta.el:jakarta.el-api": {
"shasums": {
"jar": "7e84b5bed49de32b79cc5e85d90b6f5adb1a953ac67283adbb41c1e297f9c605"
@@ -206,6 +212,12 @@
},
"version": "1.2.0"
},
+ "javax.annotation:javax.annotation-api": {
+ "shasums": {
+ "jar": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b"
+ },
+ "version": "1.3.2"
+ },
"javax.el:javax.el-api": {
"shasums": {
"jar": "0b46b36709ecbb9791ac4ba44d16125b9d65b576112afdaaa286052b6e498bc4"
@@ -572,6 +584,36 @@
},
"version": "9.9"
},
+ "org.reactivestreams:reactive-streams": {
+ "shasums": {
+ "jar": "1dee0481072d19c929b623e155e14d2f6085dc011529a0a0dbefc84cf571d865"
+ },
+ "version": "1.0.3"
+ },
+ "org.springframework.boot:spring-boot": {
+ "shasums": {
+ "jar": "fca95eb7a9547b89f9722010eceae9604a098dfbfb4cf37f80ed16354bcfdaba"
+ },
+ "version": "2.4.13"
+ },
+ "org.springframework.boot:spring-boot-autoconfigure": {
+ "shasums": {
+ "jar": "f7776b60bbaeda3cec1d44c3fbd875530d124bc0b4e330a22aef0148fc06794e"
+ },
+ "version": "2.4.13"
+ },
+ "org.springframework.cloud:spring-cloud-function-context": {
+ "shasums": {
+ "jar": "c0ef37245f521774f9e540bc258981c5931e4c462b246fd452c52f861ebad47b"
+ },
+ "version": "3.1.6"
+ },
+ "org.springframework.cloud:spring-cloud-function-core": {
+ "shasums": {
+ "jar": "2e11536d4667402d30befdbb050bba992a12eb9d95303d546038a37d848a92a6"
+ },
+ "version": "3.1.6"
+ },
"org.springframework:spring-aop": {
"shasums": {
"jar": "de43e11b649cbc0294dc6d7a5fd82c2b07a55114a5b6558d776c115ad5719a16"
@@ -608,6 +650,12 @@
},
"version": "6.1.4"
},
+ "org.springframework:spring-messaging": {
+ "shasums": {
+ "jar": "3f84fa9a4963a1a1764a235ee76bb85af6012f1c2358a399ddaad065889d271e"
+ },
+ "version": "6.1.4"
+ },
"org.springframework:spring-test": {
"shasums": {
"jar": "43ea85da5dc0a4b4c250aa2e12b77bb679bd63c7cd765c80c73c7b52bed47402"
@@ -709,6 +757,9 @@
"io.micrometer:micrometer-observation": [
"io.micrometer:micrometer-commons"
],
+ "io.projectreactor:reactor-core": [
+ "org.reactivestreams:reactive-streams"
+ ],
"javax.xml.bind:jaxb-api": [
"javax.activation:javax.activation-api"
],
@@ -909,6 +960,26 @@
"org.ow2.asm:asm-tree": [
"org.ow2.asm:asm"
],
+ "org.springframework.boot:spring-boot": [
+ "org.springframework:spring-context",
+ "org.springframework:spring-core"
+ ],
+ "org.springframework.boot:spring-boot-autoconfigure": [
+ "org.springframework.boot:spring-boot"
+ ],
+ "org.springframework.cloud:spring-cloud-function-context": [
+ "com.fasterxml.jackson.core:jackson-databind",
+ "javax.annotation:javax.annotation-api",
+ "net.jodah:typetools",
+ "org.springframework.boot:spring-boot-autoconfigure",
+ "org.springframework.cloud:spring-cloud-function-core",
+ "org.springframework:spring-messaging"
+ ],
+ "org.springframework.cloud:spring-cloud-function-core": [
+ "io.projectreactor:reactor-core",
+ "javax.annotation:javax.annotation-api",
+ "org.springframework:spring-core"
+ ],
"org.springframework:spring-aop": [
"org.springframework:spring-beans",
"org.springframework:spring-core"
@@ -929,6 +1000,10 @@
"org.springframework:spring-expression": [
"org.springframework:spring-core"
],
+ "org.springframework:spring-messaging": [
+ "org.springframework:spring-beans",
+ "org.springframework:spring-core"
+ ],
"org.springframework:spring-test": [
"org.springframework:spring-core"
],
@@ -1277,6 +1352,18 @@
"io.micrometer.observation.docs",
"io.micrometer.observation.transport"
],
+ "io.projectreactor:reactor-core": [
+ "reactor.adapter",
+ "reactor.core",
+ "reactor.core.publisher",
+ "reactor.core.scheduler",
+ "reactor.util",
+ "reactor.util.annotation",
+ "reactor.util.concurrent",
+ "reactor.util.context",
+ "reactor.util.function",
+ "reactor.util.retry"
+ ],
"jakarta.el:jakarta.el-api": [
"jakarta.el"
],
@@ -1289,6 +1376,11 @@
"javax.activation:javax.activation-api": [
"javax.activation"
],
+ "javax.annotation:javax.annotation-api": [
+ "javax.annotation",
+ "javax.annotation.security",
+ "javax.annotation.sql"
+ ],
"javax.el:javax.el-api": [
"javax.el"
],
@@ -2160,6 +2252,200 @@
"org.ow2.asm:asm-tree": [
"org.objectweb.asm.tree"
],
+ "org.reactivestreams:reactive-streams": [
+ "org.reactivestreams"
+ ],
+ "org.springframework.boot:spring-boot": [
+ "org.springframework.boot",
+ "org.springframework.boot.admin",
+ "org.springframework.boot.ansi",
+ "org.springframework.boot.availability",
+ "org.springframework.boot.builder",
+ "org.springframework.boot.cloud",
+ "org.springframework.boot.context",
+ "org.springframework.boot.context.annotation",
+ "org.springframework.boot.context.config",
+ "org.springframework.boot.context.event",
+ "org.springframework.boot.context.logging",
+ "org.springframework.boot.context.metrics.buffering",
+ "org.springframework.boot.context.properties",
+ "org.springframework.boot.context.properties.bind",
+ "org.springframework.boot.context.properties.bind.handler",
+ "org.springframework.boot.context.properties.bind.validation",
+ "org.springframework.boot.context.properties.source",
+ "org.springframework.boot.convert",
+ "org.springframework.boot.diagnostics",
+ "org.springframework.boot.diagnostics.analyzer",
+ "org.springframework.boot.env",
+ "org.springframework.boot.info",
+ "org.springframework.boot.jackson",
+ "org.springframework.boot.jdbc",
+ "org.springframework.boot.jdbc.metadata",
+ "org.springframework.boot.jms",
+ "org.springframework.boot.json",
+ "org.springframework.boot.jta.atomikos",
+ "org.springframework.boot.jta.bitronix",
+ "org.springframework.boot.liquibase",
+ "org.springframework.boot.logging",
+ "org.springframework.boot.logging.java",
+ "org.springframework.boot.logging.log4j2",
+ "org.springframework.boot.logging.logback",
+ "org.springframework.boot.origin",
+ "org.springframework.boot.orm.jpa",
+ "org.springframework.boot.orm.jpa.hibernate",
+ "org.springframework.boot.reactor",
+ "org.springframework.boot.rsocket.context",
+ "org.springframework.boot.rsocket.messaging",
+ "org.springframework.boot.rsocket.netty",
+ "org.springframework.boot.rsocket.server",
+ "org.springframework.boot.security.reactive",
+ "org.springframework.boot.security.servlet",
+ "org.springframework.boot.system",
+ "org.springframework.boot.task",
+ "org.springframework.boot.type.classreading",
+ "org.springframework.boot.util",
+ "org.springframework.boot.validation",
+ "org.springframework.boot.validation.beanvalidation",
+ "org.springframework.boot.web.client",
+ "org.springframework.boot.web.codec",
+ "org.springframework.boot.web.context",
+ "org.springframework.boot.web.embedded.jetty",
+ "org.springframework.boot.web.embedded.netty",
+ "org.springframework.boot.web.embedded.tomcat",
+ "org.springframework.boot.web.embedded.undertow",
+ "org.springframework.boot.web.error",
+ "org.springframework.boot.web.reactive.context",
+ "org.springframework.boot.web.reactive.error",
+ "org.springframework.boot.web.reactive.filter",
+ "org.springframework.boot.web.reactive.function.client",
+ "org.springframework.boot.web.reactive.result.view",
+ "org.springframework.boot.web.reactive.server",
+ "org.springframework.boot.web.server",
+ "org.springframework.boot.web.servlet",
+ "org.springframework.boot.web.servlet.context",
+ "org.springframework.boot.web.servlet.error",
+ "org.springframework.boot.web.servlet.filter",
+ "org.springframework.boot.web.servlet.server",
+ "org.springframework.boot.web.servlet.support",
+ "org.springframework.boot.web.servlet.view",
+ "org.springframework.boot.webservices.client"
+ ],
+ "org.springframework.boot:spring-boot-autoconfigure": [
+ "org.springframework.boot.autoconfigure",
+ "org.springframework.boot.autoconfigure.admin",
+ "org.springframework.boot.autoconfigure.amqp",
+ "org.springframework.boot.autoconfigure.aop",
+ "org.springframework.boot.autoconfigure.availability",
+ "org.springframework.boot.autoconfigure.batch",
+ "org.springframework.boot.autoconfigure.cache",
+ "org.springframework.boot.autoconfigure.cassandra",
+ "org.springframework.boot.autoconfigure.codec",
+ "org.springframework.boot.autoconfigure.condition",
+ "org.springframework.boot.autoconfigure.context",
+ "org.springframework.boot.autoconfigure.couchbase",
+ "org.springframework.boot.autoconfigure.dao",
+ "org.springframework.boot.autoconfigure.data",
+ "org.springframework.boot.autoconfigure.data.cassandra",
+ "org.springframework.boot.autoconfigure.data.couchbase",
+ "org.springframework.boot.autoconfigure.data.elasticsearch",
+ "org.springframework.boot.autoconfigure.data.jdbc",
+ "org.springframework.boot.autoconfigure.data.jpa",
+ "org.springframework.boot.autoconfigure.data.ldap",
+ "org.springframework.boot.autoconfigure.data.mongo",
+ "org.springframework.boot.autoconfigure.data.neo4j",
+ "org.springframework.boot.autoconfigure.data.r2dbc",
+ "org.springframework.boot.autoconfigure.data.redis",
+ "org.springframework.boot.autoconfigure.data.rest",
+ "org.springframework.boot.autoconfigure.data.solr",
+ "org.springframework.boot.autoconfigure.data.web",
+ "org.springframework.boot.autoconfigure.diagnostics.analyzer",
+ "org.springframework.boot.autoconfigure.domain",
+ "org.springframework.boot.autoconfigure.elasticsearch",
+ "org.springframework.boot.autoconfigure.elasticsearch.rest",
+ "org.springframework.boot.autoconfigure.flyway",
+ "org.springframework.boot.autoconfigure.freemarker",
+ "org.springframework.boot.autoconfigure.groovy.template",
+ "org.springframework.boot.autoconfigure.gson",
+ "org.springframework.boot.autoconfigure.h2",
+ "org.springframework.boot.autoconfigure.hateoas",
+ "org.springframework.boot.autoconfigure.hazelcast",
+ "org.springframework.boot.autoconfigure.http",
+ "org.springframework.boot.autoconfigure.http.codec",
+ "org.springframework.boot.autoconfigure.influx",
+ "org.springframework.boot.autoconfigure.info",
+ "org.springframework.boot.autoconfigure.integration",
+ "org.springframework.boot.autoconfigure.jackson",
+ "org.springframework.boot.autoconfigure.jdbc",
+ "org.springframework.boot.autoconfigure.jdbc.metadata",
+ "org.springframework.boot.autoconfigure.jersey",
+ "org.springframework.boot.autoconfigure.jms",
+ "org.springframework.boot.autoconfigure.jms.activemq",
+ "org.springframework.boot.autoconfigure.jms.artemis",
+ "org.springframework.boot.autoconfigure.jmx",
+ "org.springframework.boot.autoconfigure.jooq",
+ "org.springframework.boot.autoconfigure.jsonb",
+ "org.springframework.boot.autoconfigure.kafka",
+ "org.springframework.boot.autoconfigure.ldap",
+ "org.springframework.boot.autoconfigure.ldap.embedded",
+ "org.springframework.boot.autoconfigure.liquibase",
+ "org.springframework.boot.autoconfigure.logging",
+ "org.springframework.boot.autoconfigure.mail",
+ "org.springframework.boot.autoconfigure.mongo",
+ "org.springframework.boot.autoconfigure.mongo.embedded",
+ "org.springframework.boot.autoconfigure.mustache",
+ "org.springframework.boot.autoconfigure.neo4j",
+ "org.springframework.boot.autoconfigure.orm.jpa",
+ "org.springframework.boot.autoconfigure.quartz",
+ "org.springframework.boot.autoconfigure.r2dbc",
+ "org.springframework.boot.autoconfigure.rsocket",
+ "org.springframework.boot.autoconfigure.security",
+ "org.springframework.boot.autoconfigure.security.oauth2.client",
+ "org.springframework.boot.autoconfigure.security.oauth2.client.reactive",
+ "org.springframework.boot.autoconfigure.security.oauth2.client.servlet",
+ "org.springframework.boot.autoconfigure.security.oauth2.resource",
+ "org.springframework.boot.autoconfigure.security.oauth2.resource.reactive",
+ "org.springframework.boot.autoconfigure.security.oauth2.resource.servlet",
+ "org.springframework.boot.autoconfigure.security.reactive",
+ "org.springframework.boot.autoconfigure.security.rsocket",
+ "org.springframework.boot.autoconfigure.security.saml2",
+ "org.springframework.boot.autoconfigure.security.servlet",
+ "org.springframework.boot.autoconfigure.sendgrid",
+ "org.springframework.boot.autoconfigure.session",
+ "org.springframework.boot.autoconfigure.solr",
+ "org.springframework.boot.autoconfigure.task",
+ "org.springframework.boot.autoconfigure.template",
+ "org.springframework.boot.autoconfigure.thymeleaf",
+ "org.springframework.boot.autoconfigure.transaction",
+ "org.springframework.boot.autoconfigure.transaction.jta",
+ "org.springframework.boot.autoconfigure.validation",
+ "org.springframework.boot.autoconfigure.web",
+ "org.springframework.boot.autoconfigure.web.client",
+ "org.springframework.boot.autoconfigure.web.embedded",
+ "org.springframework.boot.autoconfigure.web.format",
+ "org.springframework.boot.autoconfigure.web.reactive",
+ "org.springframework.boot.autoconfigure.web.reactive.error",
+ "org.springframework.boot.autoconfigure.web.reactive.function.client",
+ "org.springframework.boot.autoconfigure.web.servlet",
+ "org.springframework.boot.autoconfigure.web.servlet.error",
+ "org.springframework.boot.autoconfigure.webservices",
+ "org.springframework.boot.autoconfigure.webservices.client",
+ "org.springframework.boot.autoconfigure.websocket.reactive",
+ "org.springframework.boot.autoconfigure.websocket.servlet"
+ ],
+ "org.springframework.cloud:spring-cloud-function-context": [
+ "org.springframework.cloud.function.actuator",
+ "org.springframework.cloud.function.cloudevent",
+ "org.springframework.cloud.function.context",
+ "org.springframework.cloud.function.context.catalog",
+ "org.springframework.cloud.function.context.config",
+ "org.springframework.cloud.function.context.message",
+ "org.springframework.cloud.function.context.test",
+ "org.springframework.cloud.function.json",
+ "org.springframework.cloud.function.utils"
+ ],
+ "org.springframework.cloud:spring-cloud-function-core": [
+ "org.springframework.cloud.function.core"
+ ],
"org.springframework:spring-aop": [
"org.aopalliance.aop",
"org.aopalliance.intercept",
@@ -2327,6 +2613,31 @@
"org.apache.commons.logging",
"org.apache.commons.logging.impl"
],
+ "org.springframework:spring-messaging": [
+ "org.springframework.messaging",
+ "org.springframework.messaging.converter",
+ "org.springframework.messaging.core",
+ "org.springframework.messaging.handler",
+ "org.springframework.messaging.handler.annotation",
+ "org.springframework.messaging.handler.annotation.reactive",
+ "org.springframework.messaging.handler.annotation.support",
+ "org.springframework.messaging.handler.invocation",
+ "org.springframework.messaging.handler.invocation.reactive",
+ "org.springframework.messaging.rsocket",
+ "org.springframework.messaging.rsocket.annotation",
+ "org.springframework.messaging.rsocket.annotation.support",
+ "org.springframework.messaging.rsocket.service",
+ "org.springframework.messaging.simp",
+ "org.springframework.messaging.simp.annotation",
+ "org.springframework.messaging.simp.annotation.support",
+ "org.springframework.messaging.simp.broker",
+ "org.springframework.messaging.simp.config",
+ "org.springframework.messaging.simp.stomp",
+ "org.springframework.messaging.simp.user",
+ "org.springframework.messaging.support",
+ "org.springframework.messaging.tcp",
+ "org.springframework.messaging.tcp.reactor"
+ ],
"org.springframework:spring-test": [
"org.springframework.mock.env",
"org.springframework.mock.http",
@@ -2586,9 +2897,11 @@
"io.github.classgraph:classgraph",
"io.micrometer:micrometer-commons",
"io.micrometer:micrometer-observation",
+ "io.projectreactor:reactor-core",
"jakarta.el:jakarta.el-api",
"jakarta.servlet:jakarta.servlet-api",
"javax.activation:javax.activation-api",
+ "javax.annotation:javax.annotation-api",
"javax.el:javax.el-api",
"javax.persistence:javax.persistence-api",
"javax.validation:validation-api",
@@ -2650,12 +2963,18 @@
"org.ow2.asm:asm",
"org.ow2.asm:asm-commons",
"org.ow2.asm:asm-tree",
+ "org.reactivestreams:reactive-streams",
+ "org.springframework.boot:spring-boot",
+ "org.springframework.boot:spring-boot-autoconfigure",
+ "org.springframework.cloud:spring-cloud-function-context",
+ "org.springframework.cloud:spring-cloud-function-core",
"org.springframework:spring-aop",
"org.springframework:spring-beans",
"org.springframework:spring-context",
"org.springframework:spring-core",
"org.springframework:spring-expression",
"org.springframework:spring-jcl",
+ "org.springframework:spring-messaging",
"org.springframework:spring-test",
"org.springframework:spring-web",
"org.springframework:spring-webmvc",
@@ -2705,6 +3024,11 @@
"io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor"
]
},
+ "io.projectreactor:reactor-core": {
+ "reactor.blockhound.integration.BlockHoundIntegration": [
+ "reactor.core.scheduler.ReactorBlockHoundIntegration"
+ ]
+ },
"org.apache.logging.log4j:log4j-api": {
"org.apache.logging.log4j.util.PropertySource": [
"org.apache.logging.log4j.util.EnvironmentPropertySource",
diff --git a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt
index 0dbb16794..6fd54ba3a 100644
--- a/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt
+++ b/sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers/ExpressionLanguageInjection.kt
@@ -1,4 +1,3 @@
-
/*
* Copyright 2024 Code Intelligence GmbH
*
@@ -29,15 +28,19 @@ import java.lang.invoke.MethodHandle
@Suppress("unused_parameter", "unused")
object ExpressionLanguageInjection {
/**
- * Try to call the default constructor of the honeypot class.
+ * Try to call the el() method of the honeypot class.
*/
private const val EXPRESSION_LANGUAGE_ATTACK =
"\${Byte.class.forName(\"$HONEYPOT_CLASS_NAME\").getMethod(\"el\").invoke(null)}"
+ private const val SPRING_EXPRESSION_LANGUAGE_ATTACK = "T($HONEYPOT_CLASS_NAME).el()"
init {
require(EXPRESSION_LANGUAGE_ATTACK.length <= 64) {
"Expression language exploit must fit in a table of recent compares entry (64 bytes)"
}
+ require(SPRING_EXPRESSION_LANGUAGE_ATTACK.length <= 64) {
+ "Expression language exploit must fit in a table of recent compares entry (64 bytes)"
+ }
}
@MethodHooks(
@@ -102,4 +105,32 @@ object ExpressionLanguageInjection {
val message = arguments[0] as String
Jazzer.guideTowardsContainment(message, EXPRESSION_LANGUAGE_ATTACK, hookId)
}
+
+ /**
+ * Guides Spring Expression Language (SpEL) parsing towards payloads that execute RCE, enabling discovery of
+ * CVE-2022-22963-like bugs where SpEL evaluation is unexpectedly attacker-controlled.
+ */
+ @MethodHooks(
+ MethodHook(
+ type = HookType.BEFORE,
+ targetClassName = "org.springframework.expression.spel.standard.SpelExpressionParser",
+ targetMethod = "parseRaw",
+ ),
+ MethodHook(
+ type = HookType.BEFORE,
+ targetClassName = "org.springframework.expression.common.TemplateAwareExpressionParser",
+ targetMethod = "parseExpression",
+ ),
+ )
+ @JvmStatic
+ fun hookSpelParseExpression(
+ method: MethodHandle?,
+ thisObject: Any?,
+ arguments: Array,
+ hookId: Int,
+ ) {
+ if (arguments.isEmpty()) return
+ val expr = arguments[0] as? String ?: return
+ Jazzer.guideTowardsContainment(expr, SPRING_EXPRESSION_LANGUAGE_ATTACK, hookId)
+ }
}
diff --git a/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel b/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
index 583af67dc..0f6091d67 100644
--- a/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
+++ b/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/BUILD.bazel
@@ -25,5 +25,7 @@ java_junit5_test(
"@maven//:javax_validation_validation_api",
"@maven//:org_junit_jupiter_junit_jupiter_api",
"@maven//:org_junit_jupiter_junit_jupiter_params",
+ "@maven//:org_springframework_cloud_spring_cloud_function_context",
+ "@maven//:org_springframework_cloud_spring_cloud_function_core",
],
)
diff --git a/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/HookBindingSanityTest.java b/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/HookBindingSanityTest.java
index d7ab7f250..6dc6d1955 100644
--- a/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/HookBindingSanityTest.java
+++ b/sanitizers/src/test/java/com/code_intelligence/jazzer/sanitizers/HookBindingSanityTest.java
@@ -109,7 +109,11 @@ public boolean equals(Object o) {
final Set SKIPPED_JDK_8 =
Collections.unmodifiableSet(
Stream.of(
- new MethodRef("jakarta.el.ExpressionFactory"), // -> UnsupportedClassVersionError
+ new MethodRef(
+ "org.springframework.expression.common.TemplateAwareExpressionParser"),
+ new MethodRef(
+ "org.springframework.expression.spel.standard.SpelExpressionParser"),
+ new MethodRef("jakarta.el.ExpressionFactory"),
new MethodRef("java.util.regex.Pattern$CharPredicate"),
new MethodRef("javax.xml.xpath.XPath", "evaluateExpression", null),
new MethodRef(