Skip to content

[java] add test to ensure shipped bytecode supports Java 11 - #18166

Merged
titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:java-bytecode-check
Oct 13, 2026
Merged

titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:java-bytecode-check

Conversation

@titusfortner

@titusfortner titusfortner commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

🔗 Related Issues

💥 What does this PR do?

  • Restores Java 11 support for selenium-node-kubernetes by pinning fabric8 back to 7.9.0
  • Adds //java:bytecode-version-test, which fails when anything Selenium ships, including third-party jars, contains classes newer than Java 11

🔧 Implementation Notes

  • Nothing caught this because --release 11 only checks our own sources against the Java 11 API, javac ignores the class version of jars on the classpath, and all Java tests run on JDK 25
  • The test scans the runtime jars of the published java_exports (what Maven users resolve) plus the server deploy jar, so compiler plugins like NullAway (Java 17) and test-only jars aren't flagged
  • module-info.class and META-INF/versions/** are skipped because multi-release entries target newer Java by design (byte-buddy, Bouncy Castle and reactor-core would fail otherwise)
  • java:update and Renovate now cap fabric8 below 8, matching the existing byte-buddy and graphql caps
  • The Kubernetes export declares jackson-core and jackson-databind at the Jackson BOM's version (2.22.3), so its POM overrides the 2.21.4 that fabric8 7.9.0 brings in, which has known advisories; this comes out with the fabric8 cap

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code (Claude Opus 5.5)
    • What was generated: the bytecode check, the fabric8 pin and Jackson override, and this description
    • I reviewed all AI output and can explain the change

🔄 Types of changes

  • Bug fix (backwards compatible)

@selenium-ci selenium-ci added B-grid Everything grid and server related C-java Java Bindings B-build Includes scripting, bazel and CI integrations labels Oct 13, 2026
@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Restore Java 11 support and check shipped Java bytecode

🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Pin fabric8 to 7.9.0 to restore Java 11 support for the Kubernetes node.
• Reject shipped jars containing base classes newer than Java 11, including third-party
 dependencies.
• Align Kubernetes Jackson dependencies with 2.22.3 and prevent automated fabric8 8.x upgrades.
Diagram

graph TD
  P["Pinned dependencies"] --> A["Published runtime jars"] --> C["Jar manifest"] --> D["Bytecode scanner"] --> E{"Java 11 safe?"} --> F["Pass test"]
  B["Server deploy jar"] --> C
  E --> G["Fail build"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Run compatibility tests on JDK 11
  • ➕ Can catch runtime linkage or API failures that class-file inspection cannot.
  • ➖ Requires JDK 11 CI coverage and representative execution paths; unused incompatible classes may go undetected.
  • ➖ Does not provide the same direct inventory of incompatible shipped jars.

Recommendation: Keep the shipped-artifact bytecode check: it detects the regression directly, including third-party jars that --release 11 does not constrain, without requiring every class to execute. JDK 11 smoke tests would be a useful complement for runtime behavior, not a replacement.

Files changed (11) +599 / -433

Bug fix (1) +5 / -0
BUILD.bazelOverride Kubernetes Jackson runtime dependencies +5/-0

Override Kubernetes Jackson runtime dependencies

• Adds Jackson core and databind as runtime dependencies of the Kubernetes Maven export. This makes the newer selected versions explicit in its published dependency metadata rather than relying on fabric8 7.x's versions.

java/src/org/openqa/selenium/grid/node/kubernetes/BUILD.bazel

Tests (6) +248 / -1
BUILD.bazelRegister the shipped-bytecode compatibility test +19/-1

Register the shipped-bytecode compatibility test

• Adds '//java:bytecode-version-test' with a Java 11 ceiling. It covers runtime dependencies of the client, Kubernetes node, and JDBC exports, plus the server deploy jar.

java/BUILD.bazel

defs.bzlExpose the bytecode test rule +2/-0

Expose the bytecode test rule

• Loads and re-exports the new private Starlark rule for use in Java BUILD files.

java/defs.bzl

bytecode_version_test.bzlBuild a runtime-jar manifest for the bytecode test +57/-0

Build a runtime-jar manifest for the bytecode test

• Adds a rule that lists transitive runtime jars and explicit jars by artifact label and supplies them as test runfiles. Its macro runs the Java scanner with the manifest and maximum Java version.

java/private/bytecode_version_test.bzl

BUILD.bazelDefine the bytecode scanner library +14/-0

Define the bytecode scanner library

• Adds a Java library for the scanner, compiled using the tools Java release setting and made available to the test rule.

java/src/dev/selenium/tools/bytecode/BUILD.bazel

BytecodeVersionCheck.javaCheck shipped class-file versions +155/-0

Check shipped class-file versions

• Reads jar class headers and fails when base classes exceed the configured Java version, reporting offending jars and example classes. Skips multi-release entries and module descriptors, and fails if an artifact has no classes to check.

java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java

BUILD.bazelExpose the server deploy jar to the Java test package +1/-0

Expose the server deploy jar to the Java test package

• Grants the Java package visibility to the server binary target so the compatibility test can inspect its deploy jar.

java/src/org/openqa/selenium/grid/BUILD.bazel

Other (4) +346 / -432
MODULE.bazelPin fabric8 7.9.0 and select Jackson 2.22.3 +7/-4

Pin fabric8 7.9.0 and select Jackson 2.22.3

• Downgrades the four explicitly requested fabric8 artifacts from 8.0.0 to 7.9.0 for Java 11 compatibility. Adds the Jackson BOM and direct core and databind artifacts so compatible, newer Jackson versions can be selected.

MODULE.bazel

maven_install.jsonRegenerate the Maven dependency lockfile +326/-427

Regenerate the Maven dependency lockfile

• Records fabric8 7.9.0, its changed Vert.x and transitive dependency graph, and Jackson 2.22.3 artifacts. Updates corresponding checksums and dependency metadata.

java/maven_install.json

java.rakeKeep java:update below fabric8 8 +8/-1

Keep java:update below fabric8 8

• Caps fabric8 updates at the latest stable release below 8 while Selenium supports Java 11. Extends the Maven version lookup helper with an optional upper bound.

rake_tasks/java.rake

renovate.jsonPrevent Renovate from proposing fabric8 8.x +5/-0

Prevent Renovate from proposing fabric8 8.x

• Adds a package rule restricting all 'io.fabric8' artifacts to versions below 8, preventing the incompatible dependency upgrade from recurring.

renovate.json

@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Java 11 regressions can ship unchecked ✗ Dismissed
Description
The new bytecode test omits the separately published selenium-manager export from its deps and
does not include it through the server deploy jar. A future manager dependency or class compiled
above Java 11 can therefore be released without this test detecting it, leaving the advertised Java
11 compatibility check incomplete.
Code

java/BUILD.bazel[R68-72]

+    deps = [
+        "//java/src/org/openqa/selenium:client-combined",
+        "//java/src/org/openqa/selenium/grid/node/kubernetes",
+        "//java/src/org/openqa/selenium/grid/sessionmap/jdbc",
+    ],
Evidence
The checklist requires focused tests that reliably verify the relevant behavior. The new manifest
rule scans only the explicitly supplied deps and jars, and the PR supplies three dependencies
plus the server deploy jar. The repository publishes selenium-manager as a separate export, while
the server deploy jar's runtime dependencies do not include it, so manager bytecode is outside the
test's coverage.

AGENTS.md: Use Focused, Reliable Tests Without Misleading Mocks
java/BUILD.bazel[57-72]
java/private/bytecode_version_test.bzl[7-14]
java/src/org/openqa/selenium/grid/BUILD.bazel[103-121]
java/src/org/openqa/selenium/manager/BUILD.bazel[5-34]
java/src/org/openqa/selenium/grid/BUILD.bazel[160-183]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The bytecode compatibility test only scans three exports plus the server deploy jar, but `selenium-manager` is a separately published Java export and is not included by those inputs. Its classes and runtime dependencies can exceed Java 11 without failing the check.

## Fix Focus Areas
- java/BUILD.bazel[68-72]
- java/src/org/openqa/selenium/manager/BUILD.bazel[4-34]

## Recommended Fix
Add `//java/src/org/openqa/selenium/manager` to the bytecode test inputs, or otherwise construct the test inputs from every published Java export so that all shipped Selenium bytecode and its runtime dependencies are checked against Java 11.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. The bytecode check fails before scanning ✗ Dismissed
Description
_bytecode_manifest_impl writes external artifacts using raw file.short_path, while
BytecodeVersionCheck opens each manifest path directly with ZipFile. When the manifest contains
external Maven jars whose paths require runfiles conversion, the test can abort with
NoSuchFileException before checking those jars.
Code

java/private/bytecode_version_test.bzl[R4-5]

+def _short_path(file):
+    return file.short_path
Evidence
The new manifest rule writes raw file.short_path values, including paths for external
repositories, and supplies the jars through runfiles. The Java checker parses those values as
filesystem paths and passes them directly to ZipFile; existing repository helpers document that
external short paths require runfiles conversion, so external runtime jars can prevent the focused
bytecode test from scanning its inputs.

AGENTS.md: Add Focused Tests That Exercise Real API Contracts
java/private/bytecode_version_test.bzl[4-5]
java/private/bytecode_version_test.bzl[13-25]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[72-76]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-107]
dotnet/private/docfx.bzl[3-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The bytecode manifest records raw `short_path` values, which can use `../repo/...` for external repositories, while the Java checker opens those paths directly. External Maven jars can therefore cause the test to fail before their bytecode is scanned.

## Fix Focus Areas
- java/private/bytecode_version_test.bzl[4-14]
- java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[72-76]

## Recommended Fix
Convert external repository paths to the corresponding runfiles paths when writing the manifest, or resolve manifest entries through the test runfiles mechanism before opening them. Ensure both workspace-local and external Maven jars are resolved and scanned successfully under Bazel.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Java 11-incompatible overrides pass the check ✗ Dismissed
Description
isBaseClass skips every META-INF/versions/ entry, including classes in directories 9 through 11.
If a future dependency places bytecode newer than Java 11 in one of those directories, Java 11 can
select it at runtime while this test reports success.
Code

java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[135]

+          && !entryName.startsWith("META-INF/versions/")
Evidence
The test sets its maximum to Java 11. Only entries accepted by isBaseClass reach the class-version
comparison, but the Java 11 JAR specification says its class loader searches versioned directories
from 11 down to 9 before the base entry.

java/BUILD.bazel[57-64]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-137]
🌐 On Java release N, a multi-release JAR's class loader searches versioned directories from N down to 9 before the base directory.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The checker skips multi-release entries that Java 11 can load, allowing incompatible bytecode in versioned directories 9–11 to pass.
## Fix Focus Areas
- java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-137]
## Recommended Fix
Inspect versioned class entries whose directory version is no greater than the configured Java maximum; continue excluding entries targeting later Java releases. Add a test jar with an incompatible class under `META-INF/versions/11`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Repin drops Jackson 2 below its security patches ✓ Resolved
Description
The repinned lockfile resolves com.fasterxml.jackson.core:jackson-core and the newly added
jackson-databind/jackson-datatype-jsr310/jackson-dataformat-yaml to 2.21.4, and it records a
conflict resolution that moves a requested jackson-core:2.21.5 down to 2.21.4 (before this PR
jackson-core was at 2.21.6). Jackson 2.21.5 fixed several databind CVEs, including the
@JsonIgnoreProperties/@JsonView bypasses and InetAddress DNS resolution, so every Bazel-built
Kubernetes node target and test classpath now runs fabric8 on a known-vulnerable Jackson.
Code

java/maven_install.json[R1760-1761]

+    "com.fasterxml.jackson.core:jackson-annotations:2.20": "com.fasterxml.jackson.core:jackson-annotations:2.21",
+    "com.fasterxml.jackson.core:jackson-core:2.21.5": "com.fasterxml.jackson.core:jackson-core:2.21.4",
Evidence
The lockfile diff moves jackson-core from 2.21.6 to 2.21.4 and adds databind at 2.21.4. The
conflict_resolution entry shows a dependency asking for 2.21.5 being resolved down to 2.21.4.
MODULE.bazel has no explicit Jackson pin, so nearest-wins resolution picks the older version. The
Jackson 2.21.5 release notes list the CVE fixes that 2.21.4 does not have.

java/maven_install.json[505-540]
java/maven_install.json[1759-1771]
🌐 2.21.5 includes fixes for CVE-2026-54515, GHSA-mhm7-754m-9p8w, CVE-2026-77310 and CVE-2026-59889 in databind.
🌐 Lists further jackson-core CVEs (e.g. CVE-2026-89407 regex DoS in NumberInput.looksLikeValidNumber).

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After going back to fabric8 7.9.0, Jackson 2 resolves to 2.21.4. That is below the 2.21.5+ security patches, and below the 2.21.6 jackson-core that was pinned before this PR.

## Fix Focus Areas
- MODULE.bazel[211-220]
- java/maven_install.json[505-540]

## Recommended Fix
Add explicit `com.fasterxml.jackson.core:jackson-core`, `jackson-databind`, `jackson-annotations`, `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` and `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml` entries at the latest 2.21.x patch (Java 8 bytecode) to `maven.install` artifacts, or use the jackson-bom. Then repin so `maven_install.json` no longer downgrades jackson-core. Run `//java:bytecode-version-test` to confirm the result is still Java 11 compatible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: Auto: 🧠 Deep: Multiple independent logic paths include scanner, builds, dependencies, tests, and workflow.

Grey Divider

Tip of the day
💡 Did you know, you can turn these tips off under Display > Comment parts

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit b594516

Results up to commit ad7efbb 🧠 Deep


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. The bytecode check fails before scanning ✗ Dismissed
Description
_bytecode_manifest_impl writes external artifacts using raw file.short_path, while
BytecodeVersionCheck opens each manifest path directly with ZipFile. When the manifest contains
external Maven jars whose paths require runfiles conversion, the test can abort with
NoSuchFileException before checking those jars.
Code

java/private/bytecode_version_test.bzl[R4-5]

+def _short_path(file):
+    return file.short_path
Evidence
The new manifest rule writes raw file.short_path values, including paths for external
repositories, and supplies the jars through runfiles. The Java checker parses those values as
filesystem paths and passes them directly to ZipFile; existing repository helpers document that
external short paths require runfiles conversion, so external runtime jars can prevent the focused
bytecode test from scanning its inputs.

AGENTS.md: Add Focused Tests That Exercise Real API Contracts
java/private/bytecode_version_test.bzl[4-5]
java/private/bytecode_version_test.bzl[13-25]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[72-76]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-107]
dotnet/private/docfx.bzl[3-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The bytecode manifest records raw `short_path` values, which can use `../repo/...` for external repositories, while the Java checker opens those paths directly. External Maven jars can therefore cause the test to fail before their bytecode is scanned.

## Fix Focus Areas
- java/private/bytecode_version_test.bzl[4-14]
- java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[72-76]

## Recommended Fix
Convert external repository paths to the corresponding runfiles paths when writing the manifest, or resolve manifest entries through the test runfiles mechanism before opening them. Ensure both workspace-local and external Maven jars are resolved and scanned successfully under Bazel.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Java 11-incompatible overrides pass the check ✗ Dismissed
Description
isBaseClass skips every META-INF/versions/ entry, including classes in directories 9 through 11.
If a future dependency places bytecode newer than Java 11 in one of those directories, Java 11 can
select it at runtime while this test reports success.
Code

java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[135]

+          && !entryName.startsWith("META-INF/versions/")
Evidence
The test sets its maximum to Java 11. Only entries accepted by isBaseClass reach the class-version
comparison, but the Java 11 JAR specification says its class loader searches versioned directories
from 11 down to 9 before the base entry.

java/BUILD.bazel[57-64]
java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-137]
🌐 On Java release N, a multi-release JAR's class loader searches versioned directories from N down to 9 before the base directory.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The checker skips multi-release entries that Java 11 can load, allowing incompatible bytecode in versioned directories 9–11 to pass.
## Fix Focus Areas
- java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java[106-137]
## Recommended Fix
Inspect versioned class entries whose directory version is no greater than the configured Java maximum; continue excluding entries targeting later Java releases. Add a test jar with an incompatible class under `META-INF/versions/11`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Repin drops Jackson 2 below its security patches ✓ Resolved
Description
The repinned lockfile resolves com.fasterxml.jackson.core:jackson-core and the newly added
jackson-databind/jackson-datatype-jsr310/jackson-dataformat-yaml to 2.21.4, and it records a
conflict resolution that moves a requested jackson-core:2.21.5 down to 2.21.4 (before this PR
jackson-core was at 2.21.6). Jackson 2.21.5 fixed several databind CVEs, including the
@JsonIgnoreProperties/@JsonView bypasses and InetAddress DNS resolution, so every Bazel-built
Kubernetes node target and test classpath now runs fabric8 on a known-vulnerable Jackson.
Code

java/maven_install.json[R1760-1761]

+    "com.fasterxml.jackson.core:jackson-annotations:2.20": "com.fasterxml.jackson.core:jackson-annotations:2.21",
+    "com.fasterxml.jackson.core:jackson-core:2.21.5": "com.fasterxml.jackson.core:jackson-core:2.21.4",
Evidence
The lockfile diff moves jackson-core from 2.21.6 to 2.21.4 and adds databind at 2.21.4. The
conflict_resolution entry shows a dependency asking for 2.21.5 being resolved down to 2.21.4.
MODULE.bazel has no explicit Jackson pin, so nearest-wins resolution picks the older version. The
Jackson 2.21.5 release notes list the CVE fixes that 2.21.4 does not have.

java/maven_install.json[505-540]
java/maven_install.json[1759-1771]
🌐 2.21.5 includes fixes for CVE-2026-54515, GHSA-mhm7-754m-9p8w, CVE-2026-77310 and CVE-2026-59889 in databind.
🌐 Lists further jackson-core CVEs (e.g. CVE-2026-89407 regex DoS in NumberInput.looksLikeValidNumber).

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After going back to fabric8 7.9.0, Jackson 2 resolves to 2.21.4. That is below the 2.21.5+ security patches, and below the 2.21.6 jackson-core that was pinned before this PR.

## Fix Focus Areas
- MODULE.bazel[211-220]
- java/maven_install.json[505-540]

## Recommended Fix
Add explicit `com.fasterxml.jackson.core:jackson-core`, `jackson-databind`, `jackson-annotations`, `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` and `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml` entries at the latest 2.21.x patch (Java 8 bytecode) to `maven.install` artifacts, or use the jackson-bom. Then repin so `maven_install.json` no longer downgrades jackson-core. Run `//java:bytecode-version-test` to confirm the result is still Java 11 compatible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread java/private/bytecode_version_test.bzl
Comment thread java/src/dev/selenium/tools/bytecode/BytecodeVersionCheck.java
Comment thread java/maven_install.json Outdated
@titusfortner
titusfortner marked this pull request as draft October 13, 2026 17:46
@titusfortner titusfortner changed the title [java] restore Java 11 support for selenium-node-kubernetes and check shipped bytecode [java] add test to ensure shipped bytecode supports Java 11 Oct 13, 2026
@selenium-ci

Copy link
Copy Markdown
Member

/agentic_review

@titusfortner
titusfortner marked this pull request as ready for review October 13, 2026 18:10
@selenium-ci

Copy link
Copy Markdown
Member

/agentic_review

Comment thread java/BUILD.bazel
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit b594516

@titusfortner
titusfortner merged commit eac9fad into SeleniumHQ:trunk Oct 13, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-grid Everything grid and server related C-java Java Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants