chore(deps): update dependency org.mock-server:mockserver-netty-no-dependencies to v7.6.0 - #2386
Open
renovate[bot] wants to merge 1 commit into
Open
Conversation
…pendencies to v7.6.0
renovate
Bot
requested review from
dhoard,
fstab,
jaydeluca and
zeitlinger
as code owners
August 17, 2026 22:35
Contributor
Benchmark resultsBenchmark run finished with conclusion
Benchmark summary artifact was not found; see the workflow run for details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
7.5.0→7.6.0Release Notes
mock-server/mockserver-monorepo (org.mock-server:mockserver-netty-no-dependencies)
v7.6.0Changed
helm_sidecar_injection(admission-webhook sidecar injection),
helm_clustered_convergence, andhelm_jgroups_dns_ping(both the-clusteredInfinispan image) — now run blocking in CI instead of recording a skip. Previously the CI helm stepran with no JDK and never built those images, so all three recorded an honest but permanent SKIP — coverage that
looked green while proving nothing. A new
:maven: build container-test images (jars)step in themockserver-container-testspipeline builds themockserver-netty,mockserver-k8s-webhook, andmockserver-state-infinispanjars from the tree (once, via the Maven-in-Docker reactor) and hands them to thehelm step as Buildkite artifacts; the helm step downloads them and does cheap
docker builds (a COPY intodistroless) to produce the
-clusteredandmockserver-webhookimages, then runs the suite. The jars travel asartifacts (~200 MB total) rather than
docker saved images (~1.3 GB), mirroring the node-launcher / WAR hand-off. Bothlayers fail closed: the helm step exits non-zero if any jar artifact is absent, and the harness runs with
REQUIRE_CLUSTERED_IMAGE=true/REQUIRE_WEBHOOK_IMAGE=trueso an image that is expected-but-absent is recorded as aFAILURE, never a skip — a skip in CI is now impossible. Local
container_integration_tests/integration_tests.shis unchanged for a developer without the images: it still records a comprehensible SKIP (no fail-closed flag set),
and the harness now also builds the
mockserver-webhookimage locally (build_webhook_docker) so the sidecar caseruns blocking in local dev too — it was never built by the harness before, so that case had always skipped.
Added
check-false-green-guards.shCI gate. Rule 4 fails the build if a CI step runs thecontainer-integration harness with the helm/k3d cases active but does not export both
REQUIRE_CLUSTERED_IMAGE=trueandREQUIRE_WEBHOOK_IMAGE=true— the exact way the three image-dependent Kubernetes cases could silently revert to a greenSKIP. It is keyed on the step's behaviour (invokes
integration_tests.shwithoutSKIP_HELM_TESTS=true), not on afilename, so a rename or a second helm-running step is covered automatically. Rule 5 fails the build if a
mockserver-coretest that performs a JVM-global logging side effect (reachingLogManager.readConfiguration's handlerreset()via the staticConfigurationPropertieslogging setters or a forced fresh<clinit>) is not in thesequential-testsinclude list — the shape behind a release-blocking flake thatParallelStaticStateGuardTeststructurally cannot catch. Both rules fail closed on an empty corpus and carry a rotating allow-list. See
docs/operations/false-green-guards.md.
jarPathlauncher option and matchingMOCKSERVER_JAR_PATHenvironment variable formockserver-node, pointingstart_mockserverat a pre-provisionedmockserver-nettyjar-with-dependencies instead of downloading one fromMaven Central. When set, that exact jar is launched and no download is attempted; a configured-but-missing path is a
hard error (
... refusing to fall back to downloading a release) rather than a silent fall-back to a releasedjar, so a missing artifact fails loudly. Mirrors the existing
MOCKSERVER_BINARY_BASE_URLbring-your-own-artifactpath for the standalone binary, and serves air-gapped/corporate installs as well as testing a locally-built jar
(
jarPathtakes precedence overmockServerVersion/artifactory*; the option beats the env var). The Node launcherintegration tests now use it in CI: a new
:maven: build node launcher jarstep builds the jar from the tree andthe launcher-test step downloads it as an artifact and launches it via
MOCKSERVER_JAR_PATH— so the suite finallytests the repo's own code instead of the last release. Previously it ran a downloaded release chosen by
package.json's version, so amockserver-corefix could not green it and a regression could not red it; thatreleased jar also carried the shipped dynamic-CA generation race (fixed on master in
4cff56e61) and flaked ~8% ofruns. The launcher step fails closed if the tree-built jar is absent rather than reverting to a download, and
local
npm testoutside CI is unchanged (with neither the option nor the env var set it still downloads as before).LlmCodecStructuralContractTest), breaking theself-derivation weakness in the golden-file drift test.
LlmCodecGoldenFileTestregenerates its golden bodiesfrom the codec itself (
-Dmockserver.updateLlmGoldens=true), so a structural codec defect — a renamed field, awrong SSE event name, a dropped
finish_reason— bakes straight into its own golden and the byte-for-byte drifttest then passes forever, confirming only that the codec is consistent with itself (token counts were already
pinned separately by
shouldEncodeCanonicalTokenUsageCounts; the bodies were not). The new test asserts the livecodec output against hand-authored expectations taken from each provider's published API schema — required
fields, JSON types, the enum discriminators each provider uses (
object/type/finish_reason/stop_reason/finishReason/status/done), the tool-call envelope shape (arguments as a JSON string for OpenAI/Responsesvs a structured object for Anthropic/Gemini/Ollama), and the exact SSE event-name sequence for the event-typed
providers — across all seven chat/completion providers (Azure and Bedrock via their delegate codecs). Crucially it
never reads the golden files and is unaffected by
-Dmockserver.updateLlmGoldens=true, so regenerating goldenscannot silence it. Each named defect class was injected into a codec and confirmed to turn the test red without
regenerating goldens (OpenAI renamed
finish_reason; Anthropic SSEcontent_block_delta→content_delta; Geminidropped
finishReason; Responses renamedstatus; Ollama renamed terminaldone), then reverted. Residualstreaming-over-the-wire behaviour remains covered by
LlmAgentLoopE2eTest.HttpStateCassetteEndpointTest) and an authoritativeCassetteRegistryjavadoc note pinningthe settled decision that loading and recording register a cassette automatically. The
record_llm_fixturesand
load_expectations_from_fileMCP tools already auto-register the fixture in the process-wideCassetteRegistry(keyed by file path, origin
recorded/loaded, upserting on re-load/re-record) so it surfaces underGET /mockserver/cassettesand in the dashboard's Cassettes tab without a manualPUT /mockserver/cassettes— butnothing pinned that a so-registered cassette is actually retrievable through the GET endpoint, and the registration
policy lived only in the two MCP callers, inviting the question to be re-opened. The new test drives
GET /mockserver/cassettes(and the bare/cassettesalias) against a registry populated the way the load/recordhandlers populate it and asserts the documented body shape (path, derived filename, expectation count, origin,
lastUsed), that record-then-load on one path yields a single upserted entry, and that a server reset clears it. The
MCP tools and consumer docs (
ai_mcp_tools.html) already stated this behaviour; both are now clarified to say thefixture is registered automatically. This closes the "CassetteRegistry auto-population" product-decision item by
recording the decision in code, docs, and a test rather than leaving it to resurface.
(
.buildkite/scripts/steps/ui-client-codegen-compile.sh, wired intopipeline-ui.yml), closing the gap wherefive of the composer's seven languages had no compile check — only Java (
ui-java-codegen-compile.sh) and C#(
ComposerCodegenEquivalenceTests.cs) were gated, so an emitter bug or a client-API rename would ship brokengenerated code to users caught by nothing (the existing per-language tests only string/byte-compare the emitter
output, never feed it to a compiler). Each gate drives the shared representative composer matrix
(
extractParityCases.ts— the exactcombosthe byte-identity parity tests use) through that language's emitterand runs the lightest credible toolchain check:
python -m py_compileandruby -ccatch any emitter bug thatproduces malformed source (the strongest static check for a dynamically-typed client with no shipped type stubs),
while Go (
go build/go vet ./...) and Rust (cargo check) compile the generated code against the realin-repo
mockserver-client-go/mockserver-client-rust, so a renamed client method fails the build — the directanalog of the Java
javacgate. Node was already covered: thetsctype-proof innode.test.ts(
typecheck-node-codegen.mjs) runs undernpm test, so the orphaned-script concern was already resolved by thattest. All four new gates are proven to go red (emitter drift → non-zero exit) and green. Each phase runs in its
toolchain's Docker image via
run-in-docker.sh; setCODEGEN_COMPILE_USE_DOCKER=falsefor host validation.(
GcsBlobStoreRegistrarConfigWiringTest,AzureBlobStoreRegistrarConfigWiringTest), closing the gap whereonly S3 (
S3BlobStoreRegistrarConfigWiringTest) proved thatblobStoreTypeconfiguration is turned into aworking client. The GCS and Azure contract tests hand-build their clients (
new GcsBlobStore(storage, …),new AzureBlobStore(containerClient, …)), so the registrar that reads the bucket/container name, endpoint,credentials and project from configuration and constructs the client was never exercised — a wiring bug there
would ship silently because the contract tests bypass it. The new tests drive each registrar from configuration
only (as production does) against the same Docker emulator the contract tests use (fake-gcs-server for GCS,
Azurite
3.36.0 --skipApiVersionCheckfor Azure) and assert the wiring that can actually be got wrong:endpoint override, credentials, project id, and that a round-tripped object lands in the configured
bucket/container under the configured key prefix — verified through an independent admin client so a
mis-wired name cannot pass. Docker-gated via the canonical
DockerAvailability.isAvailable(...)probe.mockserver-testcontainers/node,mockserver-testcontainers/python)now start a real MockServer container in CI and assert against it, closing a false-green gap where both published
client libraries had jobs that passed having exercised nothing (
npm run test:unitandpytest -m "not docker"both skipped the container). Their CI steps now mount the Docker socket, run the existing integration tests that
start a
mockserver/mockservercontainer and drive it over HTTP, and — mirroring the Go/.NET/Rust steps — failclosed by grepping for an evidence marker the test prints only after a real container answered
PUT /mockserver/statuswith200. A skip (test filtered out, renamed, or Docker unusable) therefore fails theCI build loudly instead of reading as green, while the tests still degrade gracefully to a skip off-CI.
.github/workflows/dependency-submission.yml) now submits the resolved Maven dependencygraph so Dependabot vulnerability alerts stay accurate for the monorepo layout. GitHub's managed Maven
auto-submission only discovers a project at the repository root, so it silently stopped when the Java project moved
into
mockserver/— freezing the alerting graph at a pre-move snapshot that produced phantom Spring advisories andhid already-landed
log4j-api/jsoupfixes. The workflow resolves and submits themockserver/reactor (whichincludes
examples/java) and the separatemockserver-maven-pluginbuild under distinct correlators, path-gated tomockserver/**/pom.xml. Dependabot's security-update pull requests were unaffected — they read manifests directly;it was only the alerting graph that had gone stale.
.buildkite/scripts/steps/check-certificate-expiry.sh, wired into the Java pipeline) now sweeps everycommitted certificate PEM and fails the build when any certificate is already expired or expires within 30 days,
warning between 30 and 180 days. It checks every certificate in a chain file, allow-lists the one intentional
expired test fixture while asserting it stays expired, and enforces two structural invariants that previously had
no automation: every
leaf-cert.pemmust expire on or before its siblingca.pem, and the shipped default CAfiles must stay in lockstep (the PKCS#1 and PKCS#8 CA private keys are the same key, that key matches the CA
certificate, and the two committed copies of the CA certificate remain byte-identical). Certificate expiry had
previously only ever been discovered by the build going red.
check-false-green-guards.sh) that fails closed when a new "false-green" test shape isintroduced — a test or CI step that reports success while verifying nothing. The 2026-07-21 coverage audit named
these shapes but they lived only in plan documents, and the repository then produced ~a dozen fresh instances in a
single day. The guard enforces the three that can be pinned down precisely and each caused a real shipped false
green: (1) every JUnit suite gated by
Assume.assumeTrue(DockerAvailability.isAvailable(...))must be paired withassert-suite-ran.shover its reports, or a broken Docker socket skips it while the build stays green; (2) no CIstep may mount the Docker socket and then deselect the Docker-marked tests (e.g.
pytest -m "not docker"),starting no container yet passing; (3) no container-integration
logTestSkipmay park deferred work ("CI wiring isa follow-up") as a green skip. It runs always-on (a new false green can enter from any of several pipelines) and
carries a justified, self-verifying allow-list that fails if an entry no longer names what it claims. Wiring this
up also closed a live gap it found — the
Gcs/AzureRegistrarConfigWiringTestcloud suites ran under a Dockersocket in CI but were not fail-closed-asserted. See
docs/operations/false-green-guards.md.K3D_LOCAL_CA_BUNDLEhook incontainer_integration_tests/helm-deploy.shso the Helmintegration suite's k3d cluster can be stood up on a developer machine behind a corporate TLS-inspection proxy.
When set,
start-up-k8sovermounts the given combined CA bundle (system/public roots + corporate root) as thek3s node's containerd trust store at cluster-create time; when unset the
k3d cluster createcommand isbyte-identical to before, so CI (which never sets it) is unchanged. Warns rather than fails if the variable is set
but the file is missing, mirroring
LOCAL_DOCKER_CA_BUNDLEin.buildkite/scripts/run-in-docker.sh. This unblocksthe three Kubernetes test-coverage gaps that were previously (and incorrectly) deferred as impossible behind the
proxy: the host Docker daemon already trusts the corporate root so the node image pulls, but the in-node
containerd has its own public-roots-only trust store and otherwise cannot pull even the
rancher/mirrored-pausesandbox image (every pod fails sandbox creation with
x509: certificate signed by unknown authority). Seedocs/operations/build-system.md→ Local Development Behind a Corporate TLS-Inspection Proxy.previously shipped unproven.
helm_sidecar_injectiondeploys the chart withwebhook.enabled=true(self-signed TLSbootstrap Jobs + webhook handler Deployment +
MutatingWebhookConfiguration), drives a real labelled podCREATEthrough the admission path, and asserts the resulting pod spec carries the injected
mockserver-sidecarcontainer,mockserver-iptables-initinit container, andmockserver.org/injectedannotation — with a negative-control pod (noopt-in annotation) that must not be injected, so a webhook that injects unconditionally fails the test.
helm_jgroups_dns_pingdeploys two clustered replicas and asserts the headless Service is truly headless(
clusterIP: None), thatJGROUPS_DNS_QUERYis wired to its FQDN, that it resolves to ≥2 pod IPs (Endpoints plus anin-cluster
nslookup), that a ≥2-node JGroups/Infinispan view forms (the anti "two clusters of one" guard), and thatstate converges across the pods — exercising the Kubernetes DNS discovery path that
JGroupsKubernetesStackTest(XML-parse only) and
ClusteredTwoNodeTest(loopback MPING) never run. Both were proven red by degrading the exactbehaviour they name (deleting the
MutatingWebhookConfiguration; deleting the headless Service and rolling the pods).Both depend on Java-built images (the
-clusteredvariant and themockserver-webhookhandler); when those imagesare absent — e.g. the CI helm step runs with
SKIP_JAVA_BUILD=trueand no JDK — the cases record an honest SKIPrather than a misleading pass, and run blocking only where the images exist.
Changed
helm_clustered_convergenceis now a blocking container-integration test rather thannon_blocking || true. Theswallowed
k3d image import ... 2>/dev/null || trueis replaced by a deterministic import that verifies the image ispresent in the k3d node's containerd (via
crictl) before deploying, and a pre-deployensure_namespace_absentguardremoves the real back-to-back flake (
helm installinto a still-Terminatingnamespace left by a prior run/retry).When the
-clusteredimage is absent (CI helm step, no JDK) the case records an honest SKIP; when present it runsblocking so a genuine clustering regression reds the suite.
docker_compose_war_tomcatcontainer integration test (MockServer deployed as a WAR into Tomcat 10.1) nowactually runs in CI, closing a false-green gap where it silently skipped with "WAR artifact not present … CI wiring
is a follow-up" — a working behavioural test that never ran, in a demonstrated weak spot (the ROOT-context
percent-decode regression
66b5d51d2shipped and broke builds, and this is the suite that would have caught it).Buildkite steps share no filesystem, so the WAR (already built by the reactor in the
:maven: buildstep but neverpublished) is now uploaded via that step's
artifact_pathsand downloaded bycontainer-tests-run.shinto the paththe test globs. A missing WAR now fails the step closed (both an explicit presence check in
container-tests-run.shand, defensively,
prepare_warinintegration_test.shred the case) instead of skipping — a skip that reads as greenis the exact defect being closed. The case runs in the Java pipeline's master-only
:docker: container integration testsstep (triggered bymockserver/,mockserver-ui/,test-fixtures/changes); it was already declared requiredin
expected_tests.manifest, so no manifest change was needed.4.2.16.Finalto4.2.17.Finaland, in lockstep,netty-tcnative-boringssl-staticfrom2.0.78.Finalto2.0.81.Final(the tcnative version the Netty 4.2.17 BOM aligns to). The two must move together:the Netty BOM pins the transitively-resolved native-classifier tcnative jars to
2.0.81.Final, so a mismatchedmain-artifact pin fails the enforcer
DependencyConvergencerule inmockserver-core. TheNETTY_TCNATIVEbuildargs in every
docker/*/Dockerfilewere updated to match. This unblocks Dependabot PRs #2523 and #2532.netty-tcnativeversion-synchronisation step that made every Netty upgrade a convergence trap.netty-tcnative-boringssl-staticno longer has its own version property ordependencyManagementoverride — theimported
netty-bomnow governs the base artifact and every OS/arch native classifier together, so the two can nolonger diverge and break the enforcer
DependencyConvergencerule. The server jars (which ship tcnative classesbut, per #1778, no natives) are stamped at build time with their resolved tcnative version at
META-INF/mockserver-tcnative.version, and everydocker/*/Dockerfilenow derives the native.sodownload fromthat stamp instead of a hardcoded
NETTY_TCNATIVE=build arg (SHA256 verification of the download is unchanged).Both jars a Dockerfile can consume carry the stamp: the shaded
mockserver-netty-no-dependenciesjar used by thesource=copypath (release/snapshot/CI) and themockserver-nettyassembly-jar-with-dependencies.jarused bythe default
source=downloadpath (the public reference build), stamped via the same script so they cannot drift. ANetty bump therefore needs no tcnative pin update and no Docker edit, and the native
.socan never be a differentversion than the tcnative classes it pairs with.
TLSv1.2,TLSv1.3(previouslyTLSv1,TLSv1.1,TLSv1.2), andtlsAllowInsecureProtocolsnow defaults tofalse(previouslytrue).TLSv1 and TLSv1.1 are deprecated by RFC 8996 and vulnerable to BEAST/POODLE, and TLSv1.3 was previously never
negotiated unless explicitly configured. This is a breaking change for a client that can only speak TLSv1 or
TLSv1.1: its handshake to MockServer will now fail. To
restore the legacy protocols set
mockserver.tlsProtocols=TLSv1,TLSv1.1,TLSv1.2ANDmockserver.tlsAllowInsecureProtocols=true(both are required — the insecure-protocol filter strips TLSv1/TLSv1.1unless it is explicitly allowed). The inbound server always applies the strong
Http2SecurityUtilcipher suites, sono weak-cipher combination becomes reachable as a result of this change.
expiry) and the three Netty TLS integration CAs were re-issued with a 10-year validity, and every leaf they sign
was re-issued with a shorter 5-year validity so that a leaf can no longer outlive its issuing CA. The existing CA
and leaf private keys were preserved (so key encodings, Subject/Authority Key Identifiers and existing signatures
are unchanged); only the certificates were re-minted.
Security
org.apache.logging.log4j:log4j-apito2.25.5to resolve GHSA-qv9r-c865-cp47. It is pulled in transitivelyat compile scope (via
spring-boot-starter-logging->log4j-to-slf4j) and lands in a shaded artifact, so it isshipped; the pin manages
log4j-apionly (log4j-coreis not on the dependency tree).org.jsoup:jsoupto1.23.1to resolve GHSA-pmhh-3w7g-xqp8. jsoup is used only at test scope (never shipped);the pin guards against a future transitive downgrade below the fixed version.
com.azure:azure-storage-blobfrom12.29.1to12.35.0in the optionalmockserver-blob-azuremodule toresolve the
io.projectreactor.netty:reactor-netty-httpchained-redirect credential-leak advisory (fixed inreactor-netty
1.2.8). The old stack pulledazure-core-http-netty:1.15.10, which pins the vulnerablereactor-netty
1.0.48pair transitively;12.35.0pullsazure-core-http-netty:1.16.5, which advances bothreactor-netty-httpandreactor-netty-coreto1.2.18as a matched pair. This exposure surfaced only whenGitHub's Maven dependency-graph submission was restored (submission had silently frozen at a pre-move snapshot, so
the alert had been invisible to Dependabot). The whole Azure stack (azure-core
1.58.1, reactor-netty1.2.18,reactor-core
3.7.19) remains Java-8 bytecode, so the Java 17 floor is preserved. A module-scopeddependencyManagementpin ofio.projectreactor:reactor-coreto3.7.19reconciles the one internal off-by-one inthe 12.35.0 stack (azure-core declares
3.7.18, reactor-netty declares3.7.19) so the enforcerDependencyConvergencerule stays satisfied. The Docker-gated Azurite contract test moves to Azurite3.36.0with--skipApiVersionCheck, since the newer SDK negotiates a Storage REST API version that runs ahead of every releasedAzurite build.
dependency-graph submission was restored (submission had silently frozen at a pre-move snapshot on 5 May, so these
real exposures had been invisible to Dependabot). None required moving the direct dependency that introduces them:
tools.jackson.core:jackson-databind,:jackson-core,tools.jackson.dataformat:jackson-dataformat-yaml)pinned to
3.1.5to resolve GHSA-5gvw-p9qm-jgwh (vulnerable>=3.0.0, <=3.1.4). It arrives at compile scope viacom.networknt:json-schema-validator:3.0.6and is shade-relocated intoshaded_package.tools.jackson, so itships in every distributed jar — the one broad, shipped, runtime exposure of the three. All three artifacts
resolve in lockstep and are pinned together so the enforcer
DependencyConvergencerule stays satisfied. The pinis inherited by the separate
mockserver-maven-pluginbuild (same parent pom), closing its alert too.at.yawk.lz4:lz4-javapinned to1.11.1to resolve GHSA-6qcp-4vqm-vf35 (native XXHash JVM crash; vulnerable<=1.11.0). Arrives viaorg.apache.kafka:kafka-clients:3.9.2— optional inmockserver-netty, runtime inmockserver-async. Note this is theat.yawk.lz4fork coordinate, notorg.lz4.org.apache.commons:commons-compresspinned to1.27.1to resolve the>=1.21, <1.26.0advisories. Arrives attest scope via
org.testcontainers:testcontainers:1.21.4, which resolves it at1.24.0. The direct pin overridesthat transitive version without bumping Testcontainers, which is deliberately held at
1.21.4for Docker Desktop4.67+ compatibility.
forwardProxyTLSX509CertificatesTrustManagerTypeisJVMorCUSTOM— the modes that actually validate theupstream certificate chain — MockServer now verifies the upstream host name against the certificate (RFC 2818 / HTTPS
endpoint identification) on every outbound path. Netty already enabled this for client connections opened with a
known host/port, but NOT for the no-host relay paths, so verification was silently skipped on some paths and there
was no way to turn it off; without the host-name binding a certificate signed by a trusted CA for any host name would
be accepted, leaving a user who had opted into real upstream validation open to a man-in-the-middle. It is now forced
uniformly at the single point every outbound path shares (HTTP/1.1, HTTP/2, CONNECT-tunnelled relay, websocket relay,
the reverse-proxy relay — which verifies against the CONNECT target host/port, not the connected socket address — and
the LLM forward paths). It has no effect on
the default
ANYtrust manager, which deliberately trusts everything and performs no host-name verification. A newforwardProxyTLSHostnameVerificationEnabledproperty (defaulttrue) turns off just the host-name check whilekeeping chain validation, for the legitimate testing case of an upstream whose certificate host name does not match
the address connected to (it actively clears the algorithm Netty would otherwise default on); it is carried by
ConfigurationDTOand folded into the client SSL-context cache key so a runtime change takes effect. Thetrust-manager javadoc, which previously implied only
ANYskipped host-name verification, has been corrected.MockServer jar) is the trust anchor signing served traffic, naming the two supported fixes
(
dynamicallyCreateCertificateAuthorityCertificate=true, or--proxy-setup). Shipping the CA key is intentional anddocumented and the default is unchanged; the warning just makes the trade-off visible so an operator does not mistake
the bundled CA for real interception security. Logged once per JVM, never per handshake.
stat only — never a per-handshake re-parse) so a long-running server surfaces a problem instead of silently serving
it: a certificate rotated in place on disk forces a rebuild (which re-runs validation and picks up the replacement,
failing loudly if it too is expired), and an unchanged-but-expired certificate is surfaced with a single WARN. Wave 1
had deliberately left this gap (self-generated leaves already self-renew; fixed certificates were validated only at
startup).
PUT /mockserver/configurationlowers or alters TLS security posture —downgrading the forward-proxy trust manager to
ANY, turning offtlsMutualAuthenticationRequired, turning offforwardProxyTLSHostnameVerificationEnabled, or repointing the TLS key/certificate/CA paths. Control-planeauthentication is off by default, so such a runtime downgrade would otherwise be silent. The change is audited, not
blocked (blocking would be an init-only breaking change).
resilience only — certificate validity periods and extensions are unchanged). The cached server SSL context now
regenerates a fresh leaf once the current one passes a renewal threshold (80% of its validity elapsed) instead of
serving an expired certificate for the JVM lifetime; the cache-reuse decision is driven by a content signature over
the Subject-Alternative-Name set, certificate-authority identity, key/cert paths, mTLS and protocol inputs
(replacing a single consumable boolean that could return a certificate missing a just-added SAN under concurrency);
client SSL contexts and the memoised certificate authority now self-invalidate when their inputs are rotated at
runtime. Certificate generation now publishes the new private key and certificate atomically (a mid-flight failure
keeps the previous working pair instead of leaving a new key paired with the old certificate), and SNI-driven
provisioning runs off the Netty event loop with per-host coalescing. Only the leaf drives that renewal trigger: a
dynamically-generated certificate authority nearing its own expiry is warned about once (it is never rotated
automatically, which would invalidate every client trust store that imported it) rather than demanding a leaf
regeneration the certificate-authority guard can never satisfy — which would otherwise re-mint the leaf on every
handshake indefinitely.
maxSubjectAlternativeNamesproperty(default 100; when the cap is reached the genuinely oldest dynamically-discovered entry is evicted first, in FIFO
order, with a warning, while configured and default SANs such as localhost are never evicted) and now
normalise/validate each SNI hostname and
Hostheader (lowercase, length and label-charset checked) before it isadded, closing a denial-of-service vector where any client could force the leaf certificate to be re-minted with an
unbounded SAN list. Both
maxSubjectAlternativeNamesandsslCertificateLeafValidityInDaysare now carried byConfigurationDTO, so they round-trip throughGET/PUT /mockserver/configurationand can be set per-instancerather than only via the static store (a runtime change to
maxSubjectAlternativeNamestakes effect on the next SANadded).
written owner-readable-only (
0600) and atomically, and public certificates0644; a corrupt or unreadablecertificate-authority PEM now fails loudly instead of being silently treated as absent and overwritten (which would
invalidate every pinned client trust store), with cross-process locking around certificate-authority generation.
PEMToFileoff the class-load path, restoring the lazy-BouncyCastle startupoptimisation.
stays inside Apple's 825-day maximum for TLS server certificates (iOS 13 / macOS 10.15) — the previous 10-year leaf
exceeded that cap and is the likely cause of TLS handshake failures on Apple platforms
(#2531). The generated Certificate Authority keeps
its long (10-year) life, and the Wave 1 proactive renewal (regenerating the leaf once 80% of its validity has
elapsed) means a long-running server never serves an expired leaf; the previous long-lived behaviour can be restored
with the new
sslCertificateLeafValidityInDaysproperty (e.g.3650). That override is clamped to a usable range of30–3650 days (a WARN is logged when a value is clamped): a value below 30 would mint a leaf that — because the
notBeforeis back-dated 5 days — is either already expired at issuance or already past its renewal threshold (so itwould be re-minted on every handshake), and a value above 3650 could push the expiry past the X.509 date ceiling; a
non-positive value still falls back to the 397-day default. The generated leaf now also carries a
serverAuth+clientAuthextended-key-usage (Apple requiresserverAuthon the leaf independently of validity), acritical
digitalSignature+keyEnciphermentkey-usage, and an authority-key-identifier derived from the CA; theroot CA no longer carries a (non-idiomatic) extended-key-usage. Certificate serial numbers are now forced positive
as required by RFC 5280, and the HTTP/3 legacy echo-mode self-signed fallback gained a back-dated
notBefore,subject-alternative-names,
serverAuthextended-key-usage and a key-usage (it keeps the long validity because it isboth trust anchor and server certificate with no renewal loop).
Removed
PKCS1CertificateAuthorityPrivateKey.pemresource from the publishedmockserver-corejar. Itwas the explicit PKCS#1 half of an encoding pair added in 2020 for the since-removed JDK key/certificate builder,
whose deletion in 2022 left it with no references for around four years. It was byte-identical to the original
CertificateAuthorityPrivateKey.pem, which is retained (it backs a published raw-URL link and remains the stablePKCS#1 form); the code continues to load
PKCS8CertificateAuthorityPrivateKey.pem.Fixed
mockserver-coreunit-test flake (ConfigurationValueRedactionTest/ConfigurationDTOCredentialMaskingTestfailing withExpected: is <1> but: was <0>when asserting an exact count ofcaptured log records). These tests attach a
java.util.logginghandler and assert what is emitted. Two other tests —ClassInitializationDeadlockTestandConfigurationPropertiesInitializationTest— force a fresh<clinit>ofMockServer classes in an isolated child-first classloader; that initialisation reaches
MockServerLogger.installDefaultJavaLoggingFormat()→LogManager.getLogManager().readConfiguration(...), andjava.util.logging.LogManageris a JVM-global singleton the child-first classloader does not isolate, so itsreadConfigurationperforms areset()that removes every handler from every logger in the JVM. When one of thoseclasses ran in the parallel Surefire phase alongside a handler-capturing test, the reset detached the capturing
handler mid-test and a subsequent emit went uncounted. Both fresh-
<clinit>classes are now pinned to the sequential(
parallel=none) Surefire phase inmockserver-core/pom.xml, so they can never run concurrently with ahandler-capturing test. No production behaviour changes.
webhook.tls.setupImage: bitnami/kubectl:1.31no longer exists — Bitnami withdrew the tag from Docker Hub — so bothbootstrap Jobs failed, the
MutatingWebhookConfiguration'scaBundlewas never patched, and becausefailurePolicy: Failthe webhook then rejected every matched pod CREATE. Anyone enablingwebhook.enabled=truefrom the shipped defaults got an admission path that blocked pod creation rather than injecting a sidecar. The
bootstrap now uses
registry.k8s.io/ingress-nginx/kube-webhook-certgen, the purpose-built tool for exactly thisjob, published on the most stable-publication registry available and self-contained (no shell, no
opensslCLI, noinstall-at-runtime step that would fail on an airgapped or proxied cluster). Phase 2's ClusterRole also gains the
updateverb, which the patch step genuinely needs.helm_sidecar_injectionnow renders and asserts the chartdefault is pullable rather than overriding it, so this cannot silently rot again.
WebhookServerparsed PKCS#8 onlyand explicitly rejected anything else, which broke the cert-manager path as well: the chart's
Certificaterequests
algorithm: RSAwithoutencoding: PKCS8, i.e. PKCS#1, so a cert-manager-issued key could not be loaded.It now accepts any standard unencrypted PEM private key — PKCS#8, PKCS#1, and SEC1 EC. This coupling is why the
broken bootstrap image above could not simply be swapped: the handler only tolerated the one format that one
withdrawn image happened to emit.
MockServer serving broken TLS for the rest of the process's lifetime. When
dynamicallyCreateCertificateAuthorityCertificateis enabled, two startup paths could generate the CAconcurrently — the proxy-setup log (
proxySetupLogging, on by default in the CLI) writing the CA to disk, and thefirst HTTPS handshake building the server certificate. Generation was serialised only by a cross-process file lock;
a second lock attempt within the same JVM threw
OverlappingFileLockException, which was caught and treated as"proceed without serialisation", so both threads minted different CA key pairs and interleaved their writes. The
result was a torn CA key/certificate pair on disk: every leaf certificate was then signed with one generation's CA
key but verified against the other's CA public key, failing with
SignatureException: certificate does not verify with supplied key— and because the mismatched CA was memoised, every subsequent TLS handshake failed for thelife of the process. CA generation (and the paired load of the CA key + certificate) is now serialised within the
JVM on a per-directory monitor in addition to the cross-process file lock, so the key and certificate are always
published from the same generation. Embedded
ClientAndServerusers were unaffected (they defaultproxySetupLoggingoff); standalone, CLI and Docker users generating a dynamic CA could hit it intermittently.mockserver/andmockserver/mockserver-maven-plugin/were modernised from the legacy Takari format to theApache
only-scriptwrapper (wrapperVersion3.3.4, Maven pinned at 3.9.16), so themaven-wrapper-updatercanparse them;
mockserver-vscodewas split into its own npm job (a lockfileEOVERRIDEthere no longer aborts theother npm directories) and pinned
js-yamlto^4.1.1viaoverrides; and/.opencode(which had only anorphan lockfile and no tracked manifest) was removed from the npm directories and its lockfile untracked.
X509Certificatemodel that contains certificate metadata without an underlying Java certificate nolonger throws a
NullPointerException; metadata-only and certificate-backed models now both preserve their statewhen cloned (#2527).
resolves to. MockServer parses both documents with Jackson and hands json-unit the resulting nodes to avoid
re-parsing on every match, but json-unit picks its provider by asking each in turn whether it claims the value
and falling back to the last one registered — and only its Jackson provider claims a Jackson node. Where another
provider won (for example
org.json, whether because Jackson was not visible to json-unit or becausejson-unit.librariespinned it) every JSON match threwUnsupported type class com.fasterxml.jackson.databind.node.ObjectNodeand no JSON body ever matched. MockServer now falls back togiving json-unit the raw JSON text, which it parses with whichever provider it resolved to
(#2496).
only
exception while perform json match failedand the exception was recorded solely atTRACE, so at thedefault log level there was no way to tell a malformed body from a missing class from a runtime error. The cause
is now included in the reported difference, as it already was for the XML schema, JSON path and JSON-RPC matchers.
dompurify3.4.12→3.4.13in the bundled dashboard (mockserver-ui),brace-expansion5.0.8→5.0.9(ReDoS) inmockserver-testcontainers/node, andjs-yaml4.3.0→4.3.1inmockserver-client-node,mockserver-nodeandmockserver-testcontainers/node. Thebrace-expansionfix bumps only the parent so theminimatch/archiverglob split is preserved (a blanket override previously broke
archiver); nobrace-expansionoverride was added.generation, every MockServer node in a cluster mints its own distinct CA, so a client that trusts one node's
mockserver-ca.pemgets an intermittent TLS validation failure when a load balancer routes it to another node.StateBackendFactory.create()now logs a WARN when it detectsclusterEnabled=truetogether withdynamicallyCreateCertificateAuthorityCertificate=true, and the limitation and its fix are now documented indocs/code/clustered-state.md,docs/code/tls-and-security.md, the Helm chart README, and the CentralizedDeployment consumer page.
Kubernetes Secret — the supported fix for the clustered CA defect above. New opt-in values
app.tls.*create (orreference an existing) Secret, mount it read-only, and set
certificateAuthorityCertificate/certificateAuthorityPrivateKeywithdynamicallyCreateCertificateAuthorityCertificate=falseon every pod. A CAprivate key now lands in a Secret rather than a ConfigMap. Also added
app.dynamicCertificateDir.*(a writableemptyDirfor the single-replica dynamic-CA case, so certificate writes no longer depend on a non-writable workingdirectory) and
app.extraEnv(arbitrary container environment variables, enabling anyMOCKSERVER_*property thechart does not expose directly). All new values are opt-in and default to today's behaviour.
MOCKSERVER_PROPERTY_FILEenv var wasgated on an undeclared
app.mountConfigMapvalue (always false).app.mountConfigMapis now a declared value(default
false, preserving prior behaviour) so external-ConfigMap users can opt into having MockServer pointed atthe mounted
mockserver.properties.Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.