Skip to content

fix(ci): unbreak CI after Rust 1.99 and clear October RustSec advisories - #159

Merged
JunjoSick merged 5 commits into
mainfrom
fix/fetch-update-deprecation
Oct 20, 2026
Merged

JunjoSick merged 5 commits into
mainfrom
fix/fetch-update-deprecation

Conversation

@JunjoSick

@JunjoSick JunjoSick commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

CI on main has failed every day since Oct 2 (last green run: Oct 1), and every PR is blocked. This PR fixes both causes:

  1. Rust 1.99 deprecation. The runners moved from rustc 1.98.1 to 1.99.0 on Oct 2. That release deprecates AtomicUsize::fetch_update in favor of try_update. Because ci.yml sets RUSTFLAGS: "-D warnings" for every job, the two calls in bookforge-llm/src/concurrency.rs stop clippy, test, test (windows-msvc) and corpus from compiling.
  2. RustSec advisories. RustSec advisory audit is a required check on main and fails on 12 advisories in Cargo.lock (zip, rustls, quinn-proto). Fixing only (1) would leave this PR unmergeable. Fixing them in the dependabot PRs instead is a deadlock, since those can't pass CI without (1).

Commits

  • fix(llm): both call sites go through one private fetch_update helper with a single #[allow(deprecated)]. Orderings and closures are unchanged. The compiler's suggested try_update is not used: it is unstable on the 1.88 MSRV (E0658, reproduced locally), so it would just move the failure to the msrv job.
  • fix(audio): removes an unneeded return in the cfg(windows) branch of atomic::replace_file. CI's clippy runs on Linux and never compiles that block, so this only affects a Windows-local -D warnings run. Behavior is unchanged.
  • fix(deps) rustls + quinn-proto: lockfile only. rustls 0.23.45 → 0.23.46 (RUSTSEC-2026-0346/0347/0348/0349); quinn-proto 0.11.16 → 0.11.19 (RUSTSEC-2026-0338 through 0342). quinn-proto is only reqwest's optional http3 dependency and isn't compiled, but cargo audit reads every lockfile entry.
  • fix(deps) zip 9: zip 8.6.0 → 9.0.5 (RUSTSEC-2026-0367/0368/0369). Those advisories concern extract(), which BookForge never calls, so they weren't reachable here. zip 9.0.5 declares rust-version = "1.88"; its new nix dependency is macOS/BSD-only. Supersedes dependabot build(deps): bump zip from 8.6.0 to 9.0.0 #152.
  • fix(deps) rustls-webpki + webpki-roots (added Oct 21): lockfile only. rustls-webpki 0.103.15 → 0.103.16 (RUSTSEC-2026-0382, distrusted intermediates could be accepted) and webpki-roots 1.0.8 → 1.0.10 (RUSTSEC-2026-0381, distrusted emSign intermediates). Both advisories were published on Oct 19–20, after this PR's audit first passed, and the stacked fix(validation): stop untranslated prose hiding behind reference context #160 failed the audit on them. Both releases are within the 1.88 MSRV; cargo audit --deny warnings passes again.

zip 9: what changed in behavior

  • Entry names. ZipFile::name() and ZipArchive::file_names() now return ZipResult<Cow<str>>, so 6 call sites in bookforge-epub and one test changed. zip 9 decodes names lazily: UTF-8 first, then CP437, which maps every byte. So no name that 8.6 accepted is now rejected. The only difference: an entry flagged UTF-8 that contains invalid bytes decodes as CP437 instead of lossy UTF-8; such an entry is invalid EPUB either way. validate.rs keeps its existing skip-unreadable-entry behavior. New test: a non-ASCII entry name (capítulo-東京.xhtml) resolves through inspect, read and rebuild.
  • Zip-bomb defense (stricter, review this one). zip 9 itself refuses to inflate an entry past its declared uncompressed size. A lying entry is now rejected at declared size + 1 byte, earlier than BookForge's per-entry limit used to catch it. But zip reports this as a bare InvalidData I/O error with no entry name. ArchiveReadBudget::read_entry now re-raises InvalidData read errors as EPUB decompression failed for '<entry>': …, matching its other limit errors and the module's promise to name failing entries. Genuine I/O errors are still passed through unchanged. Three tests changed:
    • The two "lying declared size" tests still require rejection and the entry name; they now expect the real reason (declared size exceeded) instead of the per-entry-limit message, which would no longer be true.
    • The running-total test built its scenario from lying entries, which zip 9 now refuses to read at all. It now uses honest 48 KiB entries with a directly constructed ArchiveReadBudget, so the read-time running total is still exercised.

No CHANGELOG entry, consistent with #145/#146/#148; the zip-bomb change is internal hardening with no user-facing option. Happy to add one if you'd like it recorded.

Validation

