920 Commits

Author SHA1 Message Date
renovate[bot] 64b5e0ba5b chore(deps): update dependency got to v16 (#4305)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [got](https://redirect.github.com/sindresorhus/got) | [`15.1.0` →
`16.0.0`](https://renovatebot.com/diffs/npm/got/15.1.0/16.0.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/got/16.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/got/15.1.0/16.0.0?slim=true)
|

---

### Release Notes

<details>
<summary>sindresorhus/got (got)</summary>

###
[`v16.0.0`](https://redirect.github.com/sindresorhus/got/releases/tag/v16.0.0)

[Compare
Source](https://redirect.github.com/sindresorhus/got/compare/v15.1.0...v16.0.0)

#### Breaking changes

- Rewrite HTTP/2 support and drop the `http2-wrapper` dependency
([#&#8203;2464](https://redirect.github.com/sindresorhus/got/issues/2464))
[`1e157c4`](https://redirect.github.com/sindresorhus/got/commit/1e157c4)
- Got now has a built-in HTTP/2 client: ALPN negotiation, a pooled
session cache with multiplexing, `GOAWAY` retirement, request and
response trailers, informational (1xx) responses, abort signals,
response caching, IPv6 authorities, and `h2c` through `h2session`.
- `agent.http2` is no longer an agent slot. It is only an opt-out flag
now: pass `false` to skip session pooling. Passing an agent instance
throws.
- Response headers no longer contain HTTP/2 pseudo-headers. Use
`response.statusCode` instead of `response.headers[':status']`.
- A custom `agent.https` combined with `http2: true` makes Got use the
native HTTP/1.1 path, because the built-in session pool does not support
custom HTTPS agents.
- HTTP/2 proxy support is gone. It came from `http2-wrapper`. It was
very buggy anyway.
- If `options.request` returns a request or response, it controls the
transport and the HTTP/2 client is bypassed. Return `undefined` to fall
back to Got's own transport.
- Rewrite DNS cache and drop the `cacheable-lookup` dependency
([#&#8203;2463](https://redirect.github.com/sindresorhus/got/issues/2463))
[`bfc400b`](https://redirect.github.com/sindresorhus/got/commit/bfc400b)
- `dnsCache: true` now uses Got's own cache. The option accepts any
object with a `lookup` function and an optional `clear(hostname?)`
function, so an existing `CacheableLookup` instance still works if you
keep the dependency yourself.
- The built-in cache resolves A and AAAA records separately, so it
cannot preserve OS-specific `verbatim` address ordering from
`dns.lookup()`.
- A `beforeRequest` hook, an `afterResponse` retry, or a pagination step
that moves the request to a different origin now strips credentials and
drops the body
([#&#8203;2465](https://redirect.github.com/sindresorhus/got/issues/2465))
[`dd3b295`](https://redirect.github.com/sindresorhus/got/commit/dd3b295)
- `authorization`, `cookie`, `cookie2`, `host`, and
`proxy-authorization` are removed, URL credentials are dropped, and an
unchanged body is cleared. Set the headers or body explicitly inside the
hook if you want them to cross the origin boundary.
- This applies whether the origin changes through `url` or through
`prefixUrl`.
- `copyPipedHeaders` no longer copies credentials
[`1d233ba`](https://redirect.github.com/sindresorhus/got/commit/1d233ba)
- `authorization`, `cookie`, `cookie2`, `set-cookie`, and `set-cookie2`
are now omitted along with `host`, the hop-by-hop headers, and anything
nominated by `Connection` / `Proxy-Connection`. Pass credentials
explicitly in `headers` when the upstream is trusted.
- Remove the deprecated `searchParameters`, `followRedirects`, and
`auth` option stubs
[`1d233ba`](https://redirect.github.com/sindresorhus/got/commit/1d233ba)
- They only existed to throw a guidance message. Passing them now throws
`Unexpected option: …`.
- Remove the `OptionsOfUnknownResponseBody` type
[`1d233ba`](https://redirect.github.com/sindresorhus/got/commit/1d233ba)
  - It was a pure alias for `StrictOptions`.

#### Improvements

- Add support for the
[`QUERY`](https://www.rfc-editor.org/rfc/rfc10008.html) HTTP method
([#&#8203;2466](https://redirect.github.com/sindresorhus/got/issues/2466))
[`e3924aa`](https://redirect.github.com/sindresorhus/got/commit/e3924aa)
- Adds `got.query()` and `got.stream.query()`. `QUERY` is safe and
idempotent, so it is retried by default and keeps a replayable body
across `301` and `302` redirects as well as `307` and `308`. It is not
stored by the built-in cache, because correct `QUERY` caching needs
cache keys that include the request content.
- `allowGetBody` now also works over HTTP/2
[`1e157c4`](https://redirect.github.com/sindresorhus/got/commit/1e157c4)
- `timeout.socket` now applies during HTTP/2 TLS negotiation and session
setup
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- It was previously folded into the connection setup timeout and
reported as a `request` timeout. It now produces a real `socket` timeout
and no longer counts DNS lookup time.
- Two fewer dependencies: `cacheable-lookup` and `http2-wrapper`
[`bfc400b`](https://redirect.github.com/sindresorhus/got/commit/bfc400b)
[`1e157c4`](https://redirect.github.com/sindresorhus/got/commit/1e157c4)

#### Fixes

- Retry on connection errors reported by `request.end()` instead of
failing the request
([#&#8203;2470](https://redirect.github.com/sindresorhus/got/issues/2470))
[`67919b2`](https://redirect.github.com/sindresorhus/got/commit/67919b2)
- Retry immediately when the server answers with `Retry-After: 0`
instead of falling back to the backoff delay
([#&#8203;2471](https://redirect.github.com/sindresorhus/got/issues/2471))
[`d35ce87`](https://redirect.github.com/sindresorhus/got/commit/d35ce87)
- Preserve the response body when a cookie jar write throws
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- `error.response.body` is now complete, decompressed, and decoded with
the configured `encoding`, and a decoding failure no longer masks the
original error.
- Wait for async cookie jar writes on terminal redirect responses, for
example with `followRedirect: false`
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- Only buffer the response body for cookie handling when the response
actually sends `set-cookie`
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- Fix `got.stream` finalizing the response before the `response` event
and before piped server response headers are set
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- Fix `strictContentLength` counting bytes from responses that were not
actually decompressed
[`c6bbb8a`](https://redirect.github.com/sindresorhus/got/commit/c6bbb8a)
- Freeze `hooks.beforeCache` along with the other hook arrays on
non-mutable defaults
[`1d233ba`](https://redirect.github.com/sindresorhus/got/commit/1d233ba)
- Keep URL credentials when `prefixUrl` is changed to a same-origin
value, and treat credentials in `prefixUrl` as explicit
[`dd3b295`](https://redirect.github.com/sindresorhus/got/commit/dd3b295)

#### Migration guide

##### HTTP/2

Remove `http2-wrapper` from your code. Got's HTTP/2 client is built in.

**Before:**

```js
import http2wrapper from 'http2-wrapper';

const {headers} = await got(url, {
	http2: true,
	request: http2wrapper.auto,
	agent: {
		http2: new http2wrapper.Agent()
	}
});

console.log(headers[':status']);
```

**After:**

```js
const {statusCode} = await got(url, {http2: true});

console.log(statusCode);
```

To opt out of HTTP/2 session pooling for a request, set `agent.http2` to
`false`.

If you need an HTTP/2 proxy, keep using `http2-wrapper` through the
`request` option. Returning a request from `request` bypasses Got's
HTTP/2 client.

##### `h2c`

The `h2session` hook example no longer needs `request` or `http2`.

**Before:**

```js
import http2 from 'http2-wrapper';

got.extend({
	hooks: {
		beforeRequest: [
			options => {
				options.h2session = getSession(options.url);
				options.http2 = true;
				options.request = http2.request;
			}
		]
	}
});
```

**After:**

```js
got.extend({
	hooks: {
		beforeRequest: [
			options => {
				options.h2session = getSession(options.url);
			}
		]
	}
});
```

##### `dnsCache`

`dnsCache: true` keeps working and now uses Got's built-in cache. If you
depend on `cacheable-lookup` specific options, install it yourself and
pass the instance:

```js
import CacheableLookup from 'cacheable-lookup';

const dnsCache = new CacheableLookup({maxTtl: 60});

await got(url, {dnsCache});
```

##### Cross-origin hooks

If a `beforeRequest` hook, an `afterResponse` retry, or a pagination
step sends the request to a different origin, set the headers and body
you want to keep explicitly:

```js
got.extend({
	hooks: {
		beforeRequest: [
			options => {
				options.url = new URL('https://other.example.com/path');
				options.headers.authorization = 'Bearer …';
			}
		]
	}
});
```

##### `copyPipedHeaders`

Credentials are no longer forwarded from a piped request. Pass them
explicitly when the upstream is trusted:

```js
got.stream(url, {
	copyPipedHeaders: true,
	headers: {
		authorization: request.headers.authorization
	}
});
```

***

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-14 14:56:25 +00:00
renovate[bot] ddab29d3cf chore(deps): update dependency testcontainers to v12.1.0 (#4303)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[testcontainers](https://redirect.github.com/testcontainers/testcontainers-node)
| [`12.0.4` →
`12.1.0`](https://renovatebot.com/diffs/npm/testcontainers/12.0.4/12.1.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/testcontainers/12.1.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/testcontainers/12.0.4/12.1.0?slim=true)
|

---

### Release Notes

<details>
<summary>testcontainers/testcontainers-node (testcontainers)</summary>

###
[`v12.1.0`](https://redirect.github.com/testcontainers/testcontainers-node/releases/tag/v12.1.0)

[Compare
Source](https://redirect.github.com/testcontainers/testcontainers-node/compare/v12.0.4...v12.1.0)

#### Changes

#### 🚀 Features

- Add MongoDB Atlas Local support
[@&#8203;jbl428](https://redirect.github.com/jbl428)
([#&#8203;1396](https://redirect.github.com/testcontainers/testcontainers-node/issues/1396))
- Add Mosquitto module
[@&#8203;ChxGuillaume](https://redirect.github.com/ChxGuillaume)
([#&#8203;1390](https://redirect.github.com/testcontainers/testcontainers-node/issues/1390))

#### 📖 Documentation

- Document review-comment style and approval gate in AGENTS.md
[@&#8203;cristianrgreco](https://redirect.github.com/cristianrgreco)
([#&#8203;1397](https://redirect.github.com/testcontainers/testcontainers-node/issues/1397))

#### 🧹 Maintenance

- Declare the supported Node.js version
[@&#8203;gameroman](https://redirect.github.com/gameroman)
([#&#8203;1414](https://redirect.github.com/testcontainers/testcontainers-node/issues/1414))
- Use default stale issue timings
[@&#8203;cristianrgreco](https://redirect.github.com/cristianrgreco)
([#&#8203;1420](https://redirect.github.com/testcontainers/testcontainers-node/issues/1420))

#### 📦 Dependency Updates

- Bump undici
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1424](https://redirect.github.com/testcontainers/testcontainers-node/issues/1424))
- Bump ip-address from 10.2.0 to 10.4.0
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1426](https://redirect.github.com/testcontainers/testcontainers-node/issues/1426))
- Bump brace-expansion from 2.1.2 to 2.1.4
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1423](https://redirect.github.com/testcontainers/testcontainers-node/issues/1423))
- Bump fast-uri from 3.1.4 to 3.1.5
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1422](https://redirect.github.com/testcontainers/testcontainers-node/issues/1422))
- Apply npm audit fixes
[@&#8203;cristianrgreco](https://redirect.github.com/cristianrgreco)
([#&#8203;1421](https://redirect.github.com/testcontainers/testcontainers-node/issues/1421))
- Bump the actions group across 1 directory with 2 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1415](https://redirect.github.com/testcontainers/testcontainers-node/issues/1415))
- Bump the dependencies group across 11 directories with 10 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1416](https://redirect.github.com/testcontainers/testcontainers-node/issues/1416))
- Bump the dependencies group across 1 directory with 15 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1417](https://redirect.github.com/testcontainers/testcontainers-node/issues/1417))
- Bump mkdocs-material from 9.7.6 to 9.7.7 in the dependencies group
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1403](https://redirect.github.com/testcontainers/testcontainers-node/issues/1403))
- Bump the dependencies group across 10 directories with 10 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1404](https://redirect.github.com/testcontainers/testcontainers-node/issues/1404))
- Bump the dependencies group with 14 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1405](https://redirect.github.com/testcontainers/testcontainers-node/issues/1405))
- Bump the dependencies group across 7 directories with 6 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1400](https://redirect.github.com/testcontainers/testcontainers-node/issues/1400))
- Bump the dependencies group with 14 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1401](https://redirect.github.com/testcontainers/testcontainers-node/issues/1401))
- Bump the dependencies group across 14 directories with 13 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1398](https://redirect.github.com/testcontainers/testcontainers-node/issues/1398))
- Bump the dependencies group with 16 updates
@&#8203;[dependabot\[bot\]](https://redirect.github.com/apps/dependabot)
([#&#8203;1399](https://redirect.github.com/testcontainers/testcontainers-node/issues/1399))

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-14 05:59:07 +00:00
renovate[bot] 474058bd12 chore(deps): update dependency mockserver-client to v7.6.0 (#4302)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [mockserver-client](http://mock-server.com)
([source](https://redirect.github.com/mock-server/mockserver-monorepo/tree/HEAD/mockserver-client-node))
| [`7.5.0` →
`7.6.0`](https://renovatebot.com/diffs/npm/mockserver-client/7.5.0/7.6.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/mockserver-client/7.6.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/mockserver-client/7.5.0/7.6.0?slim=true)
|

---

### Release Notes

<details>
<summary>mock-server/mockserver-monorepo (mockserver-client)</summary>

###
[`v7.6.0`](https://redirect.github.com/mock-server/mockserver-monorepo/blob/HEAD/changelog.md#760---2026-08-17)

[Compare
Source](https://redirect.github.com/mock-server/mockserver-monorepo/compare/d23b63077056bc3f6196620e7ce2362d7d043ea8...6b63551f98e64d16759ddc9c5b01bb9bedb271f0)

##### 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 build`s (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 save`d 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](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_delta`→`content_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](https://redirect.github.com/PKCS/mockserver-monorepo/issues/1)
and
[PKCS#8](https://redirect.github.com/PKCS/mockserver-monorepo/issues/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.md` → *Local 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
[#&#8203;2523](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2523)
and
[#&#8203;2532](https://redirect.github.com/mock-server/mockserver-monorepo/issues/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
[#&#8203;1778](https://redirect.github.com/mock-server/mockserver-monorepo/issues/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

([#&#8203;2531](https://redirect.github.com/mock-server/mockserver-monorepo/issues/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](https://redirect.github.com/PKCS/mockserver-monorepo/issues/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](https://redirect.github.com/PKCS/mockserver-monorepo/issues/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](https://redirect.github.com/PKCS/mockserver-monorepo/issues/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](https://redirect.github.com/PKCS/mockserver-monorepo/issues/1),
so a cert-manager-issued key could not be loaded.
It now accepts any standard unencrypted PEM private key —
[PKCS#8](https://redirect.github.com/PKCS/mockserver-monorepo/issues/8),
[PKCS#1](https://redirect.github.com/PKCS/mockserver-monorepo/issues/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
([#&#8203;2527](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2527)).
- JSON body matching no longer depends on which JSON provider
[json-unit](https://redirect.github.com/lukas-krecan/JsonUnit)
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

([#&#8203;2496](https://redirect.github.com/mock-server/mockserver-monorepo/issues/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`.

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-14 02:02:19 +00:00
renovate[bot] 9122e05f4b chore(deps): update dependency publint to v0.3.24 (#4301)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [publint](https://publint.dev)
([source](https://redirect.github.com/publint/publint/tree/HEAD/packages/publint))
| [`0.3.22` →
`0.3.24`](https://renovatebot.com/diffs/npm/publint/0.3.22/0.3.24) |
![age](https://developer.mend.io/api/mc/badges/age/npm/publint/0.3.24?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/publint/0.3.22/0.3.24?slim=true)
|

---

### Release Notes

<details>
<summary>publint/publint (publint)</summary>

###
[`v0.3.24`](https://redirect.github.com/publint/publint/blob/HEAD/packages/publint/CHANGELOG.md#0324)

[Compare
Source](https://redirect.github.com/publint/publint/compare/publint@0.3.23...publint@0.3.24)

##### Patch Changes

- [#&#8203;257](https://redirect.github.com/publint/publint/pull/257)
[`7c4d25a`](https://redirect.github.com/publint/publint/commit/7c4d25a4a03d8a8332221248df8c386d4cd97f30)
- Check the `repository.url` value even when `repository.type` is
omitted. `type` is optional and defaults to git, so packages using the
object form without it were previously skipped for the
deprecated-protocol, invalid-git-url, and shorthand-URL checks.

###
[`v0.3.23`](https://redirect.github.com/publint/publint/blob/HEAD/packages/publint/CHANGELOG.md#0323)

[Compare
Source](https://redirect.github.com/publint/publint/compare/publint@0.3.22...publint@0.3.23)

##### Patch Changes

- [#&#8203;248](https://redirect.github.com/publint/publint/pull/248)
[`9ff88ac`](https://redirect.github.com/publint/publint/commit/9ff88ac3784a33cb36d9d78064586588de07e9e1)
- Recursively detect test files (e.g. `*.test.js`, `*.spec.ts`) for the
`USE_FILES` suggestion.

- [#&#8203;245](https://redirect.github.com/publint/publint/pull/245)
[`ad737a3`](https://redirect.github.com/publint/publint/commit/ad737a386b1b8e8e4b2dce1bf7a7ce8d4506ebba)
- The `USE_FILES` message now reports which internal files or
directories triggered it via `args.internalFilePaths`

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-13 21:03:48 +00:00
renovate[bot] 906922fbf1 chore(deps): update dependency p-retry to v8.0.1 (#4300)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [p-retry](https://redirect.github.com/sindresorhus/p-retry) | [`8.0.0`
→ `8.0.1`](https://renovatebot.com/diffs/npm/p-retry/8.0.0/8.0.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/p-retry/8.0.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/p-retry/8.0.0/8.0.1?slim=true)
|

---

### Release Notes

<details>
<summary>sindresorhus/p-retry (p-retry)</summary>

###
[`v8.0.1`](https://redirect.github.com/sindresorhus/p-retry/releases/tag/v8.0.1)

[Compare
Source](https://redirect.github.com/sindresorhus/p-retry/compare/v8.0.0...v8.0.1)

- Fix error for invalid fractional retry counts
[`3c22035`](https://redirect.github.com/sindresorhus/p-retry/commit/3c22035)

***

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-13 18:07:52 +00:00
renovate[bot] 44df47d1ff chore(deps): update dependency lockfile-lint to v5.0.1 (#4299)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[lockfile-lint](https://redirect.github.com/lirantal/lockfile-lint/tree/master/packages/lockfile-lint)
([source](https://redirect.github.com/lirantal/lockfile-lint/tree/HEAD/packages/lockfile-lint))
| [`5.0.0` →
`5.0.1`](https://renovatebot.com/diffs/npm/lockfile-lint/5.0.0/5.0.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/lockfile-lint/5.0.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/lockfile-lint/5.0.0/5.0.1?slim=true)
|

---

### Release Notes

<details>
<summary>lirantal/lockfile-lint (lockfile-lint)</summary>

###
[`v5.0.1`](https://redirect.github.com/lirantal/lockfile-lint/blob/HEAD/packages/lockfile-lint/CHANGELOG.md#501)

[Compare
Source](https://redirect.github.com/lirantal/lockfile-lint/compare/lockfile-lint@5.0.0...lockfile-lint@5.0.1)

##### Patch Changes

-
[`6ca1c1871c861888744c314652dd0e59da17fad2`](https://redirect.github.com/lirantal/lockfile-lint/commit/6ca1c1871c861888744c314652dd0e59da17fad2)
Thanks [@&#8203;lirantal](https://redirect.github.com/lirantal)! -
chore: release patch for security fix

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-13 14:37:26 +00:00
renovate[bot] 11157f1a7c chore(deps): update dependency @types/node to v24.13.4 (#4297)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node))
| [`24.13.3` →
`24.13.4`](https://renovatebot.com/diffs/npm/@types%2fnode/24.13.3/24.13.4)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/24.13.4?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/24.13.3/24.13.4?slim=true)
|

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-13 05:10:48 +00:00
renovate[bot] 16e5603f38 chore(deps): update dependency js-yaml to v4.3.2 [security] (#4296)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [js-yaml](https://redirect.github.com/nodeca/js-yaml) | [`4.3.1` →
`4.3.2`](https://renovatebot.com/diffs/npm/js-yaml/4.3.1/4.3.2) |
![age](https://developer.mend.io/api/mc/badges/age/npm/js-yaml/4.3.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/js-yaml/4.3.1/4.3.2?slim=true)
|

---

### JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and
4.x) — CVE-2026-59870 fix not backported

[GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj)

<details>
<summary>More information</summary>

#### Details
##### Quadratic CPU consumption in `!!omap` resolution (js-yaml 3.x and
4.x)

##### Summary

`resolveYamlOmap()` enforces key uniqueness for `!!omap` sequences with
a linear
scan (`objectKeys.indexOf(...)`) inside the per-element loop, making
resolution
**O(n²)** in the number of entries. A modestly sized YAML document
therefore
consumes disproportionate CPU inside `yaml.load()`, giving a denial of
service
against any consumer that parses untrusted YAML.

`!!omap` is registered in the **default schema**
(`lib/schema/default.js` → `require('../type/omap')`), so a plain
`yaml.load(untrustedInput)` with no options is affected — no custom
schema or
non-default configuration is required.

**This is the same weakness as CVE-2026-59870 / GHSA-724g-mxrg-4qvm**,
which was
fixed in the 5.x line in 5.2.1. That fix was never backported: both
currently
maintained legacy lines still carry the original implementation.

##### Affected versions

| Line | Latest tested | Status |
|---|---|---|
| 3.x | **3.15.0** | Affected — `objectKeys.indexOf(pairKey)` at
`lib/type/omap.js:29` |
| 4.x | **4.3.0** | Affected — `objectKeys.indexOf(pairKey)` at
`lib/type/omap.js:30` |
| 5.x | 5.2.2 | **Not affected** — fixed in 5.2.1 (uses a `Set`) |

Both figures are the newest release of each line at the time of writing,
so
this is not a "you are on an old version" issue.

##### Details

`lib/type/omap.js` (js-yaml 4.3.0):

```js
if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey)
else return false
```

`objectKeys` grows by one element per entry, and
`Array.prototype.indexOf` is a
linear scan, so resolving an `n`-entry `!!omap` performs roughly
`1 + 2 + … + n` comparisons — quadratic in `n`. The work happens
synchronously
inside `yaml.load()`, blocking the event loop for its whole duration.

The 5.x line already solves exactly this by tracking seen keys in a
`Set`
(`src/tag/sequence/omap.ts`):

```ts
if (carrier.seen.has(key)) return 'duplicate key in ordered map'
carrier.seen.add(key)
```

##### Proof of concept

```js
// poc.js  —  node poc.js
const yaml = require('js-yaml');
const doc = n => '!!omap\n' + Array.from({length: n}, (_, i) => `- k${i}: ${i}`).join('\n') + '\n';

for (const n of [10000, 20000, 40000, 80000]) {
  const d = doc(n), t = Date.now();
  yaml.load(d);                      // default schema, no options
  console.log(`n=${n} bytes=${d.length} load=${Date.now() - t}ms`);
}
```

##### Measured (node v20.20.2, default heap, no flags)

**js-yaml 4.3.0**

```
n=10000  bytes=137787   load=54ms
n=20000  bytes=297787   load=169ms
n=40000  bytes=617787   load=646ms
n=80000  bytes=1257787  load=2607ms
```

**js-yaml 3.15.0**

```
n=10000  bytes=137787   load=53ms
n=20000  bytes=297787   load=166ms
n=40000  bytes=617787   load=641ms
n=80000  bytes=1257787  load=2567ms
```

Runtime grows by a factor of ~4 for each doubling of `n`, which is the
signature of O(n²) (linear growth would be ~2×).

Scaling further: a **2.48 MB** document with 150,000 entries blocked
`yaml.load()` for **10.8 seconds**.

##### Impact

Any service that parses attacker-influenced YAML with js-yaml 3.x or 4.x
can be
stalled with a small input. Because the loop is synchronous, a single
request
blocks the Node.js event loop and stalls every other request in the
process —
so the amplification is per-process, not just per-request.

Suggested severity: consistent with **CVE-2026-59870** (the same
weakness in
5.x), i.e. Availability-only impact, network attack vector, no
privileges or
user interaction required.

##### Suggested fix

Mirror the 5.x fix — replace the linear scan with a `Set`:

```js
// lib/type/omap.js
const seen = new Set()
// ...
if (seen.has(pairKey)) return false
seen.add(pairKey)
```

This preserves the existing duplicate-key rejection semantics exactly
while
making resolution O(n). A `maxOmapLength`-style cap would also work, but
the
`Set` matches what 5.x already ships and requires no new option.

##### References

- CVE-2026-59870 / GHSA-724g-mxrg-4qvm — same weakness in 5.0.0–5.2.0,
fixed in 5.2.1
- `lib/type/omap.js` (3.x, 4.x) — the affected resolver
- `lib/schema/default.js` — registers `!!omap` in the default schema

##### Discovery

Found by an automated static-analysis and executed-proof-of-concept
scanner run
against js-yaml 4.2.0, then manually verified against 3.15.0 and 4.3.0
by
executing the proof of concept above. All timings in this report were
measured
on the **current** releases of each line, not on the version originally
scanned.

#### Severity
- CVSS Score: 7.5 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`

#### References
-
[https://github.com/nodeca/js-yaml/security/advisories/GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-5p4m-2wfm-xmqj)
-
[https://github.com/advisories/GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge
sources
[CVE-2026-84375](https://nvd.nist.gov/vuln/detail/CVE-2026-84375) /
[GHSA-2883-xcg3-v3hh](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh)

<details>
<summary>More information</summary>

#### Details
##### Summary

`maxTotalMergeKeys` does not count empty mappings. An attacker can
repeatedly merge a large sequence of them and consume significant CPU
without reaching the configured limit.

##### Example

```yaml
arr: &arr [{}, {}, {}, ...] # N empty mappings
targets:
  - <<: *arr                # repeated K times
```

For every target, the loader iterates all `N` elements of `arr`. This
results in `O(N * K)` work while `totalMergeKeys` remains unchanged.

##### PoC

```js
import { performance } from 'node:perf_hooks'
import { load, YAML11_SCHEMA } from 'js-yaml'

const n = 20000

const src =
  'arr: &arr [' + '{},'.repeat(n).slice(0, -1) + ']\n' +
  'targets:\n' +
  '  - <<: *arr\n'.repeat(n)

const started = performance.now()

load(src, { schema: YAML11_SCHEMA })

console.log(`${(performance.now() - started).toFixed(1)} ms`)
```

Observed results:

| N | YAML size | Time |
|---:|---:|---:|
| 800 | ~13 KB | ~20 ms |
| 3200 | ~50 KB | ~180 ms |
| 20000 | ~500 KB | ~13 s |

##### Impact

An attacker can submit a relatively small YAML document that causes
prolonged CPU consumption despite the default `maxTotalMergeKeys` limit.

##### Fix

Count each merge-source mapping as one budget unit, in addition to
counting its keys.

##### Difference with v5

In v3 & v4, merge is enabled by default. So, the severity score is
higher.

#### Severity
- CVSS Score: 7.5 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`

#### References
-
[https://github.com/nodeca/js-yaml/security/advisories/GHSA-2883-xcg3-v3hh](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-2883-xcg3-v3hh)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-84375](https://nvd.nist.gov/vuln/detail/CVE-2026-84375)
-
[https://github.com/nodeca/js-yaml/pull/797](https://redirect.github.com/nodeca/js-yaml/pull/797)
-
[https://github.com/nodeca/js-yaml/commit/3485bc06ff8a0251505f44a00414d90df2466639](https://redirect.github.com/nodeca/js-yaml/commit/3485bc06ff8a0251505f44a00414d90df2466639)
-
[https://github.com/nodeca/js-yaml/commit/6a8e05f9a485188ed730ac81e81ae221352ef480](https://redirect.github.com/nodeca/js-yaml/commit/6a8e05f9a485188ed730ac81e81ae221352ef480)
-
[https://github.com/nodeca/js-yaml/commit/d90b6612a5a84385bdcb556c44578eac76dc0f6b](https://redirect.github.com/nodeca/js-yaml/commit/d90b6612a5a84385bdcb556c44578eac76dc0f6b)
-
[https://github.com/nodeca/js-yaml/releases/tag/3.15.2](https://redirect.github.com/nodeca/js-yaml/releases/tag/3.15.2)
-
[https://github.com/nodeca/js-yaml/releases/tag/4.3.2](https://redirect.github.com/nodeca/js-yaml/releases/tag/4.3.2)
-
[https://github.com/advisories/GHSA-2883-xcg3-v3hh](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh)

This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>nodeca/js-yaml (js-yaml)</summary>

###
[`v4.3.2`](https://redirect.github.com/nodeca/js-yaml/compare/4.3.1...4.3.2)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/4.3.1...4.3.2)

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-13 05:01:04 +00:00
renovate[bot] d27db1b86f chore(deps): update dependency js-yaml to v4.3.1 (#4279)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [js-yaml](https://redirect.github.com/nodeca/js-yaml) | [`4.3.0` →
`4.3.1`](https://renovatebot.com/diffs/npm/js-yaml/4.3.0/4.3.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/js-yaml/4.3.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/js-yaml/4.3.0/4.3.1?slim=true)
|

---

### Release Notes

<details>
<summary>nodeca/js-yaml (js-yaml)</summary>

###
[`v4.3.1`](https://redirect.github.com/nodeca/js-yaml/compare/4.3.0...4.3.1)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/4.3.0...4.3.1)

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-03 20:59:55 +00:00
renovate[bot] 7bf348df58 chore(deps): update dependency nock to v14.0.17 (#4275)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [nock](https://redirect.github.com/nock/nock) | [`14.0.16` →
`14.0.17`](https://renovatebot.com/diffs/npm/nock/14.0.16/14.0.17) |
![age](https://developer.mend.io/api/mc/badges/age/npm/nock/14.0.17?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/nock/14.0.16/14.0.17?slim=true)
|

---

### Release Notes

<details>
<summary>nock/nock (nock)</summary>

###
[`v14.0.17`](https://redirect.github.com/nock/nock/releases/tag/v14.0.17)

[Compare
Source](https://redirect.github.com/nock/nock/compare/v14.0.16...v14.0.17)

##### Bug Fixes

- remove interceptor from scope.interceptors on nock.removeInterceptor
([#&#8203;2997](https://redirect.github.com/nock/nock/issues/2997))
([1ee467c](https://redirect.github.com/nock/nock/commit/1ee467c68d601ddc22629d7a657061e6c27097c2))

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 22:35:03 +00:00
renovate[bot] 2ec3ba29f2 chore(deps): update dependency mockserver-client to v7.5.0 (#4274)
> ℹ️ **Note**
> 
> This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [mockserver-client](http://mock-server.com)
([source](https://redirect.github.com/mock-server/mockserver-monorepo/tree/HEAD/mockserver-client-node))
| [`7.4.0` →
`7.5.0`](https://renovatebot.com/diffs/npm/mockserver-client/7.4.0/7.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/mockserver-client/7.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/mockserver-client/7.4.0/7.5.0?slim=true)
|

---

### Release Notes

<details>
<summary>mock-server/mockserver-monorepo (mockserver-client)</summary>

###
[`v7.5.0`](https://redirect.github.com/mock-server/mockserver-monorepo/blob/HEAD/changelog.md#750---2026-07-29)

[Compare
Source](https://redirect.github.com/mock-server/mockserver-monorepo/compare/fe29eedcd2fbf31c047176b117172026774034d6...d23b63077056bc3f6196620e7ce2362d7d043ea8)

##### Security

- **BREAKING: response templates can no longer reach arbitrary Java
classes by default, closing the
  template remote-code-execution path reported as

[GHSA-7pwj-xvc2-hfpc](https://redirect.github.com/mock-server/mockserver-monorepo/security/advisories/GHSA-7pwj-xvc2-hfpc).**
A caller who can reach the management API can register an expectation,
and a response template was able
to load `java.lang.Runtime` and execute OS commands in the MockServer
process. Both engines that could
  do this are now sandboxed out of the box:
- `velocityDisallowClassLoading` now defaults to **`true`** (was
`false`), installing Velocity's
`SecureUberspector` so a template cannot reach classes through
`$request.class.classLoader.loadClass(...)`.
This is the more exposed half of the issue, and the half the report did
not cover: Velocity ships in
    the DEFAULT distribution, whereas the JavaScript engine does not.
- JavaScript templates now resolve **no** Java classes unless an
operator grants them. Previously an
empty `javascriptAllowedClasses` *and* empty
`javascriptDisallowedClasses` meant unrestricted
`Java.type(...)` access; that combination — the out-of-the-box state —
now denies every class.
- The GraalJS guest context no longer grants access to the members of
`java.lang.Class` or
`java.lang.ClassLoader`. Denying classes at `Java.type(...)` alone was
**not** sufficient: real host
objects are bound into the context (`faker` and the other built-in
helpers), and under the previous
`HostAccess.ALL` a template could walk from one of them to a classloader
—
`faker.getClass().getClassLoader().loadClass('java.lang.Runtime')` —
reaching `Runtime` without the
class filter ever being consulted. That walk is now closed, so
host-class lookup is the single complete
gate; a regression test drives four such walks (including through
`request`) and fails if any resolves.
Velocity's `SecureUberspector` already blocked the equivalent walk
through its own bound helpers, which
    is now covered by a test too.
Both flips are fully reversible with one property and remove no
functionality: set
`mockserver.velocityDisallowClassLoading=false`, or list the classes
your templates need in
`mockserver.javascriptAllowedClasses` (the single entry `*` lets any
class resolve again). Templates that
do not touch Java classes are unaffected, which is the overwhelming
majority — JavaScript templates have
the full ES2023 standard library available regardless of this setting. A
refused class is logged once at
WARN naming the class and the property to set, because GraalJS otherwise
surfaces a refusal only as the
class being undefined ("... is not a function"); the log is bounded and
de-duplicated so a hostile
template cannot flood it. `mockserver.javascriptAllowedClasses` is now
also settable through the Spring
test listener's `@MockServerTest` properties, which it was not before —
it was a nice-to-have while the
default was unrestricted, and is the only way to grant a class now that
it is not. The insecure-mode WARN
    now fires when an operator has explicitly opened the
sandbox rather than when it is closed. Proven end-to-end by a Netty
integration test that registers the
reported payload through the real management API and asserts the OS
command creates no marker file, with
a negative control on a deliberately unsandboxed server that DOES create
it — so a regression cannot pass
as an inert payload. This lands DEF-2 and DEF-3 of
`docs/plans/later/security-defaults.md` ahead of the
other default flips listed there; JavaScript went further than that plan
proposed (deny everything, not a
built-in "safe types" allow-list) because deny-by-default is the only
form that stays safe as the JDK
    grows new reachable classes.

##### Fixed

- **A property file that cannot be read is now reported instead of
ignored in silence

([#&#8203;2358](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2358)).**
When a
`mockserver.propertyFile` an operator had explicitly configured could
not be read, MockServer applied
none of its properties and said nothing about it — at any log level. The
only symptom was that every
property in the file appeared to be at its default, which surfaces far
downstream as unexplained
behaviour: in the reported case an unreadable (but present) mounted file
meant `initializationJsonPath`
was never set, so no expectations loaded, no `loading JSON
initialization file:` line appeared, and no
error was logged either. The message existed but was unreachable in
practice — gated at DEBUG *and*
emitted during static initialisation, before any log level has been
applied, so neither
`-Dmockserver.logLevel=DEBUG` nor a `-logLevel` argument could surface
it. Such a file is now logged at
WARN, naming the path and the underlying reason verbatim; because
`FileNotFoundException` covers "not
there" and "not allowed to read it" alike, that reason is usually the
whole answer (`Permission denied`
in the reported case, typically SELinux labelling or a
rootless/user-namespace UID mismatch). A property
file that is merely absent at its default location stays quiet, as does
the Docker image's built-in
`-Dmockserver.propertyFile=/config/mockserver.properties`, which the
entrypoint always passes and which
therefore expresses no intent — otherwise every container started
without a mounted config would warn.
Inside the image, only `MOCKSERVER_PROPERTY_FILE` can express that
intent, and it does.
- **The `mockserver-node` launcher suite no longer fails intermittently
on a TLS handshake reset.** The
two tests that exercise `jvmOptions` did so over HTTPS against a server
started with
`dynamicallyCreateCertificateAuthorityCertificate=true`, and issued that
HTTPS request as soon as
`start_mockserver` resolved. `start_mockserver` only proves the HTTP
control plane is answering — it
polls `PUT /mockserver/retrieve` over plain HTTP — but with a
dynamically created certificate
authority the server still has to generate a CA key pair and a leaf
certificate before it can serve
TLS on that same (port-unified) port. A handshake arriving in that
window was closed mid-negotiation
and surfaced as `ECONNRESET` "Client network socket disconnected before
secure TLS connection was
established", failing whichever of the two tests lost the race. This
accounted for every
`mockserver-node` failure on `master` over the preceding 40 builds (5 of
40, \~12%), so it was the sole
cause of the pipeline's intermittent red. Both tests now wait for an
actual TLS handshake to complete
before asserting, which gates them on the condition they really depend
on rather than retrying the
assertions. The new `waitForTlsReady` helper is verified to reject — not
resolve — both when nothing
is listening and when a listener accepts the TCP connection then
destroys it mid-handshake, which is
exactly the failure signature it exists to absorb. The readiness budget
is deliberately generous
(120s): waiting costs nothing when the server is healthy, since a ready
server completes the
handshake on the first attempt in milliseconds, so the limit only
decides how much CI contention is
tolerated before a slow start is misreported as a fault. An earlier 30s
budget went green five builds
running and then expired on a loaded agent — the same flake wearing a
clearer error message. A start
that takes over 5s is now reported even when it passes, because
readiness creeping towards the limit
  is the signal that the next run will not make it.
- **`archiver.glob()` works again in `@mockserver/testcontainers`
(Node), and CVE-2026-14257 stays
closed.** The previous remedy for the `brace-expansion` denial of
service (GHSA-mh99-v99m-4gvg,
patched only in 5.0.8) was a blanket `"brace-expansion": "^5.0.8"`
override. That resolved the whole
tree to a single hoisted 5.0.8 and `npm audit` reported zero
vulnerabilities — but 5.x changed the
CommonJS export from a callable function to an object (`{ expand,
EXPANSION_MAX, ... }`), while the
minimatch copies actually installed (3.1.5, 5.1.9, 9.0.9) all call it as
`expand(pattern)`. Every
glob containing a brace therefore threw `TypeError: expand is not a
function`, crashing
`archiver.glob()`. The blast radius is narrower than it first looks —
`testcontainers` copies files
with `archiver.directory()`/`.append()`, which pass no brace pattern and
still work — so what broke
is brace globbing for anything in this module's runtime tree that does
use it. The failure was
invisible because minimatch short-circuits patterns with no `{`, so
plain globs kept working and the
unit suite stayed green. The override is now targeted: `readdir-glob`
and `archiver-utils`' `glob`
take `minimatch@^10.2.5`, which depends on `brace-expansion@^5.0.5` and
is written against the new
API, so both runtime copies land on the patched 5.0.8 with a matching
minimatch. jest keeps its own
`minimatch@3.1.5` + `brace-expansion@1.1.16` pairing and is untouched.
`npm audit --omit=dev` still
reports 0 vulnerabilities, and a new `dependency-integrity` unit test
drives a brace pattern through
both runtime minimatch copies and through a real `archiver.glob()` tar,
plus asserts expansion stays
bounded — it fails against the blanket override, so the silent half of
this cannot return.
- **A forward `responseOverride` that replaces the body no longer
inherits the upstream response's
`Content-Length`, which truncated the response on the wire.** The
override swapped the body but left the
upstream header in place, so the client read only as many bytes as the
body it replaced — a 34-byte
override behind an upstream `Content-Length: 13` arrived as 13 bytes —
or hung waiting for bytes that
never came. The stale header is now dropped so the encoder recomputes it
from what is actually written;
a `Content-Length` set by the override itself, and
`connectionOptions.contentLengthHeaderOverride`, are
still honoured, and a header-only override (one that sets no body) is
untouched. This affects every body
override, and it was the remaining reason a `FILE` response body
returned from a `responseOverride`
  still reached the client wrong after

[#&#8203;2450](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2450):
the file was materialised
correctly and then cut short by the stale length. Covered by a Netty
integration test that drives a real
forward-with-override through a real upstream and asserts the bytes the
client receives.
- **The JetBrains plugin's LLM tool window now sends a valid expectation

([#&#8203;2455](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2455)).**
"Load into Server" was
rejected with `400 incorrect expectation json format` because the
builder emitted a shape that never
existed on the server: a flat `completion` string, a top-level
`finishReason`, `stream`, and `usage`,
and a `provider` of `OPEN_AI`. The completion text, streaming flag, stop
reason, and token usage
belong INSIDE the `completion` object (`text`, `streaming`,
`stopReason`, `usage.inputTokens` /
`usage.outputTokens`), and providers are the `Provider` enum names
(`OPENAI`, `AZURE_OPENAI`, …). The
provider and field catalogues shared with the VS Code extension are
corrected the same way — they
offered `OPEN_AI`, `VERTEX_AI`, `messages`, `stream`, `finishReason` and
a top-level `usage`, none of
which the server accepts — and completion inside a `completion` object
now offers the nested fields.
The plugin has always bundled the correct schema; it simply never
validated its own output against it,
and the previous tests asserted the builder matched the same invented
shape it produced. Both editors
  now validate against the bundled schema in their test suites.
- **`httpLlmResponse.provider` now accepts every provider MockServer
implements.** The JSON Schema enum
listed 9 of the 14 `org.mockserver.model.Provider` constants, so
`MISTRAL`, `XAI`, `DEEPSEEK`, `GROQ`,
and `OPENROUTER` were rejected with `400 incorrect expectation json
format` even though each has a
fully registered response codec. The five missing values are added to
the core schema, the generated
VS Code and JetBrains schemas, and both copies of the OpenAPI
specification, and a new parity test
fails if the enum and `Provider` ever diverge again in either direction.
The provider list on the
LLM response mocking documentation and in the Rust client's field docs
is updated to match.
- **The cloud blob-store, async-broker and transparent-proxy CI steps no
longer OOM-kill their own build
before any test runs.** Each ran its Docker container with
`--memory=4g`, but `mockserver/.mvn/jvm.config`
pins the Maven JVM to `-Xmx6144m` and the wrapper prepends it to
`MAVEN_OPTS`, so the `-am` dependency
build was permitted a 6 GB heap inside a 4 GB cgroup and the kernel
intermittently killed it with exit 137
— losing the very coverage those fail-closed steps exist to guarantee.
Raised each to `--memory=7g`, the
value every other `./mvnw` step already uses and which fits the
single-agent `c5.2xlarge`/`m5.2xlarge`
  default-queue instances with margin.

##### Added

- **A cassette is now auto-registered when a fixture is loaded or
recorded via the MCP tools, so it
appears under `GET /mockserver/cassettes` without a separate `PUT
/mockserver/cassettes` call.**
Previously the server-side cassette registry was populated only by an
explicit
`PUT /mockserver/cassettes`, so a fixture loaded with the
`load_expectations_from_file` MCP tool, or
written with `record_llm_fixtures`, never showed up in the dashboard's
Cassettes tab unless the
caller also registered it by hand. Both MCP tool handlers now register
the fixture in
`CassetteRegistry` at the point the file is loaded/written — the file
path as the key, the loaded/
written expectation count, and an `origin` of `loaded` or `recorded`
respectively — so
`GET /mockserver/cassettes` (which serialises that registry) lists it
automatically. Re-loading or
re-recording the same path updates the existing entry in place rather
than duplicating it.

- **Clustered (Infinispan) expectation reload-on-startup is now proven
end-to-end.** A new test
(`ClusteredExpectationPersistenceReloadTest` in
`mockserver-state-infinispan`) forms an in-JVM
JGroups cluster consisting of a bare "fleet keeper"
`InfinispanStateBackend` that stays up for the
whole test plus a full MockServer node started with
`stateBackend=infinispan`,
`clusterEnabled=true` and `persistExpectations=true`. An expectation is
created on that node over
the wire, the persisted document is polled for through the *keeper's*
backend (proving it really
replicated across the REPL\_SYNC blob cache), the node is then stopped
completely, and a fresh node
is started against the same cluster and the same
`persistedExpectationsPath` — which must restore
the expectation and MATCH a real HTTP request with it. The local
persisted file is asserted to be
empty first, so the restore cannot be coming from the
filesystem-initializer route. The reload path
in `ExpectationFileSystemPersistence` was already covered at unit level
in `mockserver-core`
(`ExpectationBlobStoreRestoreTest`, against an `InMemoryBlobStore`, with
no server and no cluster)
and end-to-end only against S3/MinIO behind a Docker gate; what no test
proved is that a clustered
node's `InfinispanBlobStore` is the store `HttpState` wires into that
restore, nor that a real
restarted member of a live cluster recovers the fleet's shared
expectations. A second test sets
`blobStoreRestoreTimeoutSeconds=0` (the documented way to skip the
restore) and asserts the fresh
node does NOT serve the expectation, which permanently pins the fact
that no other mechanism —
JGroups state transfer of the expectations cache, a stray invalidation
event, or the local file —
restores expectations when a node starts. Verified by a positive
control: disabling the reload
path in production makes the restarted node answer with an empty body
and turns the test red.

- **The response-aware arm of the eviction false-green guard is now
proven end-to-end over HTTP.** A new
Netty integration test (`EvictedResponseVerificationIntegrationTest`)
boots a real server with
`maxLogEntries=2` and `failVerificationOnEvictedLog=true`, registers an
expectation so a `GET
/was-responded` exchange is recorded as a real `EXPECTATION_RESPONSE`
request-response pair, then floods
the bounded event log with further unmatched traffic so that pair is
evicted. A subsequent
`verify(request("/was-responded"), response().withStatusCode(418),
never())` through the Java client must
throw an `AssertionError` saying the **response** "could not be
verified" because entries were discarded
after reaching `maxLogEntries`. `MockServerEventLog` implements this
guard twice — once in `verifyRequest`
and once, through a completely separate counting path over recorded
pairs, in `verifyResponse` — and only
the request arm had an `*IntegrationTest`; the response arm was covered
solely by an engine-level test
against an in-process event log. The test uses `never()` because it is
the simplest shape that reaches
the guard: the guard sits on the PASS branch behind any asserted upper
bound (`getAtMost() != -1` — so
`atMost(n)`, `between(0,n)` and `exactly(0)` reach it too), whereas an
`atLeast(1)`/`once()` verification
of an evicted pair fails earlier with an ordinary "Response not found"
message and proves nothing.
`never()` is exactly the case a guard-less server would answer with a
silent false green. The assertion pins the message to `Response could
not be verified` so it cannot be
satisfied by the request-side arm. Verified by a positive control
(disabling only the response-side guard
in production makes the verification pass silently and turns the test
red).

- **The eviction false-green guard is now proven end-to-end over HTTP.**
A new Netty integration test
(`EvictedLogVerificationIntegrationTest`) boots a real server with
`maxLogEntries=2` and
`failVerificationOnEvictedLog=true`, records a `GET /was-called`
request, then floods the bounded
request-log ring with further traffic so the `/was-called` entry is
evicted. A subsequent
`verify(request("/was-called"), never())` through the Java client must
throw an `AssertionError` whose
message says the log "could not be verified" because entries were
discarded after reaching
`maxLogEntries` — proving the guard refuses to certify absence it can no
longer see, rather than
silently passing. Previously the guard was only covered by an
engine-level test against an in-process
`MockServerEventLog` and no `*IntegrationTest` exercised it across the
wire. Verified by a positive
control (disabling the guard in production makes `verify(never())` pass
silently and turns the test
  red).

- **Custom gRPC response metadata and trailing metadata are now proven
against a real `grpc-java`
client.** Two new tests in `GrpcUnaryClientIntegrationTest` register an
expectation whose gRPC
response carries both custom response metadata authored with
`withHeader(...)` and custom trailing
metadata authored with `withTrailer(...)`, drive it with a live
`grpc-java` client, and read the
values back off the real `io.grpc.Metadata` objects the client receives
(via a capturing
`ClientInterceptor`, and via `StatusRuntimeException.getTrailers()` on
the error path). The
assertions are deliberately discriminating: the response metadata must
arrive in the *initial
headers* and not in the trailers, the trailing metadata must arrive in
the *trailers* and not be
folded into the initial headers, and both values must round-trip
byte-for-byte including a value
carrying `=`, `;`, `,` and spaces. Previously this behaviour was
exercised only structurally
(`EmbeddedChannel` / model-level assertions, which cannot tell a trailer
emitted as a trailer from
one folded into the headers) and by the existing `-bin` metadata tests,
which deliberately accept
the value from either side because a body-less unary response may
legitimately collapse to
Trailers-Only. Verified by positive controls: dropping the user-authored
trailers turns both tests
red, and dropping the user-authored response headers turns the header
assertion red.

- **The `maxResponseBodySize` limit is now proven behaviourally against
a real upstream.** A new
integration test (`MaxResponseBodySizeIntegrationTest`) boots a
forwarding MockServer configured with a
4KB `maxResponseBodySize`, points it at a raw upstream socket that
returns a 64KB body, and drives it
over a plain client socket: the oversized body fails the forward and the
client receives **502 Bad
Gateway** with none of the payload relayed, while a control request
whose body sits under the limit is
forwarded intact. A third case repeats the oversized body with
`Transfer-Encoding: chunked` and no
`Content-Length`, proving the cap is enforced against the bytes actually
accumulated by the forward
client's aggregator rather than merely against a declared header.
Previously this documented,
memory-protecting bound — read whenever a forward-client pipeline is
built — had no behavioural
coverage at all, so a regression that dropped the wiring (or passed an
unbounded value) would have
removed the limit silently; only the inbound analogue
`maxRequestBodySize` was verified. The new test
covers the HTTP/1.1 forward aggregator; the HTTP/2 forward path reads
the same property (for the
per-stream aggregator and to derive the client's `maxFrameSize`) and
remains uncovered.
`maxResponseBodySize` accordingly moves from `ENFORCEMENT_EXEMPT` to
`ENFORCEMENT_VERIFIED` in
`ConfigurationEnforcementClassificationTest`. Verified by a positive
control (restoring an unbounded
aggregator lets the oversized body through with a 200 and turns both
over-limit assertions red).

- **The Ruby client now proves live SSE stream consumption over the
wire.** New integration examples
(`spec/integration_spec.rb` → `SSE streaming`) register an
`httpSseResponse` expectation via the Ruby
client against a running MockServer, then open a real streaming HTTP
consumer and assert every `data:`
frame arrives in order, that the reconstructed multi-delta message
matches, and that a multi-line
`data:` payload survives the framing intact (`Content-Type:
text/event-stream`). Previously the Ruby
suite only asserted the JSON keys of a built streaming expectation
(`a2a_spec`) and never consumed a
live SSE stream, so a silent server-emission or client-parsing drop
would have gone uncaught. Verified
by a positive control (dropping events from the emitted stream turns the
received-frames assertion red).

- **The `assumeAllRequestsAreHttp` protocol-detection fallback now has
direct unit coverage.** Two
paired `EmbeddedChannel` tests in `DirectProxyUnificationHandlerTest`
drive
`PortUnificationHandler.decode()` with an HTTP request using a
non-standard method (`PURGE`, which is
  not one of GET/POST/PUT/HEAD/OPTIONS/PATCH/DELETE/TRACE/CONNECT): with
`assumeAllRequestsAreHttp=true` the full HTTP pipeline is added (rather
than falling to binary request
proxying), and with the flag disabled the HTTP codec is not added —
proving the flag is the only
difference. Previously the fallback branch was exercised only by a
live-socket integration test and
the config getter's own unit test, so the `EmbeddedChannel`
protocol-detection path for the flag was
  unexercised.

- **HTTP/3 streaming response bodies are now proven end-to-end through
the action pipeline with a real
QUIC client.** A new integration test
(`Http3StreamingForwardIntegrationTest`) registers a `forward`
expectation on the HTTP/3 port (with `streamingResponsesEnabled`)
pointing at an upstream Server-Sent
Events stream that serves an early event immediately and withholds the
late event for 1.5s, then drives
it with a live Netty QUIC client and asserts both events arrive as
SEPARATE DATA frames spread across
that delay — proving the streaming relay funnels through
`HttpActionHandler` ->
`ResponseWriter.writeResponse` ->
`Http3ResponseWriter.writeStreamingResponse` and emits chunks
incrementally. Previously `Http3StreamingIntegrationTest` drove
`Http3ResponseWriter` directly from a
hand-built QUIC server (bypassing expectation matching), and
`Http3MockingMatrixIntegrationTest`
exercised the real pipeline over QUIC but only with non-streaming
actions, so incremental delivery of a
streamed body through the full pipeline was untested. QUIC-gated like
the sibling HTTP/3 tests so it
  skips cleanly where the native transport is unavailable.

- **The dashboard's Monaco code editor is now proven in a real browser
end-to-end.** A new Playwright
e2e test (`mockserver-ui/e2e/dashboard.spec.ts`) drives the actual
bundled Monaco editor in the
served dashboard's composer against a live MockServer: it asserts
Monaco's own DOM
(`.monaco-editor` / `.view-lines`) renders, authors a JSON response body
via real editor input,
raises and clears a live validation marker from Monaco's JSON language
web worker, then registers
the mock and confirms the Monaco-authored body round-trips to the server
(present in
`PUT /mockserver/retrieve` and served verbatim on the matching request).
Previously the 178
jsdom/vitest specs globally replaced Monaco with a bare `<textarea>`, so
nothing exercised the real
  editor's tokenisation, web-worker validation, or DOM.

- **SOCKS4 CONNECT tunnelling is now proven end-to-end over a real
socket.** A new socket-level
integration test (`NettyHttpProxySOCKS4IntegrationTest`) performs a raw
SOCKS4 CONNECT handshake
against a bound MockServer targeting a loopback `EchoServer`, then sends
an HTTP GET through the
granted tunnel and asserts the EchoServer received the request and
returned 200 (bytes relayed by
`Socks4ConnectHandler`). Previously SOCKS4 was only exercised by an
`EmbeddedChannel` unit test
(`Socks4ProxyHandlerTest`, which asserts handler removal) while every
real-socket proxy integration
test used SOCKS5, so nothing drove the SOCKS4 relay path over the wire.

- **Per-host forward-proxy client-certificate selection is now proven at
a real TLS handshake.** A new
integration test
(`ForwardWithCustomClientCertificateByHostIntegrationTest`) configures
`forwardProxyClientCertificatesByHost` to present two independent client
certificates (each backed by
its own CA) keyed by host, then forwards through MockServer to two
secure upstream `EchoServer`s that
each `REQUIRE` client auth and trust only ONE of the two client CAs.
Because the host string is the
cert-mapping key while the connection target is fixed independently by
the forward port, the same
upstream is reached under both host keys: the mapped cert is accepted
(200) and the mismatched cert is
rejected at the handshake (502). This makes the presented client
certificate a load-bearing assertion,
verified by a positive control (degrading the mapping to always present
cert A flips host B's accepted
case to 502). Previously `NettySslContextFactoryTest` asserted only
`SslContext` identity/distinctness
and the pure resolver -- nothing drove the per-host cert to an actual
mTLS handshake.

- **LLM streaming physics for Gemini, Ollama, and Bedrock are now proven
over a real socket.** Streaming
physics over the wire was previously e2e-tested only for Anthropic and
OpenAI (both SSE); Gemini, Ollama,
and Bedrock rested on self-derived golden JSONL plus codec unit tests,
with no socket streaming e2e. Three
new tests in `LlmAgentLoopE2eTest` serve a streaming `httpLlmResponse`
for each provider, connect a real
socket client, and assert both that the wire `Content-Type` is the
provider's streaming media type
(`text/event-stream` for Gemini SSE, `application/x-ndjson` for Ollama
NDJSON,
`application/vnd.amazon.eventstream` for Bedrock AWS event-stream binary
framing) and that the text
reconstructed by concatenating the streamed deltas — Gemini
`candidates[].content.parts[].text`, Ollama
`message.content`, and Bedrock's base64-wrapped Anthropic `text_delta`
fragments decoded from the
CRC32-validated binary event-stream messages — equals the completion
text exactly. The Bedrock case
de-chunks the HTTP/1.1 chunked body and decodes the binary framing end
to end.

- **The PHP client's fidelity harness now gates the TYPED model, not
just raw replay.** The existing
`RoundTripFidelityTest` deserialises each shared fixture with
`Expectation::fromArray()`, which stores
the decoded array verbatim in `rawData` and replays it unchanged -- so
it records zero gaps for every
fixture BY CONSTRUCTION and can never detect a field the typed builders
(`HttpResponse`, `HttpForward`,
`HttpError`, `HttpRequest`) fail to model. A new
`TypedRoundTripFidelityTest` closes that tautology:
for every shared fixture it reconstructs each action/matcher block
THROUGH the typed model (reflection
copies across only the properties each class declares, recursing into
declared nested typed objects,
then serialises via the class's own `toArray()`) and diffs the rebuilt
structure back against the
fixture -- the server-schema side of the contract -- so any server field
the typed model drops surfaces
as a concrete diff path derived from the corpus, never from the client's
own key list. This immediately
documented five real request-matcher gaps the raw harness hid
(`dnsClass`/`dnsName`/`dnsType`,
`pathParameters`, `protocol`, plus NottableString `method`/`path`), each
pinned in a per-field gap
ledger with a stale-entry ratchet, while the
`httpResponse`/`httpForward`/`httpError` models are proven
to cover their entire fixture surface. A positive-control test proves
the gate fires when a typed
builder drops a field it is supposed to carry (removing `statusCode`
from `HttpResponse::toArray()`
turns the suite red for every fixture carrying it, and green again on
restore) -- the exact regression
the raw-replay harness cannot catch. Shared comparator logic is
extracted to `FidelityComparator`.

- **The Go client's FORWARD and ERROR response actions are now proven
over the wire.** New integration
tests (`response_action_integration_test.go`) register a `httpForward`
and a `httpError`
(`dropConnection`) action via the Go client and drive real requests that
assert the SERVER actually
performs them: the forwarded request loops back through a match-once,
higher-priority self-forward to
a distinct upstream response body (needing no externally-reachable
upstream, so it runs identically in
the CI sibling-container harness and against a locally port-mapped
server), and the error endpoint
drops the connection at the transport level (no HTTP response) while a
control endpoint on the same
server still answers cleanly. Previously the Go client's response-action
coverage was builder/JSON
only -- no test drove a forward or error action to completion over a
socket.

- **Rust client wire tests proving the server actually enforces negation
matchers and performs
response actions.** The Rust integration suite previously only asserted
control-plane serialization
(`matcher_value_tests.rs`) and registered a forward expectation it never
drove (`test_forward_expectation`
"won't actually forward"), so nothing proved a running MockServer acted
on either. Three new
`#[ignore]`d integration tests drive a live server over the data plane
via a dependency-free raw
socket: `test_negation_matcher_enforced_over_wire` registers a
`NottableString` negation (bare
`"!foo"`, explicit `MatcherValue::not_literal`, and an escaped literal
`"!foo"`) and asserts the
server matches a non-`foo` value (200) while excluding `foo` (404) — and
that an escaped `"!foo"`
matches literally rather than as a negation;
`test_forward_action_actually_forwards` registers a
higher-priority run-once FORWARD that loops back to the server's own
port plus a lower-priority
fall-through RESPOND, and asserts the distinctive fall-through body is
returned only if the forward
  genuinely executed (topology-independent — no external upstream); and
`test_error_action_actually_returns_raw_bytes` registers an ERROR action
and asserts the server
writes the configured raw bytes back. Run in CI by the existing
`rust-integration-test` step.

- **Wire-level .NET client test coverage for NottableString header
negation and for
forward/error response actions.** The .NET integration suite asserted
expectation *creation* but
never proved the server honours what the client sends: there was no test
that a `!foo` header
matcher (`MatcherValue.NotLiteral`) is transmitted and enforced over the
wire, nor that a
registered forward or error action is actually performed. A new
`WireBehaviorTests` drives real
requests through a running MockServer (reached via the existing
`MOCKSERVER_URL` harness) to prove:
a "not foo" header matcher matches a non-`foo` request (200) and rejects
a `foo` request (404); the
escaped literal `MatcherValue.Literal("!foo")` matches only a header
whose value really is `!foo`;
a forward action is genuinely forwarded (the path is received twice —
original plus the re-entered
forward — not merely served directly); and an error action actually
drops the connection (a
transport failure, not an HTTP status). Each assertion was confirmed to
redden when the
  corresponding client behaviour is degraded.

- **Live-broker test coverage proving Kafka SASL credentials reach and
are enforced by a real
broker.** Kafka SASL/SSL security was only asserted at the property-map
level
(`KafkaMessagePublisherSecurityTest`) and every live-broker Kafka
integration test used a plaintext
bootstrap, so nothing proved the configured credentials actually
authenticate against a broker. A
new `KafkaSecurityLiveBrokerIntegrationTest` starts a Testcontainers
Kafka whose external listener
is `SASL_PLAINTEXT`/`PLAIN` with a broker-side JAAS config that knows a
single credential, then
drives MockServer's `KafkaMessagePublisher` with a `KafkaSecurity`: a
correctly-credentialed
publisher publishes successfully and the message is read back by a
matching credentialed consumer,
while a wrong-password publisher is rejected with an authentication
exception (proving enforcement,
not merely that plaintext works). Docker-gated so it SKIPS cleanly when
Docker is unavailable.

- **Credential-enforcement test coverage for MQTT security against a
secured live broker.** MQTT
`MqttSecurity` credentials were only asserted at the options-carrier
unit level
(`MqttSecurityOptionsTest`), while the sole live Mosquitto integration
test ran an
`allow_anonymous` plaintext broker — so nothing proved credentials are
actually applied and
enforced on the wire. A new `MqttTlsLiveBrokerIntegrationTest` drives
MockServer's MQTT publisher
against a Testcontainers Mosquitto broker configured with a
`password_file` and
`allow_anonymous false`: it asserts that a publisher wired with the
correct `MqttSecurity`
username/password authenticates and delivers a message to an
authenticated subscriber, and that a
publisher with the wrong password (and one with no credentials) is
rejected by the broker at
  CONNECT. Docker-gated so it skips cleanly when Docker is unavailable.

- **Live-broker test coverage for the AsyncAPI control-plane `load()` →
publish-on-load path.** The
control-plane's broker-connecting path (`createBrokerConnections` /
`publishOnLoad`) was untested
against a real broker — `AsyncApiControlPlaneImplTest` loads without a
reachable broker (asserting
`publishers=0`), and the endpoint IT covers only the broker-less
endpoints. A new Docker-gated
`AsyncApiControlPlaneLiveBrokerIntegrationTest` (Testcontainers Kafka)
drives `load()` with a real
`brokerConfig`, `publishOnLoad:true` and `consume:true`, then proves the
control-plane genuinely
connected and published by consuming the on-load message with a plain
third-party Kafka client and
asserting `status()` reports `publishers>0`, a subscriber, and the
recorded round-tripped message.

- **Over-the-wire test coverage for an LLM refusal preset served with a
rate-limit quota.** The LLM
refusal presets, provider-specific rate-limit headers, and stateful
request-count quota were only
asserted at the body-builder / handler-unit level; nothing drove them
through a running server. A
new `LlmRefusalQuotaRateLimitIntegrationTest` serves an
`httpLlmResponse` configured with an
Anthropic refusal preset and a 2-request quota, then asserts on the raw
socket response that the
first two requests return a `200` refusal envelope
(`stop_reason:"refusal"`) carrying the
`anthropic-ratelimit-requests-*` headers, and that the third
(over-quota) request flips to a `429`
`rate_limit_error` envelope with the exhausted rate-limit headers and
`Retry-After`.

##### Fixed

- **The `testcontainers-mockserver` (Python) port assertions no longer
break against testcontainers
4.15.0, which keys `DockerContainer.ports` by `str(port)` rather than
`int`.** `with_exposed_ports`
now stores `self.ports[str(port)] = None` (4.15.0 types the attribute as
`dict[str, Optional[int]]`), so the suite's `assert 1080 in
container.ports` started failing with
`assert 1080 in {'1080': None}` and took five tests — and the whole
`MockServer Python` pipeline,
and with it the umbrella `MockServer` build — red on `master`. The tests
now read the exposed ports
through a normaliser that parses each key to an `int` (tolerating a
`"1080/tcp"` protocol suffix),
so they assert the same thing under either key style. This also closes a
latent false green: the
`assert MOCKSERVER_PORT not in container.ports` check in
`test_replaces_default_port` passed
trivially once the keys became strings, and so would no longer have
caught `with_server_port`
failing to drop the previously exposed port. Only the tests changed —
`MockServerContainer` itself
was already correct, as `get_exposed_port` takes an `int` and normalises
internally.
`<blobStoreKeyPrefix>/<file name>` instead of under the writing
machine's absolute local path, and a
`blobStoreKeyPrefix` that does not end in a separator is now treated as
a folder-style prefix instead
of being glued straight onto the key (`mockserver` + `x.json` was
`mockserverx.json` and is now
`mockserver/x.json`). Anything persisted by an earlier version is stored
under the OLD name and will
NOT be restored after upgrading — the one-line migration is below.\*\*
The blob key was the ABSOLUTE
local `persistedExpectationsPath` (for example
`/var/folders/.../persistedExpectations.json`) and the
configured `blobStoreKeyPrefix` was concatenated onto it with plain
string addition. With the prefix
shape the documentation recommends — `blobStoreKeyPrefix="mockserver/"`,
with a trailing separator —
that composed `mockserver//var/folders/.../persistedExpectations.json`,
and MinIO rejects the doubled
`//` outright with HTTP 400, "Object name contains unsupported
characters", so under that one
configuration nothing was ever persisted and consequently nothing could
be restored on restart. Under
every other prefix shape the write SUCCEEDED and restore worked — a
leading `/` is a legal byte in an
S3 object name, and the read composed exactly the same name back — but
the object was then named after
the writing container's local filesystem layout, so a second instance
that resolved
`persistedExpectationsPath` to a different absolute path (started from a
different working directory,
or in a different container) looked under a different name and silently
restored nothing. The key is
now derived by the new shared `org.mockserver.state.BlobKeys` helper in
`mockserver-core`: for every
store other than `FilesystemBlobStore` the key is the FILE NAME of
`persistedExpectationsPath` alone,
and prefix and key are joined with exactly one separator, with any
leading separator dropped and any
repeated separators collapsed, so every prefix shape a user can
configure — unset, `mockserver`,
`mockserver/`, `/mockserver/` — now produces the same valid object name
(`mockserver/persistedExpectations.json`). That normalisation is applied
for all
`put`/`get`/`list`/`delete` operations wherever `blobStoreKeyPrefix` is
applied, so it renames EVERY
blob key, not only the persisted-expectations document.
`FilesystemBlobStore` is unaffected: it
interprets the key as a file path, so it keeps the absolute path and
writes exactly the file it always
did. **Upgrading:** because the old key always embedded the absolute
local path and the new key is the
bare file name, the object name changes for every non-filesystem
persistence user, under every prefix
shape and on every platform — there is no configuration in which the old
name is preserved. On the
first start after upgrading, the restore looks under the new name,
misses (logged at `INFO` with the
name it looked for), and the instance starts with no restored
expectations; the next expectation change
then writes a fresh object under the new name and leaves the old one
behind. To carry existing state
across the upgrade, copy or rename the object once before starting the
new version, for example
`aws s3 mv s3://<bucket>/<prefix>/<old absolute path>
s3://<bucket>/<prefix>/<file name>` — otherwise
accept the miss and let the first expectation change re-create it. One
long-standing footgun goes away
with the change: restoring after a restart no longer requires the two
instances to resolve
`persistedExpectationsPath` to the same absolute path, only to the same
file name. Deployments that
must NOT share state within one bucket should give each its own
`blobStoreKeyPrefix` (or its own file
name). Proven by a Docker-gated MinIO round trip that writes and then
reads back an expectation with a
trailing-slash `blobStoreKeyPrefix` (the exact configuration that
returned HTTP 400 before), a MinIO
put/get/list/delete round trip across all four prefix shapes, and
Docker-free unit coverage of the key
  composition and of the key the persistence layer derives.
- **The configuration enforcement-evidence guard no longer certifies
evidence it cannot see.**
`ConfigurationEnforcementClassificationTest` records, for every risky
configuration property, the
`Class#method` test that proves an instance-set value changes observable
behaviour. It validated those
pointers by loading the class — but it runs in `mockserver-core`, so any
pointer naming a test in a
sibling module was silently skipped on `ClassNotFoundException`. That
exempted precisely the most
valuable evidence, the end-to-end Layer C pointers: renaming, moving or
deleting the referenced test
left a dangling pointer and the guard still passed green, for
`maxRequestBodySize`,
`maxResponseBodySize`, `wasmEnabled`, `redactSecretsInLog`,
`clusterEnabled`, `dnsEnabled`,
`grpcBidiStreamingEnabled`, `http3ConnectUdpEnabled` and
`transparentProxyEnabled`. A class that
  cannot be loaded is now resolved
by locating its `.java` source under any module's `src/test/java` and
asserting the file declares both
the class and the referenced method, so cross-module pointers are
checked in a full reactor build and
when only some modules are built. The guard fails closed: a pointer
resolvable by neither route is now
a failure naming the dangling pointer, never a silent skip. Anti-vacuity
assertions in the spirit of
the sibling `ConfigurationCallSiteGuardTest` keep the scan honest — the
set of pointers resolved by
source scan must match the declared cross-module ratchet exactly,
`mockserver-netty` and
`mockserver-state-infinispan` must both have contributed, and classpath
resolution must still cover
the bulk of the pointers — so a scan that resolves nothing cannot pass.
Verified by degrading a real
`mockserver-netty` test method name: the guard now fails with a message
naming the dangling pointer,
where the previous version passed green with the identical defect in
place.
- **gRPC trailing metadata is no longer silently dropped over HTTP/3.**
Every trailer an expectation
authored — `response().withTrailer("x-request-cost", "42")`, and the
gRPC chaos profile's
`customTrailers` — reached an HTTP/1.1 or HTTP/2 client but **never
reached an HTTP/3 client at
all**, in every branch of the HTTP/3 gRPC response path (with a body and
body-less, and both with
and without proto descriptors loaded). The same expectation therefore
produced different trailing
metadata depending only on which transport the client happened to use,
and there was no error or
warning anywhere to indicate the loss — a test asserting on trailing
metadata over HTTP/3 simply
saw nothing. The cause was that `Http3GrpcResponseWriter` builds its
HTTP/3 frames by hand rather
than through
`MockServerHttpResponseToFullHttpResponse.mapResponseWithTrailers`
(which is what
carries trailers on the other transports), and
`GrpcHttp3Adapter.buildTrailingHeadersFrame` /
`buildTrailersOnlyFrame` populated only `grpc-status` and
`grpc-message`; the response's own
trailers were never read. They are now emitted on the terminal frame: on
the trailing HEADERS
frame when the response has a body, and folded into the Trailers-Only
frame when it does not,
which is the shape gRPC defines for that form (`HTTP-Status Content-Type
Trailers`, where
`Trailers` includes custom metadata) and leaves the framing unchanged —
there is still exactly one
terminal frame, written with `SHUTDOWN_OUTPUT`, so an added trailer
cannot cost the response its
end-of-stream marker the way it did on HTTP/2 before the fix above. A
user-authored trailer cannot
  override or spoof the transport's own status: the new shared
`GrpcResponseStatusResolver.passThroughTrailers` (the trailer twin of
`passThroughHeaders`)
excludes `grpc-status`, `grpc-message` and `grpc-status-name`, mirroring
the exclusion the HTTP/2
path makes in `remainingTrailers`, and also excludes the
connection-specific fields,
`content-length`/`content-type` and pseudo-header names that RFC 9114
forbids in a trailer section.
Trailer field names are lower-cased and CR/LF stripped from values
before reaching the frame,
because HTTP/3 field names must be lower-case and Netty's
`DefaultHttp3Headers` rejects an
upper-case one by throwing — so an expectation authoring
`withTrailer("X-Request-Cost", …)` would
otherwise have taken the client's entire response down rather than
dropping a single field.
Verified over the wire by two new tests in `Http3GrpcIntegrationTest`
that drive a live in-JVM
Netty QUIC client and read the metadata off the HEADERS frames it
actually received, asserting
which side each value arrived on (a trailer must not be folded into the
initial headers, a
response header must not be repeated as a trailer) and asserting the
HEADERS-frame count so the
metadata cannot arrive at the cost of correct framing, plus
adapter-level coverage in
`GrpcHttp3AdapterTest`. Positive control: neutering the trailer
pass-through in production turns
  all seven new assertions red with the trailer absent.
- **A FILE response body is now served verbatim (and templated) on every
response path, and a FILE
request body is now actually matched
([#&#8203;2450](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2450)).**
A response whose body is a `FILE` (a `filePath` with
no template engine) was previously read on the static response action
only; the same FILE body
returned from an object callback, a class callback, a response template,
or a forward
`responseOverride` reached the wire unread, emitting the file *path*
string instead of the file
*contents*. Materialisation now lives in a single shared
`FileBodyMaterialiser` invoked from the two
response-write funnels (`writeResponseActionResponse`, covering the
static, object-callback,
class-callback, response-template and SSE paths, and
`writeForwardActionResponse`, covering the
forward `responseOverride`), so all five producers — and the shared
WAR/servlet path — serve the file
contents. Templated FILE bodies (a `FileBody` carrying a
Velocity/Mustache `templateType`) are rendered
against the request on these paths too, not only verbatim ones; a text
content type yields the decoded
string and a binary or absent content type yields the raw bytes intact.
A missing or unreadable file
now produces a clean, logged `500` whose body does not leak the path,
instead of a broken connection or
the path string. Separately, a `FILE` body used for **request matching**
had no case in
`BodyMatcherBuilder`, so the body constraint was silently ignored (it
matched any body); it now matches
the request body against the exact file contents (string or binary).
This resolves the earlier
  "static response only" caveat.
- **A forward `responseOverride` that replaces the body is no longer
truncated to the upstream
`Content-Length`.** When a forwarded request's response is overridden
with a new body,
`HttpResponse.update()` replaced the body but kept the `Content-Length`
inherited from the upstream
response. A replacement body longer than the upstream body was therefore
truncated on the wire (and a
shorter one could over-run) — visible only to a real client, since every
layer above the encoder held
the full, correct response. `update()` now drops the inherited
`Content-Length` whenever the override
supplies a new body (or a `generateFromSchema`), unless the override
itself sets an explicit
`Content-Length` (which is still honoured verbatim), so the encoder
recomputes the length from the
actual body. This closes the residual on the forward-override path left
by the FILE-body fix above.
- **A gRPC error response carrying custom trailing metadata no longer
loses its status.** On HTTP/2 a
body-less gRPC response is collapsed into the gRPC Trailers-Only form,
which moves `grpc-status`
into the initial HEADERS frame and relies on that frame being
end-of-stream. When the expectation
also authored a custom trailer with `withTrailer(...)`, that trailer
kept a separate trailing
HEADERS frame alive, so the initial frame was no longer end-of-stream: a
real client read it as
ordinary headers (where `grpc-status` is ignored) and then found no
status at all in the terminal
frame, failing the call with `UNKNOWN: missing GRPC status`. In other
words, adding a single
trailer to an error response destroyed the error — the caller lost both
the status code and the
message. `GrpcToHttpResponseHandler.asTrailersOnlyIfHttp2` now skips the
Trailers-Only collapse
whenever any user-authored trailer remains, keeping
`grpc-status`/`grpc-message` in the trailing
HEADERS frame alongside the custom metadata, which is the correct shape
in that case. The same fix
covers the gRPC chaos fault path, which produced the byte-identical
broken shape: a fault response
configured with `customTrailers` emits them as real trailers alongside
`grpc-status`/`grpc-message`
on a body-less response, so a chaos-injected error over HTTP/2 also
reached the client as
`UNKNOWN: missing GRPC status` instead of the configured status. Found
by the new real-client
  trailing-metadata coverage described under Added.
- **gRPC-Web now re-frames matched-expectation responses correctly over
a real HTTP/1.1 socket, and
is covered by an over-the-wire integration test.** Every previous
gRPC-Web test drove the handler
through an `EmbeddedChannel` and set `x-grpc-web-content-type` directly
on the response, so none
exercised the actual mock-matching path: there the marker lives on the
request only and was lost,
and a matched expectation went back to a browser client as
`application/grpc` with `grpc-status` in
HTTP trailers a gRPC-Web client cannot read. The original request
content-type is now retained in
the per-stream `GrpcPendingRequests` record alongside the resolved
service/method, so
`GrpcToHttpResponseHandler` re-frames the response as gRPC-Web
(length-prefixed message frame + a
`0x80` trailer frame carrying `grpc-status` in the body, base64-encoded
for the `-text` variant).
A new `GrpcWebOverTheWireIntegrationTest` posts a real
`application/grpc-web` and
`application/grpc-web-text` framed request to a running server over a
raw socket and asserts on the
  exact bytes a browser client would receive.
- **The AsyncAPI control-plane HTTP endpoints now have an over-the-wire
integration test.**
`PUT /mockserver/asyncapi`, `GET /mockserver/asyncapi` and `PUT
/mockserver/asyncapi/verify` were
only exercised at the orchestrator/control-plane level, so a regression
in the Netty →
`HttpState` → `AsyncApiControlPlaneRegistry` routing or response wiring
would not have been caught.
A new `AsyncApiControlPlaneIntegrationTest` boots a real MockServer and
drives all three endpoints
over a raw socket without any live broker: it asserts the load response
(`201`, `loaded:true`,
channel count, zero publishers/subscribers), the status response (`200`,
channels, counts, and the
empty/unloaded case), and the broker-less verify verdict (`406` with the
"at least 1 … found 0"
  failure detail) plus the blank-body `400`.
- **A `StreamingBody` response delivered to a real HTTP/2 inbound client
is now covered end-to-end.**
`NettyResponseWriter.writeStreamingResponse` re-stamps the request's
HTTP/2 stream id onto the
streaming response head (the field is not part of the copied header
multimap) and flushes each chunk
as it arrives, but no test drove that path with a real HTTP/2 client —
`Http2SseStreamingIntegrationTest`
covered only the SSE sibling and explicitly noted the `StreamingBody`
case was untested, while the
existing streaming-relay tests drive an HTTP/1.1 inbound socket. A new
`Http2StreamingBodyIntegrationTest`
drives a real prior-knowledge h2c multiplex client through a
`streamingResponsesEnabled` forward
MockServer to an SSE upstream and asserts on the frames the client
receives on its OWN request stream:
both events arrive (proving the stream-id stamp — the
[#&#8203;2419](https://redirect.github.com/mock-server/mockserver-monorepo/issues/2419)
class), and the early event's DATA frame
arrives promptly as one of at least two separate, in-order DATA frames
rather than being buffered into
one. Degrading the write path to buffer chunks until stream completion
was verified to make the
  incremental-delivery assertion go red.
- **PROXY-protocol destination resolution is now proven to drive
transparent-proxy forwarding over a real
socket.** `ProxyProtocolOriginalDestinationHandler` was only exercised
via `EmbeddedChannel`, which asserts
the handler sets the `REMOTE_SOCKET` channel attribute but never that
this attribute actually chooses the
forward target end-to-end. A new non-privileged loopback
`ProxyProtocolForwardingIntegrationTest` runs
MockServer with `transparentProxyEnabled=true` and no fixed remote,
opens a raw socket, writes a valid
PROXY v1 `TCP4` header naming a loopback `EchoServer` as the destination
followed by a plain GET whose
`Host` header points at an unrelated (closed) decoy port, and asserts
the EchoServer reflects the request
back — proving the PROXY-protocol `REMOTE_SOCKET`, and not the `Host`
header, drives forwarding. The test
needs no `NET_ADMIN`/privileged capability because the PROXY-protocol
header is an application-level byte
prefix. Verified as a genuine regression guard by a positive control:
ignoring the PROXY-header
destination turns the forwarding assertions RED, restoring it returns
them GREEN.
- **A FILE response body with no template engine now serves the file
contents, not the file path ([#&#8203;2450](https://r

> ✂ **Note**
> 
> PR body was truncated to here.


</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-02 01:57:35 +00:00
renovate[bot] d3b772477f chore(deps): update npm to v12.0.2 (#4273)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [npm](https://docs.npmjs.com/)
([source](https://redirect.github.com/npm/cli)) |
[`12.0.1+sha512.2f94fd8bf600416416a934bfc59c4991e8bff7372ef7d842784e2a8b8d48c81555ee645069ddea73625fb8e92dc261feab0188fd5dab6c22fefd46316f5f9140`
→ `12.0.2`](https://renovatebot.com/diffs/npm/npm/12.0.1/12.0.2) |
![age](https://developer.mend.io/api/mc/badges/age/npm/npm/12.0.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/npm/12.0.1/12.0.2?slim=true)
|

---

### Release Notes

<details>
<summary>npm/cli (npm)</summary>

###
[`v12.0.2`](https://redirect.github.com/npm/cli/blob/HEAD/CHANGELOG.md#1202-2026-07-27)

[Compare
Source](https://redirect.github.com/npm/cli/compare/v12.0.1...v12.0.2)

##### Bug Fixes

-
[`834408e`](https://redirect.github.com/npm/cli/commit/834408e8f0f2295d02205d8ff5d011c859835225)
[#&#8203;9760](https://redirect.github.com/npm/cli/pull/9760) pack:
honor min-release-age-exclude
([#&#8203;9760](https://redirect.github.com/npm/cli/issues/9760))
([@&#8203;martinrrm](https://redirect.github.com/martinrrm),
[@&#8203;Copilot](https://redirect.github.com/Copilot))
-
[`ef6cfea`](https://redirect.github.com/npm/cli/commit/ef6cfeaae218d1177e29f4fd26c591d0a1b86a46)
[#&#8203;9786](https://redirect.github.com/npm/cli/pull/9786) owner: use
scoped registry for user lookup
([#&#8203;9786](https://redirect.github.com/npm/cli/issues/9786))
([@&#8203;martinrrm](https://redirect.github.com/martinrrm),
[@&#8203;Copilot](https://redirect.github.com/Copilot))

##### Documentation

-
[`9e17dc0`](https://redirect.github.com/npm/cli/commit/9e17dc064782ca1a1fd7f549e5a2fc7903f7fe51)
[#&#8203;9805](https://redirect.github.com/npm/cli/pull/9805) clarify
npm 12 unknown-config breaking change in changelogs
([#&#8203;9805](https://redirect.github.com/npm/cli/issues/9805))
([@&#8203;Hashim1999164](https://redirect.github.com/Hashim1999164))

##### Chores

-
[`7b1f6c1`](https://redirect.github.com/npm/cli/commit/7b1f6c173d17b3bf30e45426f6df39473c6a1163)
[#&#8203;9747](https://redirect.github.com/npm/cli/pull/9747) parse pack
--json object output in node integration
([#&#8203;9747](https://redirect.github.com/npm/cli/issues/9747))
([@&#8203;reggi](https://redirect.github.com/reggi),
[@&#8203;Copilot](https://redirect.github.com/Copilot))

##### Dependencies

-
[workspace](https://redirect.github.com/npm/cli/releases/tag/arborist-v10.0.2):
`@npmcli/arborist@10.0.2`
-
[workspace](https://redirect.github.com/npm/cli/releases/tag/config-v11.0.1):
`@npmcli/config@11.0.1`
-
[workspace](https://redirect.github.com/npm/cli/releases/tag/libnpmdiff-v9.0.2):
`libnpmdiff@9.0.2`
-
[workspace](https://redirect.github.com/npm/cli/releases/tag/libnpmexec-v11.0.2):
`libnpmexec@11.0.2`
-
[workspace](https://redirect.github.com/npm/cli/releases/tag/libnpmfund-v8.0.2):
`libnpmfund@8.0.2`
-
[workspace](https://redirect.github.com/npm/cli/releases/tag/libnpmpack-v10.0.2):
`libnpmpack@10.0.2`

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-01 21:34:30 +00:00
renovate[bot] 514dc5f5ae chore(deps): update dependency npm-run-all2 to v9.0.3 (#4271)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [npm-run-all2](https://redirect.github.com/bcomnes/npm-run-all2) |
[`9.0.2` →
`9.0.3`](https://renovatebot.com/diffs/npm/npm-run-all2/9.0.2/9.0.3) |
![age](https://developer.mend.io/api/mc/badges/age/npm/npm-run-all2/9.0.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/npm-run-all2/9.0.2/9.0.3?slim=true)
|

---

### Release Notes

<details>
<summary>bcomnes/npm-run-all2 (npm-run-all2)</summary>

###
[`v9.0.3`](https://redirect.github.com/bcomnes/npm-run-all2/blob/HEAD/CHANGELOG.md#v903)

[Compare
Source](https://redirect.github.com/bcomnes/npm-run-all2/compare/v9.0.2...v9.0.3)

##### Merged

- Upgrade: Bump ansi-styles from 6.2.3 to 7.0.0
[`#244`](https://redirect.github.com/bcomnes/npm-run-all2/pull/244)
- Upgrade: Bump zizmorcore/zizmor-action from 0.6.0 to 0.6.1
[`#243`](https://redirect.github.com/bcomnes/npm-run-all2/pull/243)
- Upgrade: Bump zizmorcore/zizmor-action from 0.5.7 to 0.6.0
[`#242`](https://redirect.github.com/bcomnes/npm-run-all2/pull/242)
- Upgrade: Bump actions/setup-node from 6 to 7
[`#241`](https://redirect.github.com/bcomnes/npm-run-all2/pull/241)
- chore: update schema version in .knip.jsonc to v6
[`#237`](https://redirect.github.com/bcomnes/npm-run-all2/pull/237)
- Upgrade: Bump
[@&#8203;types/node](https://redirect.github.com/types/node) from 25.9.4
to 26.0.0
[`#239`](https://redirect.github.com/bcomnes/npm-run-all2/pull/239)
- Upgrade: Bump actions/checkout from 6 to 7
[`#238`](https://redirect.github.com/bcomnes/npm-run-all2/pull/238)

##### Commits

- Harden GitHub Actions workflows
[`7f5fca6`](https://redirect.github.com/bcomnes/npm-run-all2/commit/7f5fca6de6a0b454458baf1e650b9535b689aa1d)
- Remove obsolete shell-quote types
[`d970668`](https://redirect.github.com/bcomnes/npm-run-all2/commit/d970668ba2c5a1c835df1d22c5f66a2abc507e9c)

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 22:13:48 +00:00
renovate[bot] af1fa1cbc5 chore(deps): update dependency fs-extra to v11.4.0 (#4267)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [fs-extra](https://redirect.github.com/jprichardson/node-fs-extra) |
[`11.3.6` →
`11.4.0`](https://renovatebot.com/diffs/npm/fs-extra/11.3.6/11.4.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/fs-extra/11.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/fs-extra/11.3.6/11.4.0?slim=true)
|

---

### Release Notes

<details>
<summary>jprichardson/node-fs-extra (fs-extra)</summary>

###
[`v11.4.0`](https://redirect.github.com/jprichardson/node-fs-extra/blob/HEAD/CHANGELOG.md#1140--2026-07-23)

[Compare
Source](https://redirect.github.com/jprichardson/node-fs-extra/compare/11.3.6...11.4.0)

- Proper `EEXIST` error when trying to create a symlink and the dest
already exists as a broken symlink
([#&#8203;925](https://redirect.github.com/jprichardson/node-fs-extra/issues/925),
[#&#8203;1076](https://redirect.github.com/jprichardson/node-fs-extra/pull/1076))

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-26 20:46:53 +00:00
renovate[bot] e1a4833a18 chore(deps): update dependency publint to v0.3.22 (#4266)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [publint](https://publint.dev)
([source](https://redirect.github.com/publint/publint/tree/HEAD/packages/publint))
| [`0.3.21` →
`0.3.22`](https://renovatebot.com/diffs/npm/publint/0.3.21/0.3.22) |
![age](https://developer.mend.io/api/mc/badges/age/npm/publint/0.3.22?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/publint/0.3.21/0.3.22?slim=true)
|

---

### Release Notes

<details>
<summary>publint/publint (publint)</summary>

###
[`v0.3.22`](https://redirect.github.com/publint/publint/blob/HEAD/packages/publint/CHANGELOG.md#0322)

##### Patch Changes

- [#&#8203;241](https://redirect.github.com/publint/publint/pull/241)
[`7740a62`](https://redirect.github.com/publint/publint/commit/7740a62fe60cc874de25fe491793eaf2bfc11d9e)
- Support npm v12 and pnpm v11

-
[`cad0425`](https://redirect.github.com/publint/publint/commit/cad0425333aaa3c7a2dbca492477c80a7bd7fd09)
- Relax git url regex for repository validation

- [#&#8203;244](https://redirect.github.com/publint/publint/pull/244)
[`393ac4d`](https://redirect.github.com/publint/publint/commit/393ac4d2fda4b26b8f921e021bf6efeab2849467)
- Recognize more common internal files for the `USE_FILES` suggestion.

- [#&#8203;242](https://redirect.github.com/publint/publint/pull/242)
[`4a8dc75`](https://redirect.github.com/publint/publint/commit/4a8dc757f7928dd884c762c9315f6e99d8805917)
- Refactor supported package manager handling from `@publint/pack`

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-26 05:31:01 +00:00
renovate[bot] a546db2995 chore(deps): update dependency sinon to v22.1.0 (#4263)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [sinon](https://sinonjs.org/)
([source](https://redirect.github.com/sinonjs/sinon)) | [`22.0.0` →
`22.1.0`](https://renovatebot.com/diffs/npm/sinon/22.0.0/22.1.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/sinon/22.1.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/sinon/22.0.0/22.1.0?slim=true)
|

---

### Release Notes

<details>
<summary>sinonjs/sinon (sinon)</summary>

###
[`v22.1.0`](https://redirect.github.com/sinonjs/sinon/blob/HEAD/CHANGES.md#2210)

[Compare
Source](https://redirect.github.com/sinonjs/sinon/compare/v22.0.0...v22.1.0)

-
[`d36e921b`](https://redirect.github.com/sinonjs/sinon/commit/d36e921baf382598cfa9323cb672c87ce85e2ad5)
  fix: let returns override returnsArg (王胜)
  > Signed-off-by: 王胜 <2318857637@&#8203;qq.com>
-
[`40c4caa0`](https://redirect.github.com/sinonjs/sinon/commit/40c4caa0f47b8fb184319a8873bac616c3eef871)
fix: align restoreObject docs test with idempotent behavior
([#&#8203;2745](https://redirect.github.com/sinonjs/sinon/issues/2745))
(Julia Miller)
> restoreObject was made idempotent in
[#&#8203;2737](https://redirect.github.com/sinonjs/sinon/issues/2737) —
it no longer throws
  > for objects with no restorable methods. Updates the docs page
  > and its corresponding test to reflect this.
-
[`a68dac19`](https://redirect.github.com/sinonjs/sinon/commit/a68dac198c6d0d9ce797dc9178d0c57ea6455aab)
fix: throw a clear error when throwArg index equals the argument count
([#&#8203;2743](https://redirect.github.com/sinonjs/sinon/issues/2743))
(spokodev)
> - fix: throw a clear error when throwArg index equals the argument
count
  >
> spyCall.throwArg(pos) guarded with pos > this.args.length, so calling
it
> with pos equal to the number of recorded arguments slipped past the
guard
  > and reached throw this.args\[pos], throwing undefined instead of the
> intended TypeError. A thrown undefined cannot be inspected as an Error
and
  > is reported by test frameworks as no exception thrown.
  >
> Use >= to match ensureArgs in behavior.js and the sibling callArg
helpers,
  > which already reject an out-of-range index with a clear error.
-
[`9ea504e3`](https://redirect.github.com/sinonjs/sinon/commit/9ea504e317e9a27c376a9740bc04ec75f699ed30)
feat: make sinon.restoreObject idempotent
([#&#8203;2737](https://redirect.github.com/sinonjs/sinon/issues/2737))
(Ilia Choly)
  > Passing an object with no live fakes to restoreObject now restores
  > nothing instead of throwing, giving it symmetry with sinon.restore()
> and sandbox.restore(). The strict "found no methods" check is retained
  > for spy(object) and stub(object), which now use a dedicated
> walkObjectStrict export, while restoreObject uses the loose
walkObject.
  > Passing a falsy value still throws.
  >
> Fixes
[#&#8203;2736](https://redirect.github.com/sinonjs/sinon/issues/2736)
-
[`4db4feff`](https://redirect.github.com/sinonjs/sinon/commit/4db4feffe19d3f117d46b9015b4890187a282cf0)
  fix: preserve fake undefined argument validation (Vishal Kumar Singh)
-
[`755a40d7`](https://redirect.github.com/sinonjs/sinon/commit/755a40d772b02a7ba7e9a1d465a5f26d486ebbdb)
fix: isolate callId counter per sandbox for parallel test support
(Vishal Kumar Singh)
> The global callId counter in proxy-invoke.js caused
calledImmediatelyBefore
> and calledImmediatelyAfter to fail when tests run in parallel with
separate
> sandboxes. Each sandbox now maintains its own callId counter, passed
through
  > the spy/stub/fake creation chain via a context object.
  >
> Fixes
[#&#8203;2472](https://redirect.github.com/sinonjs/sinon/issues/2472)

*Released by Morgan Roderick on 2026-07-20.*

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzUuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI3NS4yIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 20:48:59 +00:00
renovate[bot] 3575bb8c92 chore(deps): update dependency c8 to v12 (#4255)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [c8](https://redirect.github.com/bcoe/c8) | [`11.0.0` →
`12.0.0`](https://renovatebot.com/diffs/npm/c8/11.0.0/12.0.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/c8/12.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/c8/11.0.0/12.0.0?slim=true)
|

---

### Release Notes

<details>
<summary>bcoe/c8 (c8)</summary>

###
[`v12.0.0`](https://redirect.github.com/bcoe/c8/blob/HEAD/CHANGELOG.md#1200-2026-07-14)

[Compare
Source](https://redirect.github.com/bcoe/c8/compare/v11.0.0...v12.0.0)

##### ⚠ BREAKING CHANGES

- yargs enforces a stricter range of Node versions ^20.19.0 || ^22.12.0
|| >=23

##### Features

- update yargs to 18.x.x
([#&#8203;599](https://redirect.github.com/bcoe/c8/issues/599))
([dc2d6c8](https://redirect.github.com/bcoe/c8/commit/dc2d6c81e0b82aad80ffcc38d78bd4a76665675e))

</details>

---

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

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/semantic-release/semantic-release).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI2NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-18 00:31:24 +00:00
renovate[bot] 956baf4d6d chore(deps): update npm to v12.0.1 (#4247)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 18:17:46 +00:00
renovate[bot] 7abb56289a chore(deps): update npm to v12 (#4244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 22:56:45 +00:00
renovate[bot] 8b15865161 chore(deps): update dependency @types/node to v24.13.3 (#4243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 07:09:01 +00:00
renovate[bot] c62136f5b1 chore(deps): update dependency mockserver-client to v7.4.0 (#4240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-07 20:01:54 +00:00
renovate[bot] 3259ebbd87 chore(deps): update dependency got to v15.1.0 (#4238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-05 14:32:30 +00:00
renovate[bot] 1c17eb50e6 chore(deps): update dependency mockserver-client to v7.3.0 (#4237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-04 10:12:39 +00:00
renovate[bot] b3da275054 chore(deps): update dependency testcontainers to v12.0.4 (#4233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 01:05:03 +00:00
renovate[bot] d0467bb6e0 chore(deps): update dependency fs-extra to v11.3.6 (#4236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 22:50:13 +00:00
renovate[bot] 96fb7833bf chore(deps): update npm to v11.18.0 (#4235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 19:22:59 +00:00
renovate[bot] 2837e96e51 chore(deps): update dependency got to v15.0.7 (#4231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-01 22:45:44 +00:00
renovate[bot] 4546915407 chore(deps): update dependency js-yaml to v4.3.0 (#4229)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-29 23:46:38 +00:00
renovate[bot] 028a9f3c8a chore(deps): update dependency prettier to v3.8.5 (#4228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-29 20:07:57 +00:00
renovate[bot] 53e6dde454 chore(deps): update dependency nock to v14.0.16 (#4227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-29 13:56:31 +00:00
Matt Travi 86c1c8223c ci(renovate): configure managerFilePatterns for docker-compose to include the e2e compose file 2026-06-28 23:08:12 -05:00
Matt Travi c2586029d3 test(e2e): migrate from raw use of dockerode to use testcontainers instead (#4221)
which uses dockerode internally. this is a follow up to
https://github.com/semantic-release/semantic-release/pull/4220 to simplify teardown and avoid
managing the complexity as directly
2026-06-28 14:21:27 -07:00
renovate[bot] 8a5129225e chore(deps): update dependency dockerode to v5.0.1 (#4219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-27 22:10:38 +00:00
renovate[bot] 91dbcc4b4d chore(deps): update dependency mockserver-client to v7.2.0 (#4218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-25 22:57:17 +00:00
renovate[bot] 8d1b7ffb63 chore(deps): update dependency got to v15.0.6 (#4217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-24 17:57:08 +00:00
renovate[bot] 892f7b3806 chore(deps): update dependency ls-engines to v0.10.1 (#4215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 07:48:31 +00:00
renovate[bot] 7eb073590d chore(deps): update dependency mockserver-client to v7.1.0 (#4211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 00:08:21 +00:00
Olabode Lawal-Shittabey d871161133 Merge pull request #4209 from semantic-release/chore/docs
docs: integrate new doc site
2026-06-17 10:17:02 -07:00
renovate[bot] 6ca39fd752 chore(deps): update dependency npm-run-all2 to v9.0.2 (#4208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-16 02:08:02 +00:00
renovate[bot] 2257d88c34 chore(deps): update npm to v11.17.0 (#4206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-14 22:16:28 +00:00
renovate[bot] 86372c2a65 chore(deps): update dependency @types/node to v24.13.2 (#4205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-14 01:58:20 +00:00
renovate[bot] e189e63a74 chore(deps): update dependency prettier to v3.8.4 (#4204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 18:01:13 +00:00
renovate[bot] f6da026308 chore(deps): update dependency mockserver-client to v7 (#4201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-09 19:43:04 +00:00
renovate[bot] 46d5517f45 chore(deps): update dependency @types/node to v24.13.1 (#4198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-09 01:53:30 +00:00
renovate[bot] bc7d29057f chore(deps): update dependency @types/node to v24.13.0 (#4196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-07 10:13:10 +00:00
renovate[bot] f6879b3665 chore(deps): update dependency js-yaml to v4.2.0 (#4193)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 00:10:26 +00:00
renovate[bot] dc96ac592e chore(deps): update npm to v11.16.0 (#4190)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-02 22:15:00 +00:00
renovate[bot] 6b84503567 chore(deps): update dependency mockserver-client to v6.1.0 (#4188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-30 09:39:08 +00:00
renovate[bot] b10b9bd12d chore(deps): update dependency npm-run-all2 to v9.0.1 (#4184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 06:02:23 +00:00
renovate[bot] 8ca39a341b chore(deps): update dependency mockserver-client to v6 (#4176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-24 09:52:34 +00:00