sec: answer the advisory gate's first catch, remove hpke-rs 0.2.0, and clear every yanked dependency #449

Merged
dcharlot merged 4 commits from sec/unsound-bumps-2026-10 into main 2026-10-24 20:47:02 -04:00
Owner

Advisory follow-ups to #443, rebased onto main (which #447 fixed — the joule-moq-room lockfile failure on the first attempt was that pre-existing breakage, red on main since #427, not this branch).

The gate caught something on its first real run

Two advisories published 2026-10-23, after the sweep in #443 was done. Both are constant-time failures:

crate bump advisory
ctutils 0.4.2/0.4.3 → 0.4.4 RUSTSEC-2026-0385 — non-constant-time mask generation and u128 comparisons. Reaches openpay's wallet crates via digest → hmac/hkdf, so it sits under HMAC on payment paths.
crypto-bigint 0.7.3 → 0.7.6 RUSTSEC-2026-0387 — secret-dependent branches in subtraction, zero counting, division and Montgomery multiplication, operations whose whole purpose is to be constant-time.

The 0.5.5 line of crypto-bigint cannot follow: elliptic-curve 0.13 pins it, so reaching ≥0.7.6 means moving the p256/elliptic-curve stack across openpay, sandbox, wai and jouleclaw. That is an upgrade, not a bump. It is allowed with that reason recorded, on paths that sign and derive keys, so it reads as debt rather than a verdict.

Writing that allowance exposed a hole in the check

ALLOW was keyed by advisory id alone, so an entry excusing the stuck 0.5.5 line would have gone on excusing the advisory if a patched 0.7.x copy later regressed — the same shape as sealing by name instead of by digest. An entry may now pin the exact pairs it covers:

"RUSTSEC-...": {"why": "...", "only": ("crypto-bigint 0.5.5",)}

Any other version of that crate fails, and a named pair that disappears makes the entry stale, so the pin cannot keep describing a tree that moved. The version-blind string form stays for reasons that genuinely do not depend on version (a call we never make), and the two forms are documented against each other. The self-test now builds its documents by importing the real ALLOW instead of scraping it with grep, so the two cannot drift, and covers both new failure modes.

Also in this branch

  • hpke-rs 0.2.0 removed — this was written for #443 but never landed: that PR merged at 4e7fa646e while this commit was still in flight, so main kept the old openmls 0.6/0.3 dev-dep stack and its four advisories. Pinning joule-code-collab's dev-deps to the generation joule-moq-mls already runs removes hpke-rs 0.2.0, hpke-rs-crypto, hpke-rs-rust-crypto and openmls 0.6.0 outright, and with them RUSTSEC-2026-0069/-0070/-0071 (9.3)/-0072. mls_keying passes. It also carries the correction that the openpay quick-xml copy is not on a parse path.
  • Three unsoundness fixes a patch away: anyhow 1.0.103 (RUSTSEC-2026-0190), event-listener 5.4.2 (-0221), lru 0.18.2 (-0253, a potential use-after-free in LruCache::pop()).
  • Every yanked dependency gone, each a patch bump: chacha20 0.10.0/0.10.1 → 0.10.2 (a cipher, six lockfiles), der 0.8.0 → 0.8.2, spin 0.9.8 → 0.9.9 and 0.10.0 → 0.10.1, iroh-blobs 0.101.0 → 0.101.1. A yank is the author withdrawing a release; none of these needed to stay on one. No yanked dependency remains anywhere.

Remaining unsound, recorded rather than left unexplained: im 15.1.0 and sized-chunks 0.6.5 have no fixed release at all (both unmaintained; the sized-chunks one is use-after-free / double-free), while lru 0.12.5, rand 0.7.3 and glib 0.18.5 each need a semver-major move and are transitive.

Verified: all 47 lockfiles current, advisory check green at 15 documented allowances, self-test green on all six cases, and the affected workspaces check clean with mls_keying passing.

🤖 Generated with Claude Code


