Skip to content

chore(release): prepare 2.2.5 - #494

Open
tobyhede wants to merge 11 commits into
release/2.2from
claude/project-thread-yj11ps
Open

tobyhede wants to merge 11 commits into
release/2.2from
claude/project-thread-yj11ps

Conversation

@tobyhede

@tobyhede tobyhede commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Toby

Prepare-release PR for 2.2.5, the next 2.2.x maintenance release, cut from release/2.2.

Changes

  • Bump the workspace version to 2.2.5 (Cargo.toml, Cargo.lock).
  • Move the [Unreleased] fixes into a ## [2.2.5] CHANGELOG section, with its compare link, so the release workflow's verify-release check passes for tag v2.2.5.
  • Set flavor: latest=false on both docker/metadata-action steps in release.yml. The default latest=auto would tag any non-prerelease semver release as cipherstash/proxy:latest, which must stay on 3.x.
  • Backport security dependency updates (lockfile only): rustls 0.23.45, rustls-webpki 0.103.14, aws-lc-rs 1.18.2 / aws-lc-sys 0.46.0, rand 0.9.3 / 0.10.2, quinn-proto 0.11.15, hickory-proto / hickory-resolver 0.26.3 via reqwest 0.13.4, diesel 2.3.12 and postgres-protocol 0.6.12, with a ### Security CHANGELOG entry. tokio-postgres stays on 0.7.13 (0.7.18 conflicts through cipherstash-client); that advisory is left for 2.2.6.
  • Remove the failing Multitudes notification step from release.yml, as ci: remove the Multitudes deployment notification #477 did on main.
  • Document how 2.2.x releases are cut from release/2.2 in DEVELOPMENT.md.

Included in 2.2.5

The ## [2.2.5] date is set to today; I'll move it to the tag date if tagging happens later.

Cutting the release after merge

mise run release only runs on main, so tag manually from the merged commit on release/2.2:

git tag v2.2.5 <merged sha>
git push origin v2.2.5
gh release create v2.2.5 --target release/2.2 --latest=false \
  --generate-notes --notes-start-tag v2.2.4

Acknowledgment

By submitting this pull request, I confirm that CipherStash can use, modify, copy, and redistribute this contribution, under the terms of CipherStash's choice.


Generated by Claude Code

@tobyhede tobyhede self-assigned this Oct 26, 2026
@tobyhede
tobyhede force-pushed the claude/project-thread-yj11ps branch from 741e9b5 to d4456f4 Compare October 26, 2026 23:51
@tobyhede
tobyhede requested a review from freshtonic October 27, 2026 00:48

