fix(security): clear 20 advisories (yara-x 1.7, sevenz-rust2, lru 0.18.2) and repin fleet-ci - #17
Conversation
…sion Picks up SecurityRonin/fleet-ci#9. Repin only -- no gate behaviour changes. Two fixes, both of which only matter when a check context is REQUIRED: - A skipped job publishes its `name:` EXPRESSION, not the evaluated value. The coverage job's name was a multi-line ternary, so any repo turning the coverage gate off published a check named with the raw expression source, newlines included. That name is now the constant `Coverage`. - Matrix jobs cannot serve as required contexts, because GitHub appends the matrix values -- `MSRV (<crate>, <floor>)`, `Test (<os>)` -- so the context name moves whenever a crate is added or a floor is raised, and a required context that stops reporting makes every PR unmergeable with no explanation. The workflow now publishes an aggregate `All checks` job that needs all twelve others and whose name never varies. `ci / All checks` becomes requirable once every repo carries this pin.
`LruCache::pop()` was not panic-safe: if a stored key's `Drop` panicked during `pop()`, `self.detach()` never ran, leaving dangling pointers in the internal doubly-linked list. A later insertion that triggers eviction then writes through them. CWE-416 use-after-free, CWE-415 double free, both reachable from safe Rust when unwinding panics are enabled and `catch_unwind` is used with a key type whose `Drop` can panic. https://rustsec.org/advisories/RUSTSEC-2026-0253 patched = [">= 0.18.2"] The declared requirement was `lru = "0.16"`, which a caret bump cannot cross, so the manifest moves to "0.18" as well as the lock -- layer 1 of the caret staleness modes, not merely a stale lock. `cargo check --workspace --all-features` is clean across all members: 0.16 -> 0.18 touches no API this workspace uses. Found by cargo-deny going red on the fleet-ci repin branch while `main` sat green: the advisory postdates main's last CI run, and cargo-deny re-reads the advisory database every run. Main is not green because it is clean; it is green because nothing has re-asked the question since 2026-05-12.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
…ories
cargo-deny on this tree went from 23 advisory errors to 3, and the three that
remain fail identically on `main` -- they are pre-existing, not introduced here.
Cleared:
- 18 wasmtime vulnerabilities, including four sandbox escapes and several
host panics, reached through yara-x 0.12 -> wasmtime 26.0.1
- RUSTSEC-2026-0253, the `LruCache::pop()` use-after-free (carried from the
previous commit, still verified here)
- RUSTSEC-2026-0246, `sevenz-rust` unmaintained, plus the path-traversal
arbitrary-file-write it carries
- `paste` unmaintained, dropped incidentally with the yara-x graph
yara-x is pinned with a TILDE, and the pin is the load-bearing part. yara-x
1.8+ moves to wasmtime 43.x, and NO 43.x release is patched for
GHSA-hgjw-h833-99q9 or its five siblings -- the fixed ranges are 24.0.12,
36.0.13, 46.0.2+ and 47.0.3+. Only the 1.7 line resolves wasmtime ^36.0.2, and
so reaches the patched 36.0.13. Upgrading to the newest yara-x therefore looks
like the safer move and silently reinstates all six; a plain `cargo update`
during this work did exactly that, which is why the constraint lives in the
manifest rather than only in the lock. Remove the pin once yara-x depends on a
patched wasmtime range.
sevenz-rust has no safe upgrade, so the 7z path moves to the maintained fork
sevenz-rust2: `SevenZReader`/`SevenZWriter`/`SevenZArchiveEntry` become
`ArchiveReader`/`ArchiveWriter`/`ArchiveEntry`, and `Error::io` is `pub(crate)`
there so the io error is built from the public `Error::Io` variant. No
behaviour change in the extraction path.
`MIT-0` joins the licence allow-list for ppmd-rust, which arrives via
sevenz-rust2 and via zip inside yara-x. It is "CC0-1.0 OR MIT-0"; MIT-0 is
OSI-approved and satisfies the OR alone, so CC0-1.0 stays off the list.
Verified: `cargo check --workspace --all-features --all-targets` clean, and
`cargo deny check` reports licenses/bans/sources ok.
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
…two advisories
Takes `cargo deny check` on this repo to `advisories ok, bans ok, licenses ok,
sources ok`. The three advisories left after the yara-x work all predate it —
they fail identically on `main` — and they are resolved in the order the policy
demands, real fix first and suppression only where no fix exists.
- number_prefix (RUSTSEC-2025-0119, unmaintained) is reached only through
indicatif 0.17. indicatif 0.18 drops the dependency outright, so this is a
genuine remediation: the crate leaves the graph. Requirement moves 0.17 ->
0.18; the workspace compiles unchanged on all targets.
The remaining two carry `patched = []` upstream — there is no release to move
to, no maintained replacement, and both are pulled by yara-x, which is itself
pinned to ~1.7 because every later line depends on an unpatched wasmtime. Each
is ignored individually with a reason and a removal condition:
- RUSTSEC-2023-0071, rsa Marvin Attack. The advisory is a timing sidechannel
in private-key DECRYPTION. That it is unreachable here was checked, not
assumed: yara-x touches rsa in exactly one file, and only through
`DecodePublicKey`, `RsaPublicKey` and `Pkcs1v15Sign::verify` for PE
authenticode signatures. `RsaPrivateKey`, `.decrypt(` and Oaep appear
nowhere in the crate. The comment carries a RE-CHECK instruction, because
that reachability claim expires the moment yara-x is upgraded.
- RUSTSEC-2025-0141, bincode unmaintained. A maintenance-status advisory
rather than a defect; yara-x pins it for rule serialisation.
Both live in this repo's own deny.toml, not an inherited shared list. That is
the point of the local config: a suppression has to be argued for in the repo
that carries the risk.
Verified the ignores are load-bearing rather than decorative by deleting them
and confirming `cargo deny check advisories` goes red — with the deletion
asserted to have applied first, since a mutation that silently no-ops reports
green while testing nothing.
…tion
The yara-x/sevenz-rust2/indicatif work moved 36 crate names into the graph and
31 out, which left `cargo vet` with 138 missing audits. `main` vets clean, so
this gap is introduced here rather than inherited — it is the migration's cost,
and it is paid with the strongest mechanism that applies rather than the
cheapest.
Order of resolution, per the supply-chain policy:
- 65 crates are now TRUSTED BY PUBLISHER, not exempted: dtolnay, kennykerr,
BurntSushi, epage, sunfishcode, cuviper, philipc, seanmonstar, fitzgen,
Amanieu and alexcrichton, each already trusted by the imported mozilla and
bytecode-alliance audit sets. `--allow-multiple-publishers` was deliberately
NOT passed: where a crate has several publishers, trusting it on one name is
a weaker claim than the flag makes it look.
- the remainder are exemptions, which honestly assert "nobody audited this".
Net effect is a BETTER posture than before the migration, not merely a restored
one: exemptions fall 302 -> 242 and fully-audited crates rise 111 -> 189.
25 crates are newly exempted, i.e. genuinely new unaudited surface. Most are
successors or renames of crates leaving in the same change — sevenz-rust2 for
sevenz-rust, lzma-rust2 for lzma-rust, wasmtime-internal-asm-macros for
wasmtime-asm-macros — and the rest are bincode 2.x internals and compression
codecs reached through the 7z path:
android_system_properties, base64, bincode_derive, cpubits, either, ipnet,
jobserver, lzma-rust2, nom, pkg-config, ppmd-rust, psl, psl-types,
sevenz-rust2, strum_macros, unit-prefix, unty, virtue,
wasmtime-internal-asm-macros, zeroize_derive, zip, zlib-rs, zstd, zstd-safe,
zstd-sys
Listed rather than summarised on purpose: a count is not reviewable, and this
is the one part of the change that adds trust surface instead of removing it.
`cargo vet --locked` reports Vetting Succeeded.
…security fixes (#18) PR #17 fixed this repo's tree but published nothing, and the reason is structural rather than a mistake in the commit subject: the squash touched 8 files, none of them under `crates/`, and release-plz runs with `dependencies_update = false`. So no library package changed by its own reckoning, and it correctly declined — logging `no commit matches the release_commits regex` for every member. The consequence is that downstream is still exposed. The PUBLISHED crates declare the old requirements: memf-windows 0.4.4 yara-x ^0.12 -> wasmtime 26.0.1, 18 advisories memf-strings 0.2.2 yara-x ^0.12 -> same memf-core 0.2.5 lru ^0.16 -> below the patched 0.18.2 memf-format 0.3.1 lru ^0.16 -> same `4n6mount` and `issen` consume those published crates, so nothing reaches them until these four are republished. This bump is what makes release-plz's `release` job publish them: it ships any library whose version is ahead of crates.io, regardless of commit type. Bump levels are chosen from whether the dependency is reachable through each crate's PUBLIC API, checked by reading every occurrence rather than by a pattern match: - memf-windows 0.4.4 -> 0.5.0 (MINOR — breaking under 0.x). `pub fn scan_yara(reader, ps_head_vaddr, rules: &yara_x::Rules, ...)` takes a yara-x type across the API boundary. A caller holding a `Rules` built by yara-x 0.12 cannot pass it to a signature expecting 1.7, so this is a breaking change and a patch bump would have shipped it silently. - memf-core 0.2.5 -> 0.2.6, memf-format 0.3.1 -> 0.3.2, memf-strings 0.2.2 -> 0.2.3 (PATCH). In each, the dependency is held behind a private field — `tlb_cache`, `cache`, `rules` — and no public signature mentions it. The compiled dependency set changes; the API does not. The workspace's path-dep declarations move with them: they pin exact versions (`memf-windows = { version = "0.4.4", path = ... }`), and `^0.4.4` cannot match 0.5.0, so leaving them would break the build rather than merely lag. `chore(release):` is deliberate. `release_commits` excludes chore, so this does not trigger a second version bump on top of this one, while the `release` job still publishes on the version-ahead-of-crates.io rule. Verified: `cargo update -w` moves all four in the lock, and `cargo check --workspace --all-features --all-targets` is clean. KNOWN GAP, deliberately not fixed here: published `memf-carve 0.1.5` requires `memf-windows ^0.4` and so cannot resolve 0.5.0. Nothing downstream of this repo currently consumes memf-carve, so it is left for its own release rather than widened blind in a security bump. Co-authored-by: h4x0r <h4x0r@github.com>
Repins the shared workflow to
9234e1fa9f85b14a8d6fddc7a8f7378f7e30b183, whichmakes this repo publish a stable, requirable check context.
Two defects in
fleet-ciblocked required status checks fleet-wide:The coverage job's
name:was an expression. GitHub does not evaluate ajob's
name:when the job is skipped — it publishes the raw expressiontext as the check name, newlines and all. Repos that skip coverage were
therefore publishing a check named after the ternary that computed it, which
no branch-protection rule can name. The job is now statically
Coverage.There was no single invariant context to require. The remaining checks
are matrix jobs (
MSRV (<crate>, <ver>),Test (<os>)), whose publishednames vary per repo and per matrix entry. A new aggregate job,
All checks,needs:every other job withif: always()and fails unless eachone succeeded or was deliberately skipped — so
ci / All checksis onecontext every consumer publishes identically.
The gate refuses an empty result set and refuses an all-skipped run: an empty
check list means not run, never passed.
No behaviour change to this repo's own CI configuration — only the pinned
revision moves. Nothing is required yet; branch protection is left untouched
until every consumer carries this pin, because requiring a context before it
exists everywhere would block merges.
Verified end-to-end on
sqlite-forensicbefore this sweep: the pre-changecommit published the expression-named check run, the post-change commit
publishes
ci / Coverage(skipped) plusci / All checks(success), with thegate's own log listing twelve results matching its twelve
needs:.