From a2592fe7f95bba0e8d4c3da7f27fef78361c74cf Mon Sep 17 00:00:00 2001 From: hwisu Date: Sun, 11 Oct 2026 13:09:21 +0900 Subject: [PATCH] fix: patch vulnerable JVM dependencies and expand security checks --- gradle/libs.versions.toml | 9 +- gradle/verification-metadata.xml | 303 ++++++++++++++++++++++ hocuspocus-benchmark/gradle.lockfile | 47 ++-- hocuspocus-ktor-example/gradle.lockfile | 43 +-- hocuspocus-ktor/build.gradle.kts | 10 + hocuspocus-ktor/gradle.lockfile | 6 +- hocuspocus-redis/gradle.lockfile | 20 +- hocuspocus-storage-s3/build.gradle.kts | 4 +- hocuspocus-storage-s3/gradle.lockfile | 22 +- package.json | 2 +- scripts/check-runtime-vulnerabilities.mjs | 22 +- scripts/runtime-security-policy.mjs | 25 ++ scripts/runtime-security-policy.test.mjs | 94 +++++++ 13 files changed, 527 insertions(+), 80 deletions(-) create mode 100644 scripts/runtime-security-policy.mjs create mode 100644 scripts/runtime-security-policy.test.mjs 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); +});