fix: clear 13 advisories, report the running build, settle #148, per-release changelog #154

Merged
jlxq0 merged 5 commits from fix/t_3496b128-matrix-audit into main 2026-10-24 03:15:59 +00:00
Owner

Four fixes from the matrix-mcp audit, approved by Julian (fleet t_3496b128). Base d79c7c2.

1. Dependency advisories

cargo audit at d79c7c2: 13 vulnerabilities. Targeted cargo update -p, no ignores:
rustls 0.23.46, rustls-webpki 0.103.16, webpki-roots / webpki-root-certs 1.0.11, quinn-proto 0.11.19, ctutils 0.4.5. Now 0 vulnerabilities (the 3 allowed warnings remain: two unmaintained, tokio RUSTSEC-2026-0386 unsound). All bumped crates declare rust-version ≤ 1.87.

2. Release identity a client and an operator can read

  • serverInfo reported {"name":"rmcp","version":"1.7.0"} on both mounts. rmcp's ServerInfo::new → Implementation::from_build_env(), whose env! expands inside rmcp. It now reports matrix-mcp, <tag>+<sha>.
  • src/build_info.rs reads MATRIX_MCP_VERSION / MATRIX_MCP_REVISION via option_env!; absent, empty or misshapen → unstamped, never 0.0.1.
  • GET /version (public, exactly name/version/revision; trade-off stated in the code) and matrix-mcp --version.
  • Stale-binary guard: the Dockerfile runs the built binary's --version against the build args and fails the image on any difference. Measured locally: a good stamp builds (binary identity: matrix-mcp v0.0.0-audit e354597…); MATRIX_MCP_VERSION=main fails with binary identity mismatch: built 'matrix-mcp unstamped …'. OCI version/revision labels match.
  • CI stamps the commit on every build and the version only on v* tags, logs image digest: with ref and sha, and fails a release push that reports no digest.

3. #94 / #148: strict discovery, no shim

scripts/strict-oauth-discovery.py (stdlib, read-only) walks the 401 pointer → RFC 9728 → RFC 8414 with byte-exact resource and issuer checks. Against https://matrix-mcp.kampong.social/mcp: 0 failures (issuer https://matrixauthservice.kampong.social/ matches byte for byte; S256, code, registration endpoint present). So the compliant path already works, and a copy served from this origin cannot be made RFC 8414 §3.3-compliant (#148). this_origin_never_serves_authorization_server_metadata fails if any of those paths serves a document naming an issuer. Closes #148; #94 will be closed as superseded.

4. Docs

  • CHANGELOG: v0.2.3..v0.10.14 get their own sections (each entry under the first tag whose CHANGELOG.md held it; none of the 117 entries lost, duplicated or changed; 23 entry-less releases quote their tag), compare links, and v0.10.15 is cut.
  • README names read_reactions and says 62 tools; the_readme_names_every_tool_the_server_advertises fails on a missing one. docs/api-reference.md documents read_reactions and states it is a subset.
  • AGENTS.md Known Pitfalls: rmcp's serverInfo default, the stamped identity and its build-time check, the #148 decision, audit freshness, and the changelog.

Review

Independent review by a separate model session before this PR: round 1 had no blockers and 4 should-fix (unassertable digest line, /version rationale, an overstated three-source claim, a test pinning the fallback rather than the decision), all fixed in 58d41f9; round 2 had no blockers and no should-fix.

