@@ -59,7 +59,14 @@ SPDX-License-Identifier: MIT
5959 <lombok .version>1.18.46</lombok .version>
6060 <errorprone .version>2.50.0</errorprone .version>
6161 <nullaway .version>0.14.0</nullaway .version>
62+ <!-- Build-time Checker Framework processor. Runs on the CI JDK, never ships, so it
63+ tracks the newest release. Deliberately NOT the same property as the annotations
64+ below: those are shipped and must stay Java 8 bytecode. -->
6265 <checker .version>4.2.2</checker .version>
66+ <!-- Shipped checker-qual annotations. Last release whose classes are class-file
67+ major 52; 4.0.0 moved the line to Java 11. See the dependency for why the pin,
68+ not just the optional flag, is what protects the artifact. -->
69+ <checker .qual.version>3.55.1</checker .qual.version>
6370 <jackson .version>2.22.2</jackson .version>
6471 <reactor .version>3.8.7</reactor .version>
6572 <slf4j .version>2.0.18</slf4j .version>
@@ -183,10 +190,20 @@ SPDX-License-Identifier: MIT
183190 <artifactId >jspecify</artifactId >
184191 <version >${jspecify.version} </version >
185192 </dependency >
193+ <!-- Pinned to the newest Java 8 line on purpose. checker-qual 4.x is Java 11
194+ bytecode (class-file major 55) and this artifact targets Java 8; its
195+ annotations are @Retention(RUNTIME), so anything reflecting over an
196+ annotated element (Jackson does) loads them and a Java 8 JVM then throws
197+ UnsatisfiedClassVersionError. Marking it optional keeps it out of
198+ consumers' transitive graph but NOT out of the fat jar; the
199+ jar-with-dependencies descriptor filters on scope only, so the version
200+ pin is the part that actually protects the shipped artifact.
201+ 3.55.1 is the last release whose classes are major 52; the break is at 4.0.0. -->
186202 <dependency >
187203 <groupId >org.checkerframework</groupId >
188204 <artifactId >checker-qual</artifactId >
189- <version >${checker.version} </version >
205+ <version >${checker.qual.version} </version >
206+ <optional >true</optional >
190207 </dependency >
191208 <dependency >
192209 <groupId >com.fasterxml.jackson.core</groupId >
@@ -200,11 +217,34 @@ SPDX-License-Identifier: MIT
200217 <version >${slf4j.version} </version >
201218 </dependency >
202219 <!-- Default SLF4J binding shipped with this library. Runtime scope: not
203- required on the compile classpath, only loaded at JVM startup. -->
220+ required on the compile classpath, only loaded at JVM startup.
221+
222+ slf4j-simple rather than logback, for two independent reasons:
223+
224+ (1) Java 8. Every logback release from 1.4.0 on is Java 11 bytecode, so
225+ LogbackServiceProvider cannot load on the Java 8 this artifact targets:
226+ SLF4J's ServiceLoader finds it at startup and the JVM throws
227+ UnsupportedClassVersionError. The Java 8 line (1.3.x) would fix that but
228+ is end-of-life: 1.3.16 (2025-10-29) is its last release, and every logback
229+ CVE disclosed since has been fixed only in 1.5.x/1.6.x with no backport
230+ (CVE-2026-1225, CVE-2026-9828, CVE-2026-10532; CVE-2026-19880 is fixed only in 1.6.3,
231+ which is Java 11 bytecode and therefore unreachable from here).
232+
233+ (2) Attack surface. Essentially every logback CVE lives in its configuration
234+ or socket layers: Janino expression evaluation, HardenedObjectInputStream,
235+ SaxEventRecorder, SocketReceiver. slf4j-simple is six classes with no
236+ config parser, no socket server and no deserialization, so those classes
237+ of defect cannot exist in it. It also ships in the same release train as
238+ slf4j-api above, so the two can never drift apart.
239+
240+ What consumers lose: no logback.xml. Configure via a classpath
241+ simplelogger.properties or -Dorg.slf4j.simpleLogger.* system properties.
242+ Anyone who wants logback (or any other binding) excludes this one and
243+ declares their own; that is the point of the SLF4J split. -->
204244 <dependency >
205- <groupId >ch.qos.logback </groupId >
206- <artifactId >logback-classic </artifactId >
207- <version >${logback .version} </version >
245+ <groupId >org.slf4j </groupId >
246+ <artifactId >slf4j-simple </artifactId >
247+ <version >${slf4j .version} </version >
208248 <scope >runtime</scope >
209249 </dependency >
210250 <!-- @IgnoreJRERequirement marker used by OSInfo (vendored from xerial/sqlite-jdbc)
@@ -651,6 +691,26 @@ SPDX-License-Identifier: MIT
651691 <configuration >
652692 <!-- -XX:+EnableDynamicAgentLoading: silences the JDK 21 byte-buddy self-attach agent warning that intermittently corrupts Surefire's fork channel ("Corrupted channel ..." / bogus "timeout in the fork"). See workspace policy ci-test-diagnostics.md section 2.1: https://github.com/bernardladenthin/workspace/blob/main/policies/ci-test-diagnostics.md - root cause: https://github.com/raphw/byte-buddy/issues/1639 -->
653693 <argLine >@{argLine} -Xmx2g -XX:ErrorFile=hs_err_pid%p.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=. -XX:+EnableDynamicAgentLoading</argLine >
694+ <!--
695+ Keep the shipped SLF4J binding OFF the test classpath.
696+
697+ slf4j-simple is a runtime-scope dependency, and runtime scope is part of
698+ the test classpath too. LogCaptor (io.github.hakky54, test scope) brings
699+ logback, so both providers would be present, SLF4J would pick one
700+ arbitrarily, and LogCaptor fails outright when it does not get logback:
701+ "SLF4J Logger implementation should be of the type
702+ [ch.qos.logback.classic.Logger] but found [org.slf4j.simple.SimpleLogger]"
703+ (7 tests across LoggingSmokeTest, TimingsLoggerTest, ChatResponseParserTest
704+ and CompletionResponseParserTest).
705+
706+ Excluding it here leaves logback as the sole provider during tests and does
707+ not touch the artifact: the shipped jar and the fat jar still carry
708+ slf4j-simple. The tests assert what our code logs, which is binding-agnostic;
709+ which binding renders it is not what they are about.
710+ -->
711+ <classpathDependencyExcludes >
712+ <classpathDependencyExclude >org.slf4j:slf4j-simple</classpathDependencyExclude >
713+ </classpathDependencyExcludes >
654714 <!--
655715 Capture each test class's stdout/stderr into
656716 target/surefire-reports/<class>-output.txt. When a native crash
@@ -2145,9 +2205,12 @@ SPDX-License-Identifier: MIT
21452205 <groupId >org.apache.maven.plugins</groupId >
21462206 <artifactId >maven-assembly-plugin</artifactId >
21472207 <configuration >
2148- <descriptorRefs >
2149- <descriptorRef >jar-with-dependencies</descriptorRef >
2150- </descriptorRefs >
2208+ <!-- Our own descriptor, not the predefined jar-with-dependencies ref: it is
2209+ a verbatim copy of that one plus simplelogger.properties, which must
2210+ reach the runnable jar without being published in the library jar. -->
2211+ <descriptors >
2212+ <descriptor >src/assembly/fat-jar.xml</descriptor >
2213+ </descriptors >
21512214 <archive >
21522215 <manifest >
21532216 <mainClass >net.ladenthin.llama.server.ServerLauncher</mainClass >
0 commit comments