Carries cfd341b05 from sec/after-443-b (#456 closed in favour of this)

The quick-xml 0.36 → 0.41 bump in #443 left two things behind in jouleclaw, both mine: three calls to Attribute::unescape_value, which 0.41 deprecates, and — from moving the TRX dispatch out of the Event::Text arm — four in_* flags and a cur_field_value round-trip that were assigned and never read.

That fails the last step of the macOS energy job (.github/workflows/standards-build.yml:577-581), which runs cargo clippy --all-targets --no-deps -- -D warnings over jouleclaw-loader-gguf. The test step in the same job only warns and passes, which is why this looked green from the wrong angle.

normalized_value(XmlVersion::Implicit1_0) is upstream's own prescribed replacement and still resolves entities — normalized_value_with(version, 1, resolve_predefined_entity) — so it does not reintroduce the silent entity-dropping this branch's parent fixed. The transform-trx half is semantically equivalent: cur_field_value was assigned then immediately .take()n.

Verified on a macOS host against the exact command: -p jouleclaw-loader-gguf and -p jouleclaw-transform-trx both exit 0 under -D warnings, with no deprecated and no unused-variable errors.

Advisory follow-ups to #443, rebased onto main (which #447 fixed — the `joule-moq-room` lockfile failure on the first attempt was that pre-existing breakage, red on main since #427, not this branch). ### The gate caught something on its first real run Two advisories published **2026-10-23**, after the sweep in #443 was done. Both are constant-time failures: | crate | bump | advisory | |---|---|---| | `ctutils` | 0.4.2/0.4.3 → 0.4.4 | RUSTSEC-2026-0385 — non-constant-time mask generation and `u128` comparisons. Reaches openpay's wallet crates via `digest → hmac/hkdf`, so it sits under HMAC on payment paths. | | `crypto-bigint` | 0.7.3 → 0.7.6 | RUSTSEC-2026-0387 — secret-dependent branches in subtraction, zero counting, division and Montgomery multiplication, operations whose whole purpose is to be constant-time. | The **0.5.5 line of crypto-bigint cannot follow**: `elliptic-curve` 0.13 pins it, so reaching ≥0.7.6 means moving the p256/elliptic-curve stack across openpay, sandbox, wai and jouleclaw. That is an upgrade, not a bump. It is allowed with that reason recorded, on paths that sign and derive keys, so it reads as debt rather than a verdict. ### Writing that allowance exposed a hole in the check `ALLOW` was keyed by advisory id alone, so an entry excusing the stuck 0.5.5 line would have gone on excusing the advisory **if a patched 0.7.x copy later regressed** — the same shape as sealing by name instead of by digest. An entry may now pin the exact pairs it covers: ```python "RUSTSEC-...": {"why": "...", "only": ("crypto-bigint 0.5.5",)} ``` Any other version of that crate fails, and a named pair that disappears makes the entry stale, so the pin cannot keep describing a tree that moved. The version-blind string form stays for reasons that genuinely do not depend on version (a call we never make), and the two forms are documented against each other. The self-test now builds its documents by **importing the real `ALLOW`** instead of scraping it with grep, so the two cannot drift, and covers both new failure modes. ### Also in this branch - **`hpke-rs 0.2.0` removed** — this was written for #443 but never landed: that PR merged at `4e7fa646e` while this commit was still in flight, so main kept the old openmls 0.6/0.3 dev-dep stack and its four advisories. Pinning `joule-code-collab`'s dev-deps to the generation `joule-moq-mls` already runs removes hpke-rs 0.2.0, hpke-rs-crypto, hpke-rs-rust-crypto and openmls 0.6.0 outright, and with them RUSTSEC-2026-0069/-0070/-0071 (9.3)/-0072. `mls_keying` passes. It also carries the correction that the openpay quick-xml copy is **not** on a parse path. - **Three unsoundness fixes** a patch away: `anyhow` 1.0.103 (RUSTSEC-2026-0190), `event-listener` 5.4.2 (-0221), `lru` 0.18.2 (-0253, a potential **use-after-free** in `LruCache::pop()`). - **Every yanked dependency gone**, each a patch bump: `chacha20` 0.10.0/0.10.1 → 0.10.2 (a cipher, six lockfiles), `der` 0.8.0 → 0.8.2, `spin` 0.9.8 → 0.9.9 and 0.10.0 → 0.10.1, `iroh-blobs` 0.101.0 → 0.101.1. A yank is the author withdrawing a release; none of these needed to stay on one. **No yanked dependency remains anywhere.** Remaining unsound, recorded rather than left unexplained: `im` 15.1.0 and `sized-chunks` 0.6.5 have **no fixed release at all** (both unmaintained; the sized-chunks one is use-after-free / double-free), while `lru` 0.12.5, `rand` 0.7.3 and `glib` 0.18.5 each need a semver-major move and are transitive. Verified: all 47 lockfiles current, advisory check green at 15 documented allowances, self-test green on all six cases, and the affected workspaces check clean with `mls_keying` passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- ### Carries cfd341b05 from `sec/after-443-b` (#456 closed in favour of this) The quick-xml 0.36 → 0.41 bump in #443 left two things behind in jouleclaw, both mine: three calls to `Attribute::unescape_value`, which 0.41 deprecates, and — from moving the TRX dispatch out of the `Event::Text` arm — four `in_*` flags and a `cur_field_value` round-trip that were assigned and never read. That fails the **last step of the macOS energy job** (`.github/workflows/standards-build.yml:577-581`), which runs `cargo clippy --all-targets --no-deps -- -D warnings` over `jouleclaw-loader-gguf`. The *test* step in the same job only warns and passes, which is why this looked green from the wrong angle. `normalized_value(XmlVersion::Implicit1_0)` is upstream's own prescribed replacement and still resolves entities — `normalized_value_with(version, 1, resolve_predefined_entity)` — so it does not reintroduce the silent entity-dropping this branch's parent fixed. The transform-trx half is semantically equivalent: `cur_field_value` was assigned then immediately `.take()`n. Verified on a macOS host against the exact command: `-p jouleclaw-loader-gguf` and `-p jouleclaw-transform-trx` both exit 0 under `-D warnings`, with no deprecated and no unused-variable errors.
sec: take the three unsoundness fixes that are a patch bump away
Some checks failed
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Failing after 3m35s
standards workspaces build + test / smart-byte-rs (smart-byte/smart-byte-rs, smart-byte-rs, linux) (pull_request) Successful in 4m22s
0ad2d8f50c
`cargo audit` reports 67 warnings the advisory gate deliberately does not fail
on, because maintenance advice would bury the reachable defects. Nine of them
are not maintenance advice though — they are unsoundness, and one is a
use-after-free. Three have a semver-compatible fix:

  anyhow         1.0.102 -> 1.0.103  RUSTSEC-2026-0190, unsoundness in
                                     `Error::downcast_mut()`
  event-listener 5.4.1   -> 5.4.2    RUSTSEC-2026-0221, `!Send` tags crossing a
                                     thread boundary via `StackSlot`
  lru            0.18.0  -> 0.18.2   RUSTSEC-2026-0253, potential
                                     use-after-free from missing panic safety
                                     in `LruCache::pop()`

All three are in smart-byte-rs, which still checks clean.

The remaining six are recorded here rather than left unexplained:

  - `im` 15.1.0 (RUSTSEC-2023-0126, aliasing violation in `OrdSet` insertion)
    and `sized-chunks` 0.6.5 (RUSTSEC-2026-0255, panic-safety unsoundness in
    `Chunk`/`RingBuffer`/`InlineArray` — use-after-free and double-free) have
    **no fixed release at all**; both crates are unmaintained.
  - `lru` 0.12.5 (RUSTSEC-2026-0002, -0253), `rand` 0.7.3 (RUSTSEC-2026-0097)
    and `glib` 0.18.5 (RUSTSEC-2024-0429) need a semver-major move, and all
    three are transitive, so the dependent has to move first.

Co-Authored-By: Claude Opus 5 <[email protected]>
dcharlot force-pushed sec/unsound-bumps-2026-10 from 0ad2d8f50c
Some checks failed
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Failing after 3m35s
standards workspaces build + test / smart-byte-rs (smart-byte/smart-byte-rs, smart-byte-rs, linux) (pull_request) Successful in 4m22s
to 4bbb4cd254
Some checks failed
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 9s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 17s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 17s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 4m9s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 5m46s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 39s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Failing after 10m26s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Failing after 10m44s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 9m33s
standards workspaces build + test / eoc-rs (eoc/eoc-rs, -p eoc-meter --features cuda, eoc-rs, linux) (pull_request) Successful in 3m13s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 3m37s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Successful in 1m51s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 2m5s
standards workspaces build + test / smart-byte-rs (smart-byte/smart-byte-rs, smart-byte-rs, linux) (pull_request) Successful in 4m10s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 7m43s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 7m49s
standards workspaces build + test / openpay (openpay, openpay, linux-arm64) (pull_request) Successful in 5m48s
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
2026-10-24 20:06:08 -04:00
Compare
dcharlot changed title from sec: take the three unsoundness fixes that are a patch bump away to sec: answer the advisory gate's first catch, remove hpke-rs 0.2.0, and clear every yanked dependency 2026-10-24 20:06:33 -04:00
jouleclaw: read attribute values with normalized_value, not the deprecated unescape_value
All checks were successful
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 20s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 20s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Successful in 1m41s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 7s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 10s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m11s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 2m28s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 1m38s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 3m8s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 14s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m5s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 2m7s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 10m42s
991e077232
#443 moved jouleclaw to quick-xml 0.41, which deprecates
Attribute::unescape_value. The OpenVINO IR loader and the Maltego TRX parser
still called it, three times each, so every build that denies warnings (the
macOS energy-backends leg) failed to compile jouleclaw-loader-gguf. Both now
use normalized_value(XmlVersion::Implicit1_0), which unescapes the same way
and also normalizes attribute whitespace as XML 1.0 specifies.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit cfd341b057)
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!449
No description provided.