diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e570ae..f6c0e60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: run: SKIP_CIRCUIT_BUILD=1 cargo doc --locked --no-deps --document-private-items security-audit: - name: 🔐 Security Audit (non-blocking) + name: 🔐 Security Audit needs: fast-checks runs-on: ubuntu-latest timeout-minutes: 10 @@ -132,11 +132,8 @@ jobs: if ! command -v cargo-audit >/dev/null 2>&1; then cargo install cargo-audit --locked --version 0.22.1 fi - - name: Run cargo audit (informational only) - # Only this step is non-blocking — every other step in this job - # (checkout, caches, cargo-audit install) must fail loudly so we - # don't silently skip the audit. - continue-on-error: true + # Blocking so advisories fail PRs/main before the release publish workflow. + - name: Run cargo audit run: cargo audit examples: diff --git a/.github/workflows/create-release-tag-and-publish.yml b/.github/workflows/create-release-tag-and-publish.yml index 383a062..27bbf83 100644 --- a/.github/workflows/create-release-tag-and-publish.yml +++ b/.github/workflows/create-release-tag-and-publish.yml @@ -146,7 +146,7 @@ jobs: ref: ${{ needs.create-tag.outputs.version }} - name: Install cargo-audit - run: cargo install cargo-audit --locked + run: cargo install cargo-audit --locked --version 0.22.1 - name: Run security audit run: cargo audit diff --git a/Cargo.lock b/Cargo.lock index dcb8bfa..8938890 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,15 +155,6 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "argon2" version = "0.5.3" @@ -650,6 +641,29 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -875,6 +889,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -982,6 +998,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -1364,17 +1389,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "derive_more" version = "1.0.0" @@ -1513,6 +1527,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -1672,11 +1692,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -1877,6 +1896,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -2711,6 +2736,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -2739,6 +2794,16 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.103" @@ -2779,7 +2844,7 @@ dependencies = [ "pin-project", "rustls", "rustls-pki-types", - "rustls-platform-verifier", + "rustls-platform-verifier 0.5.3", "soketto", "thiserror 1.0.69", "tokio", @@ -2829,7 +2894,7 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-types", "rustls", - "rustls-platform-verifier", + "rustls-platform-verifier 0.5.3", "serde", "serde_json", "thiserror 1.0.69", @@ -3730,7 +3795,7 @@ dependencies = [ "hex", "hmac 0.12.1", "pbkdf2", - "reqwest", + "reqwest 0.12.28", "sha2 0.10.9", ] @@ -3968,6 +4033,7 @@ dependencies = [ "clap", "colored", "dirs", + "event-listener", "hex", "indicatif", "jsonrpsee", @@ -3989,9 +4055,10 @@ dependencies = [ "qp-zk-circuits-common", "quinn-proto", "rand 0.9.5", - "reqwest", + "reqwest 0.12.28", "rpassword", "rustls-webpki", + "self-replace", "self_update", "serde", "serde_json", @@ -4007,15 +4074,6 @@ dependencies = [ "toml 0.9.12+spec-1.1.0", ] -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", -] - [[package]] name = "quinn" version = "0.11.11" @@ -4042,6 +4100,7 @@ version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ + "aws-lc-rs", "bytes", "fastbloom", "getrandom 0.4.3", @@ -4320,6 +4379,46 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier 0.7.0", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower 0.5.3", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4411,6 +4510,7 @@ version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -4450,7 +4550,7 @@ checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.21.1", "log", "once_cell", "rustls", @@ -4463,6 +4563,27 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs 1.0.9", + "windows-sys 0.61.2", +] + [[package]] name = "rustls-platform-verifier-android" version = "0.1.1" @@ -4475,6 +4596,7 @@ version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -4765,18 +4887,17 @@ dependencies = [ [[package]] name = "self_update" -version = "0.43.1" +version = "1.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6644febaa58f323b28f7321d04e24d0020d117c27619ab869d6abdf76be9aac6" +checksum = "5b7cc0b87814417b0e0697ae582dfc5f614f469fbac9b138e40457d88913187d" dependencies = [ "either", "flate2", "http", "indicatif", "log", - "quick-xml", "regex", - "reqwest", + "reqwest 0.13.4", "self-replace", "semver", "serde", @@ -5005,6 +5126,22 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simple-mermaid" version = "0.1.1" @@ -5946,6 +6083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", + "js-sys", "num-conv", "powerfmt", "serde_core", @@ -6352,6 +6490,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9" +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typenum" version = "1.20.1" @@ -7233,24 +7377,24 @@ dependencies = [ [[package]] name = "zip" -version = "6.0.0" +version = "8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" dependencies = [ - "arbitrary", "crc32fast", "flate2", "indexmap", "memchr", "time", + "typed-path", "zopfli", ] [[package]] name = "zipsign-api" -version = "0.1.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba6063ff82cdbd9a765add16d369abe81e520f836054e997c2db217ceca40c0" +checksum = "32a55ebb27e67d9a9d116dd3a19637ee8cc0570c8ef816fb504c453f15448c99" dependencies = [ "base64", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index ae1f73e..021e4f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,17 +60,21 @@ blake3 = "1.8" reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false } # Self-update: download and replace the running binary from GitHub releases. -# Pinned to 0.43.x: it shares the same reqwest (0.12) and indicatif (0.18) as the -# rest of the crate, avoiding duplicate dependencies. The only change in 0.44 is a -# bump to reqwest 0.13, which would force a second reqwest build with no real gain. -self_update = { version = "0.43", default-features = false, features = [ +# 1.0.0-rc.x is required for quick-xml >= 0.41 (RUSTSEC-2026-0194 / 0195); stable +# 0.44 still pins quick-xml ^0.38. Pulls reqwest 0.13 alongside our 0.12. +self_update = { version = "1.0.0-rc.6", default-features = false, features = [ "archive-tar", "archive-zip", - "compression-flate2", + "compression-tar-gz", "compression-zip-deflate", + "github", + "progress-bar", "reqwest", "rustls", ] } +# 1.0 stopped re-exporting these; the update flow uses them directly. +self-replace = "1" +tempfile = "3" # Force patched version of bytes (RUSTSEC-2026-0007) bytes = "1.11.1" @@ -81,6 +85,9 @@ quinn-proto = "0.11.14" # Force patched version of rustls-webpki (RUSTSEC-2026-0098, RUSTSEC-2026-0099, RUSTSEC-2026-0104) rustls-webpki = "0.103.13" +# Force patched event-listener (RUSTSEC-2026-0221); smoldot allows ^5.3 +event-listener = "5.4.2" + # Blockchain deps: align with chain workspace codec = { package = "parity-scale-codec", version = "3.7", features = ["derive"] } jsonrpsee = { version = "0.24", features = ["client"] } diff --git a/src/cli/update.rs b/src/cli/update.rs index 2140218..066115a 100644 --- a/src/cli/update.rs +++ b/src/cli/update.rs @@ -109,7 +109,7 @@ fn configure_updater() -> self_update::backends::github::UpdateBuilder { // `{{ version }}` is substituted without the leading `v`, so it is // added back as a literal here. .bin_path_in_archive("quantus-cli-v{{ version }}-{{ target }}/{{ bin }}") - .identifier(ASSET_IDENTIFIER) + .asset_identifier(ASSET_IDENTIFIER) .current_version(env!("CARGO_PKG_VERSION")); builder } @@ -120,12 +120,15 @@ fn configure_updater() -> self_update::backends::github::UpdateBuilder { /// Blocking: `self_update` performs synchronous I/O, so call this off the async /// runtime's worker threads (e.g. via `spawn_blocking`). pub fn latest_stable_version() -> crate::error::Result { - let release = configure_updater() + let releases = configure_updater() .build() .map_err(map_self_update_err)? .get_latest_release() .map_err(map_self_update_err)?; - Ok(release.version.trim_start_matches('v').to_string()) + let release = releases.latest().ok_or_else(|| { + QuantusError::Generic("No GitHub releases found for Quantus CLI".to_string()) + })?; + Ok(release.version().trim_start_matches('v').to_string()) } /// Semver comparison that surfaces unparseable release tags as errors instead @@ -222,54 +225,57 @@ fn run_update( let target_tag = version.map(|v| if v.starts_with('v') { v } else { format!("v{v}") }); if let Some(ref tag) = target_tag { - builder.target_version_tag(tag); + builder.release_tag(tag); } let updater = builder.build().map_err(map_self_update_err)?; let release = if let Some(ref tag) = target_tag { updater.get_release_version(tag).map_err(map_self_update_err)? } else { - let latest = updater.get_latest_release().map_err(map_self_update_err)?; - if !version_is_newer(current, &latest.version)? { - return Ok(UpdateOutcome::AlreadyLatest(latest.version)); + let releases = updater.get_latest_release().map_err(map_self_update_err)?; + let latest = releases.latest().cloned().ok_or_else(|| { + QuantusError::Generic("No GitHub releases found for Quantus CLI".to_string()) + })?; + if !version_is_newer(current, latest.version())? { + return Ok(UpdateOutcome::AlreadyLatest(latest.version().to_string())); } latest }; - install_verified_release(updater.as_ref(), &release, yes)?; - Ok(UpdateOutcome::Updated(release.version)) + install_verified_release(&updater, &release, yes)?; + Ok(UpdateOutcome::Updated(release.version().to_string())) } /// Download the release archive and its published sha256sums, verify integrity, /// then extract and replace the running binary. fn install_verified_release( - updater: &dyn self_update::update::ReleaseUpdate, + updater: &impl self_update::update::ReleaseUpdate, release: &self_update::update::Release, yes: bool, ) -> crate::error::Result<()> { let target = updater.target(); - let archive_asset = release.asset_for(&target, Some(ASSET_IDENTIFIER)).ok_or_else(|| { + let archive_asset = release.asset_for(target, Some(ASSET_IDENTIFIER)).ok_or_else(|| { QuantusError::Generic(format!( "No release archive found for target `{target}` (looking for {ASSET_IDENTIFIER})" )) })?; let sums_asset = release - .assets + .assets() .iter() - .find(|a| a.name.contains("sha256sums") && a.name.contains(&target)) + .find(|a| a.name().contains("sha256sums") && a.name().contains(target)) .cloned() .ok_or_else(|| { QuantusError::Generic(format!( "No sha256sums asset found for target `{target}` in release v{}", - release.version + release.version() )) })?; log_print!(""); log_print!("{} release status:", BIN_NAME); log_print!(" * Current exe: {:?}", updater.bin_install_path()); - log_print!(" * New exe release: {}", archive_asset.name); - log_print!(" * Checksum file: {}", sums_asset.name); + log_print!(" * New exe release: {}", archive_asset.name()); + log_print!(" * Checksum file: {}", sums_asset.name()); log_print!( "\nThe new release will be downloaded, SHA-256 verified, extracted, and the existing binary will be replaced." ); @@ -280,15 +286,15 @@ fn install_verified_release( log_print!("Downloading checksums..."); let mut sums_bytes = Vec::new(); - download_asset(&sums_asset.download_url, &mut sums_bytes, MAX_SUMS_ASSET_BYTES, false)?; + download_asset(sums_asset.download_url(), &mut sums_bytes, MAX_SUMS_ASSET_BYTES, false)?; let sums_text = std::str::from_utf8(&sums_bytes).map_err(|e| { QuantusError::Generic(format!("Release sha256sums file is not valid UTF-8: {e}")) })?; - let expected_hex = expected_hash_from_sha256sums(sums_text, &archive_asset.name)?; + let expected_hex = expected_hash_from_sha256sums(sums_text, archive_asset.name())?; - let tmp_dir = self_update::TempDir::new() + let tmp_dir = tempfile::TempDir::new() .map_err(|e| QuantusError::Generic(format!("Failed to create temp dir for update: {e}")))?; - let archive_path = tmp_dir.path().join(&archive_asset.name); + let archive_path = tmp_dir.path().join(archive_asset.name()); log_print!("Downloading..."); { @@ -296,7 +302,7 @@ fn install_verified_release( QuantusError::Generic(format!("Failed to create temp archive file: {e}")) })?; download_asset( - &archive_asset.download_url, + archive_asset.download_url(), &mut archive_file, MAX_ARCHIVE_ASSET_BYTES, true, @@ -313,10 +319,10 @@ fn install_verified_release( log_print!(" Checksum OK."); let bin_path = substitute_bin_path( - &updater.bin_path_in_archive(), - &release.version, - &target, - &updater.bin_name(), + updater.bin_path_in_archive(), + release.version(), + target, + updater.bin_name(), ); log_print!("Extracting archive..."); @@ -332,11 +338,11 @@ fn install_verified_release( QuantusError::Generic(format!("Failed to resolve current executable path: {e}")) })?; if install_path == current_exe { - self_update::self_replace::self_replace(&new_exe) + self_replace::self_replace(&new_exe) .map_err(|e| QuantusError::Generic(format!("Failed to replace running binary: {e}")))?; } else { self_update::Move::from_source(&new_exe) - .to_dest(&install_path) + .to_dest(install_path) .map_err(map_self_update_err)?; } @@ -400,11 +406,8 @@ fn download_asset( let mut limited = LimitedWriter::new(dest, max_bytes); let mut download = self_update::Download::from_url(url); download - .set_header( - reqwest::header::ACCEPT, - "application/octet-stream".parse().expect("static ACCEPT header"), - ) - .show_progress(show_progress); + .request_header(reqwest::header::ACCEPT, "application/octet-stream") + .show_download_progress(show_progress); download.download_to(&mut limited).map_err(map_self_update_err) }