diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b042d2003..67e236d38 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -5,18 +5,21 @@ jmh = "1.37"
coroutines = "1.11.0"
serialization = "1.11.0"
ktor = "3.5.2"
-netty = "4.2.17.Final"
+# October 2026 security releases (HTTP parsing, HTTP/2 resource limits, SNI).
+netty = "4.2.19.Final"
# Floor for the AWS SDK's Netty 4.1 client. Keep this on the patched 4.1 line;
# the Ktor runtime above intentionally uses Netty 4.2.
-netty41 = "4.1.137.Final"
+netty41 = "4.1.139.Final"
lettuce = "7.6.0.RELEASE"
awsSdk = "2.49.4"
# Floor only, not a direct dependency: the AWS SDK's apache5-client pulls
# httpclient5 5.6.2, which GHSA-hjcp-jmpx-g3qm fixes in 5.6.3. Drop the
# constraint in hocuspocus-storage-s3 once the SDK's own floor clears it.
+# Also patches the Ktor test host's older Apache client and HttpCore transitives.
httpclient5 = "5.6.4"
sqliteJdbc = "3.53.4.0"
-logback = "1.5.38"
+# CVE-2026-104721: reject unsafe MDC discriminator values.
+logback = "1.6.5"
junit = "5.13.4"
yks = "0.2.15"
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 3cf658c52..9667cda28 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -21,6 +21,14 @@
+
+
+
+
+
+
+
+
@@ -37,6 +45,14 @@
+
+
+
+
+
+
+
+
@@ -47,6 +63,11 @@
+
+
+
+
+
@@ -886,6 +907,11 @@
+
+
+
+
+
@@ -901,6 +927,11 @@
+
+
+
+
+
@@ -925,6 +956,14 @@
+
+
+
+
+
+
+
+
@@ -957,6 +996,14 @@
+
+
+
+
+
+
+
+
@@ -981,6 +1028,14 @@
+
+
+
+
+
+
+
+
@@ -1005,6 +1060,14 @@
+
+
+
+
+
+
+
+
@@ -1037,6 +1100,14 @@
+
+
+
+
+
+
+
+
@@ -1061,6 +1132,14 @@
+
+
+
+
+
+
+
+
@@ -1093,6 +1172,14 @@
+
+
+
+
+
+
+
+
@@ -1117,6 +1204,14 @@
+
+
+
+
+
+
+
+
@@ -1141,6 +1236,14 @@
+
+
+
+
+
+
+
+
@@ -1165,6 +1268,14 @@
+
+
+
+
+
+
+
+
@@ -1189,6 +1300,14 @@
+
+
+
+
+
+
+
+
@@ -1213,6 +1332,14 @@
+
+
+
+
+
+
+
+
@@ -1237,6 +1364,14 @@
+
+
+
+
+
+
+
+
@@ -1261,6 +1396,14 @@
+
+
+
+
+
+
+
+
@@ -1293,6 +1436,14 @@
+
+
+
+
+
+
+
+
@@ -1317,6 +1468,14 @@
+
+
+
+
+
+
+
+
@@ -1349,6 +1508,14 @@
+
+
+
+
+
+
+
+
@@ -1364,6 +1531,11 @@
+
+
+
+
+
@@ -1384,6 +1556,11 @@
+
+
+
+
+
@@ -1408,6 +1585,14 @@
+
+
+
+
+
+
+
+
@@ -1440,6 +1625,14 @@
+
+
+
+
+
+
+
+
@@ -1472,6 +1665,14 @@
+
+
+
+
+
+
+
+
@@ -1496,6 +1697,14 @@
+
+
+
+
+
+
+
+
@@ -1528,6 +1737,14 @@
+
+
+
+
+
+
+
+
@@ -1552,6 +1769,14 @@
+
+
+
+
+
+
+
+
@@ -1576,6 +1801,14 @@
+
+
+
+
+
+
+
+
@@ -1600,6 +1833,14 @@
+
+
+
+
+
+
+
+
@@ -1642,6 +1883,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1684,6 +1939,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1708,6 +1977,14 @@
+
+
+
+
+
+
+
+
@@ -1740,6 +2017,14 @@
+
+
+
+
+
+
+
+
@@ -3234,6 +3519,14 @@
+
+
+
+
+
+
+
+
@@ -3244,6 +3537,11 @@
+
+
+
+
+
@@ -3259,6 +3557,11 @@
+
+
+
+
+
diff --git a/hocuspocus-benchmark/gradle.lockfile b/hocuspocus-benchmark/gradle.lockfile
index a977344d3..90cfda8d5 100644
--- a/hocuspocus-benchmark/gradle.lockfile
+++ b/hocuspocus-benchmark/gradle.lockfile
@@ -2,8 +2,8 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
# To regenerate this file, run: ./gradlew :hocuspocus-benchmark:dependencies --write-locks
-ch.qos.logback:logback-classic:1.5.38=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-ch.qos.logback:logback-core:1.5.38=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+ch.qos.logback:logback-classic:1.6.5=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+ch.qos.logback:logback-core:1.6.5=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.typesafe:config:1.4.9=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
dev.yks:yks:0.2.15=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.ktor:ktor-events-jvm:3.5.2=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -31,26 +31,26 @@ io.ktor:ktor-websocket-serialization:3.5.2=compileClasspath,jmhCompileClasspath,
io.ktor:ktor-websockets-jvm:3.5.2=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.ktor:ktor-websockets:3.5.2=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.lettuce:lettuce-core:7.6.0.RELEASE=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-io.netty:netty-bom:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-buffer:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-base:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-compression:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-dns:4.2.17.Final=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-io.netty:netty-codec-http2:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-http:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-marshalling:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-protobuf:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-common:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-handler:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-resolver-dns:4.2.17.Final=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-io.netty:netty-resolver:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-classes-epoll:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-classes-kqueue:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-epoll:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-kqueue:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-unix-common:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport:4.2.17.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-bom:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-base:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-compression:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-dns:4.2.19.Final=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.netty:netty-codec-http2:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-http:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-marshalling:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-protobuf:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-common:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-handler:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-resolver-dns:4.2.19.Final=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
+io.netty:netty-resolver:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-classes-epoll:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-classes-kqueue:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-epoll:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-kqueue:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-unix-common:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport:4.2.19.Final=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.projectreactor:reactor-core:3.6.6=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
net.sf.jopt-simple:jopt-simple:5.0.4=jmh,jmhCompileClasspath,jmhRuntimeClasspath
org.apache.commons:commons-math3:3.6.1=jmh,jmhCompileClasspath,jmhRuntimeClasspath
@@ -116,7 +116,8 @@ org.openjdk.jmh:jmh-generator-reflection:1.37=jmh,jmhCompileClasspath,jmhRuntime
org.opentest4j:opentest4j:1.3.0=jmhRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm:9.0=jmh,jmhCompileClasspath,jmhRuntimeClasspath
org.reactivestreams:reactive-streams:1.0.4=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
-org.slf4j:slf4j-api:2.0.18=compileClasspath,jmhCompileClasspath,jmhRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.slf4j:slf4j-api:2.0.18=compileClasspath,jmhCompileClasspath,testCompileClasspath
+org.slf4j:slf4j-api:2.0.19=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.xerial:sqlite-jdbc:3.53.4.0=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
redis.clients.authentication:redis-authx-core:0.1.1-beta2=jmhRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
empty=annotationProcessor,implementationDependenciesMetadata,jmhAnnotationProcessor,jmhImplementationDependenciesMetadata,jmhKotlinScriptDefExtensions,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,testAnnotationProcessor,testImplementationDependenciesMetadata,testKotlinScriptDefExtensions
diff --git a/hocuspocus-ktor-example/gradle.lockfile b/hocuspocus-ktor-example/gradle.lockfile
index 08baa3ee0..38d402b59 100644
--- a/hocuspocus-ktor-example/gradle.lockfile
+++ b/hocuspocus-ktor-example/gradle.lockfile
@@ -2,8 +2,8 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
# To regenerate this file, run: ./gradlew :hocuspocus-ktor-example:dependencies --write-locks
-ch.qos.logback:logback-classic:1.5.38=runtimeClasspath,testRuntimeClasspath
-ch.qos.logback:logback-core:1.5.38=runtimeClasspath,testRuntimeClasspath
+ch.qos.logback:logback-classic:1.6.5=runtimeClasspath,testRuntimeClasspath
+ch.qos.logback:logback-core:1.6.5=runtimeClasspath,testRuntimeClasspath
com.typesafe:config:1.4.9=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
dev.yks:yks:0.2.15=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.ktor:ktor-events-jvm:3.5.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -32,24 +32,24 @@ io.ktor:ktor-websocket-serialization-jvm:3.5.2=compileClasspath,runtimeClasspath
io.ktor:ktor-websocket-serialization:3.5.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.ktor:ktor-websockets-jvm:3.5.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.ktor:ktor-websockets:3.5.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-bom:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-buffer:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-base:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-compression:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-http2:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-http:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-marshalling:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-protobuf:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-common:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-handler:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-resolver:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-classes-epoll:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-classes-kqueue:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-epoll:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-kqueue:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-unix-common:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-bom:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-base:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-compression:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-http2:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-http:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-marshalling:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-protobuf:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-common:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-handler:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-resolver:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-classes-epoll:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-classes-kqueue:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-epoll:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-kqueue:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-unix-common:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
@@ -105,5 +105,6 @@ org.junit.platform:junit-platform-engine:1.13.4=testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.13.4=testRuntimeClasspath
org.junit:junit-bom:5.13.4=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
-org.slf4j:slf4j-api:2.0.18=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+org.slf4j:slf4j-api:2.0.18=compileClasspath,testCompileClasspath
+org.slf4j:slf4j-api:2.0.19=runtimeClasspath,testRuntimeClasspath
empty=annotationProcessor,implementationDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,testAnnotationProcessor,testImplementationDependenciesMetadata,testKotlinScriptDefExtensions
diff --git a/hocuspocus-ktor/build.gradle.kts b/hocuspocus-ktor/build.gradle.kts
index a003dc410..43fe3d92a 100644
--- a/hocuspocus-ktor/build.gradle.kts
+++ b/hocuspocus-ktor/build.gradle.kts
@@ -6,4 +6,14 @@ dependencies {
testImplementation(libs.ktor.server.test.host)
testImplementation(libs.ktor.client.websockets)
+
+ constraints {
+ testImplementation(libs.httpclient5) {
+ because(
+ "Ktor's test host brings httpclient5 5.5.1 and httpcore5 5.3.6; " +
+ "5.6.4 also raises HttpCore to 5.4.3, fixing GHSA-hjcp-jmpx-g3qm, " +
+ "GHSA-v3jc-474w-2wm6, and GHSA-hf6x-8p5f-cgmf.",
+ )
+ }
+ }
}
diff --git a/hocuspocus-ktor/gradle.lockfile b/hocuspocus-ktor/gradle.lockfile
index 78ccc2db5..14786e745 100644
--- a/hocuspocus-ktor/gradle.lockfile
+++ b/hocuspocus-ktor/gradle.lockfile
@@ -50,9 +50,9 @@ net.bytebuddy:byte-buddy-agent:1.17.6=testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.6=testRuntimeClasspath
net.java.dev.jna:jna-platform:5.9.0=testRuntimeClasspath
net.java.dev.jna:jna:5.9.0=testRuntimeClasspath
-org.apache.httpcomponents.client5:httpclient5:5.5.1=testCompileClasspath,testRuntimeClasspath
-org.apache.httpcomponents.core5:httpcore5-h2:5.3.6=testCompileClasspath,testRuntimeClasspath
-org.apache.httpcomponents.core5:httpcore5:5.3.6=testCompileClasspath,testRuntimeClasspath
+org.apache.httpcomponents.client5:httpclient5:5.6.4=testCompileClasspath,testRuntimeClasspath
+org.apache.httpcomponents.core5:httpcore5-h2:5.4.3=testCompileClasspath,testRuntimeClasspath
+org.apache.httpcomponents.core5:httpcore5:5.4.3=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.fusesource.jansi:jansi:2.4.3=testRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
diff --git a/hocuspocus-redis/gradle.lockfile b/hocuspocus-redis/gradle.lockfile
index a47a06526..288a74510 100644
--- a/hocuspocus-redis/gradle.lockfile
+++ b/hocuspocus-redis/gradle.lockfile
@@ -4,16 +4,16 @@
# To regenerate this file, run: ./gradlew :hocuspocus-redis:dependencies --write-locks
dev.yks:yks:0.2.15=testCompileClasspath,testRuntimeClasspath
io.lettuce:lettuce-core:7.6.0.RELEASE=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-bom:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-buffer:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-base:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-codec-dns:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-common:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-handler:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-resolver-dns:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-resolver:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-unix-common:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-transport:4.2.17.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-bom:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-base:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-codec-dns:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-common:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-handler:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-resolver-dns:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-resolver:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-unix-common:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-transport:4.2.19.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.projectreactor:reactor-core:3.6.6=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.4.0=kotlinAbiValidationCompatClasspath
diff --git a/hocuspocus-storage-s3/build.gradle.kts b/hocuspocus-storage-s3/build.gradle.kts
index 353654668..086928d29 100644
--- a/hocuspocus-storage-s3/build.gradle.kts
+++ b/hocuspocus-storage-s3/build.gradle.kts
@@ -2,8 +2,8 @@ dependencies {
api(project(":hocuspocus-core"))
implementation(platform(libs.netty41.bom)) {
because(
- "GHSA-8c42-7qj2-3j46: the AWS SDK resolves Netty 4.1.136.Final; " +
- "CorsHandler is fixed in 4.1.137.Final.",
+ "The AWS SDK resolves Netty 4.1.136.Final; keep its HTTP client on " +
+ "the 4.1.139.Final security release (HTTP parsing, HTTP/2 resource limits, SNI).",
)
}
implementation(libs.aws.s3)
diff --git a/hocuspocus-storage-s3/gradle.lockfile b/hocuspocus-storage-s3/gradle.lockfile
index 530eefc90..922ae0d34 100644
--- a/hocuspocus-storage-s3/gradle.lockfile
+++ b/hocuspocus-storage-s3/gradle.lockfile
@@ -2,17 +2,17 @@
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
# To regenerate this file, run: ./gradlew :hocuspocus-storage-s3:dependencies --write-locks
-io.netty:netty-bom:4.1.137.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
-io.netty:netty-buffer:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-codec-http2:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-codec-http:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-codec:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-common:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-handler:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-resolver:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-transport-classes-epoll:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-transport-native-unix-common:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
-io.netty:netty-transport:4.1.137.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-bom:4.1.139.Final=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
+io.netty:netty-buffer:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-codec-http2:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-codec-http:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-codec:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-common:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-handler:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-resolver:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-transport-classes-epoll:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-transport-native-unix-common:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
+io.netty:netty-transport:4.1.139.Final=runtimeClasspath,testRuntimeClasspath
org.apache.httpcomponents.client5:httpclient5:5.6.4=runtimeClasspath,testRuntimeClasspath
org.apache.httpcomponents.core5:httpcore5-h2:5.4.3=runtimeClasspath,testRuntimeClasspath
org.apache.httpcomponents.core5:httpcore5:5.4.3=runtimeClasspath,testRuntimeClasspath
diff --git a/package.json b/package.json
index 2e0ff900d..fed425f90 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"test:jvm:migration": "HOCUSPOCUS_BENCHMARK_SCRIPT=./benchmark/compare-infrastructure.mjs bash scripts/benchmark-ab.sh --compatibility-only --check",
"test:jvm:benchmark-stats": "node --test benchmark/process-stats.test.mjs",
"test:jvm:matrix": "node scripts/verify-upstream-server-matrix.mjs && node scripts/verify-junit-discovery.mjs",
- "test:jvm:security": "node scripts/check-runtime-vulnerabilities.mjs",
+ "test:jvm:security": "node --test scripts/runtime-security-policy.test.mjs && node scripts/check-runtime-vulnerabilities.mjs",
"benchmark:jvm:ab": "bash scripts/benchmark-ab.sh",
"benchmark:jvm:infra-ab": "HOCUSPOCUS_BENCHMARK_SCRIPT=./benchmark/compare-infrastructure.mjs bash scripts/benchmark-ab.sh"
},
diff --git a/scripts/check-runtime-vulnerabilities.mjs b/scripts/check-runtime-vulnerabilities.mjs
index 8770a4c08..53ac6b9b8 100644
--- a/scripts/check-runtime-vulnerabilities.mjs
+++ b/scripts/check-runtime-vulnerabilities.mjs
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
+import { runtimeSecurityFinding } from "./runtime-security-policy.mjs";
const repository = path.resolve(import.meta.dirname, "..");
const lockfiles = fs.readdirSync(repository)
@@ -16,7 +17,11 @@ for (const lockfile of lockfiles) {
for (const line of fs.readFileSync(lockfile, "utf8").split(/\r?\n/u)) {
const [coordinate, configurations = ""] = line.split("=", 2);
const match = coordinate.match(/^([^:=]+):([^:=]+):([^=]+)$/u);
- if (match === null || !configurations.split(",").includes("runtimeClasspath")) continue;
+ // Test hosts bring their own HTTP clients; audit those transitives as well.
+ const runtime = configurations.split(",").some((name) =>
+ name === "runtimeClasspath" || name === "testRuntimeClasspath",
+ );
+ if (match === null || !runtime) continue;
const [, group, artifact, version] = match;
const id = `${group}:${artifact}:${version}`;
coordinates.set(id, {
@@ -30,7 +35,12 @@ for (const lockfile of lockfiles) {
}
const entries = [...coordinates.entries()].sort(([left], [right]) => left.localeCompare(right));
-assert.ok(entries.length > 0, "no locked JVM runtime dependencies were found");
+assert.ok(entries.length > 0, "no locked JVM runtime or test dependencies were found");
+
+const supplementalFindings = entries.flatMap(([coordinate, query]) => {
+ const id = runtimeSecurityFinding(query);
+ return id === null ? [] : [{ coordinate, id }];
+});
const response = await fetch("https://api.osv.dev/v1/querybatch", {
method: "POST",
@@ -47,20 +57,20 @@ if (!response.ok) {
const body = await response.json();
assert.equal(body.results.length, entries.length, "OSV returned an incomplete result set");
-const findings = body.results.flatMap((result, index) =>
+const findings = supplementalFindings.concat(body.results.flatMap((result, index) =>
(result.vulns ?? []).map((vulnerability) => ({
coordinate: entries[index][0],
id: vulnerability.id,
})),
-);
+));
if (findings.length > 0) {
for (const finding of findings) {
process.stderr.write(`${finding.coordinate}: ${finding.id}\n`);
}
- throw new Error(`${findings.length} known JVM runtime vulnerability finding(s)`);
+ throw new Error(`${findings.length} JVM runtime security finding(s)`);
}
process.stdout.write(
- `OSV runtime dependency check: ${entries.length} locked Maven coordinates, no known findings\n`,
+ `OSV + security floor check: ${entries.length} locked Maven runtime/test coordinates, no known findings\n`,
);
diff --git a/scripts/runtime-security-policy.mjs b/scripts/runtime-security-policy.mjs
new file mode 100644
index 000000000..3e92e6b6b
--- /dev/null
+++ b/scripts/runtime-security-policy.mjs
@@ -0,0 +1,25 @@
+// Release advisories can precede OSV indexing. Keep these floors alongside OSV.
+// https://github.com/netty/netty/releases/tag/netty-4.2.19.Final
+// https://github.com/netty/netty/releases/tag/netty-4.1.139.Final
+// https://logback.qos.ch/news.html#1.6.5 (CVE-2026-104721)
+const nettyFloors = new Map([["4.1", 139], ["4.2", 19]]);
+
+export function runtimeSecurityFinding({ package: dependency, version }) {
+ if (dependency.ecosystem !== "Maven") return null;
+ if (dependency.name.startsWith("io.netty:netty-")) {
+ const match = version.match(/^(4\.[12])\.(\d+)\.Final$/u);
+ if (match && Number(match[2]) < nettyFloors.get(match[1])) {
+ return `Netty security release requires >= ${match[1]}.${nettyFloors.get(match[1])}.Final`;
+ }
+ }
+ if (["ch.qos.logback:logback-classic", "ch.qos.logback:logback-core"].includes(dependency.name)) {
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/u);
+ if (match) {
+ const [, major, minor, patch] = match.map(Number);
+ if (major < 1 || (major === 1 && (minor < 6 || (minor === 6 && patch < 5)))) {
+ return "CVE-2026-104721: Logback security floor is 1.6.5";
+ }
+ }
+ }
+ return null;
+}
diff --git a/scripts/runtime-security-policy.test.mjs b/scripts/runtime-security-policy.test.mjs
new file mode 100644
index 000000000..930f69a2a
--- /dev/null
+++ b/scripts/runtime-security-policy.test.mjs
@@ -0,0 +1,94 @@
+import assert from "node:assert/strict";
+import { spawnSync } from "node:child_process";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+import process from "node:process";
+import test from "node:test";
+import { runtimeSecurityFinding } from "./runtime-security-policy.mjs";
+
+function finding(name, version) {
+ return runtimeSecurityFinding({ package: { ecosystem: "Maven", name }, version });
+}
+
+test("rejects vulnerable Netty versions even when OSV has no findings", () => {
+ for (const version of ["4.1.137.Final", "4.1.138.Final", "4.2.17.Final", "4.2.18.Final"]) {
+ assert.match(finding("io.netty:netty-codec-http", version), /Netty security release/u);
+ }
+});
+
+test("accepts patched Netty versions on both supported release lines", () => {
+ for (const version of ["4.1.139.Final", "4.1.140.Final", "4.2.19.Final", "4.2.20.Final", "4.2.100.Final"]) {
+ assert.equal(finding("io.netty:netty-handler", version), null);
+ }
+});
+
+test("rejects Logback versions before the complete MDC sanitization fix", () => {
+ for (const name of ["ch.qos.logback:logback-classic", "ch.qos.logback:logback-core"]) {
+ for (const version of ["1.5.38", "1.6.3", "1.6.4"]) {
+ assert.match(finding(name, version), /CVE-2026-104721/u);
+ }
+ for (const version of ["1.6.5", "1.6.10", "1.7.0"]) {
+ assert.equal(finding(name, version), null);
+ }
+ }
+});
+
+test("leaves unrelated packages and independently versioned Netty artifacts to OSV", () => {
+ assert.equal(finding("org.slf4j:slf4j-api", "2.0.18"), null);
+ assert.equal(finding("io.netty:netty-tcnative-boringssl-static", "2.0.77.Final"), null);
+ assert.equal(runtimeSecurityFinding({
+ package: { ecosystem: "npm", name: "io.netty:netty-handler" },
+ version: "4.2.17.Final",
+ }), null);
+});
+
+function runAudit(t, lockfile, vulnerability = null) {
+ const repository = fs.mkdtempSync(path.join(os.tmpdir(), "hocuspocus-security-"));
+ t.after(() => fs.rmSync(repository, { recursive: true, force: true }));
+ fs.mkdirSync(path.join(repository, "scripts"));
+ fs.mkdirSync(path.join(repository, "module"));
+ fs.writeFileSync(path.join(repository, "module", "gradle.lockfile"), lockfile);
+ for (const name of ["check-runtime-vulnerabilities.mjs", "runtime-security-policy.mjs"]) {
+ fs.copyFileSync(path.join(import.meta.dirname, name), path.join(repository, "scripts", name));
+ }
+ const mock = path.join(repository, "mock-osv.mjs");
+ fs.writeFileSync(mock, `
+globalThis.fetch = async (_url, options) => ({
+ ok: true,
+ json: async () => ({ results: JSON.parse(options.body).queries.map(query => ({
+ vulns: query.package.name === "org.apache.httpcomponents.client5:httpclient5"
+ ? ${JSON.stringify(vulnerability ? [{ id: vulnerability }] : [])} : [],
+ })) }),
+});
+`);
+ const result = spawnSync(process.execPath, ["--import", mock, path.join(repository, "scripts", "check-runtime-vulnerabilities.mjs")], {
+ encoding: "utf8",
+ timeout: 10_000,
+ });
+ assert.ifError(result.error);
+ return result;
+}
+
+test("the audit fails on a vulnerable test-only dependency", (t) => {
+ const result = runAudit(t,
+ "org.slf4j:slf4j-api:2.0.19=runtimeClasspath\norg.apache.httpcomponents.client5:httpclient5:5.5.1=testRuntimeClasspath\n",
+ "GHSA-hjcp-jmpx-g3qm",
+ );
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /httpclient5:5\.5\.1: GHSA-hjcp-jmpx-g3qm/u);
+});
+
+test("the audit enforces release security floors when OSV returns no findings", (t) => {
+ const result = runAudit(t, "io.netty:netty-handler:4.2.18.Final=runtimeClasspath\n");
+ assert.equal(result.status, 1);
+ assert.match(result.stderr, /requires >= 4\.2\.19\.Final/u);
+});
+
+test("the audit succeeds with patched runtime and test dependencies", (t) => {
+ const result = runAudit(t,
+ "io.netty:netty-handler:4.2.19.Final=runtimeClasspath\norg.apache.httpcomponents.client5:httpclient5:5.6.4=testRuntimeClasspath\n",
+ );
+ assert.equal(result.status, 0, result.stderr);
+ assert.match(result.stdout, /2 locked Maven runtime\/test coordinates, no known findings/u);
+});