diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 4ac983ab2e9c8..142e5759d51d3 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -62,7 +62,7 @@
2.1.0
1.0.13
3.0.1
- 3.6.0
+ 3.7.2
4.10.1
2.3.1
2.1.2
@@ -120,7 +120,7 @@
1.0.1.Final
2.2.2.Final
3.5.0.Final
- 4.4.5
+ 4.4.6
4.5.14
4.4.16
4.1.5
diff --git a/docs/src/main/asciidoc/resteasy-reactive.adoc b/docs/src/main/asciidoc/resteasy-reactive.adoc
index 873436fd8942e..d3e5399bf6cbe 100644
--- a/docs/src/main/asciidoc/resteasy-reactive.adoc
+++ b/docs/src/main/asciidoc/resteasy-reactive.adoc
@@ -16,7 +16,7 @@ include::_attributes.adoc[]
:httpspec: https://tools.ietf.org/html/rfc7231
:jsonpapi: https://javadoc.io/doc/jakarta.json/jakarta.json-api/2.1.2/jakarta.json
:injectapi: https://javadoc.io/static/jakarta.inject/jakarta.inject-api/2.0.1/jakarta.inject
-:vertxapi: https://javadoc.io/static/io.vertx/vertx-core/4.4.5
+:vertxapi: https://javadoc.io/static/io.vertx/vertx-core/4.4.6
:resteasy-reactive-api: https://javadoc.io/doc/io.quarkus.resteasy.reactive/resteasy-reactive/{quarkus-version}
:resteasy-reactive-common-api: https://javadoc.io/doc/io.quarkus.resteasy.reactive/resteasy-reactive-common/{quarkus-version}
diff --git a/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/MoneyTest.java b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/MoneyTest.java
new file mode 100644
index 0000000000000..b479c59b3cc2a
--- /dev/null
+++ b/extensions/reactive-pg-client/deployment/src/test/java/io/quarkus/reactive/pg/client/MoneyTest.java
@@ -0,0 +1,26 @@
+package io.quarkus.reactive.pg.client;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.math.BigDecimal;
+
+import org.junit.jupiter.api.Test;
+
+import io.vertx.pgclient.data.Money;
+
+/**
+ * Reproduce PG Reactive Client: Cannot create Money value in Range
+ * (-1.00, 0.00).
+ */
+public class MoneyTest {
+
+ @Test
+ void testMoney() {
+ Money money = new Money(new BigDecimal("-1.11"));
+ assertEquals(BigDecimal.valueOf(-1.11), money.bigDecimalValue());
+
+ money = new Money(new BigDecimal("-0.11"));
+ assertEquals(BigDecimal.valueOf(-0.11), money.bigDecimalValue());
+ }
+
+}
diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/EmptyHostTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/EmptyHostTest.java
new file mode 100644
index 0000000000000..bcece8cde9f19
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/EmptyHostTest.java
@@ -0,0 +1,46 @@
+package io.quarkus.vertx.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import io.vertx.ext.web.Router;
+
+/**
+ * Reproduce NullPointerException for request with empty Host
+ * header.
+ */
+public class EmptyHostTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addClasses(BeanRegisteringRouteUsingObserves.class));
+
+ @Test
+ public void testWithEmptyHost() {
+ assertEquals(RestAssured
+ .given()
+ .header("Host", "")
+ .get("/hello")
+ .asString(), "Hello World! ");
+
+ }
+
+ @ApplicationScoped
+ static class BeanRegisteringRouteUsingObserves {
+
+ public void register(@Observes Router router) {
+
+ router.route("/hello").handler(ctx -> ctx.response().end("Hello World! " + ctx.request().host()));
+ }
+
+ }
+
+}
diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java
new file mode 100644
index 0000000000000..5f4091a2b7097
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java
@@ -0,0 +1,105 @@
+package io.quarkus.vertx.http.http2;
+
+import static io.vertx.core.http.HttpMethod.GET;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.quarkus.test.common.http.TestHTTPResource;
+import io.quarkus.vertx.core.runtime.VertxCoreRecorder;
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.http.HttpClientOptions;
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.core.net.JdkSSLEngineOptions;
+import io.vertx.ext.web.Router;
+
+/**
+ * Configuration of the RST flood protection (CVE-2023-44487)
+ */
+public class Http2RSTFloodProtectionConfigTest {
+
+ @TestHTTPResource(value = "/ping", ssl = true)
+ URL sslUrl;
+
+ @TestHTTPResource(value = "/ping")
+ URL url;
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addClasses(MyBean.class)
+ .addAsResource(new File("src/test/resources/conf/ssl-jks-rst-flood-protection.conf"),
+ "application.properties")
+ .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks"));
+
+ @Test
+ void testRstFloodProtectionWithTlsEnabled() throws Exception {
+ Assumptions.assumeTrue(JdkSSLEngineOptions.isAlpnAvailable()); //don't run on JDK8
+ HttpClientOptions options = new HttpClientOptions()
+ .setUseAlpn(true)
+ .setProtocolVersion(HttpVersion.HTTP_2)
+ .setSsl(true)
+ .setTrustAll(true);
+
+ var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
+ int port = sslUrl.getPort();
+ run(client, port, false);
+ }
+
+ @Test
+ public void testRstFloodProtection() throws InterruptedException {
+ HttpClientOptions options = new HttpClientOptions()
+ .setProtocolVersion(HttpVersion.HTTP_2)
+ .setHttp2ClearTextUpgrade(true);
+ var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
+ run(client, url.getPort(), true);
+ }
+
+ void run(HttpClient client, int port, boolean plain) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ client.connectionHandler(conn -> conn.goAwayHandler(ga -> {
+ Assertions.assertEquals(11, ga.getErrorCode());
+ latch.countDown();
+ }));
+
+ if (plain) {
+ // Emit a first request to establish a connection.
+ // It's HTTP/1 so, does not count in the number of requests.
+ client.request(GET, port, "localhost", "/ping")
+ .compose(HttpClientRequest::send);
+ }
+
+ for (int i = 0; i < 20; i++) {
+ client.request(GET, port, "localhost", "/ping")
+ .onSuccess(req -> req.end().onComplete(v -> req.reset()));
+ }
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ fail("RST flood protection failed");
+ }
+ }
+
+ @ApplicationScoped
+ public static class MyBean {
+
+ public void register(@Observes Router router) {
+ router.get("/ping").handler(rc -> {
+ // Do nothing.
+ });
+ }
+
+ }
+}
diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf
new file mode 100644
index 0000000000000..09b82c863ebe4
--- /dev/null
+++ b/extensions/vertx-http/deployment/src/test/resources/conf/ssl-jks-rst-flood-protection.conf
@@ -0,0 +1,6 @@
+
+quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
+quarkus.http.ssl.certificate.key-store-password=secret
+
+ quarkus.http.limits.rst-flood-max-rst-frame-per-window=10
+ quarkus.http.limits.rst-flood-window-duration=10s
\ No newline at end of file
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java
index 1d8e91e323eed..1ef9a2b47a6fe 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ServerLimitsConfig.java
@@ -1,5 +1,6 @@
package io.quarkus.vertx.http.runtime;
+import java.time.Duration;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
@@ -86,4 +87,20 @@ public class ServerLimitsConfig {
@ConfigItem
public OptionalLong maxHeaderListSize;
+ /**
+ * Set the max number of RST frame allowed per time window, this is used to prevent
+ * HTTP/2 RST frame flood DDOS
+ * attacks. The default value is {@code 200}, setting zero or a negative value, disables flood protection.
+ */
+ @ConfigItem
+ public OptionalInt rstFloodMaxRstFramePerWindow;
+
+ /**
+ * Set the duration of the time window when checking the max number of RST frames, this is used to prevent
+ * HTTP/2 RST frame flood DDOS
+ * attacks.. The default value is {@code 30 s}, setting zero or a negative value, disables flood protection.
+ */
+ @ConfigItem
+ public Optional rstFloodWindowDuration;
+
}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java
index 20e9fa1eb6193..36239a6ceaa1f 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/HttpServerOptionsUtils.java
@@ -275,6 +275,18 @@ public static void applyCommonOptions(HttpServerOptions httpServerOptions,
settings.setMaxHeaderListSize(httpConfiguration.limits.maxHeaderListSize.getAsLong());
}
httpServerOptions.setInitialSettings(settings);
+
+ // RST attack protection - https://github.com/netty/netty/security/advisories/GHSA-xpw8-rcwv-8f8p
+ if (httpConfiguration.limits.rstFloodMaxRstFramePerWindow.isPresent()) {
+ httpServerOptions
+ .setHttp2RstFloodMaxRstFramePerWindow(httpConfiguration.limits.rstFloodMaxRstFramePerWindow.getAsInt());
+ }
+ if (httpConfiguration.limits.rstFloodWindowDuration.isPresent()) {
+ httpServerOptions.setHttp2RstFloodWindowDuration(
+ (int) httpConfiguration.limits.rstFloodWindowDuration.get().toSeconds());
+ httpServerOptions.setHttp2RstFloodWindowDurationTimeUnit(TimeUnit.SECONDS);
+ }
+
}
httpServerOptions.setUseProxyProtocol(httpConfiguration.proxy.useProxyProtocol);
diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml
index bd02b6ce15ad0..4920d8e9a3607 100644
--- a/independent-projects/resteasy-reactive/pom.xml
+++ b/independent-projects/resteasy-reactive/pom.xml
@@ -63,7 +63,7 @@
3.1.2
2.5.1
2.1.2
- 4.4.5
+ 4.4.6
5.3.2
1.0.0.Final
2.15.2