Run on Windows (GNU) with CI's exact compiler, rustc 1.99.1, and RUSTFLAGS="-D warnings", following the steps of scripts/verify.sh:

  • cargo audit --deny warnings (the required check's command): 0 advisories, exit 0
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings: pass
  • cargo +1.88.0 check --workspace --all-targets --locked (MSRV): pass
  • cargo fmt --all --check, git diff --check, scripts/validate-release-security.sh, cargo check -p bookforge-cli --no-default-features, dashboard runtime.test.cjs: pass
  • cargo test --workspace --locked: 1,426 passed, 1 failed, 9 ignored; --examples: 13 passed; bookforge-epub alone: 165 passed. The one failure was cli_pause_during_inflight_qa_request_parks_before_more_finalize_work with Access is denied. (os error 5), a Windows file-sharing race between the paused and resumed processes, unrelated to zip. It passed in both earlier gate runs of this branch, 8/8 run alone, and in two full runs of the lifecycle binary (56/56 each).
  • Not run locally: the browser smoke step (no Playwright/Chromium here), Linux, and Windows MSVC. CI covers those. The previous push of this branch passed clippy, test, test (windows-msvc), msrv, fmt, corpus (small) and CodeQL on GitHub; only the audit failed.

Known flaky test, unrelated to this PR. batch::tests::persistent_decode_error_bisects_an_oversized_batch_after_retry fails intermittently on Windows with request counts [2, 2] instead of [2, 2, 1, 1], and can hang, because server.await has no timeout. Batch execution uses the limiter touched in the first commit, so I compared under identical conditions (same toolchain, flags and machine):

Code single test ×30 (ok/hang/fail) batch::tests module ×10
main (before) 26 / 1 / 3 4 / 3 / 3 (a second batch of 8: 6 / 1 / 1)
this branch 29 / 1 / 0 9 / 0 / 1

These are small samples. They show no regression, not an improvement. The flake needs its own fix.

After merge

Rebase the remaining dependabot PRs (#153, #155, #156, #157, #158). #152 is superseded by this PR, and dependabot should close it once main has zip 9.

🤖 Generated with Claude Code

JunjoSick and others added 4 commits October 20, 2026 17:47
Rust 1.99 deprecates AtomicUsize::fetch_update in favor of try_update.
CI runs floating stable with RUSTFLAGS="-D warnings", so since the runners
moved to 1.99.0 on Oct 2 every stable job (clippy, test, test-windows,
corpus) has failed to compile bookforge-llm.

try_update is unstable on the 1.88 MSRV (E0658), so the compiler's
suggested rename would break the msrv job instead. Both call sites now go
through one private helper that keeps fetch_update behind a single
allow(deprecated). Orderings and closures are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows the cfg(not(windows)) block is compiled out, leaving the
return as the tail of replace_file, which clippy's needless_return rejects.
CI's clippy job runs on Linux and never compiles this block, so only a
Windows-local -D warnings gate saw it. Behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rustls 0.23.45 -> 0.23.46 resolves RUSTSEC-2026-0346/0347/0348/0349.
quinn-proto 0.11.16 -> 0.11.19 resolves RUSTSEC-2026-0338 through 0342.
quinn-proto is only reqwest's optional http3 dependency and is not
compiled for any target here, but cargo audit reads every lockfile entry
and RustSec is a required check on main.

Lockfile-only: two version/checksum pairs, both within the 1.88 MSRV.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zip 8.6.0 carries three advisories against ZipArchive::extract and
ZipStreamReader::extract. BookForge never calls either, but RustSec is a
required check on main. zip 9.0.5 fixes them and declares rust-version
1.88, matching the MSRV; its new nix dependency is macOS/BSD-only.

ZipFile::name() and ZipArchive::file_names() now return
ZipResult<Cow<str>>. Names decode lazily (UTF-8, then CP437, which maps
every byte), so no name that 8.6 accepted is rejected; only an entry
flagged UTF-8 with invalid bytes now decodes as CP437 instead of lossy
UTF-8. validate.rs keeps its skip-unreadable-entry behavior.

zip 9 also refuses to inflate an entry past its declared uncompressed
size, so a lying entry is now rejected at declared size + 1 byte instead
of at BookForge's per-entry limit. It reports that as a bare InvalidData
I/O error; read_entry now re-raises InvalidData read errors as an EPUB
decompression error naming the entry, like its other limit errors. Two
tests assert the new message. The running-total test now uses honest
entries with a directly built budget, since zip 9 refuses to read a
lying entry at all.

Adds a regression test for non-ASCII entry names through inspect, read
and rebuild.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JunjoSick JunjoSick changed the title fix(ci): unbreak stable CI after the Rust 1.99 fetch_update deprecation fix(ci): unbreak CI after Rust 1.99 and clear October RustSec advisories Oct 20, 2026
…1/0382

Both advisories were published after this branch's audit last passed:
rustls-webpki 0.103.15 could accept distrusted intermediates
(RUSTSEC-2026-0382) and webpki-roots 1.0.8 bundled roots that trust
distrusted emSign intermediates (RUSTSEC-2026-0381).

Lockfile-only: rustls-webpki 0.103.16 and webpki-roots 1.0.10, both
within the 1.88 MSRV. cargo audit --deny warnings passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JunjoSick
JunjoSick merged commit 83eedb7 into main Oct 20, 2026
15 checks passed
@JunjoSick
JunjoSick deleted the fix/fetch-update-deprecation branch October 21, 2026 14:19
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.

1 participant