Gates at 58d41f9 (local, CI's flags)

fmt rc=0 · clippy 1.98.0 -D warnings rc=0 · test 374 + 6 passed · cargo audit 0 vulnerabilities · cargo deny check bans licenses sources ok · cargo +1.96.0 check --all-features --locked rc=0

Mutation checks: removing with_server_info from /channel reddens both_mounts_… with {"name":"rmcp","version":"1.7.0"}; removing read_reactions from README reddens the README test.

Rollout (ops: Clark)

After merge and the v0.10.15 tag build: pod imageID == the image digest: logged by the tag's docker job; curl https://matrix-mcp.kampong.social/version → version: v0.10.15, revision == git rev-parse v0.10.15^{commit}; authenticated serverInfo carries the same identity.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo

Four fixes from the matrix-mcp audit, approved by Julian (fleet t_3496b128). Base `d79c7c2`. ## 1. Dependency advisories `cargo audit` at `d79c7c2`: **13 vulnerabilities**. Targeted `cargo update -p`, no ignores: rustls 0.23.46, rustls-webpki 0.103.16, webpki-roots / webpki-root-certs 1.0.11, quinn-proto 0.11.19, ctutils 0.4.5. Now **0 vulnerabilities** (the 3 allowed warnings remain: two unmaintained, tokio RUSTSEC-2026-0386 unsound). All bumped crates declare rust-version ≤ 1.87. ## 2. Release identity a client and an operator can read - **`serverInfo` reported `{"name":"rmcp","version":"1.7.0"}` on both mounts.** rmcp's `ServerInfo::new` → `Implementation::from_build_env()`, whose `env!` expands inside rmcp. It now reports `matrix-mcp`, `<tag>+<sha>`. - `src/build_info.rs` reads `MATRIX_MCP_VERSION` / `MATRIX_MCP_REVISION` via `option_env!`; absent, empty or misshapen → `unstamped`, never `0.0.1`. - `GET /version` (public, exactly `name`/`version`/`revision`; trade-off stated in the code) and `matrix-mcp --version`. - **Stale-binary guard:** the Dockerfile runs the built binary's `--version` against the build args and fails the image on any difference. Measured locally: a good stamp builds (`binary identity: matrix-mcp v0.0.0-audit e354597…`); `MATRIX_MCP_VERSION=main` fails with `binary identity mismatch: built 'matrix-mcp unstamped …'`. OCI `version`/`revision` labels match. - CI stamps the commit on every build and the version only on `v*` tags, logs `image digest:` with ref and sha, and fails a release push that reports no digest. ## 3. #94 / #148: strict discovery, no shim `scripts/strict-oauth-discovery.py` (stdlib, read-only) walks the 401 pointer → RFC 9728 → RFC 8414 with byte-exact `resource` and `issuer` checks. Against `https://matrix-mcp.kampong.social/mcp`: **0 failures** (issuer `https://matrixauthservice.kampong.social/` matches byte for byte; S256, `code`, registration endpoint present). So the compliant path already works, and a copy served from this origin cannot be made RFC 8414 §3.3-compliant (#148). `this_origin_never_serves_authorization_server_metadata` fails if any of those paths serves a document naming an issuer. **Closes #148; #94 will be closed as superseded.** ## 4. Docs - CHANGELOG: v0.2.3..v0.10.14 get their own sections (each entry under the first tag whose CHANGELOG.md held it; none of the 117 entries lost, duplicated or changed; 23 entry-less releases quote their tag), compare links, and v0.10.15 is cut. - README names `read_reactions` and says 62 tools; `the_readme_names_every_tool_the_server_advertises` fails on a missing one. `docs/api-reference.md` documents `read_reactions` and states it is a subset. - AGENTS.md Known Pitfalls: rmcp's `serverInfo` default, the stamped identity and its build-time check, the #148 decision, audit freshness, and the changelog. ## Review Independent review by a separate model session before this PR: round 1 had no blockers and 4 should-fix (unassertable digest line, `/version` rationale, an overstated three-source claim, a test pinning the fallback rather than the decision), all fixed in `58d41f9`; round 2 had no blockers and no should-fix. ## Gates at `58d41f9` (local, CI's flags) fmt rc=0 · clippy 1.98.0 `-D warnings` rc=0 · test 374 + 6 passed · `cargo audit` 0 vulnerabilities · `cargo deny check bans licenses sources` ok · `cargo +1.96.0 check --all-features --locked` rc=0 Mutation checks: removing `with_server_info` from `/channel` reddens `both_mounts_…` with `{"name":"rmcp","version":"1.7.0"}`; removing `read_reactions` from README reddens the README test. ## Rollout (ops: Clark) After merge and the `v0.10.15` tag build: pod `imageID` == the `image digest:` logged by the tag's `docker` job; `curl https://matrix-mcp.kampong.social/version` → `version: v0.10.15`, `revision` == `git rev-parse v0.10.15^{commit}`; authenticated `serverInfo` carries the same identity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
cargo audit at d79c7c2 reported 13 vulnerabilities, all with patched
releases. Targeted updates, no ignores:

  rustls            0.23.45  -> 0.23.46   RUSTSEC-2026-0346..0349
  rustls-webpki     0.103.15 -> 0.103.16  RUSTSEC-2026-0382
  webpki-roots      1.0.7    -> 1.0.11    RUSTSEC-2026-0381
  webpki-root-certs 1.0.7    -> 1.0.11    RUSTSEC-2026-0383
  quinn-proto       0.11.16  -> 0.11.19   RUSTSEC-2026-0338..0342
  ctutils           0.4.2    -> 0.4.5     RUSTSEC-2026-0385

cargo audit now reports 0 vulnerabilities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
serverInfo said rmcp 1.7.0 on both mounts: rmcp's ServerInfo::new fills
it from Implementation::from_build_env(), whose env! calls expand inside
rmcp. Cargo.toml's 0.0.1 names nothing either, so nothing a client or an
operator could read said which release was running.

src/build_info.rs takes the tag and full commit from option_env!
(MATRIX_MCP_VERSION, MATRIX_MCP_REVISION). An absent, empty or misshapen
stamp reads 'unstamped'. The identity goes to serverInfo on /mcp and
/channel (name matrix-mcp, version <tag>+<sha>), to a public GET /version
carrying exactly name, version and revision, and to --version, which
reads no config.

The Dockerfile takes both as build args after the dependency layer, runs
the built binary's --version against them and fails the image on any
difference, which is the stale-binary guard a unit test cannot be. The
final stage labels org.opencontainers.image.version and .revision. CI
stamps the commit on every build and the version only on a tag, and logs
the pushed digest beside the ref and sha.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
scripts/strict-oauth-discovery.py walks discovery as a strict MCP client
does: the 401's resource_metadata pointer, the PRM resource compared with
the endpoint, the RFC 8414 document at the issuer-derived well-known URL
with its issuer compared byte for byte, and S256/code/registration.
Read-only, stdlib only. Against matrix-mcp.kampong.social it passes with
0 failures today.

this_origin_never_serves_authorization_server_metadata pins the decision
in #148: a copy of MAS's document served from this origin has to carry
an issuer that fails RFC 8414 §3.3 or RFC 9207, so the origin answers 401
with the RFC 9728 pointer instead. Supersedes #94.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
CHANGELOG.md held every change since v0.2.2.1 under [Unreleased].
v0.2.3..v0.10.14 now have sections, each entry filed under the first tag
whose CHANGELOG.md already contained it (no line of the old block lost;
releases with no entry quote their tag). v0.10.15 is cut here.

README names read_reactions and says 62 tools; a test fails when it
misses one. docs/api-reference.md documents read_reactions and states it
is a subset rather than quoting a count. AGENTS.md: rmcp's serverInfo
default, the stamped identity and its build-time check, the #148
decision, audit freshness, and the changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
fix: review round 1 on the release-identity branch
All checks were successful
CI / cargo (pull_request) Successful in 7m55s
CI / docker (pull_request) Has been skipped
58d41f97db
- ci: assert the pushed digest instead of echoing whatever node printed;
  a release push with no digest now fails the job.
- /version: state the disclosure trade-off it makes; drop a redundant
  clippy allow.
- #148 test asserts the decision (no document naming an issuer), not the
  401 that only comes from the auth layer handling a route miss.
- Dockerfile labels read 'unstamped' like the binary when unstamped.
- strict-oauth-discovery.py reports unreachable hosts and non-JSON as
  FAIL lines rather than tracebacks.
- AGENTS.md: label and /version are one source, not two; 52 tags, not 53.
- CHANGELOG: compare links for v0.2.3..v0.10.15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjpM4mQ7ofAU6xVEdzdfmo
jlxq0 merged commit 9b8d589f48 into main 2026-10-24 03:15:59 +00:00
Sign in to join this conversation.
No description provided.