fix(deps): clear the quick-xml DoS advisories and close the relay/ supply-chain blind spot - #433
Merged
runyourempire merged 2 commits intoAug 15, 2026
Conversation
…ain blind spot
The quick-xml suppression in deny.toml / .cargo/audit.toml rested on
"NO consumer in our tree has a released version against >=0.41 yet".
That is no longer true, so the ignore had become a stale lie:
calamine 0.36.1 -> quick-xml ^0.41 (we were pinned "0.25")
docx-rs 0.4.22 -> quick-xml ^0.41 (we were locked 0.4.20)
plist 1.10.0 -> quick_xml ^0.41 (transitive, was 1.9.0)
Bumping all three collapses quick-xml 0.31.0 + 0.36.2 + 0.39.4 into a
single 0.41.0, so RUSTSEC-2026-0194 and RUSTSEC-2026-0195 stop firing
on their own and both ignores are DELETED rather than re-justified.
This matters because that parser sees user-supplied .xlsx/.docx.
RUSTSEC-2023-0071 (rsa) is left alone — its enterprise-gated
justification was re-checked and still holds.
calamine 0.25 -> 0.36 is eleven minor versions, but the API surface
extract_xlsx() uses is unchanged: sheet_names() and worksheet_range()
have identical signatures, and Data still has exactly the same nine
variants with the same payloads (cell_to_string matches it
exhaustively with no wildcard arm, so a new variant could not have
compiled). ExcelDateTime's Display impl is byte-identical across the
two versions, so DateTime cells format the same. office.rs needed no
edit. The 100 MB cap is still on the COMPRESSED size — untouched,
still a decompression-bomb weakness, still separate work.
Falls out of the bump: zip 1.1.4 retires (calamine and docx-rs both
moved to zip ^8.6). zip 0.6.6 stays — it is our own direct
`zip = "0.6"`, so retiring it is an 8-major API migration across
osv/cache.rs, extractors/archive.rs and embeddings_providers/
fastembed.rs. No advisory attaches to it; tracked as follow-up.
relay/ had 5 vulnerabilities, no Dependabot entry and no audit
coverage at all — a TLS-terminating server sitting on four
rustls-webpki certificate-validation flaws:
rustls-webpki 0.103.9 -> 0.103.14 (RUSTSEC-2026-0049/-0098/
-0099/-0104)
spin 0.9.8 -> 0.9.9 (0.9.8 was YANKED)
anyhow 1.0.102 -> 1.0.104 (RUSTSEC-2026-0190 unsound)
event-listener 5.4.1 -> 5.4.2 (RUSTSEC-2026-0221 unsound)
rand 0.8.5 -> 0.8.7 (RUSTSEC-2026-0097 unsound)
relay now audits clean apart from rsa 0.9.10, which has no fix and is
NOT in its build graph: it reaches Cargo.lock only via sqlx's optional
mysql backend, which relay never enables (`cargo tree -i rsa` and
`cargo tree -i sqlx-mysql` both report nothing to print). Recorded in
a new relay/.cargo/audit.toml with that evidence.
To stop this recurring: dependabot.yml gains a cargo entry for /relay
(it is not a src-tauri workspace member, so the existing entry never
saw it), and nightly-audit.yml's cargo audit step now loops over every
Cargo.lock in the repo instead of only src-tauri. Workflow footprint
is deliberately limited to those two files — validate.yml is being
reshaped by a peer PR and is not touched here.
relay/Dockerfile: `cargo build --release --locked 2>/dev/null ||
cargo build --release` silently dropped lockfile enforcement whenever
the first form failed, shipping an unaudited resolve with the reason
swallowed by 2>/dev/null. Fallback removed. Its base image also had to
move 1.82 -> 1.95 (matching rust-toolchain.toml): the lockfile already
required 1.88 via time 0.3.47 (jsonwebtoken -> simple_asn1), so that
image could not have built this crate at all — the fallback was
hiding a hard failure, not surviving a soft one.
Verified: src-tauri cargo audit exit 0, cargo deny check exit 0
("advisories ok, bans ok, licenses ok, sources ok"); relay cargo audit
exit 0 and cargo check --locked clean; clippy -D warnings clean for
default and --features experimental; 4289/4290 lib tests pass (the one
failure, locked_db_returns_recovery_failed_without_quarantine, is a
wall-clock assertion that flaked under concurrent load and passes in
0.86s in isolation — unrelated).
The real-parser office tests are #[ignore]d and no fixtures exist in
the repo, so they had never executed. Generated real OOXML .xlsx and
.docx (shared strings, inline strings, numeric/bool cells, paragraphs
and a table), confirmed both #[ignore]d tests pass against them, and
separately asserted the extracted text exactly matches the pre-bump
formatting contract — "Widget | 42", "Gadget | 3.50 | TRUE",
"=== Sheet: Budget ===", "A1 | B1".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2
…uri audit now fully clean Last advisory standing in src-tauri. `.husky/pre-push` prints "Rust dependency vulnerabilities found" on EVERY push because of it, which is exactly how a real finding gets trained into background noise. RUSTSEC-2026-0221: event-listener lets `!Send` tags cross thread boundaries via StackSlot. It is not dead weight — `cargo tree -i event-listener --target all` puts it on the Linux D-Bus path (zbus <- notify-rust / tauri-plugin-opener / tauri-plugin-single- instance), invisible to a plain `cargo tree` on Windows. Same one-line fix already applied to relay/Cargo.lock in this branch, where 5.4.2 does get compiled on this machine (flume -> sqlx-sqlite) and `cargo check --locked --all-targets` passes — so the release is known-good here even though src-tauri's copy is Linux-only. src-tauri `cargo audit` now reports zero vulnerabilities and zero warnings; `cargo check --locked` clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2
runyourempire
force-pushed
the
fix/quick-xml-advisories-and-relay-supply-chain
branch
from
August 14, 2026 15:23
f7de0cb to
e2a16c4
Compare
runyourempire
deleted the
fix/quick-xml-advisories-and-relay-supply-chain
branch
August 15, 2026 13:57
runyourempire
added a commit
that referenced
this pull request
Aug 15, 2026
main's quick-xml advisory fix bumped calamine 0.25 -> 0.36.1, zip -> 8.6.0, quick-xml (3 versions) -> 0.41.0, docx-rs 0.4.20 -> 0.4.22 and plist 1.9 -> 1.10, pulling in atoi_simd, debug_unsafe, fast-float2 and typed-path. 828 -> 832 shipping crates. The generator caught this on its first CI run: NOTICE was rendered from the pre-rebase lockfile and `--check` failed against the merge result. That is the gate doing its job — under the old hand-maintained file this dependency bump would have silently desynced the attribution users see in the Third-Party Licenses modal, exactly as the previous four version errors did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2
runyourempire
added a commit
that referenced
this pull request
Aug 15, 2026
…n the parsers (#461) Two defects on the **untrusted-input path** — data fetched from remote feeds, and files opened from the user's disk. ## Defect 1 (HIGH) — the SSRF guard was pre-flight only `url_validation::is_internal_url` is a good implementation wired at ten production sites. But **no client in the codebase configured a `redirect::Policy`**, so all 21 production `ClientBuilder`s inherited reqwest's default `Policy::limited(10)`. A hostile RSS feed (user-addable) or a hijacked curated-feed domain answering `302 Location: http://127.0.0.1:4446/api/dna` was followed, the pre-flight check never re-ran, and the internal body was stored as feed content (`content_enrichment.rs`). ### The fix `http_client.rs` gains two policies: | Policy | Behaviour | Applied to | |---|---|---| | `ssrf_guarded_redirect_policy()` | refuses **any** hop to an internal address | 10 clients: `HTTP_CLIENT`, `PROBE_CLIENT`, `TEAM_CLIENT`, `client_builder_with_proxy`, crates.io, OSV cache + sync, model registry, translation, settings validation, team-sync scheduler, webhooks, fastembed download, live reality check | | `local_aware_redirect_policy()` | refuses an internal hop **unless the request already started internal** | 11 clients: `LLMClient`, Ollama (x2), `ollama_capability`, embeddings, local-server probes (x3), calibration check, dev-frontend gate | The local-aware variant exists so a user who deliberately points 4DA at `http://127.0.0.1:11434` keeps working, while a cloud provider still cannot redirect its way inward. It mirrors the existing `if self.provider.provider != "ollama"` exemption at `llm.rs:492`. `Policy::custom` **replaces reqwest's hop limit wholesale**, so the policy counts hops itself — without that a redirect loop never terminates. There is a test for exactly that. ### Secondary: the hand-rolled URL parser `extract_host` split on `/ ? # :` but never on `@`, so `http://evil.com@127.0.0.1/` produced a host of `evil.com@127.0.0.1`, failed `parse::<IpAddr>()`, fell through a failing DNS lookup, and returned "not internal". It also missed the WHATWG-legal obfuscated literals (`http://2130706433/`, `http://0x7f000001/`, `http://127.1/`, `http://0177.0.0.1/`). Host extraction now goes through the `url` crate — the same parser reqwest uses, so there is no parser differential between validation time and request time. Credentials-in-URL are rejected outright, matching what `ipc_guard::validate_url_safe_for_request` already did. ### Correction to the brief The report suggested `rss.rs:483` / `hackernews.rs:301` would "inherit the protection" once the redirect policy landed. **They would not.** Those two fetch feed-supplied URLs guarded only by `starts_with("http")` — a *direct* SSRF where the first request is not a redirect, so no redirect policy can see it. Both now pre-flight through `validate_not_internal`. ## Defect 2 (HIGH) — decompression bombs in default-reachable parsers **`extractors/office.rs` (default-build reachable).** The 100 MB cap was on the *compressed* file, with no decompressed bound, entry count, or ratio check. A 100 MB DOCX of low-entropy XML expands to ~100 GB, buffered by `read_docx`/calamine before we ever see a parse result. `guard_ooxml_bomb` now streams every part through `io::sink()` — decompression CPU, no allocation — and counts the bytes that actually come out, bounding total size (250 MB), ratio (200:1), and part count (10,000). **`extractors/archive.rs` (feature-gated).** All three ZIP guards read `file.size()` — the *header-declared* size, which the archive's author controls. `zip` 0.6 builds the DEFLATE reader as `DeflateDecoder::new(take(compressed_size))` (`read.rs:277`), so the declared size bounds nothing: declare 1 KB, ship 45 MB. Sizes and ratios now come from capped reads. TAR gains the input cap it never had, a `Take` on the decompressed stream (a `.tar.gz` had no bound at all), and an entry-*scan* counter — `MAX_FILE_COUNT` only incremented after a successful read, so an archive of skippable entries looped once per entry on the watcher thread while holding the DB mutex. **`osv/cache.rs`.** The ecosystem download called `.bytes()` — unbounded in-memory buffering of a several-hundred-MB body — and `read_to_string` per entry. Now streams to disk under a 1 GiB cap and reads each advisory under 8 MiB. **Zip-slip is untouched** — `enclosed_name()` and the `ParentDir` rejection were already correct, and nothing is written to disk. ## Every new test was proven to fail against the pre-fix code The existing extractor "integration" tests were decorative: `#[ignore]`d *and* self-neutering, each wrapping its only assertion in `if test_docx.exists()` against a temp path nothing creates. Those two are deleted. New fixtures are built programmatically at test time — no binaries committed. Each guard was reverted in place and the tests re-run. Observed failures: | Test | Failure against pre-fix code | |---|---| | `blocks_userinfo_masking_loopback` | `assertion failed: is_internal_url("http://evil.com@127.0.0.1/")` | | `blocks_obfuscated_ipv4_literals` | `assertion failed: is_internal_url("http://2130706433/")` | | `validate_not_internal_rejects_credentials` | no error raised | | `shared_http_client_refuses_redirect_to_loopback` | `HTTP_CLIENT followed the hop: Ok(200)` | | `probe_and_team_clients_refuse_redirect_to_loopback` | `PROBE_CLIENT followed the hop` | | `docx_decompression_bomb_is_refused_on_size` | `got: Failed to parse DOCX structure` — i.e. *after* inflating | | `docx_decompression_bomb_is_refused_on_ratio` | same | | `xlsx_decompression_bomb_is_refused` | `got: Failed to open Excel workbook` — after inflating | | `ooxml_entry_count_bomb_is_refused` | same | | `zip_entry_lying_about_its_size_is_refused` | `bomb payload reached the output (47185977 bytes of text)` | | `targz_decompressed_stream_is_bounded` | `the walk ran past the extraction budget to reach the trailing entry` | | `tar_input_over_compressed_cap_is_refused` | `got: No extractable text content found in archive` | The redirect tests drive a **real loopback HTTP fixture serving a real 302** — `reqwest::redirect::Attempt` cannot be constructed outside its crate, so a live redirect is the only way to exercise a policy. A companion test asserts the *stock* reqwest policy **does** follow the hop, so the fixture cannot silently rot into proving nothing. The ZIP bomb fixture forges the declared uncompressed size at known byte offsets in both the local file header (+22) and the central directory record (+24) — which is exactly what an attacker does by hand. `legitimate_docx_still_extracts` is the anti-false-positive test: a genuine DOCX built with `docx-rs` must still parse. Guards that reject everything are not guards. ## Verification - `cargo clippy --lib -- -D warnings` — clean, default features - `cargo clippy --lib --features experimental -- -D warnings` — clean - `cargo clippy --lib --features archive -- -D warnings` — clean (archive.rs only compiles under this feature) - `cargo fmt --check` — clean - `cargo test --lib` — **4356 passed, 0 failed, 8 ignored** - `cargo test --lib --features archive` — **4367 passed, 0 failed, 8 ignored** - Integration tests — **154 passed** (migration 12, pipeline 13, source_resilience 5, stack_simulation 124) Rebased onto `8c60321b`, so this is verified against #433's `calamine` 0.36 / `docx-rs` 0.4.22. `office.rs` needed no API changes for that bump; the rebase applied with no conflicts. The rebase caught one real flake: the ratio fixture at deflate level 1 landed at ~199:1 against a 200:1 threshold — sitting on its own boundary. It now builds at level 9 (~1000:1) and asserts the margin up front, so a future flate2 bump fails with "fixture is no longer a convincing bomb" rather than a confusing parser error. ## Note `src-tauri/src/osv/cache.rs` crosses the 700-line soft warning (726). Warning only, non-blocking; splitting that module is a separate refactor. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
runyourempire
added a commit
that referenced
this pull request
Aug 15, 2026
…e NOTICE mechanically (#460) Two truthfulness defects in public-facing claims, plus the drift that caused them. ## 1. Rejection-rate claims (`fix(docs)`) **The headline numbers were real, and I found their source** — commit `d956d46f`, 2026-04-26, *"ground rejection rate claims in measured benchmark data"*, which replaced an earlier unmeasured "99%+" claim with 92% / 98% / 77% measured from the 9-persona simulation over 1,997 evaluations. So they were never fabricated. What was actually broken is that nothing regenerated or enforced them afterwards: | Claim | Status | |---|---| | "92% rejection, 98% noise accuracy" | True but stale — current measurement is **93.1% / 98.9%** | | "215 labeled items" | Stale — corpus is **245** | | "tested, not asserted" (`README:345`) | **False as written** | | `cargo test scoring::benchmark` cited for the 9-persona numbers | **Wrong suite** — it has 2 profiles; the 9 personas live in `scoring::simulation` | | de/es/fr/tr locales | Still shipping the **"over 99%"** claim `d956d46f` was written to remove — those 4 locales were missed | | 9 locales: "shown in the Evidence tab" | Evidence is in the **removed-views** list (`ui-slice-navigation.test.ts:46`); repointed to Signal | On "tested, not asserted": the suite asserts **floors**, not the headline. `reality.rs` requires aggregate precision >= 0.70, aggregate F1 >= 0.40, and >= 80% noise rejection per persona, with per-persona floors as low as P>=0.10 for bootstrap. Rejection could have fallen from 92% to 81% without failing a single test. Every doc now says so explicitly. **Re-measured on the current pipeline** (`cargo test --lib scoring::simulation -- --nocapture`, 159 passed): ``` 245 items x 9 personas -> 1,997 scored evaluations (borderline excluded) TP 119 · FP 19 · TN 1,646 · FN 213 rejection (TN+FN)/total = 93.1% noise accuracy TN/(TN+FP) = 98.9% precision = 86.2% blended recall = 35.8% strong recall = 71.3% (72/101) ``` The dashboard only prints P/R/F1, so rejection rate and noise accuracy have to be derived from the confusion matrix — that derivation is now written into the README rather than left as tribal knowledge. Sanity check under real embeddings (`--features calibrated-sim`, real fastembed fixtures rather than the synthetic CI baseline): **94.0% rejection / 98.8% noise accuracy / 83.2% precision**. The headline claims hold under both embedding modes. Also fixed `specs/ARCHITECTURE.md`'s unbacked **"99.9% rejection rate"**. **No scoring code touched.** The claims were wrong, not the thresholds. ## 2. Third-party attribution (`fix(legal)`) `NOTICE` was hand-maintained at 92 direct dependencies against **828 shipping Rust crates + 42 production npm packages**, and it is raw-imported by `ThirdPartyLicensesModal.tsx` — so it is what users actually see. Confirmed drift: - `ts-rs` listed as 10 -> **12.0.1** - `scraper` listed as "0.23 - MIT" -> **0.27.0, and ISC** (licence was wrong too) - `chacha20poly1305` 0.10 -> **0.11.0** - `ammonia` 4.1.4 -> **absent entirely** - **All 8 shipping MPL-2.0 crates unattributed** (`cssparser` x2, `cssparser-macros` x2, `selectors` x2, `dtoa-short`, `option-ext`) — MPL-2.0 §3.2 requires notice - **Both OFL-1.1 fonts unattributed**, and the OFL text reached no build output at all. The fonts ship the licence in `node_modules`, but nothing copied it into `dist/` — OFL-1.1 requires the licence to travel with the font software. **Route taken: mechanical generation**, so it cannot drift again. `scripts/generate-notice.cjs` derives NOTICE from `cargo metadata` (non-dev closure: normal + build edges, all platforms) and `pnpm licenses list --prod`. It also: - emits a reciprocal-licence section with a source-availability statement (MPL/EPL) - embeds the full OFL text for each bundled font - **hard-fails** on strong copyleft, or on an OFL package missing its licence file - does not misclassify dual licences — `r-efi` (`MIT OR Apache-2.0 OR LGPL-2.1-or-later`) neither blocks nor lands in the reciprocal section Licence *compatibility* was already clean: **no GPL/AGPL/SSPL** anywhere in the graph. ### Why 29 old entries disappeared Worth checking during review, since a shrinking NOTICE looks alarming. All 29 are correct removals: - **25 dev-only** (`criterion`, `eslint`, `vitest`, `playwright`, `sharp`, `tailwindcss`, …) — the old file had explicit "Dev Dependencies" sections. Dev tooling is not distributed, so it needs no attribution. - **`ocrs`, `rten`** — behind the non-default `ocr` feature (`default = ["fastembed-local"]`), so they are not linked into the shipped binary. Release builds pass no `--features` override. - **`uhlc`, `tokio-tungstenite`** — genuinely gone; neither is in `Cargo.lock` and `tokio-tungstenite` is not even declared in `Cargo.toml`. The hand-maintained file was attributing crates that had already been removed. ### Gate placement `--check` runs in **`repo-guards`**, the only CI job with no `if:` — so a Rust-only dependency bump cannot slip past it. That costs the job a Rust toolchain and a dependency install it did not previously need (~2-3 min); the alternative was a gate that skipped on exactly the PRs that move dependencies. `--require` makes a missing toolchain a failure there, so the gate cannot pass by doing nothing. Also wired into `pnpm run validate` as `validate:notice`. 19 self-tests added to `test:scripts`, per the repo convention that every guard verifies its own claims. ### The gate caught real drift on its first run Worth recording, because it is the whole argument for the change. This branch was cut before #433 landed. CI builds the merge commit, so `--check` rendered against a lockfile where #433 had bumped `calamine` 0.25 -> 0.36.1, `zip` -> 8.6.0, `quick-xml` (three versions) -> 0.41.0, `docx-rs` 0.4.20 -> 0.4.22 and `plist` 1.9 -> 1.10, pulling in `atoi_simd`, `debug_unsafe`, `fast-float2` and `typed-path` — 828 -> 832 shipping crates. It failed, named the exact crates, and the branch was rebased and regenerated. Under the hand-maintained file that bump would have silently desynced the attribution users see in the Third-Party Licenses modal — which is precisely how the four pre-existing version errors got there. ## Verification - `cargo test --lib scoring::simulation` — 159 passed - `pnpm run test` — **1249 passed** (111 files) - `pnpm run typecheck`, `pnpm run build` — clean - `pnpm run test:scripts` — **88 passed** (69 + 19 new) - `check-file-sizes`, `check-doc-location`, `check-vanity-metrics`, `check-llm-gate-honesty`, `validate-translations` — clean (14 translation warnings, all pre-existing and unchanged) - `generate-notice.cjs --check` — idempotent ## Follow-ups (not done here, deliberately) 1. **`--features calibrated-sim` currently fails 9 tests** (4 persona reality tests, 2 enrichment, golden snapshot, registry precision, version comparison). Pre-existing — this PR touches no Rust. The feature is off by default and not in CI, but its thresholds have drifted from its fixtures. 2. The simulation prints P/R/F1 but not rejection rate or noise accuracy. Emitting them directly would remove the hand-derivation the README now documents — needs a change in `scoring/`, which was out of bounds for this PR. 3. No guard ties the documented CI floors to the actual `assert_quality` values in `reality.rs`. If someone lowers a threshold, the docs go stale silently. 4. **Bundled ML model weights are unattributed.** `tauri.conf.json` bundles `src-tauri/models` (`text-detection.rten` 2.4 MB, `text-recognition.rten` 9.3 MB, fetched by `fetch-ocr-models.cjs`, untracked in git). These are third-party artifacts that ship in the installer, and this generator covers source dependencies only. Separately worth a look: the models ship even though the `ocr` feature that reads them is off by default. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01AUeKTKwNmdow8yUk3q8RB2 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The premise, verified first
An audit lane claimed the quick-xml suppression in
deny.toml:96-112/.cargo/audit.toml:15-29had gone stale. It rested on this justification:Confirmed false. Read straight out of the registry index (
rust_versionanddepsper published version):calamine^0.31→^0.41^1.0→^8.6docx-rs^0.36→^0.41(since 0.4.21)^0.6.3→^8.6plist^0.39.2→^0.41All three shipped support. The ignores were suppressing a live, fixable advisory pair on a parser that reads user-supplied
.xlsx/.docx.What changed
quick-xml (RUSTSEC-2026-0194 / -0195) — resolved, not re-justified. Bumping the three consumers collapses
quick-xml0.31.0 + 0.36.2 + 0.39.4 → a single 0.41.0. Both advisories stop firing on their own, so both ignores are deleted fromdeny.tomland.cargo/audit.toml(they had diverged; both were checked).RUSTSEC-2023-0071(rsa) left alone as instructed.office.rsneeded no edit — and that is a verified claim, not an absence of errors:sheet_names()andworksheet_range()have byte-identical signatures in 0.25 and 0.36.Datastill has exactly the same nine variants with the same payloads.cell_to_stringmatches it exhaustively with no wildcard arm, so an added variant could not have compiled.ExcelDateTime'sDisplayimpl is byte-identical (write!(f, "{}", self.value)), soDateTimecells format the same.docx-rs:TableChild/TableRowChildare destructured irrefutably, so a new variant there could not have compiled either.The documented decompression-bomb weakness (the 100 MB cap is on the compressed size) is untouched — separate work, not regressed.
zip — partial.
zip 1.1.4retired as hoped.zip 0.6.6did not — it is our own directzip = "0.6", so retiring it is an 8-major API migration acrossosv/cache.rs,extractors/archive.rsandembeddings_providers/fastembed.rs. No advisory attaches to it, so it is staleness, not exposure. Left as follow-up rather than smuggled into a security PR. Tree is nowzip0.6.6 (ours) + 4.6.1 (tauri-plugin-updater) + 8.6.0 (calamine/docx-rs).relay/— 5 vulnerabilities → 0. A TLS-terminating server with no Dependabot entry, no cargo-audit, no CI.rustls-webpkispinanyhowevent-listenerrandrsa 0.9.10remains with no fix available, and is recorded in a newrelay/.cargo/audit.tomlwith evidence that it is not in the build graph: it reachesCargo.lockonly via sqlx's optionalmysqlbackend, which relay never enables —cargo tree -i rsaandcargo tree -i sqlx-mysqlboth report nothing to print.Coverage, so it stops recurring.
dependabot.ymlgains acargoentry for/relay(not asrc-tauriworkspace member, so the existing entry never saw it), andnightly-audit.yml's cargo-audit step now loops everyCargo.lockin the repo. Workflow footprint is deliberately limited to those two files —validate.ymlis being reshaped by peer PRs and is untouched here.relay/Dockerfile.cargo build --release --locked 2>/dev/null || cargo build --releasesilently dropped lockfile enforcement and swallowed the reason. Fallback removed. Its base image also had to move 1.82 → 1.95: the lockfile already required 1.88 viatime 0.3.47(jsonwebtoken→simple_asn1), so that image could not have built this crate at all — the fallback was hiding a hard failure, not surviving a soft one.Two things found on the way
1.
mainwas un-committable — independently confirmed, now fixed by #430.scripts/check-file-sizes.cjsexits 1 onsrc-tauri/src/analysis_rerank.rs(1032 lines against a 1000 hard limit, arrived with #423). The gate scans the whole repo rather than staged paths, so.husky/pre-commitfailed for every terminal on every commit — including this one. I hit it, diagnosed it, and fixed it the same way a peer did in #430 (lift the test module into a siblinganalysis_rerank_tests.rsvia#[path], 1032 → 866). #430 landed first, so that commit has been dropped from this branch by rebase — this PR now contains only the dependency work. Recording it here as an independent second confirmation of both the diagnosis and the chosen fix.2.
cargo clippy --all-targets -- -D warningsdoes not pass onmain(255 pre-existing errors at my branch point, ~allunwrap_used/expect_usedin test code). This is not the gate — CI runscargo clippy ${{ matrix.cargo-features }} -- -D warningswithout--all-targets, so the numbers below are from the CI-equivalent invocation. Reported as an observation, not touched.Verification
src-tauricargo auditsrc-tauricargo deny checkadvisories ok, bans ok, licenses ok, sources okrelaycargo auditrelaycargo check --locked --all-targetscargo clippy -- -D warnings(CI-equivalent, default)cargo clippy --features experimental -- -D warningscargo fmt --checkcargo test --libAll re-run after rebasing onto
c1fd348c(#425, #426, #427, #429, #430, #431 all landed mid-flight).--features team-syncand--features enterprisefail to compile — pre-existing rot onmain(chacha20poly1305::aead::OsRngunresolved, then cascading__cmd__*macro failures), which is what #424 exists to repair. My lockfile diff touches no crypto crate. #424 is still open as of this push, and the CI clippy matrix onmainstill carries only thedefaultandexperimentallegs — so the two legs verified above are exactly the gate.The extractor tests were
#[ignore]d and had never runThere are no
.xlsx/.docxfixtures anywhere in the repo, sotest_real_docx_extraction/test_real_xlsx_extractionwere no-ops that returned early. To gain real confidence in an 11-minor-version parser bump I generated real OOXML documents — shared strings, an inline string, numeric and boolean cells, paragraphs and a table — confirmed both#[ignore]d tests pass against them, and separately asserted the extracted text matches the pre-bump formatting contract exactly:That exercises every arm of
cell_to_stringthat a document can reach (shared/inline string, integral float →42, fractional float →3.50, bool →TRUE) plus the docx paragraph and table paths. The scratch harness was deleted; no test-file changes ship in this PR.Deliberately left
zip 0.6.6— direct dep, 8-major API migration, no advisory. Follow-up.office.rsdecompression bomb — the 100 MB cap is on the compressed size. Out of scope, not regressed.--all-targetsclippy backlog — pre-existing, not the CI gate.validate.yml— peer-owned right now, untouched on purpose.--features team-sync/enterprise— pre-existing rot, fix(build): repair team-sync + enterprise feature rot and gate them in CI #424's job.