Skip to content

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
mainfrom
renovate/org.mock-server-mockserver-netty-no-dependencies-7.x
Open

chore(deps): update dependency org.mock-server:mockserver-netty-no-dependencies to v7.6.0#2386
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/org.mock-server-mockserver-netty-no-dependencies-7.x

Conversation

@renovate

@renovate renovate Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
org.mock-server:mockserver-netty-no-dependencies (source) 7.5.07.6.0 age confidence

Release Notes

mock-server/mockserver-monorepo (org.mock-server:mockserver-netty-no-dependencies)

v7.6.0

Changed
  • The three Kubernetes container-integration cases that need Java-built images — helm_sidecar_injection
    (admission-webhook sidecar injection), helm_clustered_convergence, and helm_jgroups_dns_ping (both the
    -clustered Infinispan image) — now run blocking in CI instead of recording a skip. Previously the CI helm step
    ran 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 the
    mockserver-container-tests pipeline builds the mockserver-netty, mockserver-k8s-webhook, and
    mockserver-state-infinispan jars from the tree (once, via the Maven-in-Docker reactor) and hands them to the
    helm step as Buildkite artifacts; the helm step downloads them and does cheap docker builds (a COPY into
    distroless) to produce the -clustered and mockserver-webhook images, then runs the suite. The jars travel as
    artifacts (~200 MB total) rather than docker saved images (~1.3 GB), mirroring the node-launcher / WAR hand-off. Both
    layers 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=true so an image that is expected-but-absent is recorded as a
    FAILURE, never a skip — a skip in CI is now impossible. Local container_integration_tests/integration_tests.sh
    is 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-webhook image locally (build_webhook_docker) so the sidecar case
    runs blocking in local dev too — it was never built by the harness before, so that case had always skipped.