@freshtonic freshtonic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head f7df5137 against release/2.2 (5f99d7e0, which includes #493 and #495). Approved. The version bump, the CHANGELOG section and the latest=false change are correct, and the dependency updates remove 10 of the 29 advisories that cargo audit reports on the base.

I pushed the fixes for S1 and S2 to this branch as two signed commits:

  • e66691ac fix(deps): backport rustls 0.23.45 to 2.2. This is a 2-line change to Cargo.lock. The CHANGELOG entry now names rustls and aws-lc-rs/aws-lc-sys, and it no longer says that 2.2.5 has every fix that 3.x ships. cargo build --workspace --all-targets --locked passes, and cargo test -p cipherstash-proxy -p eql-mapper --lib passes (283 and 79). RUSTSEC-2026-0285 is gone. The rustls advisories that remain are the same as on main.
  • 103e7a86 ci: remove the Multitudes deployment notification (2.2). This is the same change as #477 on main.

This approval covers my two commits too. Please check them before you merge.

Fixed in this PR (S1 and S2)

S1. The rustls fix that main ships is not in this backport. release/2.2 locks rustls 0.23.28, which has RUSTSEC-2026-0285 ("TLS 1.3 handshake messages incorrectly accepted across encryption levels", network, no authentication). main locks 0.23.45, which fixes it. rustls is Proxy's TLS server for clients and its TLS client to PostgreSQL, so this is the most relevant advisory that remains. cargo update -p rustls --precise 0.23.45 changes only 2 lines in Cargo.lock. I built it, and the unit tests pass.

The CHANGELOG says that the update picks up "the fixes for published security advisories in those crates that Proxy 3.x already ships". That is true for the crates it names. But a reader can think that 2.2.5 has every fix that 3.x has, and it does not. Add rustls, or say which advisories stay open.

S2. The release run for v2.2.5 will end red. release.yml on 2.2 still has the "Notify Multitudes" step. #477 removed this step from main, because the endpoint has returned 500 on every run since 2026-08-17. The images are pushed before that step, so the release is published, but the run shows as failed, and the failure hides a real failure. Remove the step here too, as #477 did.

Notes (not blocking)

  • postgres-protocol 0.6.8 has RUSTSEC-2026-0179 (a malicious server can send an unbounded SCRAM iteration count) and RUSTSEC-2026-0180. main does not have these. cargo update -p postgres-protocol --precise 0.6.12 resolves on 2.2, and it builds and passes the unit tests. It adds md-5 0.11 and sha2 0.11 beside the existing versions. The server is the operator's own PostgreSQL, so the risk is low. You can add it with S1 or later.
  • tokio-postgres 0.7.13 has RUSTSEC-2026-0178 (panic on a short DataRow). main has 0.7.18 (#465), but on 2.2, --precise 0.7.18 fails on a libredox conflict through cipherstash-client in the integration package. It is not worth blocking the release for this. A ticket for 2.2.6 is enough.
  • The lockfile update goes further than the CHANGELOG says. aws-lc-sys moves from 0.38.0 to 0.46.0, which is newer than main (0.44.0), and aws-lc-rs from 1.16.1 to 1.18.2. This fixes RUSTSEC-2026-0044 and RUSTSEC-2026-0048, so it is good. But it is a large update of the C crypto library, and it is not in the CHANGELOG. Test CI builds only x86_64. The release also builds aarch64-unknown-linux-gnu, and that build has not run on this lockfile. Before you tag, start the Release workflow on this branch with workflow_dispatch. The merge job is a dry run there, but the build job pushes untagged digests.
  • The advisories that remain and that main also has (quinn-proto, h2, crossbeam-epoch, rkyv, the other rustls ones, rustls-webpki 0.103.14, webpki-root-certs) are not a 2.2 problem.

Nits

  • The CHANGELOG date is 2026-10-26. Update it if the tag goes out on another day.

What I checked and found correct

  • Version. The workspace version is 2.2.5, and cipherstash-proxy, eql-mapper-macros and showcase are 2.2.5 in Cargo.lock. verify-release reads the first version = line of Cargo.toml and looks for ## [2.2.5] in the CHANGELOG. Both match.
  • latest=false. A release event runs the workflow file from the tag, so the 2.2 release.yml is used. The merge step makes :2.2.5 ({{version}}) and :2.2 ({{major}}.{{minor}}), and not :latest. On the build step the flavor changes only labels, so it does no harm. GitHub's "Latest" is v3.0.1 now, and --latest=false in DEVELOPMENT.md keeps it there.
  • The CHANGELOG. The 2.2.5 section has #473, #475, #481, #483 and #493. It includes the sentence about plaintext predicates that I asked for in my review of #493. #491 and #495 add tests only.
  • Cargo.toml. No dependency requirement changed. The updates are in the lockfile only.
  • Signatures. All 3 commits are verified on GitHub.

What I ran

  • cargo audit on the base, this PR and main: 29, 19 and 17 advisories.
  • cargo update -p rustls --precise 0.23.45 and -p postgres-protocol --precise 0.6.12, then cargo build and cargo test -p cipherstash-proxy --lib: 282 of 283 passed. The failure, config::tandem::tests::prometheus_config, is the known environment race. I reverted the lockfile after the run.
  • CI: all 4 checks pass.

Copy link
Copy Markdown
Contributor Author

Thanks @freshtonic. I checked your two commits and they're fine. I pushed one more signed commit, 07ffc15: postgres-protocol goes to 0.6.12 for RUSTSEC-2026-0179/0180, with the CHANGELOG entry updated. It's lockfile only. It builds with --locked, and the proxy and mapper unit tests pass (283 and 79). tokio-postgres stays for 2.2.6. I'll do the aarch64 workflow_dispatch run and the date update before tagging.


Generated by Claude Code

tobyhede and others added 3 commits October 27, 2026 12:14
Bring the security patches that main already carries to release/2.2:

- rustls-webpki 0.103.14
- rand 0.9.3 and 0.10.2
- quinn-proto 0.11.15
- hickory-proto and hickory-resolver 0.26.3, via reqwest 0.13.4
- diesel 2.3.12
- postgres-protocol 0.6.12 (including an unbounded SCRAM iteration count from a malicious server), which adds md-5 0.11 and sha2 0.11 beside the existing versions

Lockfile only; no code changes were needed.
rustls 0.23.28 has RUSTSEC-2026-0285 (TLS 1.3 handshake messages
accepted across encryption levels). main already ships 0.23.45. rustls is
Proxy's TLS server for clients and its TLS client to PostgreSQL.

The CHANGELOG entry now names rustls and aws-lc-rs/aws-lc-sys, and no
longer says that 2.2.5 has every fix that 3.x ships.
The Multitudes endpoint has returned 500 on every release run since
2026-08-17, so the v2.2.5 release run would end red after the images are
published. main removed this step in #477.
@tobyhede
tobyhede force-pushed the claude/project-thread-yj11ps branch from 07ffc15 to 303f809 Compare October 27, 2026 04:31
freshtonic and others added 8 commits October 27, 2026 16:34
Scope the ZeroKMS cipher to CS_DEFAULT_KEYSET_ID whenever a connection has not selected an override. Previously Proxy only checked that the setting existed, then passed no identifier to ScopedCipher and could silently use the client's account default instead.

Application-encrypted payloads use the configured keyset explicitly. When the account and configured defaults differ, Proxy derived searchable-encryption metadata with another index key and rejected valid inbound ciphertext during authentication. Preserve connection-level keyset precedence while making the configured fallback effective.

Signed-off-by: James Sadler <james@cipherstash.com>
Bind rewrites pair encrypted values with parameters by position. ZeroKms::encrypt padded a short result from encrypt_eql with None, and Bind skips a None value, so the parameter's original plaintext was forwarded to PostgreSQL.

ZeroKms::encrypt and Context::encrypt now fail with EncryptionResultCountMismatch when they receive fewer ciphertexts than values sent for encryption.
Proxy keeps a Statement only for statements that touch encrypted columns. A Bind naming a statement Proxy had no record of was treated as plaintext and its values sent as they were, so any path that lost or dropped a statement too early would send plaintext for an encrypted column.

Every Parse already records the statement's session, so while mapping is enabled a Bind to a name with no recorded Parse now fails closed with SQLSTATE 26000, PostgreSQL's own code for a missing prepared statement. The refusal goes through PostgreSQL as other refusals do, and the connection stays usable.
The mapper never inferred the DO UPDATE SET assignments of an INSERT ... ON CONFLICT, so a literal or param assigned to an encrypted column on the conflict path was left untyped and sent to PostgreSQL as plaintext. Because the statement still type-checked, the refusal of writes Proxy cannot type check did not catch it.

Each assignment value is now unified with the type stored in its target column, as in a plain UPDATE, so it is encrypted. The `excluded` pseudo-table is brought into scope so `excluded.<col>` carries the column's EQL type, and the insert target is named so qualified references resolve. A conflict target on an encrypted column, MySQL INSERT ... SET and ON DUPLICATE KEY UPDATE are rejected.

This is the ON CONFLICT part of the mapper fix on main. Its window, aggregate and SELECT ... INTO changes are left out: those gaps act on ciphertext rather than forwarding plaintext.
tokio-postgres 0.7.18 follows the convention that an error's Display describes only that error, with the cause reached through source(), so err.to_string() logs a bare "db error" and drops the server's message.

Add ErrorChain, a Display wrapper that walks source(), and use it at the log sites that can carry a database error. Recording the error as a dyn Error field is not enough: the Structured (JSON) format, the default off a terminal, renders only Display. ErrorChain skips a cause the message already ends with, so variants that embed their cause in Display are not printed twice.
tokio-postgres 0.7.18 no longer appends the cause to Error's Display, so assertions on err.to_string() see a bare "db error".

Assert on the structured error instead: assert_db_error reads the severity and message off as_db_error(), and assert_client_error reads the kind off Display and the detail off source(). The exact customer-visible message text, including the docs/errors.md links, stays pinned.
…advisories

- rustls 0.23.46 (RUSTSEC-2026-0346, 0347, 0348 and 0349)
- rustls-webpki 0.103.16 (RUSTSEC-2026-0382)
- webpki-root-certs 1.0.11 (RUSTSEC-2026-0383)
- tokio-postgres 0.7.18 (RUSTSEC-2026-0178, a panic on a malformed DataRow), which also updates libredox, postgres-types and whoami
- h2 0.4.20 (RUSTSEC-2026-0258)
- crossbeam-epoch 0.9.21 (RUSTSEC-2026-0204)
- tokio 1.51.6 (RUSTSEC-2026-0386)
- chacha20 0.10.2, replacing the yanked 0.10.0

tokio-postgres 0.7.18 no longer enables socket2 0.5's "all" feature, which Proxy relied on for TcpKeepalive::with_retries, so Proxy now enables it itself.
Bump the workspace version to 2.2.5, move the unreleased changes into a 2.2.5 CHANGELOG section and add its compare links.

Set `latest=false` on the Docker metadata steps so that publishing a 2.2.x release does not move the `cipherstash/proxy:latest` tag off 3.x.

Document how 2.2.x maintenance releases are cut from release/2.2: the manual tag steps, `--latest=false`, and `--notes-start-tag` so the generated notes start from the previous 2.2.x tag.
@tobyhede
tobyhede force-pushed the claude/project-thread-yj11ps branch from 303f809 to 49c7057 Compare October 27, 2026 23:26

Copy link
Copy Markdown
Contributor Author

All four Test jobs fail on 49c7057. Unit tests pass, but in the integration stage the proxy container never accepts connections on localhost:6432 ("Unable to connect to localhost:6432 after 20 attempts"). 303f809, which has the same tree as 07ffc15, was green, so the cause is in the 7 backport commits on top of it. The most likely one is the dependency update e8af865 (tokio-postgres 0.7.18, rustls 0.23.46, tokio 1.51.6, socket2 all). The job output doesn't include the container's logs, so docker logs proxy after mise run proxy:up should show the startup error. I'm not re-running: it fails the same way on all four, so it isn't a flake.


Generated by Claude Code

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants