From c769c46b551966a21174cf0893d98ed37bec8a58 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Tue, 27 Oct 2026 09:50:55 +0000 Subject: [PATCH 1/2] chore(deps): bump the rust-dependencies group and record its vet audits Supersedes Dependabot #2790, which bumped the five versions alone and failed Audit: `cargo vet --locked` reported seven unvetted crates. Dependabot cannot write supply-chain audits, so a group bump never goes green on its own -- the same rule as #2706, #2720 and #2789. tokio 1.53.3 -> 1.54.1, jaq-core 3.1.1 -> 3.1.2, jaq-std 3.0.3 -> 3.0.4, napi 3.14.2 -> 3.15.0, napi-derive 3.6.12 -> 3.6.13, plus napi-derive-backend 6.1.5 -> 6.1.6 and napi-sys 3.4.0 -> 3.4.1 behind them. The lockfile carries those seven entries and nothing else. Dependabot's also re-pointed six `windows-sys` references from 0.61.2 down to 0.52.0 and moved tempfile to getrandom 0.4.3, which none of the five updates asks for; that is opportunistic re-resolution and is left out, as #2789 did. tokio needs no audit: `[[trusted.tokio]]` already trusts Alice Ryhl, so the bump needs only the publisher fact for 1.54.1 in `imports.lock`. Getting it there is what exposed the policy gap below. The other six are hand delta audits, chained onto the existing entries: - napi-sys and napi-derive are a doc-comment line and a dependency bump. - jaq-std rewrites `scan` in jq syntax. Inert here: `builtins/jq/compat.rs` prepends its own `scan` and shadows the stdlib one. Worth keeping -- the new upstream definition is `scan_(re; "g" + flags)`, which yields `"gg"` and a double-`g` compile error for a filter that already passes `g`, the case the compat definition exists to avoid. - jaq-core fixes a lexer panic on a comment that ends the input: the scanner substituted a `""` literal, breaking the invariant that the replacement is a suffix of the previous input. It does not reproduce through the jq builtin on 3.1.1 -- checked across eighteen filter shapes, malformed ones included, all identical on both versions -- so the new test pins a shape that had no coverage rather than guarding a regression. - napi-derive-backend defers borrow scopes and stops zero-sized classes sharing a dangling wrapped pointer. - napi 3.15.0 is the delta that needed real reading (24 files, ~2700 lines). No new ambient capability: no std::fs, std::net or std::env, std::process unchanged at 8 occurrences, byte-identical build.rs, and no new dependency. It does add a `napi_run_script` call, on a constant three-line source with no interpolation, only on the ReadableStream cancel path. The rest is hardening, and one piece lands squarely here: the new `class_identity` module checks the recorded Rust class before casting a `napi_unwrap` result, which only runs on builds without Node-API type tags -- which is every build `bashkit-js` makes, since it selects `napi6`. Also fixes a latent gap that blocked the tokio refresh. cargo-vet calls a workspace member first-party by provenance, so once the same name and version is also on crates.io the two collide and `cargo vet` refuses to run until `policy..audit-as-crates-io` says which to believe. `bashkit-scripted-tool` reached crates.io in 0.20.0 with no entry. Nothing failed, because CI runs `cargo vet --locked`, which never reaches that check: the Audit job stayed green while the command was broken for every developer and for refreshing `imports.lock`, which the non-locked run is the only way to do. A bump riding publisher trust therefore could not be made to pass at all. `test_ci_supply_chain.py` now derives the published set from the `release-check` recipe and fails when one of those crates has no first-party policy entry -- the gate that matters cannot catch this, since `--locked` is the gate. Verified locally: `cargo vet --locked` succeeds (98 fully audited, 86 partially, 492 exempted), `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` clean, 185 script tests pass, the new guard fails as intended with the policy entry removed, and the jq slice is 364/364. --- Cargo.lock | 28 +++++++------- crates/bashkit/src/builtins/jq/tests.rs | 28 ++++++++++++++ knowledge/log.md | 6 +++ scripts/tests/test_ci_supply_chain.py | 50 +++++++++++++++++++++++++ supply-chain/audits.toml | 41 ++++++++++++++++++++ supply-chain/config.toml | 4 ++ supply-chain/imports.lock | 4 +- 7 files changed, 145 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2b7ed9d8..ad6848504 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2708,9 +2708,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jaq-core" -version = "3.1.1" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72ba7158fe9fc1bc4baace1b71e6d0d3d27c6839dd87dc1f233dd408f5dd85b" +checksum = "723ad9253808e95daa2d10ad5b258ac4e65c07003e223e08d1d8734c2942d606" dependencies = [ "dyn-clone", "once_cell", @@ -2719,9 +2719,9 @@ dependencies = [ [[package]] name = "jaq-std" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f90e485abfd3189510c996e77be55056a51038b0fa5805e6c97b5dcf0d9ab55" +checksum = "56b378d4f7587d332111a92608348db6d6b9b8f7cb5ee1d2bd162ec715d7c5bf" dependencies = [ "aho-corasick", "base64 0.22.1", @@ -3271,9 +3271,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.14.2" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e2cf85c02d9b057898c485b824c3c416b65193689e2b7f517efc94b86ed24c" +checksum = "6a746ad50efbfb632ade1de4025519a477decbbd798cea1a6ac41d7c44f80083" dependencies = [ "bitflags 2.13.2", "ctor", @@ -3294,9 +3294,9 @@ checksum = "b899b545d3aa6dca985939059f258c5488d34e4ecf39c274e20009748f4b846d" [[package]] name = "napi-derive" -version = "3.6.12" +version = "3.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92cec6048ac04041ac8055bdce1ec4f353416cd443ba9222bf73af790c3d4495" +checksum = "3ff48271a0f61abb83f3e2d1989fa71169143db95f6f948c3a8903961f2855dc" dependencies = [ "convert_case", "ctor", @@ -3308,9 +3308,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "6.1.5" +version = "6.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae2444f4ba7333754bb73414ff8059dec2cb23b83a95bd1a0bf08169eed1be0" +checksum = "f2639052c5aff9956a21d7b6202fc61d5e89c5f19c72d289b7a0641ea94e8159" dependencies = [ "convert_case", "proc-macro2", @@ -3321,9 +3321,9 @@ dependencies = [ [[package]] name = "napi-sys" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22a4f25c16a5c5411d987cd6fbd48313522ae1789ade2d6dd3efdc6d40a0fc8" +checksum = "d153beb7d5c973f4b9395be8a83054a28968b61188f1dd1a2cc2321af22daea2" dependencies = [ "libloading 0.9.0", ] @@ -5784,9 +5784,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.53.3" +version = "1.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3335fa71841cda333a58d7615b03901380ecf09d59b3296d21f8bbac0dde4e" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" dependencies = [ "bytes", "libc", diff --git a/crates/bashkit/src/builtins/jq/tests.rs b/crates/bashkit/src/builtins/jq/tests.rs index cef57a531..b10b2bb41 100644 --- a/crates/bashkit/src/builtins/jq/tests.rs +++ b/crates/bashkit/src/builtins/jq/tests.rs @@ -2507,3 +2507,31 @@ async fn decoding_builtins_follow_jq() { \"date \\\"2026-01-15\\\" does not match format \\\"%Y-%m-%dT%H:%M:%SZ\\\"\"\n1768478400\n" ); } + +/// A filter whose trailing comment is not followed by a newline, which had +/// no coverage: every other comment case here is followed by more filter. +/// +/// jaq-core 3.1.2 fixes a lexer panic on exactly this shape (the comment +/// scanner replaced the remaining input with a `""` literal, breaking the +/// invariant that the replacement is a suffix of the previous input). That +/// panic does not reproduce through this builtin on 3.1.1 -- checked across +/// these inputs plus a dozen malformed ones, all of which returned the same +/// output on both versions -- so this pins the shape rather than guarding a +/// regression: the prepended compat prefix and the `loc`/`altpat` rewrites +/// put the filter at the end of the lexer input, where the fix applies. +#[tokio::test] +async fn filter_ending_in_a_comment_is_accepted() { + assert_eq!(run_jq(".a # trailing", r#"{"a":1}"#).await.unwrap(), "1\n"); + // The comment is the whole filter, and the input ends mid-comment. + assert_eq!(run_jq(". # c", r#"1"#).await.unwrap(), "1\n"); + // A comment ending in a carriage return, and one ending in a backslash + // (the odd-backslash line-continuation branch the fix touches). + assert_eq!( + run_jq(".a # trailing\r", r#"{"a":1}"#).await.unwrap(), + "1\n" + ); + assert_eq!( + run_jq(".a # trailing\\", r#"{"a":1}"#).await.unwrap(), + "1\n" + ); +} diff --git a/knowledge/log.md b/knowledge/log.md index df45c6ff6..17424e3c6 100644 --- a/knowledge/log.md +++ b/knowledge/log.md @@ -1,5 +1,11 @@ # Bashkit Knowledge Update Log +## 2026-10-27 + +* **Supply chain**: A first-party crate reaching crates.io silently breaks `cargo vet`. cargo-vet calls a workspace member first-party by provenance -- it was not fetched from crates.io -- so once the same name and version is also published, the two descriptions collide and the command refuses to run until `policy..audit-as-crates-io` says which to believe. `bashkit-scripted-tool` went public in 0.20.0 with no entry and nothing failed, because CI runs `cargo vet --locked`, which never reaches the collision check. The Audit job stayed green while the command was broken for every developer and for cargo-vet itself: refreshing `supply-chain/imports.lock` needs the non-locked run, so a bump that relies on publisher trust could not be made to pass at all. #2790's tokio 1.54.1 was exactly that bump -- it rides `[[trusted.tokio]]` (Alice Ryhl) plus a publisher fact, not an audit -- which is how a four-release-old gap surfaced. `scripts/tests/test_ci_supply_chain.py` now derives the published set from the `release-check` recipe and fails when any of those crates lacks a first-party policy entry; a missing one cannot be caught by the gate that matters, since `--locked` is the gate. See [Dependencies](operations/dependencies.md). +* **Operations**: The `rust-dependencies` group bump (tokio 1.53.3 -> 1.54.1, jaq-core 3.1.1 -> 3.1.2, jaq-std 3.0.3 -> 3.0.4, napi 3.14.2 -> 3.15.0, napi-derive 3.6.12 -> 3.6.13, plus napi-derive-backend and napi-sys behind them) failed Audit alone with seven unvetted crates -- the same rule as #2706/#2720 and the 2026-10-18 entry: a Dependabot group bump cannot go green on its own, because nothing in the bump records the audits the new versions need. Dependabot's lockfile also carried collateral its own description does not claim: six `windows-sys` 0.61.2 -> 0.52.0 re-points and `tempfile` moving to getrandom 0.4.3. Those are opportunistic re-resolution, not part of the five updates, and are left out -- the same call #2789 made. +* **Dependencies**: Two findings worth keeping from reviewing the deltas. jaq-std 3.0.4 rewrites `scan` to jq's semantics, and it is inert here: `builtins/jq/compat.rs` prepends its own `scan` to every filter and shadows the stdlib one. Keep that override rather than deferring to upstream -- upstream's new definition is `scan_(re; "g" + flags)`, which produces `"gg"` and a double-`g` compile error for a filter that already passes `g`, the exact case the compat definition was written to avoid. jaq-core 3.1.2 fixes a lexer panic on a comment that ends the input (the scanner substituted a `""` literal, breaking the invariant that the replacement is a suffix of the previous input); it does not reproduce through the jq builtin on 3.1.1, checked across eighteen filter shapes including malformed ones, all of which returned identical output on both versions. `filter_ending_in_a_comment_is_accepted` now pins the shape, which had no coverage. napi 3.15.0 is the delta that needed real reading: it adds a `napi_run_script` call, on a constant three-line source with no interpolation and only on the ReadableStream cancel path, and its new `class_identity` module checks the recorded Rust class before casting a `napi_unwrap` result, which matters here because `bashkit-js` selects `napi6` and so has no Node-API type tag to check instead. See [Vendored jaq-json](runtimes/jaq-json-vendor.md), [Node Package](runtimes/node-package.md). + ## 2026-10-26 * Preserve binary [HTTP transport](security/http-transport.md) output in curl diff --git a/scripts/tests/test_ci_supply_chain.py b/scripts/tests/test_ci_supply_chain.py index 880ba3d32..adb0f6a05 100644 --- a/scripts/tests/test_ci_supply_chain.py +++ b/scripts/tests/test_ci_supply_chain.py @@ -166,3 +166,53 @@ def test_fetched_secrets_are_masked_before_they_travel(self): if travels is not None: self.assertLess(masked, travels, f'${variable} escapes the fetch step before it is masked') + + +class VetFirstPartyPolicyTests(unittest.TestCase): + """Every crate this repo publishes needs a `policy.*` entry in cargo-vet. + + cargo-vet decides first-party by provenance: a workspace member is + first-party because it was not fetched from crates.io. Once that same + name and version is also *on* crates.io, the two descriptions collide + and `cargo vet` refuses to run at all -- 'Some non-crates.io-fetched + packages match published crates.io versions' -- until a + `policy..audit-as-crates-io` entry says which one to believe. + + The failure is quiet in the one place that matters. CI runs + `cargo vet --locked`, which never reaches the collision check, so the + Audit job stays green while the command is broken for every developer + and, worse, for `cargo vet` itself: refreshing `supply-chain/imports.lock` + (publisher facts for `[[trusted.*]]` crates, imported audits) needs the + non-locked run. A dependency bump that relies on publisher trust then + cannot be made to pass. bashkit-scripted-tool reached crates.io in + 0.20.0 with no entry, and nothing failed until #2790 needed tokio's. + + The published set is derived from the `release-check` recipe rather than + listed here: that recipe is what actually stages the packages. + """ + + def published_crates(self): + recipe = (ROOT / 'justfile').read_text().split('\nrelease-check:\n', 1)[1] + recipe = recipe.split('\n# Create and push', 1)[0] + # Fold shell line continuations so the whole command is one line. + publish = re.search(r'cargo publish --dry-run[^\n]*', + re.sub(r'\\\n\s*', ' ', recipe)) + self.assertIsNotNone(publish, 'release-check no longer dry-run publishes') + crates = re.findall(r'-p\s+([A-Za-z0-9_-]+)', publish.group(0)) + self.assertIn('bashkit', crates) + return crates + + def test_every_published_crate_has_a_first_party_vet_policy(self): + config = (ROOT / 'supply-chain/config.toml').read_text() + for crate in self.published_crates(): + with self.subTest(crate=crate): + entry = re.search( + rf'^\[policy\.{re.escape(crate)}\]$(.*?)(?=^\[|\Z)', + config, re.M | re.S) + self.assertIsNotNone( + entry, + f'{crate} is published to crates.io but has no [policy.{crate}] ' + 'entry, so `cargo vet` cannot run once that version is live') + self.assertIn( + 'audit-as-crates-io = false', entry.group(1), + f'[policy.{crate}] must mark the workspace copy first-party') diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 65712ce43..136ebe047 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -261,6 +261,12 @@ criteria = "safe-to-deploy" delta = "3.1.0 -> 3.1.1" notes = "Reviewed complete package delta: dependency feature metadata only; interpreter source unchanged." +[[audits.jaq-core]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.1.1 -> 3.1.2" +notes = 'Fixes a lexer panic: when a comment ended the input, the comment scanner replaced the remaining input with a "" literal, breaking the invariant that the replacement is a suffix of the previous input; it now takes the empty suffix at the end of the input, and the same change writes that invariant down on the field. Remaining delta is a new example, one test and a README note. unsafe count unchanged at 1; no new ambient access.' + [[audits.jaq-json]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -273,6 +279,12 @@ criteria = "safe-to-deploy" delta = "3.0.2 -> 3.0.3" notes = "Reviewed no_std float rounding with bounded IEEE754 bit operations; std keeps native floor/round/ceil. Corrected import feature gates; no unsafe or ambient changes." +[[audits.jaq-std]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.0.3 -> 3.0.4" +notes = "jq-level definitions only (src/defs.jq): scan becomes scan_ and gains jq's semantics, applying the g flag automatically and yielding captures rather than the whole match when the regex has capture groups, with a new scan(re; flags) wrapper. No Rust change, no unsafe, no ambient access." + [[audits.jiter]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -347,6 +359,17 @@ criteria = "safe-to-deploy" delta = "3.14.0 -> 3.14.2" notes = "Reviewed complete published package diff across 3.14.1 and 3.14.2: no new dependencies, no build scripts, no new host filesystem, network or process access. Four upstream fixes, all hardening. NativeBorrowScope now owns its storage through NonNull instead of aliasing a reference, removing an aliasing violation. serde_json::Value conversion is bounded at 128 levels of nesting, so deeply nested JSON from a caller can no longer recurse without limit - the change that matters most for this tree, where the binding converts untrusted input. The wasm cleanup is registered through the env cleanup hook rather than ad hoc. Object creation uses napi_define_properties on every Node version. Existing unsafe in the js_values conversions is refactored in place; no new unsafe blocks." +[[audits.napi]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.14.2 -> 3.15.0" +notes = """ +Reviewed the complete published delta (24 files, ~2700 lines). No new ambient capability: no std::fs, std::net or std::env appears, std::process is unchanged at 8 occurrences, build.rs is byte-identical, and the only Cargo.toml change is the napi-sys 3.4.1 bump, so no new dependency. + +New here, and the reason to look twice: napi_run_script compiles FOLLOW_LIKE_AWAIT to follow a cancel result exactly as await does. The source is a three-line async arrow held in a CStr constant with no interpolation, so there is no injection surface; it runs only on the ReadableStream cancel path, and where eval is blocked the future resolves to Err. + +The rest is hardening. New class_identity.rs records the wrapped Rust class in the REFERENCE_MAP entry and checks it before casting a napi_unwrap result to *mut T on builds without Node-API type tags, which is every build bashkit-js makes since it selects napi6, turning a type-confusion cast into InvalidArg. TypedArray views now scale their byte slice by element size instead of treating the element count as a byte count, so a multi-byte view is no longer truncated. native_borrow gains deferred scopes with lease counting and hands roots to the owning env's custom-GC threadsafe function instead of calling napi_delete_reference off-thread; its two new unsafe impl Send/Sync carry raw handles that are only dereferenced on the owner JavaScript thread or through that tsfn. New unsafe is N-API handle calls plus three extern "C" promise-settle trampolines, and the three added raw-pointer sites are slice/Vec reconstruction over N-API buffer data.""" + [[audits.napi-build]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -389,6 +412,12 @@ criteria = "safe-to-deploy" delta = "3.6.9 -> 3.6.12" notes = "Reviewed syn 3 parser migration: function arguments read their type field, new AST attributes initialized and non-exhaustive path fields ignored. No new unsafe or build-time execution." +[[audits.napi-derive]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.6.12 -> 3.6.13" +notes = "No source changes; the release only bumps the napi-derive-backend dependency to 6.1.6 in Cargo.toml." + [[audits.napi-derive-backend]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -407,6 +436,12 @@ criteria = "safe-to-deploy" delta = "6.1.4 -> 6.1.5" notes = "Reviewed syn 3 AST adaptation and generated enum property flags changed to writable/enumerable/configurable. No new unsafe or external execution." +[[audits.napi-derive-backend]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "6.1.5 -> 6.1.6" +notes = "Codegen only. Borrow scopes become deferred: a synchronous callback records its borrowed values and hands the scope to whichever async sink claims it during return-value conversion, with a guard that releases it on Err, throw or panic; the zero-argument fast path emits no defer because it builds no CallbackInfo. Class instance wraps replace a 0x1 wrapped pointer with a real one-byte allocation so zero-sized classes cannot share the dangling key napi's new class-identity check keys on. No new unsafe (74 occurrences unchanged), no ambient access." + [[audits.napi-sys]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -419,6 +454,12 @@ criteria = "safe-to-deploy" delta = "3.3.2 -> 3.4.0" notes = "Reviewed exact-target build cfg, atomic shared-memory refresh and worker-crash flag modules. Checked pointer/page bounds and target gating; native code unchanged outside tests." +[[audits.napi-sys]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.4.0 -> 3.4.1" +notes = "Doc-comment only: the wasi_heap_sync design-note reference moves from rolldown's internal docs to cli/docs/wasi-heap-sync-design.md. No code, FFI declaration or build-script change." + [[audits.num-bigint]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 1937c57e0..92f834f8c 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -30,6 +30,10 @@ criteria = "safe-to-deploy" audit-as-crates-io = false criteria = "safe-to-deploy" +[policy.bashkit-scripted-tool] +audit-as-crates-io = false +criteria = "safe-to-deploy" + [policy.bashkit-wasm] audit-as-crates-io = false criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 79d093ff1..7e1e65c9f 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -112,8 +112,8 @@ user-login = "epage" user-name = "Ed Page" [[publisher.tokio]] -version = "1.53.3" -when = "2026-10-13" +version = "1.54.1" +when = "2026-10-21" user-id = 6741 user-login = "Darksonn" user-name = "Alice Ryhl" From 2e6875628ee29fc9c5635a83215f58ef000ce8d4 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Tue, 27 Oct 2026 09:53:14 +0000 Subject: [PATCH 2/2] chore(knowledge): record the first-party cargo-vet policy rule The durable rule belongs in the Dependencies concept, not only in the log: a workspace crate published to crates.io needs `audit-as-crates-io = false`, the same `policy.*` mechanism the pinned `jiter` package uses from the other direction. Names the quiet failure mode (CI's `--locked` run never reaches the collision check) and the guard that now covers it. --- knowledge/operations/dependencies.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/knowledge/operations/dependencies.md b/knowledge/operations/dependencies.md index cfc18a3b6..5521389c2 100644 --- a/knowledge/operations/dependencies.md +++ b/knowledge/operations/dependencies.md @@ -84,6 +84,21 @@ and failed with `feature-banned` after it, while the workspace still passed. Cargo-deny's [feature restrictions](https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html#the-features-field-optional) enforce this audit boundary in CI. +A workspace crate that is published to crates.io needs the same `policy.*` +entry from the other direction: `audit-as-crates-io = false`. cargo-vet calls a +member first-party by provenance -- it was not fetched from crates.io -- so once +the same name and version is also published, the two descriptions collide and +`cargo vet` refuses to run until an entry says which to believe. The gap is +quiet where it matters: CI runs `cargo vet --locked`, which never reaches that +check, so the Audit job stays green while the command is broken for every +developer and for refreshing `supply-chain/imports.lock` -- the non-locked run +is the only way to record publisher facts, which `[[trusted.*]]` crates such as +`tokio` need on every bump. `bashkit-scripted-tool` went public in 0.20.0 with +no entry and nothing failed until #2792 needed that refresh. +`scripts/tests/test_ci_supply_chain.py` derives the published set from the +`release-check` recipe and fails when one of those crates has no entry; a new +published crate must be added in both places. + `get-size2` is constrained to `=0.10.1` as a Python-only direct dependency, so downstream resolution is protected even without this workspace's lockfile. Both 0.10.2 and 0.10.3 implement `GetSize` for `compact_str` 0.10, whereas