Added
  • Two new rules in the always-on check-false-green-guards.sh CI gate. Rule 4 fails the build if a CI step runs the
    container-integration harness with the helm/k3d cases active but does not export both REQUIRE_CLUSTERED_IMAGE=true and
    REQUIRE_WEBHOOK_IMAGE=true — the exact way the three image-dependent Kubernetes cases could silently revert to a green
    SKIP. It is keyed on the step's behaviour (invokes integration_tests.sh without SKIP_HELM_TESTS=true), not on a
    filename, so a rename or a second helm-running step is covered automatically. Rule 5 fails the build if a
    mockserver-core test that performs a JVM-global logging side effect (reaching LogManager.readConfiguration's handler
    reset() via the static ConfigurationProperties logging setters or a forced fresh <clinit>) is not in the
    sequential-tests include list — the shape behind a release-blocking flake that ParallelStaticStateGuardTest
    structurally cannot catch. Both rules fail closed on an empty corpus and carry a rotating allow-list. See
    docs/operations/false-green-guards.md.
  • A jarPath launcher option and matching MOCKSERVER_JAR_PATH environment variable for mockserver-node, pointing
    start_mockserver at a pre-provisioned mockserver-netty jar-with-dependencies instead of downloading one from
    Maven 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 released
    jar, so a missing artifact fails loudly. Mirrors the existing MOCKSERVER_BINARY_BASE_URL bring-your-own-artifact
    path for the standalone binary, and serves air-gapped/corporate installs as well as testing a locally-built jar
    (jarPath takes precedence over mockServerVersion/artifactory*; the option beats the env var). The Node launcher
    integration tests now use it in CI: a new :maven: build node launcher jar step builds the jar from the tree and
    the launcher-test step downloads it as an artifact and launches it via MOCKSERVER_JAR_PATH — so the suite finally
    tests the repo's own code instead of the last release. Previously it ran a downloaded release chosen by
    package.json's version, so a mockserver-core fix could not green it and a regression could not red it; that
    released jar also carried the shipped dynamic-CA generation race (fixed on master in 4cff56e61) and flaked ~8% of
    runs. The launcher step fails closed if the tree-built jar is absent rather than reverting to a download, and
    local npm test outside CI is unchanged (with neither the option nor the env var set it still downloads as before).
  • A structural wire-contract test for the LLM provider codecs (LlmCodecStructuralContractTest), breaking the
    self-derivation weakness in the golden-file drift test. LlmCodecGoldenFileTest regenerates its golden bodies
    from the codec itself (-Dmockserver.updateLlmGoldens=true), so a structural codec defect — a renamed field, a
    wrong SSE event name, a dropped finish_reason — bakes straight into its own golden and the byte-for-byte drift
    test 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 live
    codec 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/Responses
    vs 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 goldens
    cannot 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 SSE content_block_deltacontent_delta; Gemini
    dropped finishReason; Responses renamed status; Ollama renamed terminal done), then reverted. Residual
    streaming-over-the-wire behaviour remains covered by LlmAgentLoopE2eTest.
  • Endpoint-level test (HttpStateCassetteEndpointTest) and an authoritative CassetteRegistry javadoc note pinning
    the settled decision that loading and recording register a cassette automatically. The record_llm_fixtures
    and load_expectations_from_file MCP tools already auto-register the fixture in the process-wide CassetteRegistry
    (keyed by file path, origin recorded/loaded, upserting on re-load/re-record) so it surfaces under
    GET /mockserver/cassettes and in the dashboard's Cassettes tab without a manual PUT /mockserver/cassettes — but
    nothing 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 /cassettes alias) against a registry populated the way the load/record
    handlers 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 the
    fixture 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.
  • Compile gates for the dashboard composer's generated client code in Python, Ruby, Go and Rust
    (.buildkite/scripts/steps/ui-client-codegen-compile.sh, wired into pipeline-ui.yml), closing the gap where
    five 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 broken
    generated 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 exact combos the byte-identity parity tests use) through that language's emitter
    and runs the lightest credible toolchain check: python -m py_compile and ruby -c catch any emitter bug that
    produces 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 real
    in-repo mockserver-client-go / mockserver-client-rust, so a renamed client method fails the build — the direct
    analog of the Java javac gate. Node was already covered: the tsc type-proof in node.test.ts
    (typecheck-node-codegen.mjs) runs under npm test, so the orphaned-script concern was already resolved by that
    test. 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; set CODEGEN_COMPILE_USE_DOCKER=false for host validation.
  • Config-to-client wiring tests for the GCS and Azure blob-store backends
    (GcsBlobStoreRegistrarConfigWiringTest, AzureBlobStoreRegistrarConfigWiringTest), closing the gap where
    only S3 (S3BlobStoreRegistrarConfigWiringTest) proved that blobStoreType configuration is turned into a
    working 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 --skipApiVersionCheck for 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.
  • The Node and Python Testcontainers modules (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:unit and pytest -m "not docker"
    both skipped the container). Their CI steps now mount the Docker socket, run the existing integration tests that
    start a mockserver/mockserver container and drive it over HTTP, and — mirroring the Go/.NET/Rust steps — fail
    closed by grepping for an evidence marker the test prints only after a real container answered
    PUT /mockserver/status with 200. A skip (test filtered out, renamed, or Docker unusable) therefore fails the
    CI build loudly instead of reading as green, while the tests still degrade gracefully to a skip off-CI.
  • A GitHub Actions workflow (.github/workflows/dependency-submission.yml) now submits the resolved Maven dependency
    graph 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 and
    hid already-landed log4j-api/jsoup fixes. The workflow resolves and submits the mockserver/ reactor (which
    includes examples/java) and the separate mockserver-maven-plugin build under distinct correlators, path-gated to
    mockserver/**/pom.xml. Dependabot's security-update pull requests were unaffected — they read manifests directly;
    it was only the alerting graph that had gone stale.
  • A CI guard (.buildkite/scripts/steps/check-certificate-expiry.sh, wired into the Java pipeline) now sweeps every
    committed 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.pem must expire on or before its sibling ca.pem, and the shipped default CA
    files 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.
  • A standing CI guard (check-false-green-guards.sh) that fails closed when a new "false-green" test shape is
    introduced — 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 with
    assert-suite-ran.sh over its reports, or a broken Docker socket skips it while the build stays green; (2) no CI
    step 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 logTestSkip may park deferred work ("CI wiring is
    a 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/Azure RegistrarConfigWiringTest cloud suites ran under a Docker
    socket in CI but were not fail-closed-asserted. See docs/operations/false-green-guards.md.
  • A local-only, opt-in K3D_LOCAL_CA_BUNDLE hook in container_integration_tests/helm-deploy.sh so the Helm
    integration suite's k3d cluster can be stood up on a developer machine behind a corporate TLS-inspection proxy.
    When set, start-up-k8s overmounts the given combined CA bundle (system/public roots + corporate root) as the
    k3s node's containerd trust store at cluster-create time; when unset the k3d cluster create command is
    byte-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_BUNDLE in .buildkite/scripts/run-in-docker.sh. This unblocks
    the 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-pause
    sandbox image (every pod fails sandbox creation with x509: certificate signed by unknown authority). See
    docs/operations/build-system.mdLocal Development Behind a Corporate TLS-Inspection Proxy.
  • Two live Kubernetes container-integration tests that exercise admission-webhook and JGroups discovery paths which
    previously shipped unproven. helm_sidecar_injection deploys the chart with webhook.enabled=true (self-signed TLS
    bootstrap Jobs + webhook handler Deployment + MutatingWebhookConfiguration), drives a real labelled pod CREATE
    through the admission path, and asserts the resulting pod spec carries the injected mockserver-sidecar container,
    mockserver-iptables-init init container, and mockserver.org/injected annotation — with a negative-control pod (no
    opt-in annotation) that must not be injected, so a webhook that injects unconditionally fails the test.
    helm_jgroups_dns_ping deploys two clustered replicas and asserts the headless Service is truly headless
    (clusterIP: None), that JGROUPS_DNS_QUERY is wired to its FQDN, that it resolves to ≥2 pod IPs (Endpoints plus an
    in-cluster nslookup), that a ≥2-node JGroups/Infinispan view forms (the anti "two clusters of one" guard), and that
    state 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 exact
    behaviour they name (deleting the MutatingWebhookConfiguration; deleting the headless Service and rolling the pods).
    Both depend on Java-built images (the -clustered variant and the mockserver-webhook handler); when those images
    are absent — e.g. the CI helm step runs with SKIP_JAVA_BUILD=true and no JDK — the cases record an honest SKIP
    rather than a misleading pass, and run blocking only where the images exist.
Changed
  • helm_clustered_convergence is now a blocking container-integration test rather than non_blocking || true. The
    swallowed k3d image import ... 2>/dev/null || true is replaced by a deterministic import that verifies the image is
    present in the k3d node's containerd (via crictl) before deploying, and a pre-deploy ensure_namespace_absent guard
    removes the real back-to-back flake (helm install into a still-Terminating namespace left by a prior run/retry).
    When the -clustered image is absent (CI helm step, no JDK) the case records an honest SKIP; when present it runs
    blocking so a genuine clustering regression reds the suite.
  • The docker_compose_war_tomcat container integration test (MockServer deployed as a WAR into Tomcat 10.1) now
    actually 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 66b5d51d2 shipped 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: build step but never
    published) is now uploaded via that step's artifact_paths and downloaded by container-tests-run.sh into the path
    the test globs. A missing WAR now fails the step closed (both an explicit presence check in container-tests-run.sh
    and, defensively, prepare_war in integration_test.sh red the case) instead of skipping — a skip that reads as green
    is the exact defect being closed. The case runs in the Java pipeline's master-only :docker: container integration tests step (triggered by mockserver/, mockserver-ui/, test-fixtures/ changes); it was already declared required
    in expected_tests.manifest, so no manifest change was needed.
  • Upgraded Netty from 4.2.16.Final to 4.2.17.Final and, in lockstep, netty-tcnative-boringssl-static from
    2.0.78.Final to 2.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 mismatched
    main-artifact pin fails the enforcer DependencyConvergence rule in mockserver-core. The NETTY_TCNATIVE build
    args in every docker/*/Dockerfile were updated to match. This unblocks Dependabot PRs #​2523 and #​2532.
  • Removed the manual netty-tcnative version-synchronisation step that made every Netty upgrade a convergence trap.
    netty-tcnative-boringssl-static no longer has its own version property or dependencyManagement override — the
    imported netty-bom now governs the base artifact and every OS/arch native classifier together, so the two can no
    longer diverge and break the enforcer DependencyConvergence rule. The server jars (which ship tcnative classes
    but, per #​1778, no natives) are stamped at build time with their resolved tcnative version at
    META-INF/mockserver-tcnative.version, and every docker/*/Dockerfile now derives the native .so download from
    that 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-dependencies jar used by the
    source=copy path (release/snapshot/CI) and the mockserver-netty assembly -jar-with-dependencies.jar used by
    the default source=download path (the public reference build), stamped via the same script so they cannot drift. A
    Netty bump therefore needs no tcnative pin update and no Docker edit, and the native .so can never be a different
    version than the tcnative classes it pairs with.
  • BREAKING BEHAVIOUR: the default enabled TLS protocols are now TLSv1.2,TLSv1.3 (previously
    TLSv1,TLSv1.1,TLSv1.2), and tlsAllowInsecureProtocols now defaults to false (previously true).

    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.2 AND
    mockserver.tlsAllowInsecureProtocols=true (both are required — the insecure-protocol filter strips TLSv1/TLSv1.1
    unless it is explicitly allowed). The inbound server always applies the strong Http2SecurityUtil cipher suites, so
    no weak-cipher combination becomes reachable as a result of this change.
  • Renewed the TLS/mTLS test-certificate fixtures. The two mutual-TLS authentication CAs (which were 151 days from
    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
  • Pinned org.apache.logging.log4j:log4j-api to 2.25.5 to resolve GHSA-qv9r-c865-cp47. It is pulled in transitively
    at compile scope (via spring-boot-starter-logging -> log4j-to-slf4j) and lands in a shaded artifact, so it is
    shipped; the pin manages log4j-api only (log4j-core is not on the dependency tree).
  • Pinned org.jsoup:jsoup to 1.23.1 to 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.
  • Upgraded com.azure:azure-storage-blob from 12.29.1 to 12.35.0 in the optional mockserver-blob-azure module to
    resolve the io.projectreactor.netty:reactor-netty-http chained-redirect credential-leak advisory (fixed in
    reactor-netty 1.2.8). The old stack pulled azure-core-http-netty:1.15.10, which pins the vulnerable
    reactor-netty 1.0.48 pair transitively; 12.35.0 pulls azure-core-http-netty:1.16.5, which advances both
    reactor-netty-http and reactor-netty-core to 1.2.18 as a matched pair. This exposure surfaced only when
    GitHub'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-netty 1.2.18,
    reactor-core 3.7.19) remains Java-8 bytecode, so the Java 17 floor is preserved. A module-scoped
    dependencyManagement pin of io.projectreactor:reactor-core to 3.7.19 reconciles the one internal off-by-one in
    the 12.35.0 stack (azure-core declares 3.7.18, reactor-netty declares 3.7.19) so the enforcer
    DependencyConvergence rule stays satisfied. The Docker-gated Azurite contract test moves to Azurite 3.36.0 with
    --skipApiVersionCheck, since the newer SDK negotiates a Storage REST API version that runs ahead of every released
    Azurite build.
  • Pinned three transitive dependencies to close vulnerability alerts that surfaced only when GitHub's Maven
    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:
    • Jackson 3 (tools.jackson.core:jackson-databind, :jackson-core, tools.jackson.dataformat:jackson-dataformat-yaml)
      pinned to 3.1.5 to resolve GHSA-5gvw-p9qm-jgwh (vulnerable >=3.0.0, <=3.1.4). It arrives at compile scope via
      com.networknt:json-schema-validator:3.0.6 and is shade-relocated into shaded_package.tools.jackson, so it
      ships 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 DependencyConvergence rule stays satisfied. The pin
      is inherited by the separate mockserver-maven-plugin build (same parent pom), closing its alert too.
    • at.yawk.lz4:lz4-java pinned to 1.11.1 to resolve GHSA-6qcp-4vqm-vf35 (native XXHash JVM crash; vulnerable
      <=1.11.0). Arrives via org.apache.kafka:kafka-clients:3.9.2 — optional in mockserver-netty, runtime in
      mockserver-async. Note this is the at.yawk.lz4 fork coordinate, not org.lz4.
    • org.apache.commons:commons-compress pinned to 1.27.1 to resolve the >=1.21, <1.26.0 advisories. Arrives at
      test scope via org.testcontainers:testcontainers:1.21.4, which resolves it at 1.24.0. The direct pin overrides
      that transitive version without bumping Testcontainers, which is deliberately held at 1.21.4 for Docker Desktop
      4.67+ compatibility.
  • Made outbound TLS host name verification consistent and controllable for the forward/proxy client (Wave 3). When
    forwardProxyTLSX509CertificatesTrustManagerType is JVM or CUSTOM — the modes that actually validate the
    upstream 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 ANY trust manager, which deliberately trusts everything and performs no host-name verification. A new
    forwardProxyTLSHostnameVerificationEnabled property (default true) turns off just the host-name check while
    keeping 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
    ConfigurationDTO and folded into the client SSL-context cache key so a runtime change takes effect. The
    trust-manager javadoc, which previously implied only ANY skipped host-name verification, has been corrected.
  • Added a single startup WARN when the publicly-published bundled Certificate Authority (whose private key ships in the
    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 and
    documented 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.
  • A user-supplied FIXED server certificate is now re-checked on a cheap, time-bounded schedule (at most once a minute,
    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).
  • Added a control-plane audit WARN when a PUT /mockserver/configuration lowers or alters TLS security posture —
    downgrading the forward-proxy trust manager to ANY, turning off tlsMutualAuthenticationRequired, turning off
    forwardProxyTLSHostnameVerificationEnabled, or repointing the TLS key/certificate/CA paths. Control-plane
    authentication 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).
  • Hardened the dynamic TLS certificate cache so it can no longer serve stale, torn or over-broad material (Wave 1,
    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.
  • Bounded the dynamically-grown Subject-Alternative-Name list with a new maxSubjectAlternativeNames property
    (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 Host header (lowercase, length and label-charset checked) before it is
    added, closing a denial-of-service vector where any client could force the leaf certificate to be re-minted with an
    unbounded SAN list. Both maxSubjectAlternativeNames and sslCertificateLeafValidityInDays are now carried by
    ConfigurationDTO, so they round-trip through GET/PUT /mockserver/configuration and can be set per-instance
    rather than only via the static store (a runtime change to maxSubjectAlternativeNames takes effect on the next SAN
    added).
  • Dynamically-generated private key material (leaf key, certificate-authority key, and the JKS key store) is now
    written owner-readable-only (0600) and atomically, and public certificates 0644; a corrupt or unreadable
    certificate-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.
  • Deferred BouncyCastle registration in PEMToFile off the class-load path, restoring the lazy-BouncyCastle startup
    optimisation.
  • Shortened the auto-generated TLS leaf (server) certificate to a 397-day validity by default (Wave 2), so it
    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 sslCertificateLeafValidityInDays property (e.g. 3650). That override is clamped to a usable range of
    30–3650 days (a WARN is logged when a value is clamped): a value below 30 would mint a leaf that — because the
    notBefore is back-dated 5 days — is either already expired at issuance or already past its renewal threshold (so it
    would 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+clientAuth extended-key-usage (Apple requires serverAuth on the leaf independently of validity), a
    critical digitalSignature+keyEncipherment key-usage, and an authority-key-identifier derived from the CA; the
    root 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, serverAuth extended-key-usage and a key-usage (it keeps the long validity because it is
    both trust anchor and server certificate with no renewal loop).
Removed
  • Removed the unused PKCS1CertificateAuthorityPrivateKey.pem resource from the published mockserver-core jar. It
    was 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 stable
    PKCS#1 form); the code continues to load PKCS8CertificateAuthorityPrivateKey.pem.
Fixed
  • Fixed a rare mockserver-core unit-test flake (ConfigurationValueRedactionTest /
    ConfigurationDTOCredentialMaskingTest failing with Expected: is <1> but: was <0> when asserting an exact count of
    captured log records). These tests attach a java.util.logging handler and assert what is emitted. Two other tests —
    ClassInitializationDeadlockTest and ConfigurationPropertiesInitializationTest — force a fresh <clinit> of
    MockServer classes in an isolated child-first classloader; that initialisation reaches
    MockServerLogger.installDefaultJavaLoggingFormat()LogManager.getLogManager().readConfiguration(...), and
    java.util.logging.LogManager is a JVM-global singleton the child-first classloader does not isolate, so its
    readConfiguration performs a reset() that removes every handler from every logger in the JVM. When one of those
    classes 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 in mockserver-core/pom.xml, so they can never run concurrently with a
    handler-capturing test. No production behaviour changes.
  • Fixed the Helm chart's sidecar-injection webhook being broken out of the box. The default TLS-bootstrap image
    webhook.tls.setupImage: bitnami/kubectl:1.31 no longer exists — Bitnami withdrew the tag from Docker Hub — so both
    bootstrap Jobs failed, the MutatingWebhookConfiguration's caBundle was never patched, and because
    failurePolicy: Fail the webhook then rejected every matched pod CREATE. Anyone enabling webhook.enabled=true
    from 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 this
    job, published on the most stable-publication registry available and self-contained (no shell, no openssl CLI, no
    install-at-runtime step that would fail on an airgapped or proxied cluster). Phase 2's ClusterRole also gains the
    update verb, which the patch step genuinely needs. helm_sidecar_injection now renders and asserts the chart
    default
    is pullable rather than overriding it, so this cannot silently rot again.
  • Fixed the Kubernetes sidecar-injection webhook rejecting valid TLS private keys. WebhookServer parsed PKCS#8 only
    and explicitly rejected anything else, which broke the cert-manager path as well: the chart's Certificate
    requests algorithm: RSA without encoding: 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.
  • Fixed a thread race in dynamic Certificate Authority generation that could leave a standalone / CLI / Docker
    MockServer serving broken TLS for the rest of the process's lifetime. When
    dynamicallyCreateCertificateAuthorityCertificate is enabled, two startup paths could generate the CA
    concurrently — the proxy-setup log (proxySetupLogging, on by default in the CLI) writing the CA to disk, and the
    first 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 the
    life 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 ClientAndServer users were unaffected (they default
    proxySetupLogging off); standalone, CLI and Docker users generating a dynamic CA could hit it intermittently.
  • Unblocked the daily Dependabot updater, which had been failing and raising no PRs. The Maven wrappers under
    mockserver/ and mockserver/mockserver-maven-plugin/ were modernised from the legacy Takari format to the
    Apache only-script wrapper (wrapperVersion 3.3.4, Maven pinned at 3.9.16), so the maven-wrapper-updater can
    parse them; mockserver-vscode was split into its own npm job (a lockfile EOVERRIDE there no longer aborts the
    other npm directories) and pinned js-yaml to ^4.1.1 via overrides; and /.opencode (which had only an
    orphan lockfile and no tracked manifest) was removed from the npm directories and its lockfile untracked.
  • Cloning an X509Certificate model that contains certificate metadata without an underlying Java certificate no
    longer throws a NullPointerException; metadata-only and certificate-backed models now both preserve their state
    when cloned (#​2527).
  • JSON body matching no longer depends on which JSON provider json-unit
    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 because
    json-unit.libraries pinned it) every JSON match threw Unsupported type class com.fasterxml.jackson.databind.node.ObjectNode and no JSON body ever matched. MockServer now falls back to
    giving json-unit the raw JSON text, which it parses with whichever provider it resolved to
    (#​2496).
  • Match failures from the JSON body matcher now report why the match failed. When JSON matching threw, the log said
    only exception while perform json match failed and the exception was recorded solely at TRACE, so at the
    default 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.
  • Resolved the outstanding npm security advisories in the shipped and published Node packages: dompurify
    3.4.12 → 3.4.13 in the bundled dashboard (mockserver-ui), brace-expansion
    5.0.8 → 5.0.9 (ReDoS) in mockserver-testcontainers/node, and js-yaml
    4.3.0 → 4.3.1 in mockserver-client-node, mockserver-node and
    mockserver-testcontainers/node. The brace-expansion fix bumps only the parent so the minimatch/archiver
    glob split is preserved (a blanket override previously broke archiver); no brace-expansion override was added.
  • Documented and mitigated a clustered-deployment TLS trust defect: with the default dynamic Certificate Authority
    generation, every MockServer node in a cluster mints its own distinct CA, so a client that trusts one node's
    mockserver-ca.pem gets an intermittent TLS validation failure when a load balancer routes it to another node.
    StateBackendFactory.create() now logs a WARN when it detects clusterEnabled=true together with
    dynamicallyCreateCertificateAuthorityCertificate=true, and the limitation and its fix are now documented in
    docs/code/clustered-state.md, docs/code/tls-and-security.md, the Helm chart README, and the Centralized
    Deployment consumer page.
  • Added first-class Helm chart support for supplying one shared TLS Certificate Authority to every replica via a
    Kubernetes Secret — the supported fix for the clustered CA defect above. New opt-in values app.tls.* create (or
    reference an existing) Secret, mount it read-only, and set certificateAuthorityCertificate /
    certificateAuthorityPrivateKey with dynamicallyCreateCertificateAuthorityCertificate=false on every pod. A CA
    private key now lands in a Secret rather than a ConfigMap. Also added app.dynamicCertificateDir.* (a writable
    emptyDir for the single-replica dynamic-CA case, so certificate writes no longer depend on a non-writable working
    directory) and app.extraEnv (arbitrary container environment variables, enabling any MOCKSERVER_* property the
    chart does not expose directly). All new values are opt-in and default to today's behaviour.
  • Fixed a latent dead condition in the Helm chart Deployment template: the MOCKSERVER_PROPERTY_FILE env var was
    gated on an undeclared app.mountConfigMap value (always false). app.mountConfigMap is now a declared value
    (default false, preserving prior behaviour) so external-ConfigMap users can opt into having MockServer pointed at
    the mounted mockserver.properties.

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 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.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Aug 17, 2026
@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Aug 17, 2026
@renovate
renovate Bot enabled auto-merge (squash) August 17, 2026 22:35
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Benchmark results

Benchmark run finished with conclusion skipped for 009b05c2fdfbdee488532a4af12462148a06d529.

Benchmark summary artifact was not found; see the workflow run for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants