From a858c78623696dda6802a0440a7147984a5c4317 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 04:22:12 +0000 Subject: [PATCH 1/3] Start fix for #415 Assisted-by: Claude Code:claude-opus-5-5 From 7376eb5d6452e37b8ed116ae696fe1c075eab713 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 04:27:10 +0000 Subject: [PATCH 2/3] Find pipx-installed tools in global scans scan -g, apply -g, rollback -g and vex -g never looked inside pipx's per-app venvs, so the dependencies of a pipx-installed tool such as Hatch were never reported or patched on any OS. Global discovery now scans /venvs/* under PIPX_HOME and every pipx default home. Fixes #415 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/src/commands/apply.rs | 4 +- crates/socket-patch-cli/src/commands/list.rs | 15 +- crates/socket-patch-cli/src/commands/mod.rs | 22 +- .../socket-patch-cli/src/commands/remove.rs | 2 +- .../socket-patch-cli/src/commands/rollback.rs | 11 +- .../src/commands/scan/hosted.rs | 54 +- .../socket-patch-cli/src/commands/scan/mod.rs | 89 ++-- .../src/commands/scan/policy.rs | 68 ++- .../src/commands/scan/render.rs | 5 +- .../src/commands/scan/rollout.rs | 20 +- .../src/commands/scan/rollout_args.rs | 2 - .../tests/apply/apply_network.rs | 10 +- .../apply/in_process_gem_config_warning.rs | 4 +- .../tests/cli/covgap_output.rs | 10 +- .../tests/cli/interactive_prompts_e2e.rs | 5 +- .../tests/cli_config_fallback.rs | 7 +- .../socket-patch-cli/tests/cli_get_silent.rs | 5 +- .../socket-patch-cli/tests/cli_parse_list.rs | 11 +- .../tests/cli_parse_rollback.rs | 6 +- .../socket-patch-cli/tests/cli_parse_scan.rs | 29 +- .../coverage_fix_apply_silent_mute_exit.rs | 4 +- .../tests/covgap_commands_scan_hosted.rs | 42 +- .../tests/covgap_commands_scan_mod.rs | 9 +- crates/socket-patch-cli/tests/e2e_cargo.rs | 6 +- crates/socket-patch-cli/tests/e2e_gem.rs | 6 +- crates/socket-patch-cli/tests/e2e_maven.rs | 3 +- crates/socket-patch-cli/tests/e2e_npm.rs | 6 +- crates/socket-patch-cli/tests/e2e_nuget.rs | 6 +- crates/socket-patch-cli/tests/e2e_pypi.rs | 6 +- .../tests/e2e_redirect_gem_stale_install.rs | 3 +- .../tests/e2e_safety_cargo_build.rs | 6 +- .../socket-patch-cli/tests/e2e_safety_pnpm.rs | 18 +- .../tests/e2e_socket_yml_policy.rs | 471 ++++++++++++++---- .../tests/e2e_vex_lockfile/common_selftest.rs | 6 +- .../tests/get/get_edge_cases_e2e.rs | 12 +- .../tests/get/global_packages_e2e.rs | 5 +- .../tests/help_text_hygiene.rs | 31 +- .../tests/hosted_memory_engine.rs | 3 +- .../tests/hosted_memory_parity.rs | 183 +++++-- .../tests/hosted_memory_rollout.rs | 42 +- .../tests/in_process_get_hosted_ecosystems.rs | 12 +- .../tests/in_process_redirect/vlt.rs | 10 +- .../tests/in_process_redirect_pdm.rs | 30 +- .../tests/in_process_redirect_pipenv.rs | 73 ++- .../tests/in_process_redirect_pnpm.rs | 11 +- .../tests/repair_vendor_flavors_e2e/vlt.rs | 5 +- .../rollback/rollback_duality_invariants.rs | 3 +- .../tests/scan/covgap_ecosystem_dispatch.rs | 8 +- .../tests/scan/scan_invariants.rs | 20 +- .../tests/scan/scan_paths_e2e.rs | 12 +- .../tests/update/covgap_commands_update.rs | 8 +- .../tests/yarn_berry_common/mod.rs | 4 +- crates/socket-patch-core/src/api/ranking.rs | 17 +- .../src/crawlers/npm_crawler.rs | 6 +- .../src/crawlers/npm_crawler/oracle.rs | 6 +- .../src/crawlers/python_crawler.rs | 72 ++- .../src/formats/cargo/mod.rs | 12 +- .../src/formats/composer/hosted.rs | 2 +- .../src/formats/composer/mod.rs | 3 - .../src/formats/gem/hosted.rs | 1 - .../socket-patch-core/src/formats/gem/mod.rs | 2 - crates/socket-patch-core/src/formats/mod.rs | 8 +- .../socket-patch-core/src/formats/pnpm/mod.rs | 76 ++- .../socket-patch-core/src/formats/registry.rs | 18 +- .../socket-patch-core/src/formats/yarn/mod.rs | 5 +- .../socket-patch-core/src/hosted/guidance.rs | 10 +- .../src/hosted/memory/discover.rs | 4 +- .../src/hosted/memory/limits.rs | 12 +- .../src/hosted/memory/mod.rs | 89 ++-- .../src/hosted/memory/roots.rs | 22 +- .../src/hosted/memory/select.rs | 21 +- .../src/hosted/memory/types.rs | 4 +- crates/socket-patch-core/src/ledgers.rs | 1 - crates/socket-patch-core/src/lib.rs | 1 - .../socket-patch-core/src/manifest/records.rs | 5 +- .../redirect/cargo_lock_equivalence_tests.rs | 4 +- .../redirect/golang_equivalence_tests.rs | 6 +- .../patch/redirect/group_equivalence_tests.rs | 7 +- .../src/patch/redirect/mod.rs | 52 +- .../src/patch/redirect/npmrc.rs | 3 - .../src/patch/redirect/pdm.rs | 21 +- .../src/patch/redirect/pipenv.rs | 45 +- .../src/patch/redirect/poetry.rs | 22 +- .../src/patch/redirect/requirements.rs | 4 +- .../src/patch/redirect/state.rs | 2 - .../src/patch/redirect/upstream/bun_lockb.rs | 5 +- .../src/patch/redirect/upstream/cargo.rs | 39 +- .../src/patch/redirect/upstream/composer.rs | 4 +- .../src/patch/redirect/upstream/gem.rs | 23 +- .../src/patch/redirect/upstream/golang.rs | 9 +- .../src/patch/redirect/upstream/mod.rs | 8 +- .../src/patch/redirect/upstream/pypi_locks.rs | 11 +- .../src/patch/redirect/vlt.rs | 1 - crates/socket-patch-core/src/policy/mod.rs | 7 +- crates/socket-patch-core/src/policy/report.rs | 4 +- .../src/policy/socket_yml.rs | 27 +- crates/socket-patch-core/src/policy/tests.rs | 29 +- crates/socket-patch-core/src/rollout/stage.rs | 11 +- crates/socket-patch-core/src/telemetry.rs | 4 +- .../socket-patch-core/src/update/download.rs | 13 +- .../socket-patch-core/src/update/release.rs | 39 +- .../src/utils/group_commit.rs | 21 +- crates/socket-patch-core/src/utils/hatch.rs | 3 +- .../src/utils/line_endings.rs | 1 - crates/socket-patch-core/src/utils/mod.rs | 2 +- .../src/utils/python_script.rs | 7 +- .../src/vendor/bun_lock_text.rs | 1 - .../socket-patch-core/src/vendor/bun_lockb.rs | 9 +- .../src/vendor/cargo_lock.rs | 4 +- .../src/vendor/lock_inventory/pypi.rs | 12 +- .../src/vendor/lock_inventory/view.rs | 4 +- .../src/vendor/lock_inventory/vlt.rs | 2 +- .../src/vendor/lock_inventory/wired.rs | 2 +- .../socket-patch-core/src/vendor/prestage.rs | 5 +- .../src/vendor/toml_surgery.rs | 3 +- .../src/vex/discover/cargo.rs | 29 +- .../socket-patch-core/src/vex/discover/gem.rs | 2 +- .../src/vex/discover/maven.rs | 8 +- .../src/vex/discover/nuget.rs | 2 +- .../tests/covgap_api_blob_fetcher.rs | 5 +- .../tests/covgap_crawlers_composer_crawler.rs | 6 +- .../tests/crawler_python_e2e.rs | 185 +++++++ .../tests/hosted_inventory.rs | 10 +- .../socket-patch-core/tests/poetry_hosted.rs | 81 ++- .../tests/telemetry_helpers_e2e.rs | 6 +- .../tests/upstream_restore_golden.rs | 395 ++++++++++++--- crates/socket-patch-core/tests/uv_hosted.rs | 4 +- crates/socket-patch-node/src/lib.rs | 6 +- 128 files changed, 2298 insertions(+), 757 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/apply.rs b/crates/socket-patch-cli/src/commands/apply.rs index 761d830cf..b2207e43c 100644 --- a/crates/socket-patch-cli/src/commands/apply.rs +++ b/crates/socket-patch-cli/src/commands/apply.rs @@ -2,9 +2,7 @@ use clap::Args; use socket_patch_core::api::blob_fetcher::get_missing_blobs; use socket_patch_core::api::client::{get_api_client_with_overrides, ApiClient}; use socket_patch_core::crawlers::ruby_crawler::config_path_ignored_warning; -use socket_patch_core::crawlers::{ - detect_npm_pkg_manager, Ecosystem, NpmPkgManager, RubyCrawler, -}; +use socket_patch_core::crawlers::{detect_npm_pkg_manager, Ecosystem, NpmPkgManager, RubyCrawler}; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::{PatchFileInfo, PatchManifest, PatchRecord}; use socket_patch_core::patch::apply::{ diff --git a/crates/socket-patch-cli/src/commands/list.rs b/crates/socket-patch-cli/src/commands/list.rs index 8fc77fab1..44c719038 100644 --- a/crates/socket-patch-cli/src/commands/list.rs +++ b/crates/socket-patch-cli/src/commands/list.rs @@ -431,7 +431,10 @@ pub async fn run(args: ListArgs) -> i32 { detail: detail.clone(), }); } else if !args.common.silent { - eprintln!("Warning: {}", crate::commands::rollback::capitalize_first(detail)); + eprintln!( + "Warning: {}", + crate::commands::rollback::capitalize_first(detail) + ); } } let vendor_state = crate::commands::vendor_state_lenient(&loaded.vendor, args.common.silent); @@ -773,12 +776,18 @@ mod tests { let listings = HostedListing::from_pins( &[ pin("pkg:npm/minimist@1.2.2", &record.uuid), - pin("pkg:npm/other@1.0.0", "33333333-3333-4333-8333-333333333333"), + pin( + "pkg:npm/other@1.0.0", + "33333333-3333-4333-8333-333333333333", + ), ], Some(&legacy), ); assert_eq!(listings[0].record, record); - assert_eq!(listings[1].record.uuid, "33333333-3333-4333-8333-333333333333"); + assert_eq!( + listings[1].record.uuid, + "33333333-3333-4333-8333-333333333333" + ); assert!(listings[1].record.vulnerabilities.is_empty()); assert_eq!(listings[1].lockfiles, vec!["yarn.lock".to_string()]); } diff --git a/crates/socket-patch-cli/src/commands/mod.rs b/crates/socket-patch-cli/src/commands/mod.rs index 7099e4a60..c9750247a 100644 --- a/crates/socket-patch-cli/src/commands/mod.rs +++ b/crates/socket-patch-cli/src/commands/mod.rs @@ -1,7 +1,7 @@ pub mod apply; pub(crate) mod bun_preflight; -pub(crate) mod context; pub(crate) mod composer_hints; +pub(crate) mod context; pub(crate) mod fetch_stage; pub mod get; pub mod hosted_bundle; @@ -9,11 +9,11 @@ pub mod list; pub(crate) mod lock_cli; pub mod remove; pub mod repair; -pub(crate) mod vendored_backend; pub mod rollback; pub mod scan; pub mod update; pub mod vendor; +pub(crate) mod vendored_backend; pub mod vex; pub(crate) mod vex_consumed; pub(crate) mod vex_sources; @@ -97,9 +97,11 @@ pub(crate) async fn hosted_state_from_lockfiles( common: &crate::args::GlobalArgs, root: &Path, ) -> socket_patch_core::patch::redirect::RedirectState { - hosted_state_from_pins(&socket_patch_core::patch::redirect::upstream::HostedPin::all( - &discover_wiring(common, root).await, - )) + hosted_state_from_pins( + &socket_patch_core::patch::redirect::upstream::HostedPin::all( + &discover_wiring(common, root).await, + ), + ) } /// [`hosted_state_from_lockfiles`] over already-discovered pins. A purl @@ -109,10 +111,8 @@ pub(crate) fn hosted_state_from_pins( ) -> socket_patch_core::patch::redirect::RedirectState { let mut state = socket_patch_core::patch::redirect::RedirectState::new(); for pin in pins { - state - .records - .entry(pin.purl.clone()) - .or_insert_with(|| socket_patch_core::manifest::schema::PatchRecord { + state.records.entry(pin.purl.clone()).or_insert_with(|| { + socket_patch_core::manifest::schema::PatchRecord { uuid: pin.uuid.clone(), exported_at: String::new(), files: Default::default(), @@ -120,7 +120,8 @@ pub(crate) fn hosted_state_from_pins( description: String::new(), license: String::new(), tier: String::new(), - }); + } + }); } state } @@ -147,4 +148,3 @@ pub(crate) fn vendor_state_lenient( } } } - diff --git a/crates/socket-patch-cli/src/commands/remove.rs b/crates/socket-patch-cli/src/commands/remove.rs index 3287aa81a..c7bae9247 100644 --- a/crates/socket-patch-cli/src/commands/remove.rs +++ b/crates/socket-patch-cli/src/commands/remove.rs @@ -17,9 +17,9 @@ use super::rollback::{ pin_before_hash_blobs, rollback_patches_inner, run_hosted_leg, sweep_failure, sweep_unused_artifacts, HostedLegOutcome, InnerSelection, }; -use crate::commands::vendored_backend::{RevertedEntry, VendorRevertStep, VendoredBackend}; use crate::args::{apply_env_toggles, GlobalArgs}; use crate::commands::lock_cli::acquire_or_emit; +use crate::commands::vendored_backend::{RevertedEntry, VendorRevertStep, VendoredBackend}; use crate::json_envelope::{Command, Envelope, EnvelopeError, PatchAction, PatchEvent, Status}; use crate::ui::plural; diff --git a/crates/socket-patch-cli/src/commands/rollback.rs b/crates/socket-patch-cli/src/commands/rollback.rs index 05a00c7c7..97d35e16f 100644 --- a/crates/socket-patch-cli/src/commands/rollback.rs +++ b/crates/socket-patch-cli/src/commands/rollback.rs @@ -10,13 +10,13 @@ use socket_patch_core::manifest::operations::{ }; use socket_patch_core::manifest::schema::{PatchFileInfo, PatchManifest, PatchRecord}; use socket_patch_core::patch::apply::select_installed_variants; +use socket_patch_core::patch::redirect::upstream::HostedPin; use socket_patch_core::patch::rollback::{ cannot_rollback_error, rollback_package_patch, verify_file_rollback, RollbackResult, VerifyRollbackResult, VerifyRollbackStatus, }; use socket_patch_core::telemetry::{track_patch_rollback_failed, track_patch_rolled_back}; use socket_patch_core::utils::purl::{patch_matches, strip_purl_qualifiers}; -use socket_patch_core::patch::redirect::upstream::HostedPin; use socket_patch_core::vendor::{purl_keys_cover, RevertOpts, VendorState}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; @@ -1026,7 +1026,8 @@ pub(crate) async fn run_hosted_leg(common: &GlobalArgs, pins: &[HostedPin]) -> H .iter() .map(|(code, detail)| (code.to_string(), detail.clone())), ); - out.edited_files.extend(outcome.reverted_files.iter().cloned()); + out.edited_files + .extend(outcome.reverted_files.iter().cloned()); let unwound: Vec<_> = vlt_targets .into_iter() .filter(|t| out.reverted.iter().any(|p| p == &t.purl)) @@ -1157,7 +1158,11 @@ pub async fn run(args: RollbackArgs) -> i32 { } else if !args.common.silent { println!( "{} the pre-v5 hosted ledger {}: no lockfile pins a hosted patch.", - if args.common.dry_run { "Would remove" } else { "Removed" }, + if args.common.dry_run { + "Would remove" + } else { + "Removed" + }, socket_patch_core::patch::redirect::REDIRECT_STATE_REL ); } diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index d6031e784..290598f28 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -934,7 +934,8 @@ pub(crate) async fn run_redirect_selected( socket_patch_core::utils::fs::read_regular_to_string_sync(path).ok() }) }; - let rewrite_options = || RewriteOptions { + let rewrite_options = || { + RewriteOptions { dry_run: common.dry_run, targets_pipenv_lock, pipenv_major, @@ -946,6 +947,7 @@ pub(crate) async fn run_redirect_selected( npm_allow_remote_config: !common.no_npm_allow_remote_config, npm_outer: &npm_outer, blocking: true, + } }; // The rollout gate plans again without its deferred rows: keep what // the second pass needs. @@ -2171,13 +2173,19 @@ fn join_names(names: &[String], max: usize) -> String { /// artifacts, then verify with `vex`. After a vendored→hosted takeover /// (`vendored_removed`) the commit also has to carry the deleted vendored /// ledger entries and artifacts. -fn format_next_steps(files: &[String], edits: &[socket_patch_core::patch::redirect::FileEdit], vendored_removed: bool) -> Vec { +fn format_next_steps( + files: &[String], + edits: &[socket_patch_core::patch::redirect::FileEdit], + vendored_removed: bool, +) -> Vec { if files.is_empty() && !vendored_removed { return Vec::new(); } let mut commit: Vec = Vec::new(); if vendored_removed { - commit.push(".socket/vendor/ (the removed vendored ledger entries and artifacts)".to_string()); + commit.push( + ".socket/vendor/ (the removed vendored ledger entries and artifacts)".to_string(), + ); } commit.extend(files.iter().cloned()); let npm = files @@ -4095,19 +4103,43 @@ mod tests { use super::npm_allow_remote_one_line; let hosts = ["patch.socket.dev"]; let cases = [ - (npm_allow_remote_configured_detail(&hosts, true, false), "Note: set"), - (npm_allow_remote_configured_detail(&hosts, false, false), "Note: set"), - (npm_allow_remote_configured_detail(&hosts, true, true), "Note: would set"), - (npm_allow_remote_already_detail(&hosts), "Note: .npmrc already"), - (npm_allow_remote_user_set_detail(&hosts, "none"), "Warning: npm >=12"), - (npm_allow_remote_env_set_detail(&hosts, "npm_config_allow_remote", "none"), "Warning: npm >=12"), + ( + npm_allow_remote_configured_detail(&hosts, true, false), + "Note: set", + ), + ( + npm_allow_remote_configured_detail(&hosts, false, false), + "Note: set", + ), + ( + npm_allow_remote_configured_detail(&hosts, true, true), + "Note: would set", + ), + ( + npm_allow_remote_already_detail(&hosts), + "Note: .npmrc already", + ), + ( + npm_allow_remote_user_set_detail(&hosts, "none"), + "Warning: npm >=12", + ), + ( + npm_allow_remote_env_set_detail(&hosts, "npm_config_allow_remote", "none"), + "Warning: npm >=12", + ), (npm_allow_remote_manual_detail(&hosts), "Warning: npm >=12"), - (npm_allow_remote_unreadable_detail(&hosts, "is a symlink"), "Warning: npm >=12"), + ( + npm_allow_remote_unreadable_detail(&hosts, "is a symlink"), + "Warning: npm >=12", + ), ]; for (detail, start) in cases { let line = npm_allow_remote_one_line(&detail); assert!(line.starts_with(start), "{line}"); - assert!(!line.contains('\n') && line.ends_with("(details: --verbose)."), "{line}"); + assert!( + !line.contains('\n') && line.ends_with("(details: --verbose)."), + "{line}" + ); } } } diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index 26a1282d7..e1482bddf 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -35,17 +35,17 @@ use crate::ui::{self, plural, print_json, StatusLine}; use super::get::{download_and_apply_patches_with, DownloadParams, DownloadRun}; -pub use self::socket_yml_args::{SocketYmlArgs, MIN_SEVERITY_ENV}; use self::policy::{load_invocation_policy, InvocationPolicy, PolicyLoadError, ScanPolicy}; +pub use self::socket_yml_args::{SocketYmlArgs, MIN_SEVERITY_ENV}; mod discovery; mod gc; pub(crate) mod hosted; pub(crate) mod policy; -mod socket_yml_args; pub(crate) mod render; pub(crate) mod rollout; pub mod rollout_args; +mod socket_yml_args; pub(crate) mod vendor_flow; use self::discovery::{ @@ -65,13 +65,13 @@ use self::gc::gc_json; pub(crate) use self::hosted::boxed_run_redirect_selected; use self::hosted::run_redirect; pub(crate) use self::hosted::{vlt_rollback_heal, vlt_takeover_heal}; -pub(crate) use self::vendor_flow::{ - boxed_vendor_step, preview_vendor_json, print_dry_run_refusals, VendorStep, -}; use self::vendor_flow::{ boxed_vendor_interactive_path, boxed_vendor_json_path, fold_vendored_skips_into_apply, partition_skipped_selected, }; +pub(crate) use self::vendor_flow::{ + boxed_vendor_step, preview_vendor_json, print_dry_run_refusals, VendorStep, +}; /// Packages per batch request on the authenticated API when `--batch-size` /// is not given: the server's own per-request maximum @@ -234,9 +234,7 @@ pub fn resolve_mode_flags(args: &mut ScanArgs) -> Result<(), String> { // stays report-only (neither has a project lockfile to rewire). args.mode = Some(ScanMode::Hosted); } - if args.mode == Some(ScanMode::Hosted) - && args.common.is_global() - { + if args.mode == Some(ScanMode::Hosted) && args.common.is_global() { // Global installs have no project lockfile to repoint: the hosted // flow would "redirect 0 packages" and exit 0, a silent no-op. return Err(format!( @@ -325,11 +323,7 @@ pub struct ScanArgs { /// `requests`), or a purl with or without its version /// (`pkg:npm/lodash`, `pkg:pypi/requests@2.31.0`). Repeat the flag or /// separate with commas - #[arg( - long = "package", - env = "SOCKET_SCAN_PACKAGES", - value_delimiter = ',' - )] + #[arg(long = "package", env = "SOCKET_SCAN_PACKAGES", value_delimiter = ',')] pub packages: Vec, /// On a successful scan, also generate an OpenVEX 0.2.0 document. @@ -507,9 +501,10 @@ async fn discover_selected( telemetry.flush().await; let error_count = failures.len(); if error_count > 0 && error_count == packages.len() { - let err = failures - .last() - .map_or_else(|| "all patch-detail queries failed".to_string(), |(_, e)| e.clone()); + let err = failures.last().map_or_else( + || "all patch-detail queries failed".to_string(), + |(_, e)| e.clone(), + ); let message = format!("all {error_count} patch-detail queries failed: {err}"); if detail_error_line { eprintln!("{}", render::fetch_details_failed(&failures)); @@ -575,7 +570,11 @@ fn classified_rows( packages: &[BatchPackagePatches], result: Option<&mut serde_json::Value>, ) -> Vec { - let failed: Vec = discovered.failed.iter().map(|(purl, _)| purl.clone()).collect(); + let failed: Vec = discovered + .failed + .iter() + .map(|(purl, _)| purl.clone()) + .collect(); stage.incomplete = rollout::lookup_incomplete(&recorded.index, &failed, batch_failed); let rows = rollout::classify(&discovered.offers, &recorded.index, &stage.project); if let Some(result) = result { @@ -1324,7 +1323,8 @@ fn project_dirs(cwd: &Path, paths: &[String]) -> Result, St let joined = cwd.join(raw); if raw.contains(['*', '?', '[']) { let pattern = joined.to_string_lossy().into_owned(); - let matches = glob::glob(&pattern).map_err(|e| format!("invalid path pattern `{raw}`: {e}"))?; + let matches = + glob::glob(&pattern).map_err(|e| format!("invalid path pattern `{raw}`: {e}"))?; let before = dirs.len(); dirs.extend( matches @@ -1397,7 +1397,10 @@ async fn run_project_dirs( } // One budget per invocation (§5.2): the directories spend it in sorted // order, and a package admitted in one is admitted free in the next. - let configured = match args.rollout.resolve_from_env(invocation.policy.max_new_patches()) { + let configured = match args + .rollout + .resolve_from_env(invocation.policy.max_new_patches()) + { Ok(max) => max, Err(message) => { eprintln!("Error: {message}"); @@ -1498,7 +1501,10 @@ async fn run_scan( // error. let configured_cap = match args.rollout.carry.as_ref() { Some(carry) => carry.lock().configured, - None => match args.rollout.resolve_from_env(invocation.policy.max_new_patches()) { + None => match args + .rollout + .resolve_from_env(invocation.policy.max_new_patches()) + { Ok(max) => max, Err(message) => { eprintln!("Error: {message}"); @@ -1506,11 +1512,8 @@ async fn run_scan( } }, }; - let mut stage = rollout::Stage::new( - configured_cap, - args.rollout.carry.clone(), - &args.common.cwd, - ); + let mut stage = + rollout::Stage::new(configured_cap, args.rollout.carry.clone(), &args.common.cwd); // Strict airgap (CLI_CONTRACT.md `--offline`): scan's patch discovery // is remote data, so refuse before the crawl and before the API client @@ -1687,8 +1690,11 @@ async fn run_scan( .filter(|pkg| args.common.purl_ecosystem_selected(&pkg.purl)) .collect(); - let package_specs: Vec<&String> = - args.packages.iter().filter(|s| !s.trim().is_empty()).collect(); + let package_specs: Vec<&String> = args + .packages + .iter() + .filter(|s| !s.trim().is_empty()) + .collect(); let filtered_crawled: Vec<_> = if package_specs.is_empty() { filtered_crawled } else { @@ -1826,13 +1832,12 @@ async fn run_scan( // `redirectState` rides the empty-discovery envelope too // (same rule as the ≥1-package path). `wiringLive` is empty // by construction: this run covered zero packages. - let redirect_state = (!args.common.is_global()).then_some( - crate::commands::hosted_state_from_pins( + let redirect_state = + (!args.common.is_global()).then_some(crate::commands::hosted_state_from_pins( &socket_patch_core::patch::redirect::upstream::HostedPin::all( ctx.discovery().await, ), - ), - ); + )); if let Some(state) = redirect_state_json(redirect_state.as_ref(), &[]) { result["redirectState"] = state; } @@ -2188,7 +2193,8 @@ async fn run_scan( // A report-only run selects nothing, but a severity floor or // `enabled: false` still hides candidates; report them like the // human arm does (the detail fetch runs only then). - if !apply && !vendor && policy.reports_selection() && !all_packages_with_patches.is_empty() { + if !apply && !vendor && policy.reports_selection() && !all_packages_with_patches.is_empty() + { if let Err((code, message)) = discover_selected( &api_client, &all_packages_with_patches, @@ -2481,12 +2487,7 @@ async fn run_scan( &all_packages_with_patches, None, ); - updates = offer_updates( - &rows, - &discovered, - &recorded, - &all_packages_with_patches, - ); + updates = offer_updates(&rows, &discovered, &recorded, &all_packages_with_patches); rows } // `discover_selected` already printed the failure to stderr. @@ -2948,14 +2949,20 @@ mod tests { dirs.iter() .map(|(d, explicit)| { ( - d.strip_prefix(tmp.path()).unwrap().to_string_lossy().replace('\\', "/"), + d.strip_prefix(tmp.path()) + .unwrap() + .to_string_lossy() + .replace('\\', "/"), *explicit, ) }) .collect() }; - let got = project_dirs(tmp.path(), &["apps/*".into(), "libs/core".into(), "apps/web".into()]) - .unwrap(); + let got = project_dirs( + tmp.path(), + &["apps/*".into(), "libs/core".into(), "apps/web".into()], + ) + .unwrap(); // Named literally = explicit (also when a glob matches it too). assert_eq!( rel(got), diff --git a/crates/socket-patch-cli/src/commands/scan/policy.rs b/crates/socket-patch-cli/src/commands/scan/policy.rs index 0cd466bf5..21a0e51a6 100644 --- a/crates/socket-patch-cli/src/commands/scan/policy.rs +++ b/crates/socket-patch-cli/src/commands/scan/policy.rs @@ -11,9 +11,9 @@ use socket_patch_core::api::ranking::cmp_search_results; use socket_patch_core::api::types::PatchSearchResult; use socket_patch_core::manifest::schema::PatchManifest; use socket_patch_core::policy::{ - canon, find_repo_root_with_warnings, policy_block, FilteredEntry, RetainedEntry, patch_severity_order, repo_relative_checked, sanitize, severity_name, - DiskPolicyFs, FilterReason, Offers, PolicyError, PolicySource, PolicyWarning, Root, SelectionPolicy, - PATCHES_DISABLED, + canon, find_repo_root_with_warnings, patch_severity_order, policy_block, repo_relative_checked, + sanitize, severity_name, DiskPolicyFs, FilterReason, FilteredEntry, Offers, PolicyError, + PolicySource, PolicyWarning, RetainedEntry, Root, SelectionPolicy, PATCHES_DISABLED, }; use socket_patch_core::utils::purl::normalize_purl; @@ -42,12 +42,18 @@ pub(crate) struct InvocationPolicy { /// Load the policy for `args` (4.5): `--global` scans have no repo and read /// no file; everything else reads the repo root's socket.yml. pub(crate) fn load_invocation_policy(args: &ScanArgs) -> Result { - let overrides = args.socket_yml.overrides().map_err(PolicyLoadError::Usage)?; + let overrides = args + .socket_yml + .overrides() + .map_err(PolicyLoadError::Usage)?; let cwd = std::fs::canonicalize(&args.common.cwd).unwrap_or_else(|_| args.common.cwd.clone()); if args.common.is_global() { - let policy = SelectionPolicy::load(&socket_patch_core::policy::MemoryPolicyFs::default(), &overrides) - .map_err(PolicyLoadError::Policy)? - .0; + let policy = SelectionPolicy::load( + &socket_patch_core::policy::MemoryPolicyFs::default(), + &overrides, + ) + .map_err(PolicyLoadError::Policy)? + .0; return Ok(InvocationPolicy { policy, repo_root: cwd, @@ -56,8 +62,8 @@ pub(crate) fn load_invocation_policy(args: &ScanArgs) -> Result Self { + pub(crate) fn for_root( + invocation: &InvocationPolicy, + root_dir: &Path, + explicit: bool, + global: bool, + ) -> Self { let root_dir = std::fs::canonicalize(root_dir).unwrap_or_else(|_| root_dir.to_path_buf()); let project = repo_relative_checked(&invocation.repo_root, &root_dir).unwrap_or_default(); let root_verdict = if global { @@ -171,7 +182,9 @@ impl ScanPolicy { severity: None, }); } - let announce_warnings = !invocation.warned.swap(true, std::sync::atomic::Ordering::Relaxed); + let announce_warnings = !invocation + .warned + .swap(true, std::sync::atomic::Ordering::Relaxed); Self { policy: invocation.policy.clone(), warnings, @@ -224,7 +237,10 @@ impl ScanPolicy { /// exclude stays in the query (so `upgradeAvailable` can be reported) /// but joins the retained set, which never reaches a writer. pub(crate) fn admit_crawled(&self, purl: &str) -> bool { - let verdict = self.root_verdict.clone().and_then(|()| self.policy.admits_purl(purl)); + let verdict = self + .root_verdict + .clone() + .and_then(|()| self.policy.admits_purl(purl)); let reason = match verdict { Ok(()) => return true, Err(reason) => reason, @@ -334,7 +350,8 @@ impl ScanPolicy { // (not when a lower-ranked admitted patch simply wins). let top_withheld = self.policy.admits_severity(patch_severity_order(&group[0])); if let Err(reason) = top_withheld { - let upgrade_withheld = chosen.is_some() && chosen == recorded_at && recorded_at != Some(0); + let upgrade_withheld = + chosen.is_some() && chosen == recorded_at && recorded_at != Some(0); if chosen.is_none() || upgrade_withheld { report.filtered.push(FilteredEntry { purl: Some(canon(&purl)), @@ -522,17 +539,20 @@ pub(crate) fn policy_bypass_warnings( let verdict = if !policy.enabled() { Err(FilterReason::Disabled) } else { - root_verdict.clone().and_then(|()| policy.admits_purl(purl)).and_then(|()| { - // The floor only hides a package when none of its patches pass. - match group - .iter() - .map(|p| policy.admits_severity(patch_severity_order(p))) - .find(Result::is_ok) - { - Some(ok) => ok, - None => policy.admits_severity(patch_severity_order(group[0])), - } - }) + root_verdict + .clone() + .and_then(|()| policy.admits_purl(purl)) + .and_then(|()| { + // The floor only hides a package when none of its patches pass. + match group + .iter() + .map(|p| policy.admits_severity(patch_severity_order(p))) + .find(Result::is_ok) + { + Some(ok) => ok, + None => policy.admits_severity(patch_severity_order(group[0])), + } + }) }; if let Err(reason) = verdict { out.push(( diff --git a/crates/socket-patch-cli/src/commands/scan/render.rs b/crates/socket-patch-cli/src/commands/scan/render.rs index 12bfa5e8d..29215f69d 100644 --- a/crates/socket-patch-cli/src/commands/scan/render.rs +++ b/crates/socket-patch-cli/src/commands/scan/render.rs @@ -726,7 +726,10 @@ mod tests { #[test] fn report_only_hint_names_agent_mode() { - assert_eq!(report_only_hint()[0], "To apply these patches in place, run:"); + assert_eq!( + report_only_hint()[0], + "To apply these patches in place, run:" + ); assert!(report_only_hint()[1].contains("--mode agent")); } diff --git a/crates/socket-patch-cli/src/commands/scan/rollout.rs b/crates/socket-patch-cli/src/commands/scan/rollout.rs index 82ef99e17..fe7470a83 100644 --- a/crates/socket-patch-cli/src/commands/scan/rollout.rs +++ b/crates/socket-patch-cli/src/commands/scan/rollout.rs @@ -4,8 +4,10 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; -use socket_patch_core::rollout::{canonical_base_purl, severity_label, MaxNew, MaxNewSource, Recorded, RolloutPlan}; pub(crate) use socket_patch_core::rollout::stage::*; +use socket_patch_core::rollout::{ + canonical_base_purl, severity_label, MaxNew, MaxNewSource, Recorded, RolloutPlan, +}; use super::discovery::UpdateInfo; @@ -208,11 +210,11 @@ pub(crate) fn human_lines( mod tests { use super::*; use socket_patch_core::api::types::PatchSearchResult; - use socket_patch_core::manifest::schema::PatchManifest; - use std::path::Path; use socket_patch_core::api::types::VulnerabilityResponse; + use socket_patch_core::manifest::schema::PatchManifest; use socket_patch_core::manifest::schema::PatchRecord; use std::collections::HashMap; + use std::path::Path; fn offer(purl: &str, uuid: &str, published: &str, severities: &[&str]) -> PatchSearchResult { PatchSearchResult { @@ -357,13 +359,21 @@ mod tests { let stored = manifest(&[("pkg:composer/psr/log@3.0.2.0", "old")]); let recorded = RecordedIndex::new(Some(&stored), &[]); let offers = offers_from_results( - &[offer("pkg:composer/psr/log@v3.0.2", "new", "2026-02-01T00:00:00Z", &["high"])], + &[offer( + "pkg:composer/psr/log@v3.0.2", + "new", + "2026-02-01T00:00:00Z", + &["high"], + )], false, ); let rows = classify(&offers, &recorded, ""); let plan = socket_patch_core::rollout::plan_rollout( rows.into_iter().map(|row| row.candidate).collect(), - &MaxNew { value: Some(0), source: MaxNewSource::Flag }, + &MaxNew { + value: Some(0), + source: MaxNewSource::Flag, + }, false, &BTreeSet::new(), ); diff --git a/crates/socket-patch-cli/src/commands/scan/rollout_args.rs b/crates/socket-patch-cli/src/commands/scan/rollout_args.rs index e4d251e98..f83636045 100644 --- a/crates/socket-patch-cli/src/commands/scan/rollout_args.rs +++ b/crates/socket-patch-cli/src/commands/scan/rollout_args.rs @@ -1,7 +1,6 @@ //! `scan --max-new-patches` (see the rollout guide, //! `docs/configuration.md#gradual-rollout`). - use clap::Args; pub(crate) use socket_patch_core::rollout::stage::RolloutCarry; use socket_patch_core::rollout::{resolve_max_new, MaxNew}; @@ -77,7 +76,6 @@ impl RolloutArgs { } } - #[cfg(test)] mod tests { use super::*; diff --git a/crates/socket-patch-cli/tests/apply/apply_network.rs b/crates/socket-patch-cli/tests/apply/apply_network.rs index 837057e18..7284a5e2f 100644 --- a/crates/socket-patch-cli/tests/apply/apply_network.rs +++ b/crates/socket-patch-cli/tests/apply/apply_network.rs @@ -940,7 +940,10 @@ async fn apply_online_ignores_legacy_package_archive_when_downloads_fail() { "a legacy package archive must not cover the patch; stdout={stdout}\nstderr={stderr}" ); let content = std::fs::read(tmp.path().join("node_modules/pkgcache/index.js")).unwrap(); - assert_eq!(content, before, "the file must not be patched from the legacy archive"); + assert_eq!( + content, before, + "the file must not be patched from the legacy archive" + ); let requests = mock.received_requests().await.unwrap_or_default(); let blob_path = format!("/v0/orgs/{ORG_SLUG}/patches/blob/{after_hash}"); @@ -1043,10 +1046,7 @@ async fn mismatch_blob_topup_probes_every_copy_of_a_duplicated_package() { v["summary"]["applied"], 1, "the drifted nested copy must be warn-overwritten.\nstdout={v:#}" ); - assert_eq!( - v["summary"]["failed"], 0, - "no copy may fail.\nstdout={v:#}" - ); + assert_eq!(v["summary"]["failed"], 0, "no copy may fail.\nstdout={v:#}"); // The nested copy's blob was fetched on demand… let requests = mock.received_requests().await.unwrap(); diff --git a/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs b/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs index 6e849f90c..5bc4eacd7 100644 --- a/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs +++ b/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs @@ -201,7 +201,9 @@ fn apply_stderr_warning_gates_on_silent() { "non-silent stderr must carry the {CODE} warning; got:\n{stderr}" ); assert_eq!( - stderr.matches("Warning: bundler app config BUNDLE_PATH").count(), + stderr + .matches("Warning: bundler app config BUNDLE_PATH") + .count(), 1, "exactly ONE warning line (not one per discovery call); got:\n{stderr}" ); diff --git a/crates/socket-patch-cli/tests/cli/covgap_output.rs b/crates/socket-patch-cli/tests/cli/covgap_output.rs index 65cf0b67f..1f5e1c860 100644 --- a/crates/socket-patch-cli/tests/cli/covgap_output.rs +++ b/crates/socket-patch-cli/tests/cli/covgap_output.rs @@ -168,9 +168,8 @@ fn run_in_pty_inner( .expect("spawn socket-patch in PTY"); drop(pair.slave); - let reader_handle = crate::pty_io::PtyOutput::spawn( - pair.master.try_clone_reader().expect("clone reader"), - ); + let reader_handle = + crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); // Watchdog: detached kill after `timeout`; a no-op if the child exits // naturally first. @@ -261,7 +260,10 @@ fn remove_interactive_bare_enter_proceeds_with_default_yes() { "\n", Duration::from_secs(15), ); - assert_eq!(code, 0, "remove with bare Enter must succeed; got: {output}"); + assert_eq!( + code, 0, + "remove with bare Enter must succeed; got: {output}" + ); // The interactive confirm MUST have run — otherwise this test passes // vacuously against a regression that drops the TTY gate and // auto-proceeds. Match the distinctive prompt verbatim (the loose diff --git a/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs b/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs index 6f744bfe4..a7387e225 100644 --- a/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs +++ b/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs @@ -112,9 +112,8 @@ fn run_in_pty_bytes(args: &[&str], cwd: &Path, input: &[u8], timeout: Duration) // closed. The previous design used a chunked read+mpsc loop // because it interleaved with a try_wait poll; the simplified // design serializes wait → drop master → read_to_end joins. - let reader_handle = crate::pty_io::PtyOutput::spawn( - pair.master.try_clone_reader().expect("clone reader"), - ); + let reader_handle = + crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); // Watchdog: detach a thread that kills the child after `timeout`. // The cloned ChildKiller is independent of the main `child` diff --git a/crates/socket-patch-cli/tests/cli_config_fallback.rs b/crates/socket-patch-cli/tests/cli_config_fallback.rs index df19585db..530a53c5f 100644 --- a/crates/socket-patch-cli/tests/cli_config_fallback.rs +++ b/crates/socket-patch-cli/tests/cli_config_fallback.rs @@ -59,8 +59,7 @@ fn scan_cmd(project: &Path, data_dir: &Path) -> Command { let mut cmd = Command::new(BINARY); // Human mode: core's proxy advisory (the oracle below) is muted under // `--json`/`--silent`. - cmd.args(["scan", "-e", "npm", "--cwd"]) - .arg(project); + cmd.args(["scan", "-e", "npm", "--cwd"]).arg(project); for (key, _) in std::env::vars_os() { let name = key.to_string_lossy(); if name.starts_with("SOCKET_") { @@ -298,7 +297,9 @@ async fn corrupt_config_warns_and_keeps_json_stdout_clean() { json_cmd.arg("--json"); let json_out = run(json_cmd); assert!( - json_out.stderr.contains("could not parse socket-cli config"), + json_out + .stderr + .contains("could not parse socket-cli config"), "the parse warning must reach stderr under --json too; got:\n{}", json_out.stderr ); diff --git a/crates/socket-patch-cli/tests/cli_get_silent.rs b/crates/socket-patch-cli/tests/cli_get_silent.rs index 4e43c353d..72f454a6a 100644 --- a/crates/socket-patch-cli/tests/cli_get_silent.rs +++ b/crates/socket-patch-cli/tests/cli_get_silent.rs @@ -25,10 +25,7 @@ fn run_get(cwd: &Path, args: &[&str]) -> (i32, String) { for var in GLOBAL_ARG_ENV_VARS { cmd.env_remove(var); } - for var in [ - "SOCKET_SAVE_ONLY", - "SOCKET_ALL_RELEASES", - ] { + for var in ["SOCKET_SAVE_ONLY", "SOCKET_ALL_RELEASES"] { cmd.env_remove(var); } cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); diff --git a/crates/socket-patch-cli/tests/cli_parse_list.rs b/crates/socket-patch-cli/tests/cli_parse_list.rs index 8c997686f..9a850490d 100644 --- a/crates/socket-patch-cli/tests/cli_parse_list.rs +++ b/crates/socket-patch-cli/tests/cli_parse_list.rs @@ -370,7 +370,11 @@ fn missing_manifest_under_valid_cwd_is_not_an_error_via_binary() { let out = run_list_binary(tmp.path(), &["--json"]); let v: serde_json::Value = serde_json::from_str(String::from_utf8_lossy(&out.stdout).trim()) .expect("stdout must be valid JSON envelope"); - assert_eq!(out.status.code(), Some(0), "missing manifest is an empty list"); + assert_eq!( + out.status.code(), + Some(0), + "missing manifest is an empty list" + ); assert_eq!(v["status"], "success", "envelope: {v}"); assert_eq!(v["summary"]["discovered"], 0, "envelope: {v}"); } @@ -1313,7 +1317,10 @@ fn missing_manifest_with_corrupt_ledger_keeps_warning_in_the_envelope_via_binary assert_eq!(v["status"], "success", "envelope={v}"); let warnings = v["warnings"].as_array().expect("warnings[] present"); assert_eq!(warnings.len(), 1, "envelope={v}"); - assert_eq!(warnings[0]["code"], "redirect_ledger_corrupt", "envelope={v}"); + assert_eq!( + warnings[0]["code"], "redirect_ledger_corrupt", + "envelope={v}" + ); assert!( out.stderr.is_empty(), "--json must keep stderr clean: {}", diff --git a/crates/socket-patch-cli/tests/cli_parse_rollback.rs b/crates/socket-patch-cli/tests/cli_parse_rollback.rs index c8b77af5e..f1590292e 100644 --- a/crates/socket-patch-cli/tests/cli_parse_rollback.rs +++ b/crates/socket-patch-cli/tests/cli_parse_rollback.rs @@ -366,7 +366,11 @@ fn bare_bool_does_not_consume_next_token() { /// relied on the rejection get a test-visible flip instead of a silent one. #[test] fn multiple_targets_parse_in_order() { - let args = parse_rollback(&["pkg:npm/foo@1", "packages/api/**", "b0630680-4da6-45f9-bba8-b888e0ffd58c"]); + let args = parse_rollback(&[ + "pkg:npm/foo@1", + "packages/api/**", + "b0630680-4da6-45f9-bba8-b888e0ffd58c", + ]); assert_eq!( args.targets, vec![ diff --git a/crates/socket-patch-cli/tests/cli_parse_scan.rs b/crates/socket-patch-cli/tests/cli_parse_scan.rs index ab81fa6eb..eff55ee79 100644 --- a/crates/socket-patch-cli/tests/cli_parse_scan.rs +++ b/crates/socket-patch-cli/tests/cli_parse_scan.rs @@ -898,7 +898,11 @@ fn max_new_patches_takes_a_count_or_none() { ("NONE", None), ] { let args = parse_scan(&["--max-new-patches", raw]); - assert_eq!(args.rollout.max_new_patches, Some(MaxNewPatches(want)), "{raw}"); + assert_eq!( + args.rollout.max_new_patches, + Some(MaxNewPatches(want)), + "{raw}" + ); } } @@ -989,20 +993,33 @@ fn min_severity_flag_and_env() { assert_eq!(parse_scan(&[]).socket_yml.min_severity, None); assert_eq!(overrides(&[], &[]).unwrap().min_severity, None); assert_eq!( - overrides(&["--min-severity", "High"], &[]).unwrap().min_severity, + overrides(&["--min-severity", "High"], &[]) + .unwrap() + .min_severity, Some((Some(1), OverrideSource::Flag)) ); assert_eq!( - overrides(&["--min-severity", "none"], &[("SOCKET_MIN_SEVERITY", "critical")]).unwrap().min_severity, + overrides( + &["--min-severity", "none"], + &[("SOCKET_MIN_SEVERITY", "critical")] + ) + .unwrap() + .min_severity, Some((None, OverrideSource::Flag)) ); assert_eq!( - overrides(&[], &[("SOCKET_MIN_SEVERITY", "moderate")]).unwrap().min_severity, + overrides(&[], &[("SOCKET_MIN_SEVERITY", "moderate")]) + .unwrap() + .min_severity, Some((Some(2), OverrideSource::Env)) ); - assert_eq!(overrides(&[], &[("SOCKET_MIN_SEVERITY", "")]).unwrap().min_severity, None); + assert_eq!( + overrides(&[], &[("SOCKET_MIN_SEVERITY", "")]) + .unwrap() + .min_severity, + None + ); assert!(overrides(&[], &[("SOCKET_MIN_SEVERITY", "severe")]).is_err()); assert!(try_parse_scan(&["--min-severity", "severe"]).is_err()); assert!(overrides(&["--no-socket-yml"], &[]).unwrap().bypass); } - diff --git a/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs b/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs index 444dd2a3b..049d8356b 100644 --- a/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs +++ b/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs @@ -149,7 +149,9 @@ fn apply_silent_online_download_failure_keeps_error_output() { ); let chatter = stderr_chatter(&stderr); assert!( - chatter.iter().any(|l| l.contains("could not be downloaded")), + chatter + .iter() + .any(|l| l.contains("could not be downloaded")), "--silent must keep the download-failure error (errors only, \ never nothing); stderr was: {stderr:?}" ); diff --git a/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs b/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs index 35577532a..df8e61626 100644 --- a/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs +++ b/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs @@ -566,8 +566,7 @@ async fn wet_takeover_refuses_unrevertable_vendored_flavor_fail_closed() { "the human skipped line must name purl + reason; stderr=\n{stderr}" ); assert!( - stderr.contains("Warning: ") - && stderr.contains("could not be reverted"), + stderr.contains("Warning: ") && stderr.contains("could not be reverted"), "the takeover pre-warning must reach human stderr; stderr=\n{stderr}" ); } @@ -814,7 +813,10 @@ async fn zero_grant_wet_run_ignores_a_malformed_pre_v5_ledger() { let lock_before = std::fs::read(root.join("package-lock.json")).unwrap(); let assert_ignored = |code: i32, doc: &Value, label: &str| { - assert_eq!(code, 0, "{label}: a pre-v5 ledger is never an error: {doc:#}"); + assert_eq!( + code, 0, + "{label}: a pre-v5 ledger is never an error: {doc:#}" + ); assert_eq!(doc["status"], "success", "{label}: {doc:#}"); assert!( !doc.to_string().contains("redirect-state.json"), @@ -1017,7 +1019,10 @@ async fn hosted_human_empty_discovery_ignores_a_malformed_pre_v5_ledger() { for extra in [&[][..], &["--silent"][..]] { let (code, stdout, stderr) = scan_hosted(root, &server.uri(), extra, &[]); - assert_eq!(code, 0, "{extra:?}: an empty discovery exits 0; stderr=\n{stderr}"); + assert_eq!( + code, 0, + "{extra:?}: an empty discovery exits 0; stderr=\n{stderr}" + ); if extra.is_empty() { assert!( stdout.contains("No patches available for installed packages."), @@ -1404,16 +1409,22 @@ async fn native_bun_lockb_hosting_dry_run_rerun_and_rollback_without_bun() { ], &env, ); - assert_eq!(code, 1, "a binary bun.lockb pin is refused: {stdout}\n{stderr}"); + assert_eq!( + code, 1, + "a binary bun.lockb pin is refused: {stdout}\n{stderr}" + ); let doc: Value = serde_json::from_str(&stdout).unwrap_or_else(|e| panic!("{e}: {stdout}")); assert_eq!(doc["status"], "partial_failure", "{doc:#}"); - let failed = doc["hosted"]["failed"].as_array().unwrap_or_else(|| panic!("{doc:#}")); + let failed = doc["hosted"]["failed"] + .as_array() + .unwrap_or_else(|| panic!("{doc:#}")); assert_eq!(failed.len(), 1, "{doc:#}"); assert_eq!(failed[0]["purl"], purl, "{doc:#}"); let error = failed[0]["error"].as_str().unwrap_or_default(); assert!( - error.starts_with(&format!("cannot restore {purl} to its upstream registry entry: ")) - && error.contains("bun.lockb") + error.starts_with(&format!( + "cannot restore {purl} to its upstream registry entry: " + )) && error.contains("bun.lockb") && error.contains("git checkout"), "{error}" ); @@ -1819,7 +1830,9 @@ async fn unreadable_pnpm_workspace_gets_warning_only_guidance_in_a_live_run() { "the unreadable workspace file must be left byte-identical" ); assert!( - !tmp.path().join(".socket/vendor/redirect-state.json").exists(), + !tmp.path() + .join(".socket/vendor/redirect-state.json") + .exists(), "v5 hosted mode writes no redirect ledger" ); } @@ -1931,9 +1944,8 @@ async fn live_hosted_overlap_fires_redirect_supersedes_vendored() { let (code, _stdout, stderr) = scan_hosted(root, &server.uri(), &psu, &[]); assert_eq!(code, 0, "human overlap run exits 0; stderr=\n{stderr}"); assert!( - stderr.contains( - "Warning: Hosted wiring superseded the vendored ledger for:" - ) && stderr.contains(XPURL), + stderr.contains("Warning: Hosted wiring superseded the vendored ledger for:") + && stderr.contains(XPURL), "the supersedes warning must reach human stderr; stderr=\n{stderr}" ); } @@ -1981,8 +1993,7 @@ async fn human_dry_run_prints_would_rewrite_pnpm_guidance_and_vex_skip() { "the requested-but-skipped VEX must be announced; stderr=\n{stderr}" ); assert!( - stderr.contains("Warning: ") - && stderr.contains("trustLockfile"), + stderr.contains("Warning: ") && stderr.contains("trustLockfile"), "the pnpm trust guidance must reach human stderr; stderr=\n{stderr}" ); assert!( @@ -2420,7 +2431,8 @@ async fn human_pnpm_rerun_prints_only_the_reminder_and_heal_restores_guidance() let (code, stdout, stderr) = scan_hosted(root, &server.uri(), &[], &[]); assert_eq!(code, 0, "stdout=\n{stdout}\nstderr=\n{stderr}"); assert!( - engine_stdout(&stdout).starts_with("Switched 1 package to hosted patches; rewrote 2 files.\n"), + engine_stdout(&stdout) + .starts_with("Switched 1 package to hosted patches; rewrote 2 files.\n"), "{stdout}" ); // Everything from the pnpm warning on (the lines above it are the diff --git a/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs b/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs index 47fa71669..a15170613 100644 --- a/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs +++ b/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs @@ -1476,7 +1476,13 @@ async fn scan_hosted_paths_run_once_per_project_directory() { let header = format!("== {} ==", Path::new("apps").join(app).display()); assert!(stdout.contains(&header), "missing {header:?}: {stdout}"); } - assert_eq!(stdout.matches("Switched 0 packages to hosted patches").count(), 2, "{stdout}"); + assert_eq!( + stdout + .matches("Switched 0 packages to hosted patches") + .count(), + 2, + "{stdout}" + ); let reqs = recorded(&mock).await; assert_eq!(batch_bodies(&reqs).len(), 2, "one discovery per directory"); } @@ -1915,7 +1921,6 @@ mod pty { screen.join("\n") ); } - } // --------------------------------------------------------------------------- diff --git a/crates/socket-patch-cli/tests/e2e_cargo.rs b/crates/socket-patch-cli/tests/e2e_cargo.rs index 3978aff96..73c6acaef 100644 --- a/crates/socket-patch-cli/tests/e2e_cargo.rs +++ b/crates/socket-patch-cli/tests/e2e_cargo.rs @@ -204,8 +204,7 @@ async fn scan_discovers_fake_registry_crates() { "Expected human scan to report exactly 'Found 2 packages (2 cargo)', got:\n{combined}" ); assert!( - !combined.contains("No packages found") - && !combined.contains("No packages found"), + !combined.contains("No packages found") && !combined.contains("No packages found"), "scan reported no packages despite a populated registry:\n{combined}" ); @@ -262,8 +261,7 @@ async fn scan_discovers_vendor_crates() { "Expected human scan to report exactly 'Found 1 package (1 cargo)', got:\n{combined}" ); assert!( - !combined.contains("No packages found") - && !combined.contains("No packages found"), + !combined.contains("No packages found") && !combined.contains("No packages found"), "scan reported no packages despite a populated vendor dir:\n{combined}" ); diff --git a/crates/socket-patch-cli/tests/e2e_gem.rs b/crates/socket-patch-cli/tests/e2e_gem.rs index db8af0af8..f6f189113 100644 --- a/crates/socket-patch-cli/tests/e2e_gem.rs +++ b/crates/socket-patch-cli/tests/e2e_gem.rs @@ -583,7 +583,11 @@ fn test_gem_dry_run() { let gem_dir = find_gem_dir(cwd); // Download without applying. - assert_run_ok(cwd, &["get", GEM_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); + assert_run_ok( + cwd, + &["get", GEM_UUID, "--mode", "agent", "--no-apply"], + "get --no-apply", + ); // Read manifest to get file list and expected hashes. let manifest_path = cwd.join(".socket/manifest.json"); diff --git a/crates/socket-patch-cli/tests/e2e_maven.rs b/crates/socket-patch-cli/tests/e2e_maven.rs index 6f4474404..b6c650cad 100644 --- a/crates/socket-patch-cli/tests/e2e_maven.rs +++ b/crates/socket-patch-cli/tests/e2e_maven.rs @@ -177,8 +177,7 @@ async fn scan_discovers_maven_artifacts() { // Must NOT have hit the empty-crawl path — that line *also* contains // the word "packages". assert!( - !combined.contains("No packages found") - && !combined.contains("No packages found"), + !combined.contains("No packages found") && !combined.contains("No packages found"), "scan reported zero packages — Maven discovery did not run:\n{combined}" ); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_npm.rs b/crates/socket-patch-cli/tests/e2e_npm.rs index 89f40f9a5..7486a85c9 100644 --- a/crates/socket-patch-cli/tests/e2e_npm.rs +++ b/crates/socket-patch-cli/tests/e2e_npm.rs @@ -286,7 +286,11 @@ fn test_npm_dry_run() { assert_eq!(git_sha256_file(&index_js), BEFORE_HASH); // Download the patch *without* applying. - assert_run_ok(cwd, &["get", NPM_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); + assert_run_ok( + cwd, + &["get", NPM_UUID, "--mode", "agent", "--no-apply"], + "get --no-apply", + ); // File should still be original. assert_eq!( diff --git a/crates/socket-patch-cli/tests/e2e_nuget.rs b/crates/socket-patch-cli/tests/e2e_nuget.rs index ce4cc4998..f8ec8eb1e 100644 --- a/crates/socket-patch-cli/tests/e2e_nuget.rs +++ b/crates/socket-patch-cli/tests/e2e_nuget.rs @@ -227,7 +227,8 @@ async fn scan_discovers_global_cache_packages() { // "packages" substring check would also match). assert!( !combined.contains("No packages found") - && !combined.contains("No packages found") && !combined.contains("No global packages found"), + && !combined.contains("No packages found") + && !combined.contains("No global packages found"), "scan failed to discover the fake global cache:\n{combined}" ); // Exactly the two packages we planted (Newtonsoft.Json, System.Text.Json), @@ -285,7 +286,8 @@ async fn scan_discovers_legacy_packages() { ); assert!( !combined.contains("No packages found") - && !combined.contains("No packages found") && !combined.contains("No global packages found"), + && !combined.contains("No packages found") + && !combined.contains("No global packages found"), "scan failed to discover the legacy packages/ layout:\n{combined}" ); // Exactly the single legacy package we planted (Newtonsoft.Json.13.0.3), diff --git a/crates/socket-patch-cli/tests/e2e_pypi.rs b/crates/socket-patch-cli/tests/e2e_pypi.rs index 4531d1173..d84c6db20 100644 --- a/crates/socket-patch-cli/tests/e2e_pypi.rs +++ b/crates/socket-patch-cli/tests/e2e_pypi.rs @@ -426,7 +426,11 @@ fn test_pypi_dry_run() { let original_hash = git_sha256_file(&messages_py); // Download without applying. - assert_run_ok(cwd, &["get", PYPI_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); + assert_run_ok( + cwd, + &["get", PYPI_UUID, "--mode", "agent", "--no-apply"], + "get --no-apply", + ); // File should be unchanged. assert_eq!( diff --git a/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs b/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs index 4c2aa5327..ac6c8b31d 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs @@ -327,7 +327,8 @@ async fn gem_hosted_redirect_over_stale_install_warns_loudly() { ); assert_eq!(code, 0, "human re-scan must succeed:\n{stderr}"); assert!( - stderr.contains("Warning: ") && stderr.contains("was switched to its hosted patch, but a stale"), + stderr.contains("Warning: ") + && stderr.contains("was switched to its hosted patch, but a stale"), "human mode must print the stale-install warning on stderr:\n{stderr}" ); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs b/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs index 62e9ef05d..29e90c39d 100644 --- a/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs +++ b/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs @@ -419,7 +419,11 @@ fn manifestless_agent_patch_is_not_attested(consumer: &Path, cargo_home: &Path) "description": "d" } }); - std::fs::write(&manifest_path, serde_json::to_vec_pretty(&manifest).unwrap()).unwrap(); + std::fs::write( + &manifest_path, + serde_json::to_vec_pretty(&manifest).unwrap(), + ) + .unwrap(); let out = run_vex(&bin, consumer, &run); assert_eq!(out.code, Some(0), "manifest-backed vex:\n{out}"); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs index 7af958619..783e7337a 100644 --- a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs +++ b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs @@ -293,7 +293,11 @@ fn apply_in_a_does_not_mutate_b_or_store() { }; // -- get + apply in proj_a only ---------------------------------- - assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); + assert_run_ok( + &fx.proj_a, + &["get", NPM_UUID, "--mode", "agent"], + "socket-patch get", + ); // proj_a is patched. assert_eq!( @@ -397,7 +401,11 @@ fn pnpm_install_in_b_does_not_revert_a() { store_id }; - assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); + assert_run_ok( + &fx.proj_a, + &["get", NPM_UUID, "--mode", "agent"], + "socket-patch get", + ); assert_eq!(git_sha256_file(&index_a), AFTER_HASH); // Re-run pnpm install in proj_b with frozen lockfile — this @@ -475,7 +483,11 @@ fn apply_in_pnpm_project_emits_layout_note() { let root = tempfile::tempdir().unwrap(); let fx = setup_two_pnpm_projects(root.path()); - let (_stdout, stderr) = assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); + let (_stdout, stderr) = assert_run_ok( + &fx.proj_a, + &["get", NPM_UUID, "--mode", "agent"], + "socket-patch get", + ); // The exact phrasing is a stable contract. A bare `contains("pnpm")` // is worthless here — every pnpm store path printed on stderr diff --git a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs index 50018d6a9..9a02495b9 100644 --- a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs +++ b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs @@ -61,7 +61,11 @@ impl Patch { "low" => 3, _ => 4, }; - self.severities.iter().copied().min_by_key(|s| rank(s)).unwrap_or("unknown") + self.severities + .iter() + .copied() + .min_by_key(|s| rank(s)) + .unwrap_or("unknown") } } @@ -200,7 +204,9 @@ async fn mount_api(server: &MockServer, patches: Vec) { .await; let detail_map = by_purl.clone(); Mock::given(method("GET")) - .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) + .and(path_regex(format!( + "^/v0/orgs/{ORG}/patches/by-package/.+$" + ))) .respond_with(move |req: &Request| { let raw = req.url.path().rsplit('/').next().unwrap(); let purl = percent_decode(raw); @@ -216,11 +222,15 @@ async fn mount_api(server: &MockServer, patches: Vec) { }) }) .collect(); - ResponseTemplate::new(200).set_body_json(json!({"patches": list, "canAccessPaidPatches": false})) + ResponseTemplate::new(200) + .set_body_json(json!({"patches": list, "canAccessPaidPatches": false})) }) .mount(server) .await; - let by_uuid: BTreeMap = patches.iter().map(|p| (p.uuid.to_string(), p.clone())).collect(); + let by_uuid: BTreeMap = patches + .iter() + .map(|p| (p.uuid.to_string(), p.clone())) + .collect(); let refs = by_uuid.clone(); Mock::given(method("POST")) .and(path(format!("/v0/orgs/{ORG}/patches/package"))) @@ -277,8 +287,10 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { let dep_map: BTreeMap<&str, &str> = deps.iter().map(|d| (*d, "1.0.0")).collect(); std::fs::write( dir.join("package.json"), - serde_json::to_string_pretty(&json!({"name": "consumer", "version": "0.0.0", "dependencies": dep_map})) - .unwrap(), + serde_json::to_string_pretty( + &json!({"name": "consumer", "version": "0.0.0", "dependencies": dep_map}), + ) + .unwrap(), ) .unwrap(); let mut packages = serde_json::Map::new(); @@ -289,7 +301,11 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { for name in deps { let pkg = dir.join("node_modules").join(name); std::fs::create_dir_all(&pkg).unwrap(); - std::fs::write(pkg.join("package.json"), format!(r#"{{ "name": "{name}", "version": "1.0.0" }}"#)).unwrap(); + std::fs::write( + pkg.join("package.json"), + format!(r#"{{ "name": "{name}", "version": "1.0.0" }}"#), + ) + .unwrap(); std::fs::write(pkg.join("index.js"), orig_index(name)).unwrap(); packages.insert( format!("node_modules/{name}"), @@ -304,12 +320,20 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { "name": "consumer", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": packages }); - std::fs::write(dir.join("package-lock.json"), serde_json::to_string_pretty(&lock).unwrap() + "\n").unwrap(); + std::fs::write( + dir.join("package-lock.json"), + serde_json::to_string_pretty(&lock).unwrap() + "\n", + ) + .unwrap(); } fn write_gem(dir: &Path, name: &str, version: &str) { - std::fs::create_dir_all(dir.join("vendor/bundle/ruby/3.0.0/gems").join(format!("{name}-{version}")).join("lib")) - .unwrap(); + std::fs::create_dir_all( + dir.join("vendor/bundle/ruby/3.0.0/gems") + .join(format!("{name}-{version}")) + .join("lib"), + ) + .unwrap(); } /// The monorepo: `services/web` (alpha, beta, left-pad + a gem), @@ -349,7 +373,11 @@ impl Repo { if entry.file_type().unwrap().is_dir() { walk(&path, root, out); } else { - let rel = path.strip_prefix(root).unwrap().to_string_lossy().into_owned(); + let rel = path + .strip_prefix(root) + .unwrap() + .to_string_lossy() + .into_owned(); out.insert(rel, std::fs::read(&path).unwrap()); } } @@ -369,7 +397,8 @@ fn run_cli(cwd: &Path, args: &[&str], env: &[(&str, &str)]) -> (i32, String, Str cmd.env_remove(key); } } - cmd.env_remove("GIT_CEILING_DIRECTORIES").env_remove("VIRTUAL_ENV"); + cmd.env_remove("GIT_CEILING_DIRECTORIES") + .env_remove("VIRTUAL_ENV"); cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); // The fixture's hosted pins name this origin; it makes them recorded. cmd.env("SOCKET_PATCH_SERVER_URL", "http://patch.test"); @@ -383,7 +412,8 @@ fn run_cli(cwd: &Path, args: &[&str], env: &[(&str, &str)]) -> (i32, String, Str ] { cmd.env(var, &absent); } - cmd.env("NPM_CONFIG_ALLOW_REMOTE", "").env("npm_config_allow_remote", ""); + cmd.env("NPM_CONFIG_ALLOW_REMOTE", "") + .env("npm_config_allow_remote", ""); for (k, v) in env { cmd.env(k, v); } @@ -418,8 +448,9 @@ fn scan_json(cwd: &Path, api: &str, extra: &[&str], env: &[(&str, &str)]) -> (i3 let mut args = vec!["--json"]; args.extend_from_slice(extra); let (code, stdout, stderr) = scan(cwd, api, &args, env); - let doc: Value = serde_json::from_str(&stdout) - .unwrap_or_else(|e| panic!("stdout must be JSON ({e})\nstdout=\n{stdout}\nstderr=\n{stderr}")); + let doc: Value = serde_json::from_str(&stdout).unwrap_or_else(|e| { + panic!("stdout must be JSON ({e})\nstdout=\n{stdout}\nstderr=\n{stderr}") + }); (code, doc) } @@ -428,7 +459,12 @@ fn filtered(doc: &Value) -> Vec<(Option, String)> { .as_array() .unwrap() .iter() - .map(|f| (f["purl"].as_str().map(str::to_string), f["reason"].as_str().unwrap().to_string())) + .map(|f| { + ( + f["purl"].as_str().map(str::to_string), + f["reason"].as_str().unwrap().to_string(), + ) + }) .collect() } @@ -444,7 +480,11 @@ fn filtered_reason<'a>(doc: &'a Value, purl: &str) -> &'a Value { fn warning_codes(doc: &Value) -> Vec { doc["warnings"] .as_array() - .map(|w| w.iter().filter_map(|e| e["code"].as_str().map(str::to_string)).collect()) + .map(|w| { + w.iter() + .filter_map(|e| e["code"].as_str().map(str::to_string)) + .collect() + }) .unwrap_or_default() } @@ -464,16 +504,28 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { assert_eq!(code, 0, "{doc:#}"); let lock = repo.lock("services/web"); - assert!(lock.contains(&P_ALPHA.hosted_url()), "alpha is patched:\n{lock}"); - assert!(!lock.contains(P_BETA.uuid), "beta is below the floor:\n{lock}"); - assert!(!lock.contains(P_LEFTPAD.uuid), "left-pad is ignored:\n{lock}"); + assert!( + lock.contains(&P_ALPHA.hosted_url()), + "alpha is patched:\n{lock}" + ); + assert!( + !lock.contains(P_BETA.uuid), + "beta is below the floor:\n{lock}" + ); + assert!( + !lock.contains(P_LEFTPAD.uuid), + "left-pad is ignored:\n{lock}" + ); let policy = &doc["policy"]; assert_eq!(policy["source"], "file"); assert_eq!(policy["path"], "socket.yml"); assert_eq!(policy["sha256"].as_str().unwrap().len(), 64); assert_eq!(policy["enabled"], true); - assert_eq!(policy["minSeverity"], json!({"value": "high", "source": "file"})); + assert_eq!( + policy["minSeverity"], + json!({"value": "high", "source": "file"}) + ); let beta = filtered_reason(&doc, "pkg:npm/beta@1.0.0"); assert_eq!(beta["reason"], "policy_severity"); assert_eq!(beta["detail"], "low < high"); @@ -481,8 +533,15 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { assert_eq!(beta["project"], "services/web"); let left_pad = filtered_reason(&doc, "pkg:npm/left-pad@1.0.0"); assert_eq!(left_pad["reason"], "policy_package_ignored"); - assert_eq!(left_pad["uuid"], Value::Null, "filtered before any patch lookup"); - assert_eq!(left_pad["detail"], "pkg:npm/left-pad (patches.ignorePackages)"); + assert_eq!( + left_pad["uuid"], + Value::Null, + "filtered before any patch lookup" + ); + assert_eq!( + left_pad["detail"], + "pkg:npm/left-pad (patches.ignorePackages)" + ); let rack = filtered_reason(&doc, "pkg:gem/rack@1.0.0"); assert_eq!(rack["reason"], "policy_ecosystem"); assert_eq!(policy["counts"]["filtered"], 3); @@ -492,7 +551,10 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { for r in &reqs { if r.url.path().ends_with("/patches/batch") { let body = String::from_utf8_lossy(&r.body); - assert!(!body.contains("left-pad") && !body.contains("rack"), "{body}"); + assert!( + !body.contains("left-pad") && !body.contains("rack"), + "{body}" + ); } } assert_eq!(doc["redirect"]["redirected"], 1, "{:#}", doc["redirect"]); @@ -503,13 +565,27 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { async fn hosted_dry_run_makes_the_same_decisions_and_writes_nothing() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some("version: 2\npatches:\n minSeverity: high\n ecosystems: [npm]\n")); + let repo = Repo::new(Some( + "version: 2\npatches:\n minSeverity: high\n ecosystems: [npm]\n", + )); let before = repo.snapshot(); - let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--dry-run"], &[]); + let (code, doc) = scan_json( + &repo.dir("services/web"), + &server.uri(), + &["--dry-run"], + &[], + ); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before, "a dry run changes no bytes"); - assert_eq!(doc["redirect"]["redirected"], 2, "alpha and left-pad: {:#}", doc["redirect"]); - assert_eq!(filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], "policy_severity"); + assert_eq!( + doc["redirect"]["redirected"], 2, + "alpha and left-pad: {:#}", + doc["redirect"] + ); + assert_eq!( + filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], + "policy_severity" + ); } #[tokio::test] @@ -517,14 +593,24 @@ async fn hosted_dry_run_makes_the_same_decisions_and_writes_nothing() { async fn path_globs_apply_default_ignores_and_ignore_paths_human() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some("version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n")); + let repo = Repo::new(Some( + "version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n", + )); let legacy = repo.lock("services/legacy"); let test_lock = repo.lock("services/test"); let (code, stdout, stderr) = scan(&repo.root, &server.uri(), &["services/*"], &[]); assert_eq!(code, 0, "stdout:\n{stdout}\nstderr:\n{stderr}"); assert!(repo.lock("services/web").contains(&P_ALPHA.hosted_url())); - assert_eq!(repo.lock("services/legacy"), legacy, "ignored by patches.ignorePaths"); - assert_eq!(repo.lock("services/test"), test_lock, "a discovered test/ root is a built-in ignore"); + assert_eq!( + repo.lock("services/legacy"), + legacy, + "ignored by patches.ignorePaths" + ); + assert_eq!( + repo.lock("services/test"), + test_lock, + "a discovered test/ root is a built-in ignore" + ); assert!(stdout.contains("Policy (socket.yml)"), "{stdout}"); // Named literally, the test/ root is explicit: defaults do not apply. @@ -538,7 +624,9 @@ async fn path_globs_apply_default_ignores_and_ignore_paths_human() { async fn include_paths_limit_roots() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some("version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n")); + let repo = Repo::new(Some( + "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n", + )); let web = repo.lock("services/web"); let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); @@ -571,7 +659,10 @@ async fn invalid_file_fails_closed_before_any_request_or_write() { assert!(message.contains("--no-socket-yml"), "{message}"); assert!(doc.get("policy").is_none()); assert_eq!(repo.snapshot(), before); - assert!(server.received_requests().await.unwrap().is_empty(), "no request before the policy loads"); + assert!( + server.received_requests().await.unwrap().is_empty(), + "no request before the policy loads" + ); // Human output names the code on stderr, same exit code. let (code, _, stderr) = scan(&repo.dir("services/web"), &server.uri(), &[], &[]); @@ -579,10 +670,20 @@ async fn invalid_file_fails_closed_before_any_request_or_write() { assert!(stderr.contains("socket_yml_invalid"), "{stderr}"); // --no-socket-yml (and its env var) skips the file. - let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--no-socket-yml", "--dry-run"], &[]); + let (code, doc) = scan_json( + &repo.dir("services/web"), + &server.uri(), + &["--no-socket-yml", "--dry-run"], + &[], + ); assert_eq!(code, 0, "{doc:#}"); assert_eq!(doc["policy"]["source"], "bypassed"); - let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--dry-run"], &[("SOCKET_NO_SOCKET_YML", "1")]); + let (code, doc) = scan_json( + &repo.dir("services/web"), + &server.uri(), + &["--dry-run"], + &[("SOCKET_NO_SOCKET_YML", "1")], + ); assert_eq!(code, 0, "{doc:#}"); assert_eq!(doc["policy"]["source"], "bypassed"); } @@ -593,7 +694,11 @@ async fn both_files_disagreeing_is_ambiguous() { let server = MockServer::start().await; mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n maxNewPatches: 1\n")); - std::fs::write(repo.root.join("socket.yaml"), "version: 2\npatches:\n maxNewPatches: 2\n").unwrap(); + std::fs::write( + repo.root.join("socket.yaml"), + "version: 2\npatches:\n maxNewPatches: 2\n", + ) + .unwrap(); let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &[], &[]); assert_eq!(code, 1); assert_eq!(doc["errorCode"], "socket_yml_ambiguous"); @@ -606,26 +711,66 @@ async fn severity_flag_and_env_override_the_file() { mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n minSeverity: high\n")); let web = repo.dir("services/web"); - let (code, doc) = scan_json(&web, &server.uri(), &["--dry-run", "--min-severity", "none"], &[]); + let (code, doc) = scan_json( + &web, + &server.uri(), + &["--dry-run", "--min-severity", "none"], + &[], + ); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(doc["policy"]["minSeverity"], json!({"value": null, "source": "flag"})); - assert_eq!(doc["redirect"]["redirected"], 3, "beta too once the floor is lifted"); + assert_eq!( + doc["policy"]["minSeverity"], + json!({"value": null, "source": "flag"}) + ); + assert_eq!( + doc["redirect"]["redirected"], 3, + "beta too once the floor is lifted" + ); - let (code, doc) = scan_json(&web, &server.uri(), &["--dry-run"], &[("SOCKET_MIN_SEVERITY", "critical")]); + let (code, doc) = scan_json( + &web, + &server.uri(), + &["--dry-run"], + &[("SOCKET_MIN_SEVERITY", "critical")], + ); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(doc["policy"]["minSeverity"], json!({"value": "critical", "source": "env"})); + assert_eq!( + doc["policy"]["minSeverity"], + json!({"value": "critical", "source": "env"}) + ); assert_eq!(doc["redirect"]["redirected"], 1); // The flag beats the env; an empty env value is unset. - let (_, doc) = scan_json(&web, &server.uri(), &["--dry-run", "--min-severity", "moderate"], &[("SOCKET_MIN_SEVERITY", "critical")]); - assert_eq!(doc["policy"]["minSeverity"], json!({"value": "medium", "source": "flag"})); - let (_, doc) = scan_json(&web, &server.uri(), &["--dry-run"], &[("SOCKET_MIN_SEVERITY", "")]); - assert_eq!(doc["policy"]["minSeverity"], json!({"value": "high", "source": "file"})); + let (_, doc) = scan_json( + &web, + &server.uri(), + &["--dry-run", "--min-severity", "moderate"], + &[("SOCKET_MIN_SEVERITY", "critical")], + ); + assert_eq!( + doc["policy"]["minSeverity"], + json!({"value": "medium", "source": "flag"}) + ); + let (_, doc) = scan_json( + &web, + &server.uri(), + &["--dry-run"], + &[("SOCKET_MIN_SEVERITY", "")], + ); + assert_eq!( + doc["policy"]["minSeverity"], + json!({"value": "high", "source": "file"}) + ); // Malformed values are usage errors. let (code, _, stderr) = scan(&web, &server.uri(), &["--min-severity", "severe"], &[]); assert_eq!(code, 2, "{stderr}"); - let (code, _, stderr) = scan(&web, &server.uri(), &[], &[("SOCKET_MIN_SEVERITY", "severe")]); + let (code, _, stderr) = scan( + &web, + &server.uri(), + &[], + &[("SOCKET_MIN_SEVERITY", "severe")], + ); assert_eq!(code, 2, "{stderr}"); assert!(stderr.contains("SOCKET_MIN_SEVERITY"), "{stderr}"); } @@ -643,12 +788,20 @@ async fn narrowing_after_a_hosted_patch_leaves_the_pin_byte_identical() { assert!(pinned.contains(&P_ALPHA.hosted_url())); // A newer merged patch appears, and the repo now ignores alpha. - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ignorePackages: [alpha]\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n ignorePackages: [alpha]\n", + ) + .unwrap(); server.reset().await; mount_api(&server, vec![P_ALPHA, P_ALPHA_MERGED_NEW]).await; let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(repo.lock("services/web"), pinned, "retained: not upgraded, not removed"); + assert_eq!( + repo.lock("services/web"), + pinned, + "retained: not upgraded, not removed" + ); let retained = &doc["policy"]["retained"][0]; assert_eq!(retained["purl"], "pkg:npm/alpha@1.0.0"); assert_eq!(retained["recordedUuid"], P_ALPHA.uuid); @@ -666,7 +819,11 @@ async fn narrowing_after_a_hosted_patch_leaves_the_pin_byte_identical() { let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.lock("services/web"), pinned, "{yml}"); - assert_eq!(doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", "{yml}: {:#}", doc["policy"]); + assert_eq!( + doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", + "{yml}: {:#}", + doc["policy"] + ); } } @@ -681,9 +838,15 @@ async fn enabled_false_reports_and_writes_nothing() { assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before); assert_eq!(doc["policy"]["enabled"], false); - assert!(warning_codes(&doc).contains(&"patches_disabled".to_string()), "{doc:#}"); + assert!( + warning_codes(&doc).contains(&"patches_disabled".to_string()), + "{doc:#}" + ); let reasons: Vec = filtered(&doc).into_iter().map(|(_, r)| r).collect(); - assert!(!reasons.is_empty() && reasons.iter().all(|r| r == "policy_disabled"), "{reasons:?}"); + assert!( + !reasons.is_empty() && reasons.iter().all(|r| r == "policy_disabled"), + "{reasons:?}" + ); assert_eq!(doc["redirect"]["redirected"], 0); } @@ -692,7 +855,9 @@ async fn enabled_false_reports_and_writes_nothing() { async fn report_only_json_fails_when_every_detail_query_fails() { let server = MockServer::start().await; Mock::given(method("GET")) - .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) + .and(path_regex(format!( + "^/v0/orgs/{ORG}/patches/by-package/.+$" + ))) .respond_with(ResponseTemplate::new(500)) .with_priority(1) .mount(&server) @@ -705,7 +870,10 @@ async fn report_only_json_fails_when_every_detail_query_fails() { assert_eq!(code, 1, "{doc:#}"); assert_eq!(doc["status"], "error", "{doc:#}"); assert!( - doc["error"].as_str().unwrap_or_default().contains("patch-detail queries failed"), + doc["error"] + .as_str() + .unwrap_or_default() + .contains("patch-detail queries failed"), "{doc:#}" ); assert_eq!(repo.snapshot(), before); @@ -726,7 +894,11 @@ async fn recorded_merge_below_the_floor_is_kept_until_a_more_severe_patch_is_ava "the only available patch is pinned:\n{pinned}" ); - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n minSeverity: high\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n minSeverity: high\n", + ) + .unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!( @@ -778,11 +950,17 @@ async fn floor_with_nothing_admitted_reports_the_withheld_patch() { let (code, stdout, stderr) = scan(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{stdout}\n{stderr}"); assert_eq!(repo.lock("services/web"), lock); - assert!(stdout.contains("Policy (socket.yml): 1 skipped by filters"), "{stdout}"); + assert!( + stdout.contains("Policy (socket.yml): 1 skipped by filters"), + "{stdout}" + ); // Only critical/high are named without --verbose. assert!(!stdout.contains("skipped beta"), "{stdout}"); let (_, stdout, _) = scan(&web, &server.uri(), &["--verbose"], &[]); - assert!(stdout.contains("skipped pkg:npm/beta@1.0.0 (low): low < critical"), "{stdout}"); + assert!( + stdout.contains("skipped pkg:npm/beta@1.0.0 (low): low < critical"), + "{stdout}" + ); } #[tokio::test] @@ -793,9 +971,17 @@ async fn path_outside_the_repo_is_a_usage_error() { let repo = Repo::new(None); let outside = repo.root.parent().unwrap().join("elsewhere"); write_npm_root(&outside, &["alpha"]); - let (code, _, stderr) = scan(&repo.dir("services"), &server.uri(), &["web", "../../elsewhere"], &[]); + let (code, _, stderr) = scan( + &repo.dir("services"), + &server.uri(), + &["web", "../../elsewhere"], + &[], + ); assert_eq!(code, 2, "{stderr}"); - assert!(stderr.contains("is outside") && stderr.contains("run one scan per repository"), "{stderr}"); + assert!( + stderr.contains("is outside") && stderr.contains("run one scan per repository"), + "{stderr}" + ); } #[tokio::test] @@ -803,7 +989,9 @@ async fn path_outside_the_repo_is_a_usage_error() { async fn project_ignore_paths_is_honored_without_a_patches_block() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some("version: 2\nprojectIgnorePaths:\n - \"services/legacy/**\"\n")); + let repo = Repo::new(Some( + "version: 2\nprojectIgnorePaths:\n - \"services/legacy/**\"\n", + )); let legacy = repo.lock("services/legacy"); let (code, doc) = scan_json(&repo.dir("services/legacy"), &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); @@ -813,10 +1001,22 @@ async fn project_ignore_paths_is_honored_without_a_patches_block() { assert_eq!(entry["detail"], "services/legacy/** (projectIgnorePaths)"); // A malformed projectIgnorePaths without a patches block only warns. - std::fs::write(repo.root.join("socket.yml"), "version: 2\nprojectIgnorePaths: {a: 1}\n").unwrap(); - let (code, doc) = scan_json(&repo.dir("services/legacy"), &server.uri(), &["--dry-run"], &[]); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\nprojectIgnorePaths: {a: 1}\n", + ) + .unwrap(); + let (code, doc) = scan_json( + &repo.dir("services/legacy"), + &server.uri(), + &["--dry-run"], + &[], + ); assert_eq!(code, 0, "{doc:#}"); - assert!(warning_codes(&doc).contains(&"socket_yml_ignored_value".to_string()), "{doc:#}"); + assert!( + warning_codes(&doc).contains(&"socket_yml_ignored_value".to_string()), + "{doc:#}" + ); } // --------------------------------------------------------------------------- @@ -845,14 +1045,18 @@ async fn agent_mode_applies_only_admitted_patches() { let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "agent"], &[]); assert_eq!(code, 0, "{doc:#}"); let manifest: Value = - serde_json::from_str(&std::fs::read_to_string(web.join(".socket/manifest.json")).unwrap()).unwrap(); + serde_json::from_str(&std::fs::read_to_string(web.join(".socket/manifest.json")).unwrap()) + .unwrap(); let keys: Vec<&String> = manifest["patches"].as_object().unwrap().keys().collect(); assert_eq!(keys, ["pkg:npm/alpha@1.0.0"]); assert_eq!( std::fs::read_to_string(web.join("node_modules/alpha/index.js")).unwrap(), patched_index("alpha") ); - assert_eq!(std::fs::read_to_string(web.join("node_modules/beta/index.js")).unwrap(), orig_index("beta")); + assert_eq!( + std::fs::read_to_string(web.join("node_modules/beta/index.js")).unwrap(), + orig_index("beta") + ); } #[tokio::test] @@ -867,13 +1071,23 @@ async fn agent_mode_retains_a_recorded_patch_the_policy_now_excludes() { let manifest_before = std::fs::read(web.join(".socket/manifest.json")).unwrap(); let installed_before = std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(); - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ecosystems: [pypi]\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n ecosystems: [pypi]\n", + ) + .unwrap(); server.reset().await; mount_api(&server, vec![P_ALPHA, P_ALPHA_MERGED_NEW]).await; let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "agent"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); - assert_eq!(std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(), installed_before); + assert_eq!( + std::fs::read(web.join(".socket/manifest.json")).unwrap(), + manifest_before + ); + assert_eq!( + std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(), + installed_before + ); assert_eq!(doc["policy"]["retained"][0]["reason"], "policy_ecosystem"); assert_eq!(doc["policy"]["retained"][0]["upgradeAvailable"], true); } @@ -889,7 +1103,12 @@ async fn vendored_dry_run_previews_only_admitted_patches() { mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n packages: [\"pkg:npm/beta\", \"pkg:npm/left-pad\"]\n minSeverity: medium\n")); let before = repo.snapshot(); - let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--mode", "vendored", "--dry-run"], &[]); + let (code, doc) = scan_json( + &repo.dir("services/web"), + &server.uri(), + &["--mode", "vendored", "--dry-run"], + &[], + ); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before); let previewed: Vec<&str> = doc["vendor"]["patches"] @@ -899,8 +1118,14 @@ async fn vendored_dry_run_previews_only_admitted_patches() { .filter_map(|p| p["purl"].as_str()) .collect(); assert_eq!(previewed, ["pkg:npm/left-pad@1.0.0"], "{doc:#}"); - assert_eq!(filtered_reason(&doc, "pkg:npm/alpha@1.0.0")["reason"], "policy_package_not_listed"); - assert_eq!(filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], "policy_severity"); + assert_eq!( + filtered_reason(&doc, "pkg:npm/alpha@1.0.0")["reason"], + "policy_package_not_listed" + ); + assert_eq!( + filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], + "policy_severity" + ); } // --------------------------------------------------------------------------- @@ -932,9 +1157,16 @@ async fn get_bypasses_the_policy_with_a_warning() { let (code, stdout, stderr) = run_cli(&web, &args, &[]); assert_eq!(code, 0, "stdout:\n{stdout}\nstderr:\n{stderr}"); let doc: Value = serde_json::from_str(&stdout).unwrap(); - let warnings: Vec<&str> = doc["warnings"].as_array().unwrap().iter().filter_map(Value::as_str).collect(); + let warnings: Vec<&str> = doc["warnings"] + .as_array() + .unwrap() + .iter() + .filter_map(Value::as_str) + .collect(); assert!( - warnings.iter().any(|w| w.starts_with("(policy_bypassed)") && w.contains("alpha")), + warnings + .iter() + .any(|w| w.starts_with("(policy_bypassed)") && w.contains("alpha")), "{doc:#}" ); @@ -960,30 +1192,65 @@ async fn agent_mode_honors_path_filters_and_keeps_the_prune_universe() { // The root is excluded by path: nothing selected, and a --sync (agent // + prune) still judges the full crawl, so no entry is pruned. - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n", + ) + .unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); + assert_eq!( + std::fs::read(web.join(".socket/manifest.json")).unwrap(), + manifest_before + ); assert_eq!(doc["policy"]["filtered"][0]["purl"], Value::Null); - assert_eq!(doc["policy"]["filtered"][0]["reason"], "policy_path_not_included"); - assert_eq!(doc["policy"]["counts"]["retained"], 2, "{:#}", doc["policy"]); - assert_eq!(doc["gc"]["removed"].as_array().map_or(0, Vec::len), 0, "{:#}", doc["gc"]); + assert_eq!( + doc["policy"]["filtered"][0]["reason"], + "policy_path_not_included" + ); + assert_eq!( + doc["policy"]["counts"]["retained"], 2, + "{:#}", + doc["policy"] + ); + assert_eq!( + doc["gc"]["removed"].as_array().map_or(0, Vec::len), + 0, + "{:#}", + doc["gc"] + ); // A narrower ecosystem list under --sync prunes nothing either. - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ecosystems: [pypi]\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n ecosystems: [pypi]\n", + ) + .unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); + assert_eq!( + std::fs::read(web.join(".socket/manifest.json")).unwrap(), + manifest_before + ); // patches.enabled: false skips the GC entirely. std::fs::remove_dir_all(web.join("node_modules/beta")).unwrap(); - let pkg_lock = repo.lock("services/web").replace("\"node_modules/beta\"", "\"node_modules/gone\""); + let pkg_lock = repo + .lock("services/web") + .replace("\"node_modules/beta\"", "\"node_modules/gone\""); std::fs::write(web.join("package-lock.json"), pkg_lock).unwrap(); - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n enabled: false\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n enabled: false\n", + ) + .unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); assert!(doc.get("gc").is_none(), "{doc:#}"); - assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); + assert_eq!( + std::fs::read(web.join(".socket/manifest.json")).unwrap(), + manifest_before + ); } #[tokio::test] @@ -997,29 +1264,53 @@ async fn narrowing_after_vendoring_leaves_the_vendored_package_byte_identical() let before = compute_git_sha256_from_bytes(orig_index("alpha").as_bytes()); let after = compute_git_sha256_from_bytes(patched_index("alpha").as_bytes()); std::fs::create_dir_all(web.join(".socket/blobs")).unwrap(); - std::fs::write(web.join(".socket/blobs").join(&after), patched_index("alpha")).unwrap(); + std::fs::write( + web.join(".socket/blobs").join(&after), + patched_index("alpha"), + ) + .unwrap(); let manifest = json!({"patches": {P_ALPHA.purl(): { "uuid": P_ALPHA.uuid, "exportedAt": "2026-01-01T00:00:00Z", "files": {"package/index.js": {"beforeHash": before, "afterHash": after}}, "vulnerabilities": {}, "description": "d", "license": "MIT", "tier": "free" }}}); - std::fs::write(web.join(".socket/manifest.json"), serde_json::to_vec_pretty(&manifest).unwrap()).unwrap(); + std::fs::write( + web.join(".socket/manifest.json"), + serde_json::to_vec_pretty(&manifest).unwrap(), + ) + .unwrap(); let fixture = prebuilt_common::Server::project(&web); let (code, stdout, stderr) = run_cli( &web, &["vendor", "--json", "--cwd", web.to_str().unwrap()], - &[("SOCKET_VENDOR_URL", &fixture.uri), ("SOCKET_PATCH_SERVER_URL", &fixture.uri)], + &[ + ("SOCKET_VENDOR_URL", &fixture.uri), + ("SOCKET_PATCH_SERVER_URL", &fixture.uri), + ], ); assert_eq!(code, 0, "vendor fixture: {stdout}\n{stderr}"); - assert!(repo.lock("services/web").contains(".socket/vendor/"), "vendored lock"); + assert!( + repo.lock("services/web").contains(".socket/vendor/"), + "vendored lock" + ); let snapshot = repo.snapshot(); - std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ignorePackages: [\"pkg:npm/alpha\"]\n").unwrap(); + std::fs::write( + repo.root.join("socket.yml"), + "version: 2\npatches:\n ignorePackages: [\"pkg:npm/alpha\"]\n", + ) + .unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "vendored"], &[]); assert_eq!(code, 0, "{doc:#}"); let mut after_scan = repo.snapshot(); after_scan.remove("socket.yml"); - assert_eq!(after_scan, snapshot, "the vendored package, its lock wiring and ledger stay byte-identical"); - assert_eq!(doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", "{:#}", doc["policy"]); + assert_eq!( + after_scan, snapshot, + "the vendored package, its lock wiring and ledger stay byte-identical" + ); + assert_eq!( + doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", + "{:#}", + doc["policy"] + ); } - diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs index 83a8de749..0dd0998af 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs @@ -152,7 +152,11 @@ fn committed_pre_v5_ledger_lets_a_hosted_pin_attest_offline() { ); } api.assert_no_requests(); - assert_eq!(std::fs::read(&ledger).unwrap(), before, "vex never rewrites it"); + assert_eq!( + std::fs::read(&ledger).unwrap(), + before, + "vex never rewrites it" + ); let other = "0b0b0b0b-0b0b-4b0b-8b0b-0b0b0b0b0b0b"; let mut stale = left_pad_view(); diff --git a/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs b/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs index e32b4ea97..6cdd44ef1 100644 --- a/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs +++ b/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs @@ -469,8 +469,16 @@ fn get_help_lists_all_identifier_flags() { ); } // Help text is for users: no implementation notes from the source. - for leak in ["value_parser", "parse_bool_flag", "No env binding", "locally- installed"] { - assert!(!stdout.contains(leak), "get --help leaks {leak:?}: {stdout}"); + for leak in [ + "value_parser", + "parse_bool_flag", + "No env binding", + "locally- installed", + ] { + assert!( + !stdout.contains(leak), + "get --help leaks {leak:?}: {stdout}" + ); } } diff --git a/crates/socket-patch-cli/tests/get/global_packages_e2e.rs b/crates/socket-patch-cli/tests/get/global_packages_e2e.rs index 25bdadd21..a86958fe8 100644 --- a/crates/socket-patch-cli/tests/get/global_packages_e2e.rs +++ b/crates/socket-patch-cli/tests/get/global_packages_e2e.rs @@ -211,7 +211,10 @@ fn assert_rollback_noop(stdout: &str) { r["skipped"], "package_not_installed", "a no-op rollback may carry only not-installed markers; envelope={v}" ); - assert!(r["path"].is_null(), "marker path must be null; envelope={v}"); + assert!( + r["path"].is_null(), + "marker path must be null; envelope={v}" + ); assert!( r.get("success").is_none() && r.get("error").is_none(), "markers carry no success/error keys; envelope={v}" diff --git a/crates/socket-patch-cli/tests/help_text_hygiene.rs b/crates/socket-patch-cli/tests/help_text_hygiene.rs index 9b3280e8a..467ed46c5 100644 --- a/crates/socket-patch-cli/tests/help_text_hygiene.rs +++ b/crates/socket-patch-cli/tests/help_text_hygiene.rs @@ -61,7 +61,11 @@ fn every_help_page_has_no_developer_notes() { names.extend(cmd.get_subcommands().map(|s| s.get_name().to_string())); let mut failures = Vec::new(); for name in &names { - let path: Vec<&str> = if name.is_empty() { vec![] } else { vec![name.as_str()] }; + let path: Vec<&str> = if name.is_empty() { + vec![] + } else { + vec![name.as_str()] + }; let text = long_help(&path); let found = leaks(&text); if !found.is_empty() { @@ -147,7 +151,9 @@ fn vex_product_list_renders_one_item_per_line() { fn root_command_list_uses_the_verb_form() { let text = long_help(&[]); assert!( - text.contains("Undo patches: restore original files and unwind hosted or vendored lockfile wiring"), + text.contains( + "Undo patches: restore original files and unwind hosted or vendored lockfile wiring" + ), "{text}" ); assert!(!text.contains("Rollback patches"), "{text}"); @@ -258,11 +264,24 @@ fn short_help_lists_about_eight_options_and_long_help_lists_all() { .filter(|l| l.starts_with('-') && !l.starts_with("-h,") && !l.starts_with("-V,")) .count() }; - assert!(count(&short) <= 9, "{name} -h lists {} options:\n{short}", count(&short)); - assert!(count(&long) > count(&short), "{name} --help must list more than -h"); - assert!(short.contains("--json") && short.contains("--cwd"), "{name}"); + assert!( + count(&short) <= 9, + "{name} -h lists {} options:\n{short}", + count(&short) + ); + assert!( + count(&long) > count(&short), + "{name} --help must list more than -h" + ); + assert!( + short.contains("--json") && short.contains("--cwd"), + "{name}" + ); } let scan = cmd.find_subcommand_mut("scan").expect("scan"); let long = scan.render_long_help().to_string(); - assert!(!long.contains("--apply") && !long.contains("--vendor "), "{long}"); + assert!( + !long.contains("--apply") && !long.contains("--vendor "), + "{long}" + ); } diff --git a/crates/socket-patch-cli/tests/hosted_memory_engine.rs b/crates/socket-patch-cli/tests/hosted_memory_engine.rs index 761d34ea9..778baf094 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_engine.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_engine.rs @@ -984,7 +984,8 @@ async fn a_vlt_project_is_withheld_as_offline() { .and_then(|w| w["detail"].as_str()) .expect("the preflight warning is reported"); assert!( - detail.contains("/patch/npm//") && detail.contains(": offline; nothing was written"), + detail.contains("/patch/npm//") + && detail.contains(": offline; nothing was written"), "the offline refusal quotes the redacted URL" ); assert!(output.changed_files.is_empty()); diff --git a/crates/socket-patch-cli/tests/hosted_memory_parity.rs b/crates/socket-patch-cli/tests/hosted_memory_parity.rs index b297eaf79..0af392eb4 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_parity.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_parity.rs @@ -754,7 +754,11 @@ fn policy_repo(socket_yml: &str) -> (Vec, BTreeMap>) { let mut patches = patches_from_overrides(&npm.join("overrides.json"), None); patches.extend(patches_from_overrides(&cargo.join("overrides.json"), None)); let mut repo: BTreeMap> = BTreeMap::new(); - for (root, dir) in [("apps/web", &npm), ("apps/legacy", &npm), ("services/api", &cargo)] { + for (root, dir) in [ + ("apps/web", &npm), + ("apps/legacy", &npm), + ("services/api", &cargo), + ] { for (rel, bytes) in fixture_files(&dir.join("input")) { repo.insert(format!("{root}/{rel}"), bytes); } @@ -773,7 +777,9 @@ fn two_phase( socket_patch_cli::hosted_memory::PathSelection, socket_patch_cli::hosted_memory::HostedScanInput, ) { - use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; + use socket_patch_cli::hosted_memory::{ + select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, + }; let entries: Vec = files .iter() .map(|(p, bytes)| TreeEntryInput { @@ -814,15 +820,23 @@ fn two_phase( (selection, input) } -fn policy_input(files: &BTreeMap>) -> socket_patch_cli::hosted_memory::HostedScanInput { +fn policy_input( + files: &BTreeMap>, +) -> socket_patch_cli::hosted_memory::HostedScanInput { let (selection, input) = two_phase(files, options(false)); - assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); + assert!( + selection.policy_error.is_none(), + "{:?}", + selection.policy_error + ); input } /// Session options as selection of `files` would hand them over, without /// going through selection (for inputs a host may get wrong). -fn policy_options(files: &BTreeMap>) -> socket_patch_cli::hosted_memory::HostedScanOptions { +fn policy_options( + files: &BTreeMap>, +) -> socket_patch_cli::hosted_memory::HostedScanOptions { let (selection, _) = two_phase(files, options(false)); let mut opts = options(false); opts.policy_paths = Some(selection.policy_paths); @@ -854,25 +868,44 @@ async fn parity_socket_yml_filters_the_same_roots_and_packages() { let server = MockServer::start().await; mount_api(&server, &patches).await; let (selection, input) = two_phase(&repo, options(false)); - assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); + assert!( + selection.policy_error.is_none(), + "{:?}", + selection.policy_error + ); let memory = run_engine(&server, input).await; assert!(memory.policy_error.is_none(), "{:?}", memory.policy_error); let roots: Vec<&str> = memory.projects.iter().map(|p| p.root.as_str()).collect(); - assert_eq!(roots, vec!["apps/web", "services/api"], "the ignored root is not processed"); + assert_eq!( + roots, + vec!["apps/web", "services/api"], + "the ignored root is not processed" + ); // Selection reports the root it excluded; nothing of it is streamed. assert!(selection .ignored_sample .iter() .any(|i| i.path == "apps/legacy/package-lock.json" && i.reason == "policy_path_excluded")); - assert!(!selection.fetch_text.iter().chain(&selection.present_only).any(|p| p.starts_with("apps/legacy/"))); + assert!(!selection + .fetch_text + .iter() + .chain(&selection.present_only) + .any(|p| p.starts_with("apps/legacy/"))); let memory_policy = memory.policy.clone().expect("policy block"); assert_eq!(memory_policy["source"], "file"); let mut disk_filtered = std::collections::BTreeSet::new(); for root in ["apps/web", "apps/legacy", "services/api"] { let disk = run_disk_in(&server, &repo, root, false); - assert_eq!(disk.envelope["status"], "success", "{root}: {}", disk.stderr); - assert_eq!(disk.envelope["policy"]["sha256"], memory_policy["sha256"], "{root}"); + assert_eq!( + disk.envelope["status"], "success", + "{root}: {}", + disk.stderr + ); + assert_eq!( + disk.envelope["policy"]["sha256"], memory_policy["sha256"], + "{root}" + ); disk_filtered.extend(filtered_set(&disk.envelope["policy"])); if let Some(project) = memory.projects.iter().find(|p| p.root == root) { assert_eq!(project.redirect, disk.envelope["redirect"], "{root}"); @@ -883,13 +916,24 @@ async fn parity_socket_yml_filters_the_same_roots_and_packages() { .collect(); assert_eq!(memory_changed, disk.changed, "{root}"); } else { - assert!(disk.changed.is_empty(), "{root}: an ignored root changes nothing"); + assert!( + disk.changed.is_empty(), + "{root}: an ignored root changes nothing" + ); } } let mut memory_filtered = filtered_set(&memory_policy); - memory_filtered.insert(("apps/legacy".to_string(), None, "policy_path_excluded".to_string())); + memory_filtered.insert(( + "apps/legacy".to_string(), + None, + "policy_path_excluded".to_string(), + )); assert_eq!(memory_filtered, disk_filtered); - assert!(disk_filtered.contains(&("apps/legacy".to_string(), None, "policy_path_excluded".to_string()))); + assert!(disk_filtered.contains(&( + "apps/legacy".to_string(), + None, + "policy_path_excluded".to_string() + ))); assert!(disk_filtered.contains(&( "services/api".to_string(), Some("pkg:cargo/serde@1.0.190".to_string()), @@ -903,9 +947,17 @@ async fn parity_socket_yml_severity_floor() { let server = MockServer::start().await; mount_api(&server, &patches).await; let memory = run_engine(&server, policy_input(&repo)).await; - let web = memory.projects.iter().find(|p| p.root == "apps/web").unwrap(); + let web = memory + .projects + .iter() + .find(|p| p.root == "apps/web") + .unwrap(); assert!(web.redirected.is_empty(), "{:#}", web.redirect); - assert!(web.skipped.iter().any(|s| s.reason == "policy_severity"), "{:?}", web.skipped); + assert!( + web.skipped.iter().any(|s| s.reason == "policy_severity"), + "{:?}", + web.skipped + ); assert!(engine_changed(&memory).is_empty()); let disk = run_disk_in(&server, &repo, "apps/web", false); assert!(disk.changed.is_empty()); @@ -931,8 +983,15 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { assert_eq!(err.code, "socket_yml_invalid"); assert!(out.projects.is_empty() && out.changed_files.is_empty() && out.policy.is_none()); // Streamed present-without-content. - let out = run_engine(&server, build_input(&withheld, &["socket.yml"], opts.clone())).await; - assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); + let out = run_engine( + &server, + build_input(&withheld, &["socket.yml"], opts.clone()), + ) + .await; + assert_eq!( + out.policy_error.expect("policyError").code, + "socket_yml_invalid" + ); // Content other than what selection read. let mut changed = repo.clone(); changed.insert("socket.yml".to_string(), b"version: 2\n".to_vec()); @@ -943,7 +1002,10 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { let mut no_sha = opts.clone(); no_sha.policy_sha256 = None; let out = run_engine(&server, build_input(&repo, &[], no_sha)).await; - assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); + assert_eq!( + out.policy_error.expect("policyError").code, + "socket_yml_invalid" + ); // Invalid content: selection refuses it before anything is fetched. let (_, bad) = policy_repo("version: 2\npatches:\n apiUrl: https://evil.example\n"); let (selection, _) = two_phase(&bad, options(false)); @@ -958,7 +1020,10 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { let mut half = opts.clone(); half.no_socket_yml = Some(true); let out = run_engine(&server, build_input(&repo, &[], half)).await; - assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); + assert_eq!( + out.policy_error.expect("policyError").code, + "socket_yml_invalid" + ); // noSocketYml skips it on both sides. let mut bypass = options(false); bypass.no_socket_yml = Some(true); @@ -979,7 +1044,10 @@ async fn memory_min_severity_option_beats_the_file() { let (_, input) = two_phase(&repo, opts); let out = run_engine(&server, input).await; let policy = out.policy.unwrap(); - assert_eq!(policy["minSeverity"], serde_json::json!({"value": null, "source": "flag"})); + assert_eq!( + policy["minSeverity"], + serde_json::json!({"value": null, "source": "flag"}) + ); assert!(out.projects.iter().any(|p| !p.redirected.is_empty())); let mut bad = options(false); bad.min_severity = Some("severe".to_string()); @@ -988,7 +1056,9 @@ async fn memory_min_severity_option_beats_the_file() { #[test] fn selection_applies_the_path_policy_and_fails_closed() { - use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; + use socket_patch_cli::hosted_memory::{ + select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, + }; let blob = |path: &str, mode: &str| TreeEntryInput { path: path.to_string(), mode: mode.to_string(), @@ -1014,19 +1084,34 @@ fn selection_applies_the_path_policy_and_fails_closed() { }; let yml = "version: 2\npatches:\n ignorePaths: [\"/apps/old/\"]\n"; let selection = select_paths(&entries, &with(vec![text("socket.yml", yml)])); - assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); + assert!( + selection.policy_error.is_none(), + "{:?}", + selection.policy_error + ); assert_eq!(selection.policy_paths, vec!["socket.yml"]); assert_eq!(selection.policy_sha256.as_ref().map(String::len), Some(64)); assert!(selection.fetch_text.contains(&"socket.yml".to_string())); assert_eq!(selection.roots, vec!["apps/web"]); // Excluded roots (file list and built-in ignores, any case) are // reported and never streamed. - for path in ["apps/old/yarn.lock", "apps/web/tests/app/package-lock.json", "Fixtures/x/yarn.lock"] { + for path in [ + "apps/old/yarn.lock", + "apps/web/tests/app/package-lock.json", + "Fixtures/x/yarn.lock", + ] { assert!( - selection.ignored_sample.iter().any(|i| i.path == path && i.reason == "policy_path_excluded"), + selection + .ignored_sample + .iter() + .any(|i| i.path == path && i.reason == "policy_path_excluded"), "{path}: {selection:?}" ); - assert!(!selection.fetch_text.contains(&path.to_string()) && !selection.present_only.contains(&path.to_string()), "{path}"); + assert!( + !selection.fetch_text.contains(&path.to_string()) + && !selection.present_only.contains(&path.to_string()), + "{path}" + ); } // Named roots are explicit: the built-in ignores do not apply. let named = select_paths( @@ -1044,9 +1129,16 @@ fn selection_applies_the_path_policy_and_fails_closed() { text: None, missing: Some(true), }; - for files in [vec![], vec![missing], vec![text("socket.yml", "version: 2\npatches:\n apiUrl: x\n")]] { + for files in [ + vec![], + vec![missing], + vec![text("socket.yml", "version: 2\npatches:\n apiUrl: x\n")], + ] { let out = select_paths(&entries, &with(files)); - assert_eq!(out.policy_error.as_ref().map(|e| e.code.as_str()), Some("socket_yml_invalid")); + assert_eq!( + out.policy_error.as_ref().map(|e| e.code.as_str()), + Some("socket_yml_invalid") + ); assert!(out.roots.is_empty() && out.fetch_text.is_empty(), "{out:?}"); assert_eq!(out.policy_paths, vec!["socket.yml"]); } @@ -1054,8 +1146,14 @@ fn selection_applies_the_path_policy_and_fails_closed() { assert!(out.policy_error.is_some()); // A symlinked policy file is never read. entries.push(blob("socket.yaml", "120000")); - let out = select_paths(&entries, &with(vec![text("socket.yml", yml), text("socket.yaml", yml)])); - assert_eq!(out.policy_error.map(|e| e.code), Some("socket_yml_invalid".to_string())); + let out = select_paths( + &entries, + &with(vec![text("socket.yml", yml), text("socket.yaml", yml)]), + ); + assert_eq!( + out.policy_error.map(|e| e.code), + Some("socket_yml_invalid".to_string()) + ); // noSocketYml: only the built-in ignores; the file need not be passed. let out = select_paths( &entries, @@ -1086,8 +1184,13 @@ async fn memory_negation_reincludes_a_default_ignored_root() { ); let (selection, input) = two_phase(&repo, options(false)); assert_eq!(selection.roots, vec!["e2e/tests"]); - assert!(selection.fetch_text.contains(&"e2e/tests/package-lock.json".to_string())); - assert!(!selection.fetch_text.iter().any(|p| p.starts_with("x/")), "{selection:?}"); + assert!(selection + .fetch_text + .contains(&"e2e/tests/package-lock.json".to_string())); + assert!( + !selection.fetch_text.iter().any(|p| p.starts_with("x/")), + "{selection:?}" + ); assert!(selection .ignored_sample .iter() @@ -1095,19 +1198,31 @@ async fn memory_negation_reincludes_a_default_ignored_root() { let memory = run_engine(&server, input).await; let roots: Vec<&str> = memory.projects.iter().map(|p| p.root.as_str()).collect(); assert_eq!(roots, vec!["e2e/tests"]); - assert!(!memory.projects[0].redirected.is_empty(), "{:#}", memory.projects[0].redirect); + assert!( + !memory.projects[0].redirected.is_empty(), + "{:#}", + memory.projects[0].redirect + ); // Given every root anyway, the session applies the same filter itself. let direct = run_engine(&server, build_input(&repo, &[], policy_options(&repo))).await; let roots: Vec<&str> = direct.projects.iter().map(|p| p.root.as_str()).collect(); assert_eq!(roots, vec!["e2e/tests"]); let entry = &direct.policy.as_ref().unwrap()["filtered"][0]; - assert_eq!((entry["project"].as_str(), entry["detail"].as_str()), (Some("x/tests"), Some("tests/ (built-in default)"))); + assert_eq!( + (entry["project"].as_str(), entry["detail"].as_str()), + (Some("x/tests"), Some("tests/ (built-in default)")) + ); // Disk patches the same root the same way. let disk = run_disk_in(&server, &repo, "e2e/tests", false); assert_eq!(disk.envelope["status"], "success", "{}", disk.stderr); assert_eq!(memory.projects[0].redirect, disk.envelope["redirect"]); let memory_changed = engine_changed(&memory); - assert_eq!(memory_changed, disk.changed, "{}", describe(&memory_changed)); + assert_eq!( + memory_changed, + disk.changed, + "{}", + describe(&memory_changed) + ); } diff --git a/crates/socket-patch-cli/tests/hosted_memory_rollout.rs b/crates/socket-patch-cli/tests/hosted_memory_rollout.rs index ccaf92cc4..3c104abf4 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_rollout.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_rollout.rs @@ -237,7 +237,9 @@ async fn memory_selected( files: &BTreeMap>, mut o: HostedScanOptions, ) -> HostedScanOutput { - use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; + use socket_patch_cli::hosted_memory::{ + select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, + }; let entries: Vec = files .iter() .map(|(p, bytes)| TreeEntryInput { @@ -265,7 +267,11 @@ async fn memory_selected( ..SelectOptions::default() }, ); - assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); + assert!( + selection.policy_error.is_none(), + "{:?}", + selection.policy_error + ); let fetched: BTreeMap> = selection .fetch_text .iter() @@ -424,7 +430,11 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { b"version: 2\npatches:\n includePaths: [\"/apps/\"]\n minSeverity: high\n maxNewPatches: 2\n" .to_vec(), ); - lock(&mut files, "apps/one", &["mem-a", "mem-b", "mem-c", "mem-d", "mem-e"]); + lock( + &mut files, + "apps/one", + &["mem-a", "mem-b", "mem-c", "mem-d", "mem-e"], + ); lock(&mut files, "apps/two", &["mem-b", "mem-c", "mem-d"]); lock(&mut files, "legacy", &["mem-b", "mem-e"]); let dirs = ["apps/one", "apps/two", "legacy"]; @@ -435,8 +445,16 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { }; let expected: [Vec>; 3] = [ vec![vec!["mem-e", "mem-b"], vec!["mem-b"], vec![]], - vec![vec!["mem-e", "mem-b", "mem-c"], vec!["mem-b", "mem-c"], vec![]], - vec![vec!["mem-e", "mem-b", "mem-c"], vec!["mem-b", "mem-c"], vec![]], + vec![ + vec!["mem-e", "mem-b", "mem-c"], + vec!["mem-b", "mem-c"], + vec![], + ], + vec![ + vec!["mem-e", "mem-b", "mem-c"], + vec!["mem-b", "mem-c"], + vec![], + ], ]; let mut mem_files = files.clone(); @@ -449,7 +467,10 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { "run {}", run + 1 ); - assert_eq!(mem.policy.as_ref().map(|p| p["source"].clone()), Some(json!("file"))); + assert_eq!( + mem.policy.as_ref().map(|p| p["source"].clone()), + Some(json!("file")) + ); mem_files = apply(&mem_files, &mem); assert_eq!(&pins(&mem_files), want, "memory run {}", run + 1); @@ -469,7 +490,14 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { let (code, stdout, changed) = run_disk_args( &server, &disk_files, - &["--no-socket-yml", "--max-new-patches", "1", "apps/one", "apps/two", "legacy"], + &[ + "--no-socket-yml", + "--max-new-patches", + "1", + "apps/one", + "apps/two", + "legacy", + ], ); assert_eq!(code, 0, "{stdout}"); disk_files.extend(changed); diff --git a/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs b/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs index ff45dcd4b..1bad484a9 100644 --- a/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs +++ b/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs @@ -468,7 +468,11 @@ fn maven_hosted_get_state_attests_without_manifest( &[(purl, vlt_hosted_common::legacy_record_from_view(&view))], ); let out = run_vex(&binary(), project, &offline); - assert_eq!(out.code, Some(0), "a pre-v5 ledger record serves offline: {out}"); + assert_eq!( + out.code, + Some(0), + "a pre-v5 ledger record serves offline: {out}" + ); assert_attested(out.doc(), purl, uuid, Marker::Redirected, &vulns); quiet.assert_no_requests(); @@ -749,7 +753,11 @@ fn nuget_hosted_manifestless_vex(root: &Path, uuid: &str, purl: &str) { &[(purl, vlt_hosted_common::legacy_record_from_view(&view))], ); let out = run(VexRun::offline()); - assert_eq!(out.code, Some(0), "a pre-v5 ledger record serves offline: {out}"); + assert_eq!( + out.code, + Some(0), + "a pre-v5 ledger record serves offline: {out}" + ); assert_attested(out.doc(), purl, uuid, Marker::Redirected, vulns); std::fs::write( diff --git a/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs b/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs index f90893f39..33127617c 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs @@ -308,11 +308,15 @@ async fn scan_redirect_vlt_artifact_fetch_error() { let detail = warning_detail(&doc, UNVERIFIABLE); let redacted = url.replace(&format!("/{TOKEN}/"), "//"); assert!( - detail.starts_with(&format!("vlt would fail to verify {redacted}: fetch error ")) - && detail.ends_with(&format!("; nothing was written for {PURL}")), + detail.starts_with(&format!( + "vlt would fail to verify {redacted}: fetch error " + )) && detail.ends_with(&format!("; nothing was written for {PURL}")), "the fetch-error refusal quotes the redacted URL" ); - assert!(!detail.contains(TOKEN), "the grant token never reaches the warning"); + assert!( + !detail.contains(TOKEN), + "the grant token never reaches the warning" + ); } async fn redirect_chain(hops: usize) -> (Value, tempfile::TempDir) { diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs index 97f4a171d..4319bd6d3 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs @@ -187,7 +187,9 @@ async fn mock_api(server: &MockServer) { .mount(server) .await; Mock::given(method("GET")) - .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) + .and(path_regex(format!( + "^/v0/orgs/{ORG}/patches/by-package/.+$" + ))) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "patches": [{ "uuid": UUID, "purl": RECORD_PURL, @@ -368,9 +370,12 @@ fn legacy_record(view: &serde_json::Value) -> serde_json::Value { .remove("publishedAt") .unwrap_or_else(|| serde_json::json!("2024-01-01T00:00:00Z")); obj.insert("exportedAt".to_string(), exported); - obj.entry("description").or_insert_with(|| serde_json::json!("x")); - obj.entry("license").or_insert_with(|| serde_json::json!("MIT")); - obj.entry("tier").or_insert_with(|| serde_json::json!("free")); + obj.entry("description") + .or_insert_with(|| serde_json::json!("x")); + obj.entry("license") + .or_insert_with(|| serde_json::json!("MIT")); + obj.entry("tier") + .or_insert_with(|| serde_json::json!("free")); record } @@ -441,7 +446,11 @@ async fn lock_only_pdm_project_redirects_attests_rescans_and_rolls_back() { // 2. Idempotent re-scan: no further edits, lock byte-identical. let code = run(hosted_args(tmp.path(), server.uri(), None)).await; assert_eq!(code, 0); - assert_eq!(read(&lock_path), redirected, "re-scan must not touch the lock"); + assert_eq!( + read(&lock_path), + redirected, + "re-scan must not touch the lock" + ); // 3. The committed state, manifest-less, attests (and only while wired). assert_manifestless_vex(tmp.path(), LOCK); @@ -494,12 +503,19 @@ async fn hatchling_build_backend_does_not_veto_the_pdm_lock_redirect() { .iter() .filter(|r| r.url.path().ends_with(&format!("/patches/view/{UUID}"))) .count(); - assert_eq!(views, 1, "the pdm redirect must be confirmed despite the hatch backend"); + assert_eq!( + views, 1, + "the pdm redirect must be confirmed despite the hatch backend" + ); assert_manifestless_vex(tmp.path(), LOCK); let code = rollback_hosted(tmp.path(), &server).await; assert_eq!(code, 0, "rollback must succeed"); - assert_eq!(read(&lock_path), LOCK, "rollback must restore the pristine lock"); + assert_eq!( + read(&lock_path), + LOCK, + "rollback must restore the pristine lock" + ); } /// The legacy `[metadata.files]` lock (lock_version 2) redirects the package diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs index bf2e00fd3..335cf6cb3 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -55,7 +55,8 @@ const MAJOR_ENV: &str = socket_patch_core::utils::pipenv::MAJOR_OVERRIDE_ENV; const LOCK: &str = include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile.lock"); -const PIPFILE: &str = include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile"); +const PIPFILE: &str = + include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile"); /// The upstream and patched bytes of the record's one file, so the venv /// tests can materialize a real `Ready` (upstream) install. @@ -120,7 +121,9 @@ async fn mock_api(server: &MockServer) { .mount(server) .await; Mock::given(method("GET")) - .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) + .and(path_regex(format!( + "^/v0/orgs/{ORG}/patches/by-package/.+$" + ))) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "patches": [{ "uuid": UUID, "purl": RECORD_PURL, @@ -369,8 +372,15 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { ); let before: serde_json::Value = serde_json::from_str(LOCK).unwrap(); let after: serde_json::Value = serde_json::from_str(&redirected).unwrap(); - assert_eq!(after["_meta"], before["_meta"], "the Pipfile content hash stays"); - assert_eq!(read(&tmp.path().join("Pipfile")), PIPFILE, "Pipfile untouched"); + assert_eq!( + after["_meta"], before["_meta"], + "the Pipfile content hash stays" + ); + assert_eq!( + read(&tmp.path().join("Pipfile")), + PIPFILE, + "Pipfile untouched" + ); assert_no_ledger(tmp.path()); // Attested from this run's fetched record (keyed by RECORD_PURL, assume // applied) although the base purl the run confirmed differs from the @@ -378,13 +388,25 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { let vex: serde_json::Value = serde_json::from_str(&read(&vex_path)).unwrap(); let statements = vex["statements"].as_array().expect("statements"); assert_eq!(statements.len(), 1, "{vex}"); - assert_eq!(statements[0]["vulnerability"]["name"].as_str(), Some(GHSA), "{vex}"); - assert_eq!(statements[0]["status"].as_str(), Some("not_affected"), "{vex}"); + assert_eq!( + statements[0]["vulnerability"]["name"].as_str(), + Some(GHSA), + "{vex}" + ); + assert_eq!( + statements[0]["status"].as_str(), + Some("not_affected"), + "{vex}" + ); // 2. Idempotent re-scan: no further edits, lock byte-identical. let code = run(hosted_args(tmp.path(), server.uri(), None)).await; assert_eq!(code, 0); - assert_eq!(read(&lock_path), redirected, "re-scan must not touch the lock"); + assert_eq!( + read(&lock_path), + redirected, + "re-scan must not touch the lock" + ); assert_no_ledger(tmp.path()); // Manifest-less VEX over the committed state (the depscan / CI shape). @@ -394,7 +416,11 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { // 3. rollback restores the upstream registry entry. roll_back(tmp.path(), &server).await; - assert_eq!(read(&lock_path), LOCK, "rollback must restore the pristine lock byte for byte"); + assert_eq!( + read(&lock_path), + LOCK, + "rollback must restore the pristine lock byte for byte" + ); } #[tokio::test] @@ -417,7 +443,10 @@ async fn legacy_installer_major_selects_path_references() { "Pipenv 7–11 install `path` references: {redirected}" ); assert!(entry.get("file").is_none(), "{entry}"); - assert_eq!(entry["hashes"], serde_json::json!([format!("sha256:{}", sha256())])); + assert_eq!( + entry["hashes"], + serde_json::json!([format!("sha256:{}", sha256())]) + ); // The legacy `path` reference is discovered just like `file`. manifestless_vex(tmp.path(), "pipenv legacy path", &|p: &Path| { @@ -438,7 +467,9 @@ async fn stale_pipfile_lock_does_not_veto_the_requirements_redirect() { write_project(tmp.path()); // The Pipfile.lock left behind pins a DIFFERENT package; the project // installs from requirements.txt. - let stale = LOCK.replace("\"urllib3\"", "\"six\"").replace("==1.26.18", "==1.16.0"); + let stale = LOCK + .replace("\"urllib3\"", "\"six\"") + .replace("==1.26.18", "==1.16.0"); std::fs::write(tmp.path().join("Pipfile.lock"), &stale).unwrap(); // An unpatched, unhashed sibling makes the file's hash mode derivable, // so rollback can restore the hosted line (a file whose every line is a @@ -466,10 +497,7 @@ async fn stale_pipfile_lock_does_not_veto_the_requirements_redirect() { }); roll_back(tmp.path(), &server).await; - assert_eq!( - read(&tmp.path().join("requirements.txt")), - REQS - ); + assert_eq!(read(&tmp.path().join("requirements.txt")), REQS); assert_eq!(read(&tmp.path().join("Pipfile.lock")), stale); } @@ -490,16 +518,27 @@ async fn warm_venv_with_the_upstream_release_is_not_attested() { // attested and the embedded-VEX contract fails the command. let code = run(hosted_args(tmp.path(), server.uri(), Some(&vex_path))).await; let redirected = read(&lock_path); - assert!(redirected.contains(HOSTED_URL), "the lock is still repointed: {redirected}"); + assert!( + redirected.contains(HOSTED_URL), + "the lock is still repointed: {redirected}" + ); let attested = vex_path .exists() .then(|| serde_json::from_str::(&read(&vex_path)).unwrap()) .and_then(|v| v["statements"].as_array().map(Vec::len)) .unwrap_or(0); - assert_eq!(attested, 0, "a stale install must not be attested from the fetched record"); + assert_eq!( + attested, 0, + "a stale install must not be attested from the fetched record" + ); assert_ne!(code, 0, "nothing to attest fails the embedded-VEX run"); assert_eq!( - std::fs::read(site_packages(tmp.path()).join("urllib3").join("response.py")).unwrap(), + std::fs::read( + site_packages(tmp.path()) + .join("urllib3") + .join("response.py") + ) + .unwrap(), UPSTREAM, "the probe is read-only" ); diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs index 592d72c14..e52a6bca2 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs @@ -56,7 +56,10 @@ async fn rollback_hosted(cwd: &Path, server: &MockServer) -> i32 { }))) .mount(server) .await; - std::env::set_var("SOCKET_NPM_REGISTRY", format!("{}/npm-registry", server.uri())); + std::env::set_var( + "SOCKET_NPM_REGISTRY", + format!("{}/npm-registry", server.uri()), + ); let code = rollback::run(RollbackArgs { targets: Vec::new(), common: socket_patch_cli::args::GlobalArgs { @@ -764,7 +767,11 @@ async fn hosted_pnpm_manifestless_vex_from_lockfile_legacy_ledger_and_api() { ..VexRun::offline() }, ); - assert_eq!(out.code, Some(0), "[{lock_name}] legacy ledger, offline: {out}"); + assert_eq!( + out.code, + Some(0), + "[{lock_name}] legacy ledger, offline: {out}" + ); assert_attested(out.doc(), PURL, UUID, Marker::Redirected, vulns); assert_eq!(api.request_count(), seen); diff --git a/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs b/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs index 8779d2e84..9c08880b2 100644 --- a/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs +++ b/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs @@ -221,7 +221,10 @@ async fn vlt_repair_reports_a_missing_ledger() { lock_bytes, "{lock:?}" ); - assert!(tmp.path().join(rel()).join("index.js").is_file(), "{lock:?}"); + assert!( + tmp.path().join(rel()).join("index.js").is_file(), + "{lock:?}" + ); } } diff --git a/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs b/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs index d4830cbd9..31f457502 100644 --- a/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs +++ b/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs @@ -533,8 +533,7 @@ fn bare_word_target_stays_identifier_error() { )], false, ); - let manifest_before = - std::fs::read(socket.join("manifest.json")).expect("read manifest bytes"); + let manifest_before = std::fs::read(socket.join("manifest.json")).expect("read manifest bytes"); let (code, stdout, stderr) = run(tmp.path(), &["--offline", "lodash"]); assert_eq!( diff --git a/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs b/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs index 5fee90f88..87d4900a3 100644 --- a/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs +++ b/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs @@ -253,7 +253,10 @@ fn rollback_dispatch_branch_deno() { .unwrap_or_else(|e| panic!("rollback envelope must parse ({e}); stdout={stdout}")); let code = out.status.code().unwrap_or(-1); - assert_eq!(code, 0, "rollback --ecosystems=deno: expected exit 0; env={env}"); + assert_eq!( + code, 0, + "rollback --ecosystems=deno: expected exit 0; env={env}" + ); assert_eq!( env["status"], "success", "rollback --ecosystems=deno: expected success; env={env}" @@ -294,7 +297,8 @@ fn rollback_dispatch_branch_deno() { // The decisive check: the on-disk bytes are restored to ORIGINAL. let restored = std::fs::read(&verify_file).unwrap(); assert_eq!( - restored, ORIGINAL, + restored, + ORIGINAL, "rollback --ecosystems=deno: {} was not restored to its original bytes", verify_file.display() ); diff --git a/crates/socket-patch-cli/tests/scan/scan_invariants.rs b/crates/socket-patch-cli/tests/scan/scan_invariants.rs index c3316ee78..f4bb749e1 100644 --- a/crates/socket-patch-cli/tests/scan/scan_invariants.rs +++ b/crates/socket-patch-cli/tests/scan/scan_invariants.rs @@ -1787,7 +1787,11 @@ async fn report_only_scan_json_redirect_state_keys_on_lock_pins() { serde_json::json!([{ "purl": purl, "uuid": AGENT_WARN_UUID }]), "the lock pin is the record; envelope={v}" ); - assert_eq!(state["wiringLive"], serde_json::json!([purl]), "envelope={v}"); + assert_eq!( + state["wiringLive"], + serde_json::json!([purl]), + "envelope={v}" + ); // No pin, no ledger: the key must stay absent (additive contract). let clean = tempfile::tempdir().expect("tempdir"); @@ -1832,7 +1836,8 @@ async fn report_only_scan_json_ignores_a_stale_pre_v5_ledger_record() { integrity sha512-orig==\n", ) .unwrap(); - let ledger_before = std::fs::read(tmp.path().join(".socket/vendor/redirect-state.json")).unwrap(); + let ledger_before = + std::fs::read(tmp.path().join(".socket/vendor/redirect-state.json")).unwrap(); for extra in [&["--prune"][..], &["--mode", "agent", "--dry-run"][..]] { let (code, stdout, stderr) = run_scan(tmp.path(), &mock.uri(), extra); @@ -2053,10 +2058,7 @@ async fn scan_ignores_a_malformed_pre_v5_ledger() { "{extra:?}: a pre-v5 ledger is never read, so never reported: {stderr}" ); let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON"); - assert!( - v.get("redirectState").is_none(), - "{extra:?}: envelope={v}" - ); + assert!(v.get("redirectState").is_none(), "{extra:?}: envelope={v}"); assert_eq!( std::fs::read(vendor_dir.join("redirect-state.json")).unwrap(), b"{ torn ledger", @@ -2090,7 +2092,11 @@ async fn ecosystems_filter_keeps_records_but_not_wiring_live() { /*with_record=*/ true, ); - let (code, stdout, stderr) = run_scan(tmp.path(), &mock.uri(), &["--mode", "agent", "--dry-run", "--ecosystems", "pypi"]); + let (code, stdout, stderr) = run_scan( + tmp.path(), + &mock.uri(), + &["--mode", "agent", "--dry-run", "--ecosystems", "pypi"], + ); assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON"); let state = &v["redirectState"]; diff --git a/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs b/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs index 8ad94e551..64ea1197c 100644 --- a/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs +++ b/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs @@ -216,7 +216,11 @@ async fn paths_scope_narrows_the_query() { let tmp = tempfile::tempdir().unwrap(); write_two_subtree_project(tmp.path()); - let (code, stdout, stderr) = run_scan(tmp.path(), &server.uri(), &["packages/app", "--mode", "agent", "--dry-run"]); + let (code, stdout, stderr) = run_scan( + tmp.path(), + &server.uri(), + &["packages/app", "--mode", "agent", "--dry-run"], + ); assert_eq!( code, 0, "scoped scan must exit 0; stdout={stdout}; stderr={stderr}" @@ -477,7 +481,11 @@ async fn supplements_excluded_with_warning() { // purl reaches the API. let scoped_server = MockServer::start().await; mock_batch_empty(&scoped_server).await; - let (code, stdout, stderr) = run_scan(tmp.path(), &scoped_server.uri(), &["packages/app", "--mode", "agent", "--dry-run"]); + let (code, stdout, stderr) = run_scan( + tmp.path(), + &scoped_server.uri(), + &["packages/app", "--mode", "agent", "--dry-run"], + ); assert_eq!( code, 0, "scoped scan must exit 0; stdout={stdout}; stderr={stderr}" diff --git a/crates/socket-patch-cli/tests/update/covgap_commands_update.rs b/crates/socket-patch-cli/tests/update/covgap_commands_update.rs index b2d75aafc..16836e614 100644 --- a/crates/socket-patch-cli/tests/update/covgap_commands_update.rs +++ b/crates/socket-patch-cli/tests/update/covgap_commands_update.rs @@ -268,9 +268,8 @@ mod pty { let mut child = pair.slave.spawn_command(cmd).expect("spawn in PTY"); drop(pair.slave); - let reader_handle = crate::pty_io::PtyOutput::spawn( - pair.master.try_clone_reader().expect("clone reader"), - ); + let reader_handle = + crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); let mut killer = child.clone_killer(); std::thread::spawn(move || { @@ -338,7 +337,8 @@ mod pty { "a declined update exits 1 (codebase convention); got: {output}" ); assert!( - !output.contains("Updated socket-patch") && !output.contains("Reinstalled socket-patch"), + !output.contains("Updated socket-patch") + && !output.contains("Reinstalled socket-patch"), "a declined update must not report a swap; got: {output}" ); diff --git a/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs b/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs index 04ce2881e..a012b53d7 100644 --- a/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs +++ b/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs @@ -660,7 +660,9 @@ pub fn run_manifestless_vex_matrix(flow: &BerryVexFlow<'_>) -> Vec { crate::vex_e2e_common::assert_no_hosted_ledger(&fresh, "manifest-deleted"); } else { assert!( - fresh.join(socket_patch_core::vendor::VENDOR_STATE_REL).is_file(), + fresh + .join(socket_patch_core::vendor::VENDOR_STATE_REL) + .is_file(), "manifest-deleted: the vendored flow must have left its .socket/vendor ledger" ); } diff --git a/crates/socket-patch-core/src/api/ranking.rs b/crates/socket-patch-core/src/api/ranking.rs index 949931782..58ca27078 100644 --- a/crates/socket-patch-core/src/api/ranking.rs +++ b/crates/socket-patch-core/src/api/ranking.rs @@ -164,8 +164,14 @@ pub fn batch_supersedes(candidate: &BatchPatchInfo, applied: &BatchPatchInfo) -> /// classify a recorded patch (ALREADY vs UPGRADE) and to report /// `updates[]`, on the same records that pick the patch, so selection, /// classification and reporting cannot disagree. -pub fn search_result_supersedes(candidate: &PatchSearchResult, recorded: &PatchSearchResult) -> bool { - key_supersedes(&rank_search_result(candidate), &rank_search_result(recorded)) +pub fn search_result_supersedes( + candidate: &PatchSearchResult, + recorded: &PatchSearchResult, +) -> bool { + key_supersedes( + &rank_search_result(candidate), + &rank_search_result(recorded), + ) } fn key_supersedes(c: &RankKey<'_>, a: &RankKey<'_>) -> bool { @@ -371,12 +377,7 @@ mod tests { "2020-01-01T00:00:00Z", &["critical", "high"] ), - search_multi( - "z_new_low", - "free", - "2026-08-01T00:00:00Z", - &["low", "low"] - ), + search_multi("z_new_low", "free", "2026-08-01T00:00:00Z", &["low", "low"]), ]), "a_old_critical" ); diff --git a/crates/socket-patch-core/src/crawlers/npm_crawler.rs b/crates/socket-patch-core/src/crawlers/npm_crawler.rs index 922578491..9f7be693a 100644 --- a/crates/socket-patch-core/src/crawlers/npm_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/npm_crawler.rs @@ -958,7 +958,11 @@ impl NpmCrawler { /// Inside a store entry (`store_entry`) a link is a dependency edge into /// a sibling entry, whose own visit records that copy, so only a real /// directory there matches. - fn visit_resolver_dir(nm_path: PathBuf, store_entry: bool, pending: &[Target]) -> ResolverVisit { + fn visit_resolver_dir( + nm_path: PathBuf, + store_entry: bool, + pending: &[Target], + ) -> ResolverVisit { let listing = list_dir_sync(&nm_path); let probe_filter = ProbeFilter::new(&listing); let matched = pending diff --git a/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs b/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs index d71a02127..2d6e225c1 100644 --- a/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs +++ b/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs @@ -9,9 +9,9 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use super::{ - build_npm_purl, is_legacy_pnpm_store_dir_name, - is_safe_npm_component, parse_package_name, read_package_json, NpmCrawler, StoreEntry, - Target, NESTED_STORE_MAX_DEPTH, NESTED_STORE_MAX_DIRS, SKIP_DIRS, VLT_STORE_NAME, + build_npm_purl, is_legacy_pnpm_store_dir_name, is_safe_npm_component, parse_package_name, + read_package_json, NpmCrawler, StoreEntry, Target, NESTED_STORE_MAX_DEPTH, + NESTED_STORE_MAX_DIRS, SKIP_DIRS, VLT_STORE_NAME, }; use crate::crawlers::types::{CrawledPackage, CrawlerOptions}; use crate::utils::fs::is_dir; diff --git a/crates/socket-patch-core/src/crawlers/python_crawler.rs b/crates/socket-patch-core/src/crawlers/python_crawler.rs index 03814ab23..8cb26a0d0 100644 --- a/crates/socket-patch-core/src/crawlers/python_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/python_crawler.rs @@ -1069,7 +1069,8 @@ fn run_site_query() -> Option { /// Get global/system Python `site-packages` directories. /// /// Queries `python3` for site-packages paths, then checks well-known system -/// locations including Homebrew, conda, uv tools, pip --user, etc. +/// locations including Homebrew, conda, uv tools, pipx venvs, pip --user, +/// etc. pub async fn get_global_python_site_packages() -> Vec { let mut results = Vec::new(); let mut seen = HashSet::new(); @@ -1086,10 +1087,8 @@ pub async fn get_global_python_site_packages() -> Vec { } // 1. Ask Python for site-packages (subprocesses: on the blocking pool) - let site_output = run_blocking(|| { - SITE_QUERY_MEMO.get_or_run(site_query_key(), run_site_query) - }) - .await; + let site_output = + run_blocking(|| SITE_QUERY_MEMO.get_or_run(site_query_key(), run_site_query)).await; if let Some(stdout) = site_output { for p in parse_python_site_packages_output(&stdout) { add_path(p, &mut seen, &mut results); @@ -1293,6 +1292,26 @@ pub async fn get_global_python_site_packages() -> Vec { } } + // pipx app venvs (`pipx install hatch`): one venv per app under + // `/venvs/`. Every candidate home that exists is + // scanned, not just the one pipx would pick today: an app installed + // under an older default is still a real install, and `seen` dedups + // overlaps (e.g. PIPX_HOME set to the default). + for pipx_home in pipx_home_candidates(&home_dir) { + let venvs = pipx_home.join("venvs"); + #[cfg(not(windows))] + let mut matches = + find_python_dirs(&venvs, &["*", "lib", "python3.*", "site-packages"]).await; + #[cfg(not(windows))] + matches + .extend(find_python_dirs(&venvs, &["*", "lib64", "python3.*", "site-packages"]).await); + #[cfg(windows)] + let matches = find_python_dirs(&venvs, &["*", "Lib", "site-packages"]).await; + for m in matches { + add_path(m, &mut seen, &mut results); + } + } + // uv-managed Python interpreters (`uv python install 3.X`) live at: // Linux/macOS: ~/.local/share/uv/python/cpython-3.X.*/lib/python3.X/site-packages/ // Windows: %LOCALAPPDATA%\uv\python\cpython-3.X.*\Lib\site-packages\ @@ -1328,6 +1347,49 @@ pub async fn get_global_python_site_packages() -> Vec { results } +/// The directories pipx may use as its home, most specific first. +/// +/// pipx (>= 1.3, `pipx/paths.py`) uses `$PIPX_HOME` when set, otherwise +/// its legacy home `~/.local/pipx` if that exists, otherwise platformdirs' +/// user data dir: `$XDG_DATA_HOME/pipx` (default `~/.local/share/pipx`) on +/// Linux, `~/Library/Application Support/pipx` on macOS, and +/// `%USERPROFILE%\pipx` on Windows (with `%LOCALAPPDATA%\pipx\pipx` as its +/// platformdirs fallback). All of them are returned; callers skip the ones +/// that don't exist. +fn pipx_home_candidates(home_dir: &Path) -> Vec { + let mut homes = Vec::new(); + if let Some(pipx_home) = std::env::var_os("PIPX_HOME").filter(|v| !v.is_empty()) { + homes.push(PathBuf::from(pipx_home)); + } + homes.push(home_dir.join(".local").join("pipx")); + #[cfg(all(not(target_os = "macos"), not(windows)))] + { + // platformdirs ignores a relative XDG_DATA_HOME, per the XDG spec. + if let Some(xdg) = std::env::var_os("XDG_DATA_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + { + homes.push(xdg.join("pipx")); + } + homes.push(home_dir.join(".local").join("share").join("pipx")); + } + #[cfg(target_os = "macos")] + homes.push( + home_dir + .join("Library") + .join("Application Support") + .join("pipx"), + ); + #[cfg(windows)] + { + homes.push(home_dir.join("pipx")); + if let Ok(local) = std::env::var("LOCALAPPDATA") { + homes.push(PathBuf::from(local).join("pipx").join("pipx")); + } + } + homes +} + /// Returns true if `cwd` looks like a Python project root. /// /// Used by `PythonCrawler::get_site_packages_paths` to decide diff --git a/crates/socket-patch-core/src/formats/cargo/mod.rs b/crates/socket-patch-core/src/formats/cargo/mod.rs index 58b4dc2bc..3e9cb9c5b 100644 --- a/crates/socket-patch-core/src/formats/cargo/mod.rs +++ b/crates/socket-patch-core/src/formats/cargo/mod.rs @@ -34,7 +34,6 @@ use crate::utils::purl::simple_purl; use crate::vendor::cargo_tag; use crate::vendor::lock_inventory::{LockIntegrity, LockfileEntry, SourceKind}; - // ── entry model ── /// The `[metadata]` key a v1 lock files `name`+`version`'s checksum under. @@ -332,7 +331,6 @@ pub(crate) fn parse_ref(spelled: &str) -> (&str, Option<&str>, Option<&str>) { (name, version, source) } - // ── the model ── /// One `Cargo.lock`, parsed once (see the module docs). @@ -500,7 +498,13 @@ impl CargoLock { uuid: &str, copy_tagged: bool, ) -> CopyClaim<'_> { - vendored_copy_claim(&self.packages, &self.unused, name, version, uuid, copy_tagged) + vendored_copy_claim( + &self.packages, + &self.unused, + name, + version, + uuid, + copy_tagged, + ) } } - diff --git a/crates/socket-patch-core/src/formats/composer/hosted.rs b/crates/socket-patch-core/src/formats/composer/hosted.rs index 9564e65d2..d760cbcd0 100644 --- a/crates/socket-patch-core/src/formats/composer/hosted.rs +++ b/crates/socket-patch-core/src/formats/composer/hosted.rs @@ -10,11 +10,11 @@ use std::sync::LazyLock; use regex::Regex; -use crate::utils::composer_version::composer_versions_equivalent; use super::source as composer_source; use crate::patch::redirect::{ artifact_url_present, full_name, DepOverride, RewriteResult, RewriteWarning, }; +use crate::utils::composer_version::composer_versions_equivalent; /// Byte offset of the `}` closing the JSON object that CONTAINS `from`, which /// must be a position inside that object. Brace counting skips string literals, diff --git a/crates/socket-patch-core/src/formats/composer/mod.rs b/crates/socket-patch-core/src/formats/composer/mod.rs index 8efa3c178..d45156ceb 100644 --- a/crates/socket-patch-core/src/formats/composer/mod.rs +++ b/crates/socket-patch-core/src/formats/composer/mod.rs @@ -22,7 +22,6 @@ use crate::utils::digest::sha1_hex; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry, SourceKind}; use crate::vendor::path::{parse_vendor_path, VendorPathParts}; - // ── entry model ── /// One entry of a parsed `composer.lock` (see [`composer_lock_packages`]). @@ -107,7 +106,6 @@ pub(crate) fn composer_lock_packages(doc: &Value) -> Vec out } - // ── the model ── /// One `composer.lock`, read once (see the module docs). @@ -177,4 +175,3 @@ impl<'a> ComposerLock<'a> { out } } - diff --git a/crates/socket-patch-core/src/formats/gem/hosted.rs b/crates/socket-patch-core/src/formats/gem/hosted.rs index 0416c3594..5dd4dc8b3 100644 --- a/crates/socket-patch-core/src/formats/gem/hosted.rs +++ b/crates/socket-patch-core/src/formats/gem/hosted.rs @@ -304,4 +304,3 @@ pub(crate) fn checksum_entry_span(lock: &str, name: &str, version: &str) -> Opti } None } - diff --git a/crates/socket-patch-core/src/formats/gem/mod.rs b/crates/socket-patch-core/src/formats/gem/mod.rs index a8054fc12..4719f804f 100644 --- a/crates/socket-patch-core/src/formats/gem/mod.rs +++ b/crates/socket-patch-core/src/formats/gem/mod.rs @@ -26,7 +26,6 @@ use crate::utils::digest::sha256_hex; use crate::utils::purl::simple_purl; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry, SourceKind}; - /// The Bundler lockfiles, legacy spelling first: `Gemfile.lock` and /// `gems.locked` (what bundler writes instead when the manifest is /// `gems.rb`). @@ -203,7 +202,6 @@ impl<'t> GemfileLock<'t> { } } - /// Where a rubygems-compatible registry at `base` (no trailing `/`) serves /// `name`-`version`'s `.gem` — the inventory's resolved URL and ledger /// recovery's fetch URL. `None` for a non-http(s) base. diff --git a/crates/socket-patch-core/src/formats/mod.rs b/crates/socket-patch-core/src/formats/mod.rs index f3ea013a3..31ed9059e 100644 --- a/crates/socket-patch-core/src/formats/mod.rs +++ b/crates/socket-patch-core/src/formats/mod.rs @@ -26,13 +26,13 @@ //! [`registry()`] is the one table of which project files carry a lock or //! its wiring, and in which roles. +pub(crate) mod bun; pub mod cargo; pub mod composer; pub mod gem; pub(crate) mod maven; pub(crate) mod nuget; pub mod pnpm; -pub(crate) mod bun; pub mod registry; pub mod yarn; @@ -81,7 +81,11 @@ mod architecture_tests { .filter(|l| !l.trim_start().starts_with("//")) .collect::>() .join("\n"); - let used: Vec<&str> = IMPURE.iter().copied().filter(|n| code.contains(n)).collect(); + let used: Vec<&str> = IMPURE + .iter() + .copied() + .filter(|n| code.contains(n)) + .collect(); assert!( used.is_empty(), "{}: a format model uses {used:?} — models are pure (module docs)", diff --git a/crates/socket-patch-core/src/formats/pnpm/mod.rs b/crates/socket-patch-core/src/formats/pnpm/mod.rs index 1d495d69a..6a8ce98da 100644 --- a/crates/socket-patch-core/src/formats/pnpm/mod.rs +++ b/crates/socket-patch-core/src/formats/pnpm/mod.rs @@ -38,7 +38,6 @@ use crate::utils::digest::is_sri_pin; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry}; use crate::vendor::path::parse_vendor_path; - // ── entry model ── /// One `packages:` entry of a pnpm lock, read with the entry grammar @@ -282,7 +281,10 @@ fn lock_versions(text: &str) -> impl Iterator, u32)> + '_ { let value = rest.trim().trim_matches(|c| c == '\'' || c == '"'); let mut parts = value.split('.'); let major = parts.next().and_then(|m| m.parse::().ok()); - let minor = parts.next().and_then(|m| m.parse::().ok()).unwrap_or(0); + let minor = parts + .next() + .and_then(|m| m.parse::().ok()) + .unwrap_or(0); Some((major, minor)) }) } @@ -302,7 +304,8 @@ pub fn lock_version_major(text: &str) -> Option { /// rejects it): a `shrinkwrapVersion` lock (pnpm 1–2) or lockfileVersion /// 5.0–5.2 (pnpm 3–5). Later locks never get the `--store` note. pub fn may_need_store_flag(text: &str) -> bool { - text.lines().any(|line| line.starts_with("shrinkwrapVersion:")) + text.lines() + .any(|line| line.starts_with("shrinkwrapVersion:")) || lock_versions(text).any(|(major, minor)| major == Some(5) && minor <= 2) } @@ -490,7 +493,9 @@ pub(crate) fn vendored_npm_uuids(text: &str) -> HashSet { if !in_section { continue; } - if let Some(uuid) = lines::parse_key_line(line, 2).and_then(|(key, _, _)| vendored_npm_uuid(key)) { + if let Some(uuid) = + lines::parse_key_line(line, 2).and_then(|(key, _, _)| vendored_npm_uuid(key)) + { out.insert(uuid); } } @@ -507,17 +512,52 @@ mod tests { fn resolves_reads_every_key_generation_boundary_anchored() { let lock = |keys: &str| format!("lockfileVersion: '9.0'\n\npackages:\n\n{keys}"); let yes = [ - (" left-pad@1.3.0:\n resolution: {integrity: sha512-x}\n", "left-pad", "1.3.0"), - (" /left-pad@1.3.0:\n resolution: {}\n", "left-pad", "1.3.0"), - (" /left-pad/1.3.0:\n resolution: {}\n", "left-pad", "1.3.0"), - (" 'left-pad@1.3.0(react@18.0.0)':\n dev: false\n", "left-pad", "1.3.0"), - (" /left-pad/1.3.0_react@18.0.0:\n dev: false\n", "left-pad", "1.3.0"), - (" '@scope/name@1.0.0':\n dev: false\n", "@scope/name", "1.0.0"), - (" /@scope/name@1.0.0:\n dev: false\n", "@scope/name", "1.0.0"), - (" /@scope/name/1.0.0:\n dev: false\n", "@scope/name", "1.0.0"), + ( + " left-pad@1.3.0:\n resolution: {integrity: sha512-x}\n", + "left-pad", + "1.3.0", + ), + ( + " /left-pad@1.3.0:\n resolution: {}\n", + "left-pad", + "1.3.0", + ), + ( + " /left-pad/1.3.0:\n resolution: {}\n", + "left-pad", + "1.3.0", + ), + ( + " 'left-pad@1.3.0(react@18.0.0)':\n dev: false\n", + "left-pad", + "1.3.0", + ), + ( + " /left-pad/1.3.0_react@18.0.0:\n dev: false\n", + "left-pad", + "1.3.0", + ), + ( + " '@scope/name@1.0.0':\n dev: false\n", + "@scope/name", + "1.0.0", + ), + ( + " /@scope/name@1.0.0:\n dev: false\n", + "@scope/name", + "1.0.0", + ), + ( + " /@scope/name/1.0.0:\n dev: false\n", + "@scope/name", + "1.0.0", + ), ]; for (keys, name, version) in yes { - assert!(PnpmLock::parse(&lock(keys)).resolves(name, version), "{keys}"); + assert!( + PnpmLock::parse(&lock(keys)).resolves(name, version), + "{keys}" + ); } let no = [ (" left-pad@1.3.0-beta.1:\n dev: false\n", "left-pad", "1.3.0"), @@ -533,7 +573,10 @@ mod tests { ), ]; for (keys, name, version) in no { - assert!(!PnpmLock::parse(&lock(keys)).resolves(name, version), "{keys}"); + assert!( + !PnpmLock::parse(&lock(keys)).resolves(name, version), + "{keys}" + ); } // Keys outside `packages:` (importers, overrides) resolve nothing. let importers = "lockfileVersion: '9.0'\n\nimporters:\n\n left-pad@1.3.0:\n x: y\n"; @@ -556,7 +599,10 @@ mod tests { let other = "22222222-2222-4222-8222-222222222222"; assert!(!PnpmLock::parse(text).vendored_in_use(other)); let crlf = text.replace('\n', "\r\n"); - assert!(PnpmLock::parse(&crlf).vendored_in_use(UUID), "CRLF reads like LF"); + assert!( + PnpmLock::parse(&crlf).vendored_in_use(UUID), + "CRLF reads like LF" + ); } // An overrides declaration alone is not usage. let overrides = format!( diff --git a/crates/socket-patch-core/src/formats/registry.rs b/crates/socket-patch-core/src/formats/registry.rs index a0c703ee1..e828d3de8 100644 --- a/crates/socket-patch-core/src/formats/registry.rs +++ b/crates/socket-patch-core/src/formats/registry.rs @@ -67,7 +67,11 @@ const fn row(path: &'static str, ecosystem: &'static str, roles: u8) -> FormatFi const REGISTRY: &[FormatFile] = &[ // ── npm family ── row("package-lock.json", "npm", HOSTED | VENDORED | PROBE | ROOT), - row("npm-shrinkwrap.json", "npm", HOSTED | VENDORED | PROBE | ROOT), + row( + "npm-shrinkwrap.json", + "npm", + HOSTED | VENDORED | PROBE | ROOT, + ), row( "pnpm-lock.yaml", "npm", @@ -114,7 +118,11 @@ const REGISTRY: &[FormatFile] = &[ row(".cargo/config", "cargo", HOSTED | VENDORED | PROBE), // ── composer ── row("composer.json", "composer", VENDORED), - row("composer.lock", "composer", HOSTED | VENDORED | PROBE | ROOT), + row( + "composer.lock", + "composer", + HOSTED | VENDORED | PROBE | ROOT, + ), // ── nuget ── row("nuget.config", "nuget", HOSTED | PROBE), row("NuGet.config", "nuget", HOSTED | PROBE), @@ -201,7 +209,11 @@ mod tests { paths.dedup(); assert_eq!(before, paths.len(), "duplicate registry path"); for f in REGISTRY.iter().filter(|f| f.has(ROOT)) { - assert!(!f.path.contains('/'), "{}: a root marker is a basename", f.path); + assert!( + !f.path.contains('/'), + "{}: a root marker is a basename", + f.path + ); } } diff --git a/crates/socket-patch-core/src/formats/yarn/mod.rs b/crates/socket-patch-core/src/formats/yarn/mod.rs index c7f51d5b2..64dbd6d9a 100644 --- a/crates/socket-patch-core/src/formats/yarn/mod.rs +++ b/crates/socket-patch-core/src/formats/yarn/mod.rs @@ -62,7 +62,10 @@ mod tests { #[test] fn sniff_prefers_berry_and_skips_a_bom() { - assert_eq!(sniff_grammar("__metadata:\n version: 8\n"), Some(YarnLockGrammar::Berry)); + assert_eq!( + sniff_grammar("__metadata:\n version: 8\n"), + Some(YarnLockGrammar::Berry) + ); assert_eq!( sniff_grammar("\u{feff}# yarn lockfile v1\r\n"), Some(YarnLockGrammar::Classic) diff --git a/crates/socket-patch-core/src/hosted/guidance.rs b/crates/socket-patch-core/src/hosted/guidance.rs index 8473ff64d..3f9cf2355 100644 --- a/crates/socket-patch-core/src/hosted/guidance.rs +++ b/crates/socket-patch-core/src/hosted/guidance.rs @@ -155,9 +155,7 @@ pub fn pnpm_lock_carries_hosted_redirect( pub fn npm_lock_url_needles(artifact_url: &str) -> Vec { let mut needles: Vec = crate::patch::redirect::artifact_url_spellings(artifact_url).into(); - needles.push(crate::utils::uri::encode_uri_component( - artifact_url, - )); + needles.push(crate::utils::uri::encode_uri_component(artifact_url)); needles } @@ -281,11 +279,7 @@ fn npm_allow_remote_preamble(hosts: &[&str]) -> String { /// The auto-config variant: `allow-remote=all` was (or, on `--dry-run`, /// would be) written to the project `.npmrc`, so installs need no flags. -pub fn npm_allow_remote_configured_detail( - hosts: &[&str], - created: bool, - dry_run: bool, -) -> String { +pub fn npm_allow_remote_configured_detail(hosts: &[&str], created: bool, dry_run: bool) -> String { let how = match (created, dry_run) { (true, false) => "`allow-remote=all` was written to a new", (false, false) => "`allow-remote=all` was appended to the existing", diff --git a/crates/socket-patch-core/src/hosted/memory/discover.rs b/crates/socket-patch-core/src/hosted/memory/discover.rs index 6e5b36e09..9fc5ebfd9 100644 --- a/crates/socket-patch-core/src/hosted/memory/discover.rs +++ b/crates/socket-patch-core/src/hosted/memory/discover.rs @@ -14,9 +14,7 @@ use std::time::Duration; use crate::api::client::{ApiError, ApiFuture, PatchApi}; use crate::api::ranking::cmp_search_results; -use crate::api::types::{ - BatchPackagePatches, PackageVendorResult, PatchResponse, SearchResponse, -}; +use crate::api::types::{BatchPackagePatches, PackageVendorResult, PatchResponse, SearchResponse}; use crate::utils::purl::{normalize_purl, strip_purl_qualifiers}; use super::types::MAX_REFERENCE_BATCH; diff --git a/crates/socket-patch-core/src/hosted/memory/limits.rs b/crates/socket-patch-core/src/hosted/memory/limits.rs index 9f895d6c9..da4dd695d 100644 --- a/crates/socket-patch-core/src/hosted/memory/limits.rs +++ b/crates/socket-patch-core/src/hosted/memory/limits.rs @@ -42,12 +42,7 @@ impl ResolvedOptions { /// as `flag`), then the socket.yml `patches.maxNewPatches`, then /// unlimited; `maxNewPatchesCap` only tightens it. pub(crate) fn max_new(&self, file: Option) -> crate::rollout::MaxNew { - crate::rollout::resolve_max_new( - self.max_new_patches, - None, - file, - self.max_new_patches_cap, - ) + crate::rollout::resolve_max_new(self.max_new_patches, None, file, self.max_new_patches_cap) } } @@ -79,8 +74,9 @@ pub(crate) fn resolve_options(options: &HostedScanOptions) -> Result None, Some(value) => Some(( - crate::policy::parse_min_severity(value) - .map_err(|e| EngineError::invalid("invalid_min_severity", format!("minSeverity: {e}")))?, + crate::policy::parse_min_severity(value).map_err(|e| { + EngineError::invalid("invalid_min_severity", format!("minSeverity: {e}")) + })?, crate::policy::OverrideSource::Flag, )), }; diff --git a/crates/socket-patch-core/src/hosted/memory/mod.rs b/crates/socket-patch-core/src/hosted/memory/mod.rs index e11aa036d..b649621c1 100644 --- a/crates/socket-patch-core/src/hosted/memory/mod.rs +++ b/crates/socket-patch-core/src/hosted/memory/mod.rs @@ -58,17 +58,17 @@ pub use limits::SessionBuilder; pub use select::{candidate_files, safe_repo_path, select_paths}; pub use types::*; +use crate::policy::{ + canon, patch_severity_order, policy_block, FilterReason, FilteredEntry, MemoryPolicyFs, + PolicyError, PolicySource, Root, RootFile, SelectionPolicy, PATCHES_DISABLED, + POLICY_FILE_NAMES, +}; use crate::rollout::stage::{ classify, lookup_incomplete, mentioned_uuids, offers_from_results, Offers, RecordedIndex, Row, - Stage, - ROLLOUT_DEFERRED, + Stage, ROLLOUT_DEFERRED, }; use discover::Provider; use stages::{Planned, RewriteRefused, Rewritten, StageOptions}; -use crate::policy::{ - canon, patch_severity_order, policy_block, FilterReason, FilteredEntry, MemoryPolicyFs, PolicyError, - PolicySource, Root, RootFile, SelectionPolicy, PATCHES_DISABLED, POLICY_FILE_NAMES, -}; /// `"+"`; the sha comes from the /// `SOCKET_PATCH_GIT_SHA` build-time variable. @@ -419,11 +419,8 @@ fn memory_recorded( .map(|p| (purl.clone(), p.uuid.clone())) }) .collect(); - let merged = crate::ledgers::merge_ledger_records_for_updates( - manifest.as_ref(), - vendor.as_ref(), - &pins, - ); + let merged = + crate::ledgers::merge_ledger_records_for_updates(manifest.as_ref(), vendor.as_ref(), &pins); RecordedIndex::new(merged.as_deref(), &pins) } @@ -450,13 +447,20 @@ async fn engine( // The repo's socket.yml policy, before any root is processed: a file // that cannot be honored fails the whole session closed. - let (policy, policy_warnings) = - match SelectionPolicy::load(&memory_policy_fs(&files, &options.policy_paths), &options.policy_overrides) { - Ok(loaded) => loaded, - Err(error) => { - return Ok(policy_error_output(&error, warnings, files_input, bytes_input)); - } - }; + let (policy, policy_warnings) = match SelectionPolicy::load( + &memory_policy_fs(&files, &options.policy_paths), + &options.policy_overrides, + ) { + Ok(loaded) => loaded, + Err(error) => { + return Ok(policy_error_output( + &error, + warnings, + files_input, + bytes_input, + )); + } + }; // Path selection chose which files to send by the policy it read; a // different policy here would judge roots it never fetched. let read = match policy.source() { @@ -465,16 +469,27 @@ async fn engine( }; // Selection returns no digest when it bypassed the file, so a digest // with a bypassed session means the two sides disagree. - let expected = if options.policy_overrides.bypass { None } else { read.map(|(_, sha)| sha) }; + let expected = if options.policy_overrides.bypass { + None + } else { + read.map(|(_, sha)| sha) + }; if expected != options.policy_sha256.as_deref() { let error = PolicyError::Invalid { - file: read.map_or(POLICY_FILE_NAMES[0], |(path, _)| path).to_string(), + file: read + .map_or(POLICY_FILE_NAMES[0], |(path, _)| path) + .to_string(), key: String::new(), message: "the policy content differs from the one path selection read: pass \ selectHostedScanPaths' policySha256 and stream the same text" .to_string(), }; - return Ok(policy_error_output(&error, warnings, files_input, bytes_input)); + return Ok(policy_error_output( + &error, + warnings, + files_input, + bytes_input, + )); } for w in policy_warnings { warnings.push(EngineWarning::new(w.code, w.detail, None)); @@ -722,23 +737,25 @@ async fn engine( // the tree's manifest and vendor ledger, and the hosted pins its // lockfiles name. ALREADY rows carry the recorded uuid, so a re-scan // re-confirms a pin instead of swapping it. - let mut stage = Stage::new(options.max_new(policy.max_new_patches()), None, std::path::Path::new("")); + let mut stage = Stage::new( + options.max_new(policy.max_new_patches()), + None, + std::path::Path::new(""), + ); // A root whose every lookup failed hides packages that could have been // NEW: a capped run then admits none anywhere (§5.2). - stage.incomplete |= states - .iter() - .any(|s| s.error.as_ref().is_some_and(|e| e.code == "patch_lookup_failed")); + stage.incomplete |= states.iter().any(|s| { + s.error + .as_ref() + .is_some_and(|e| e.code == "patch_lookup_failed") + }); let roots_by_path: Vec = states.iter().map(|s| s.root.clone()).collect(); for state in states.iter_mut().filter(|s| s.error.is_none()) { let Some(project) = state.project.as_ref() else { continue; }; let recorded = memory_recorded(project, &state.root, &roots_by_path, &state.offers); - stage.incomplete |= lookup_incomplete( - &recorded, - &state.failed_details, - batch_failed, - ); + stage.incomplete |= lookup_incomplete(&recorded, &state.failed_details, batch_failed); let mut rows = classify(&state.offers, &recorded, &state.root); for row in &mut rows { row.candidate.in_flight = options.in_flight.contains(&row.candidate.base_purl); @@ -859,8 +876,11 @@ async fn engine( unknown_roots.contains(&row.candidate.project) || confirmed.contains(&(row.candidate.project.clone(), row.writer.uuid.clone())) }); - let deferred_rows: Vec<(crate::rollout::Candidate, u32)> = - stage.plan.as_ref().map(|p| p.deferred.clone()).unwrap_or_default(); + let deferred_rows: Vec<(crate::rollout::Candidate, u32)> = stage + .plan + .as_ref() + .map(|p| p.deferred.clone()) + .unwrap_or_default(); if !deferred_rows.is_empty() { let root_index: BTreeMap = states .iter() @@ -1112,7 +1132,10 @@ fn select_with_policy( let mut by_purl: BTreeMap> = BTreeMap::new(); for (patch, reason) in dropped { if !chosen.contains(patch.purl.as_str()) { - by_purl.entry(patch.purl.clone()).or_default().push((patch, reason)); + by_purl + .entry(patch.purl.clone()) + .or_default() + .push((patch, reason)); } } for (purl, mut group) in by_purl { diff --git a/crates/socket-patch-core/src/hosted/memory/roots.rs b/crates/socket-patch-core/src/hosted/memory/roots.rs index 84e0dd8d7..cc4ea8c41 100644 --- a/crates/socket-patch-core/src/hosted/memory/roots.rs +++ b/crates/socket-patch-core/src/hosted/memory/roots.rs @@ -40,17 +40,23 @@ pub const UNSUPPORTED_MARKERS: [(&str, &[&str]); 2] = [ /// trees, VCS and tool state, and vendored dependencies. Structural, so no /// policy can negate them. (Test and fixture trees are the socket.yml /// policy's overridable built-in ignores.) -pub(crate) const EXCLUDED_ROOT_SEGMENTS: [&str; 5] = ["node_modules", ".git", ".socket", ".yarn", "vendor"]; +pub(crate) const EXCLUDED_ROOT_SEGMENTS: [&str; 5] = + ["node_modules", ".git", ".socket", ".yarn", "vendor"]; /// The marker basenames of `root` among `paths` (the files the policy's /// path filters test for that root). -pub(crate) fn root_markers<'a>(root: &str, paths: impl IntoIterator) -> Vec { +pub(crate) fn root_markers<'a>( + root: &str, + paths: impl IntoIterator, +) -> Vec { let mut out: Vec = paths .into_iter() .filter_map(|path| { let (dir, base) = split_path(path); let marker = marker_ecosystem(base).is_some() - || UNSUPPORTED_MARKERS.iter().any(|(_, names)| names.contains(&base)); + || UNSUPPORTED_MARKERS + .iter() + .any(|(_, names)| names.contains(&base)); (dir == root && marker).then(|| base.to_string()) }) .collect(); @@ -211,7 +217,15 @@ mod tests { #[test] fn root_markers_name_every_marker_of_the_root_only() { assert_eq!( - root_markers("a", ["a/yarn.lock", "a/package.json", "a/b/yarn.lock", "a/pom.xml"]), + root_markers( + "a", + [ + "a/yarn.lock", + "a/package.json", + "a/b/yarn.lock", + "a/pom.xml" + ] + ), vec!["pom.xml".to_string(), "yarn.lock".to_string()] ); } diff --git a/crates/socket-patch-core/src/hosted/memory/select.rs b/crates/socket-patch-core/src/hosted/memory/select.rs index fb4dfc832..5d84c8c7c 100644 --- a/crates/socket-patch-core/src/hosted/memory/select.rs +++ b/crates/socket-patch-core/src/hosted/memory/select.rs @@ -15,8 +15,8 @@ use crate::patch::redirect::npmrc::NPMRC_REL; use crate::utils::python_lock::is_python_lock_name; use crate::policy::{ - MemoryPolicyFs, PolicyOverrides, PolicySource, Root, RootFile, SelectionPolicy, POLICY_FILE_NAMES, - SOCKET_YML_INVALID, + MemoryPolicyFs, PolicyOverrides, PolicySource, Root, RootFile, SelectionPolicy, + POLICY_FILE_NAMES, SOCKET_YML_INVALID, }; use super::roots::{ @@ -46,7 +46,12 @@ pub(crate) const VENDOR_STATE_REL: &str = ".socket/vendor/state.json"; pub(crate) const MANIFEST_REL: &str = ".socket/manifest.json"; /// Root-relative text files read beyond `REDIRECT_CANDIDATE_FILES`. -const EXTRA_TEXT_FILES: [&str; 4] = [PNPM_WORKSPACE_REL, NPMRC_REL, VENDOR_STATE_REL, MANIFEST_REL]; +const EXTRA_TEXT_FILES: [&str; 4] = [ + PNPM_WORKSPACE_REL, + NPMRC_REL, + VENDOR_STATE_REL, + MANIFEST_REL, +]; /// The one directory name the disk Cargo member walk never enters (it /// follows `members`, `exclude`, path dependencies and `[patch]` paths @@ -175,7 +180,10 @@ fn classify(rel: &str, root_files: &BTreeSet<&str>) -> Option { /// The listed root policy files with the text the caller fetched first. A /// listed file with no text (not passed, `missing`, or a symlink) is present /// without content, so loading it fails closed. -fn selection_policy_fs(blobs: &BTreeMap, supplied: &[PolicyFileInput]) -> MemoryPolicyFs { +fn selection_policy_fs( + blobs: &BTreeMap, + supplied: &[PolicyFileInput], +) -> MemoryPolicyFs { let mut fs = MemoryPolicyFs::default(); for name in POLICY_FILE_NAMES { let Some(&symlink) = blobs.get(name) else { @@ -201,7 +209,10 @@ fn selection_policy( options: &SelectOptions, ) -> Result { let supplied = options.policy_files.as_deref().unwrap_or_default(); - if let Some(bad) = supplied.iter().find(|f| !POLICY_FILE_NAMES.contains(&f.path.as_str())) { + if let Some(bad) = supplied + .iter() + .find(|f| !POLICY_FILE_NAMES.contains(&f.path.as_str())) + { return Err(PolicyErrorInfo { code: SOCKET_YML_INVALID.to_string(), detail: format!( diff --git a/crates/socket-patch-core/src/hosted/memory/types.rs b/crates/socket-patch-core/src/hosted/memory/types.rs index 2a11daed7..fa81876e8 100644 --- a/crates/socket-patch-core/src/hosted/memory/types.rs +++ b/crates/socket-patch-core/src/hosted/memory/types.rs @@ -171,7 +171,9 @@ impl<'de> Deserialize<'de> for MaxNewPatchesOption { if v == "none" { Ok(MaxNewPatchesOption(None)) } else { - Err(E::custom(format!("maxNewPatches must be a number or \"none\", not `{v}`"))) + Err(E::custom(format!( + "maxNewPatches must be a number or \"none\", not `{v}`" + ))) } } } diff --git a/crates/socket-patch-core/src/ledgers.rs b/crates/socket-patch-core/src/ledgers.rs index 9bcf56623..582ca464f 100644 --- a/crates/socket-patch-core/src/ledgers.rs +++ b/crates/socket-patch-core/src/ledgers.rs @@ -371,7 +371,6 @@ pub fn uuid_only_record(uuid: &str) -> PatchRecord { } } - /// Fold the hosted pins and the vendor ledger's patch records into the /// manifest view update detection consults. Hosted mode records purl→uuid /// ONLY in the lockfiles (`hosted_pins`, uuid only; v5 keeps no hosted diff --git a/crates/socket-patch-core/src/lib.rs b/crates/socket-patch-core/src/lib.rs index f257d0bef..ec2fb15ee 100644 --- a/crates/socket-patch-core/src/lib.rs +++ b/crates/socket-patch-core/src/lib.rs @@ -15,7 +15,6 @@ pub mod utils; pub mod vendor; pub mod vex; - #[cfg(test)] mod golden; #[cfg(test)] diff --git a/crates/socket-patch-core/src/manifest/records.rs b/crates/socket-patch-core/src/manifest/records.rs index 453e0ab2f..7032d435c 100644 --- a/crates/socket-patch-core/src/manifest/records.rs +++ b/crates/socket-patch-core/src/manifest/records.rs @@ -32,7 +32,10 @@ pub fn vulnerabilities_for_manifest( /// `patch`. `files` is the (purl-keyed) before/after-hash map the /// caller built — semantics for what counts as a "patchable file" differ /// between the get and download flows, so the caller owns that decision. -pub fn build_patch_record(patch: &PatchResponse, files: HashMap) -> PatchRecord { +pub fn build_patch_record( + patch: &PatchResponse, + files: HashMap, +) -> PatchRecord { PatchRecord { uuid: patch.uuid.clone(), exported_at: patch.published_at.clone(), diff --git a/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs index 082849761..5863631df 100644 --- a/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs @@ -97,7 +97,6 @@ fn synth_lock(rng: &mut Rng, blocks: usize, v1: bool) -> String { out } - const INDEX: &str = "sparse+https://socket.example/cargo/index/"; fn plan_new(lock: &str, name: &str, version: &str, cksum: &str) -> CargoLockPlan { @@ -182,7 +181,8 @@ fn span_splice_matches_golden_on_hand_written_locks() { "[root]\nname = \"app\"\nversion = \"0.1.0\"\ndependencies = [\n \"d 1.0.0 ({crates_io})\",\n]\n\n[[package]]\nname = \"d\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\"\n\n[[package]]\nname = \"u\"\nversion = \"2.0.0\"\nsource = \"{crates_io}\"\ndependencies = [\n \"d 1.0.0 ({crates_io})\",\n]\n\n[metadata]\n\"checksum d 1.0.0 ({crates_io})\" = \"cc\"\n\"checksum u 2.0.0 ({crates_io})\" = \"dd\"\n" ); let sourceless_v1 = "[[package]]\nname = \"s\"\nversion = \"1.0.0\"\n\n[metadata]\n\"checksum s 1.0.0 (registry+x)\" = \"ee\"\n".to_string(); - let source_at_eof = format!("[[package]]\nname = \"e\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\""); + let source_at_eof = + format!("[[package]]\nname = \"e\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\""); let bare = "version = 3\n\n[[package]]\nname = \"b\"\nversion = \"1.0.0\"\n\n[[package]]\nname = \"c\"\nversion = \"1.0.0\"\n".to_string(); let mut g = Golden::new( "cargo_lock_hand_written", diff --git a/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs index 3a8ebf5c2..075f630b4 100644 --- a/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs @@ -10,7 +10,11 @@ use super::*; use crate::golden::Golden; use crate::test_rng::Rng; -fn run(g: &mut Golden, files: &BTreeMap, overrides: &[DepOverride]) -> RewriteResult { +fn run( + g: &mut Golden, + files: &BTreeMap, + overrides: &[DepOverride], +) -> RewriteResult { let mut got = RewriteResult::default(); rewrite_golang(files, overrides, &mut got); g.next(&(files, overrides), &got); diff --git a/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs index 10fe9d510..480e315e9 100644 --- a/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs @@ -131,11 +131,12 @@ fn assert_same_with_metadata( bun_lockb_present, python_metadata, ); - let merged = merge_group_outputs(&prefix, run_groups_concurrently(&prefix, &groups)) - .map(|mut merged| { + let merged = merge_group_outputs(&prefix, run_groups_concurrently(&prefix, &groups)).map( + |mut merged| { merged.vlt_drives = vlt::vlt_drives(files, bun_lockb_present); merged - }); + }, + ); assert_eq!( merged.as_ref(), Some(&want), diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 59d148cdf..05c37e6e3 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -45,16 +45,17 @@ mod pdm; mod pipenv; pub mod presence; // The pnpm hosted planner lives with the format's model. -use crate::formats::pnpm::plan_hosted; -use crate::formats::cargo::CargoLock; -use crate::formats::composer::hosted::rewrite_composer_lock; -use crate::formats::gem::hosted::{checksum_entry_span, converge_gem_lock_source}; -pub(crate) use crate::formats::yarn::is_berry_lock; use crate::formats::cargo::hosted::CargoLockPlan; #[cfg(test)] use crate::formats::cargo::hosted::CARGO_LOCK_REFERENCE_KIND; +use crate::formats::cargo::CargoLock; +use crate::formats::composer::hosted::rewrite_composer_lock; +use crate::formats::gem::hosted::{checksum_entry_span, converge_gem_lock_source}; #[cfg(test)] use crate::formats::pnpm::hosted::pnpm_unrewritten_instances; +use crate::formats::pnpm::plan_hosted; +pub(crate) use crate::formats::yarn::is_berry_lock; +mod hosted_url; #[cfg(test)] mod pnpm_equivalence_tests; mod poetry; @@ -63,18 +64,17 @@ mod python_lock_equivalence_tests; mod requirements; mod staged; mod state; -mod hosted_url; pub mod upstream; pub mod vlt; pub mod vlt_heal; pub mod vlt_preflight; -pub use state::{ - load_redirect_state, save_redirect_state, - CorruptRedirectState, RedirectState, REDIRECT_STATE_REL, -}; /// Hosted-artifact leaf ownership rule, shared with `vex`'s bun lockfile /// discovery (which recovers a URL tuple's version from that leaf). pub(crate) use hosted_url::{hosted_url_names, hosted_url_version}; +pub use state::{ + load_redirect_state, save_redirect_state, CorruptRedirectState, RedirectState, + REDIRECT_STATE_REL, +}; /// One ecosystem's integrity hashes (mirrors the TS `PatchArtifactIntegrity`). #[derive(Debug, Clone, Default, Deserialize)] @@ -4056,7 +4056,6 @@ fn rewrite_uv_lock( } } - // ── composer.lock ──────────────────────────────────────────────────────────── /// Whether `text` points at `artifact_url` in any spelling a rewritten file may /// carry: the raw url every rewriter emits — composer.lock included, since @@ -10149,7 +10148,11 @@ mod tests { let out = r.files.get("Gemfile.lock").expect("lock rewritten"); let rows: Vec<&str> = out .lines() - .filter(|l| l.trim_start().starts_with("rails (7.0.0)") && l.starts_with(" ") && !l.starts_with(" ")) + .filter(|l| { + l.trim_start().starts_with("rails (7.0.0)") + && l.starts_with(" ") + && !l.starts_with(" ") + }) .collect(); assert_eq!( rows, @@ -10162,7 +10165,11 @@ mod tests { "{entry}: the entry keeps its line ending: {out:?}" ); let model = crate::formats::gem::GemfileLock::parse(out); - assert_eq!(model.checksum("rails", "7.0.0"), Some(patched.as_str()), "{entry}"); + assert_eq!( + model.checksum("rails", "7.0.0"), + Some(patched.as_str()), + "{entry}" + ); assert!(!out.contains("\r\r"), "line endings kept: {out:?}"); let edit = r .edits @@ -10177,7 +10184,10 @@ mod tests { files.insert("Gemfile.lock".to_string(), out.clone()); let again = rewrite_registry_redirect(&files, &[gem_override("rails", "7.0.0")]); assert!( - !again.edits.iter().any(|e| e.kind == "redirect_gemfile_lock_checksum"), + !again + .edits + .iter() + .any(|e| e.kind == "redirect_gemfile_lock_checksum"), "{entry}: rerun is a no-op: {:?}", again.edits ); @@ -10545,11 +10555,19 @@ mod tests { let redacted = format!( "https://patch.socket.dev/patch/npm/left-pad/1.3.0//{uuid}/left-pad-1.3.0.tgz?x=1" ); - assert_eq!(redact_grant_token(&url, &url, uuid), redacted, "the URL alone"); + assert_eq!( + redact_grant_token(&url, &url, uuid), + redacted, + "the URL alone" + ); let text = format!("vlt would fail to verify {url}: fetch error GET {url}: reset"); - let want = format!("vlt would fail to verify {redacted}: fetch error GET {redacted}: reset"); + let want = + format!("vlt would fail to verify {redacted}: fetch error GET {redacted}: reset"); assert_eq!(redact_grant_token(&text, &url, uuid), want, "every quote"); - assert!(!redact_grant_token(&text, &url, uuid).contains(token), "no token left"); + assert!( + !redact_grant_token(&text, &url, uuid).contains(token), + "no token left" + ); let registry = format!("https://patch.socket.dev/patch-registry/npm/{token}/{uuid}"); assert_eq!( redact_grant_token(®istry, ®istry, uuid), diff --git a/crates/socket-patch-core/src/patch/redirect/npmrc.rs b/crates/socket-patch-core/src/patch/redirect/npmrc.rs index ac102ef78..6a9c4a17a 100644 --- a/crates/socket-patch-core/src/patch/redirect/npmrc.rs +++ b/crates/socket-patch-core/src/patch/redirect/npmrc.rs @@ -33,8 +33,6 @@ //! and — when the project file is silent — the user / global / builtin //! config files ([`resolve_outer_allow_remote`]). - - /// Repo-relative path of the project `.npmrc` the auto-config edits. pub const NPMRC_REL: &str = ".npmrc"; @@ -1137,5 +1135,4 @@ mod tests { ); } } - } diff --git a/crates/socket-patch-core/src/patch/redirect/pdm.rs b/crates/socket-patch-core/src/patch/redirect/pdm.rs index 95d910f23..608dbfd74 100644 --- a/crates/socket-patch-core/src/patch/redirect/pdm.rs +++ b/crates/socket-patch-core/src/patch/redirect/pdm.rs @@ -235,9 +235,18 @@ mod tests { #[test] fn legacy_formats_warn_stale_install_risk_once() { for (fixture, warns) in [ - (include_str!("../../../tests/fixtures/pdm-native/0.12.3.lock"), true), - (include_str!("../../../tests/fixtures/pdm-native/2.8.2.lock"), true), - (include_str!("../../../tests/fixtures/pdm-native/2.29.2.lock"), false), + ( + include_str!("../../../tests/fixtures/pdm-native/0.12.3.lock"), + true, + ), + ( + include_str!("../../../tests/fixtures/pdm-native/2.8.2.lock"), + true, + ), + ( + include_str!("../../../tests/fixtures/pdm-native/2.29.2.lock"), + false, + ), ] { let mut result = RewriteResult::default(); rewrite( @@ -410,7 +419,11 @@ mod parse_reuse_equivalence_tests { let what = format!("{fixture} extra={extra} crlf={crlf}"); let mut got = RewriteResult::default(); rewrite(&files, &deps, &mut got); - g.case(what.replace(' ', "/"), &(&files, &deps), &format!("{got:?}")); + g.case( + what.replace(' ', "/"), + &(&files, &deps), + &format!("{got:?}"), + ); confirmed += got.confirmed_pdm_uuids.len(); let mut again = files.clone(); diff --git a/crates/socket-patch-core/src/patch/redirect/pipenv.rs b/crates/socket-patch-core/src/patch/redirect/pipenv.rs index 1eaf8cfda..0dc2eb2bf 100644 --- a/crates/socket-patch-core/src/patch/redirect/pipenv.rs +++ b/crates/socket-patch-core/src/patch/redirect/pipenv.rs @@ -482,7 +482,10 @@ mod tests { let original = serde_json::to_string(&value).unwrap(); // A live lock (Pipfile beside it): conflicts veto the siblings. let files = BTreeMap::from([ - ("Pipfile".to_string(), "[packages]\nurllib3 = \"*\"\n".to_string()), + ( + "Pipfile".to_string(), + "[packages]\nurllib3 = \"*\"\n".to_string(), + ), ("Pipfile.lock".to_string(), original), ]); let mut result = RewriteResult::default(); @@ -522,20 +525,30 @@ mod tests { for stale in &stale_locks { let files = BTreeMap::from([ ("Pipfile.lock".to_string(), stale.clone()), - ("requirements.txt".to_string(), "urllib3==1.26.18\n".to_string()), + ( + "requirements.txt".to_string(), + "urllib3==1.26.18\n".to_string(), + ), ]); - let result = super::super::rewrite_registry_redirect(&files, std::slice::from_ref(&dep)); + let result = + super::super::rewrite_registry_redirect(&files, std::slice::from_ref(&dep)); assert!( !result.refused_pipenv_uuids.contains("patch-one"), "a non-conflict must not veto: {stale}" ); assert!( - result.warnings.iter().any(|w| w.code == "redirect_pipenv_skipped"), + result + .warnings + .iter() + .any(|w| w.code == "redirect_pipenv_skipped"), "{:?}", result.warnings ); assert!( - result.files.get("requirements.txt").is_some_and(|t| t.contains("patch.socket.dev")), + result + .files + .get("requirements.txt") + .is_some_and(|t| t.contains("patch.socket.dev")), "requirements.txt must still be redirected past a stale Pipfile.lock: {result:?}" ); assert!(!result.files.contains_key("Pipfile.lock")); @@ -593,7 +606,10 @@ mod tests { let files = |text: &str| BTreeMap::from([("Pipfile.lock".to_string(), text.to_string())]); assert!(lock_targets(&files(&lock()), std::slice::from_ref(&dep))); assert!(!lock_targets(&files(&lock()), std::slice::from_ref(&other))); - assert!(!lock_targets(&files("{ not json"), std::slice::from_ref(&dep))); + assert!(!lock_targets( + &files("{ not json"), + std::slice::from_ref(&dep) + )); assert!(!lock_targets(&BTreeMap::new(), std::slice::from_ref(&dep))); let mut npm = dep.clone(); npm.ecosystem = "npm".into(); @@ -619,7 +635,10 @@ mod tests { let entry: Value = serde_json::from_str(&fixed).unwrap(); assert!(entry["default"]["urllib3"].get("version").is_none()); assert_eq!(entry["default"]["urllib3"]["index"], json!("pypi")); - assert!(entry["default"]["urllib3"]["file"].as_str().unwrap().contains("patch-one")); + assert!(entry["default"]["urllib3"]["file"] + .as_str() + .unwrap() + .contains("patch-one")); value["default"]["urllib3"]["version"] = json!("==2.0.0"); let conflicting = serde_json::to_string(&value).unwrap(); @@ -640,7 +659,10 @@ mod tests { assert!(owned_url(public, &dep)); assert!(!owned_url("https://example.org/patch/pypi/urllib3/1.26.18/tok/patch-one/urllib3-1.26.18-py3-none-any.whl", &dep)); dep.artifact_url = "https://patches.internal.example:8443/patch/pypi/urllib3/1.26.18/tok/patch-one/urllib3-1.26.18-py3-none-any.whl".into(); - assert!(owned_url(&dep.artifact_url, &dep), "the grant's own origin is ours"); + assert!( + owned_url(&dep.artifact_url, &dep), + "the grant's own origin is ours" + ); assert!(owned_url(public, &dep), "and so is the public service"); assert!(!owned_url("https://patches.internal.example:8443/patch/pypi/urllib3/1.26.19/tok/patch-one/urllib3-1.26.19-py3-none-any.whl", &dep), "another version is not"); // Rotation on the custom origin re-points the owned entry. @@ -650,7 +672,6 @@ mod tests { assert!(!rotation.is_empty()); assert!(second.contains("/rotated/") && !second.contains("/tok/")); } - } #[cfg(test)] @@ -701,7 +722,9 @@ mod compatibility_tests { assert!(!result.refused_pipenv_uuids.contains("patch-one")); assert!(result.files["requirements.txt"].contains("patch.socket.dev")); assert!(!result.files.contains_key("Pipfile.lock")); - assert!(result.warnings.iter().any(|w| w.code == "redirect_pipenv_refused" && w.detail.contains("no Pipfile"))); + assert!(result + .warnings + .iter() + .any(|w| w.code == "redirect_pipenv_refused" && w.detail.contains("no Pipfile"))); } - } diff --git a/crates/socket-patch-core/src/patch/redirect/poetry.rs b/crates/socket-patch-core/src/patch/redirect/poetry.rs index 624b74c4a..b84dde5fa 100644 --- a/crates/socket-patch-core/src/patch/redirect/poetry.rs +++ b/crates/socket-patch-core/src/patch/redirect/poetry.rs @@ -92,7 +92,9 @@ pub(super) fn rewrite_poetry( } } Err(detail) => { - result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .refused_python_lock_uuids + .insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), @@ -100,7 +102,9 @@ pub(super) fn rewrite_poetry( continue; } } - result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .confirmed_python_lock_uuids + .insert(dep.patch_uuid.clone()); content = rewrite.text; if !stale_warned { if let Some(format) = @@ -136,14 +140,18 @@ pub(super) fn rewrite_poetry( } // Already redirected to this artifact (idempotent re-scan). Ok(Some(_)) => { - result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .confirmed_python_lock_uuids + .insert(dep.patch_uuid.clone()); } Ok(None) => result.warnings.push(RewriteWarning { code: "redirect_poetry_entry_not_found".into(), detail: format!("no {path} entry for {}@{}", dep.name, dep.version), }), Err(detail) => { - result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .refused_python_lock_uuids + .insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), @@ -268,7 +276,11 @@ mod equivalence_tests { let mut again = files.clone(); again.extend(got.files.clone()); let got = run(rewrite_poetry, &again, &deps); - g.case(format!("{what}/re-run"), &(&again, &deps), &format!("{got:?}")); + g.case( + format!("{what}/re-run"), + &(&again, &deps), + &format!("{got:?}"), + ); } } } diff --git a/crates/socket-patch-core/src/patch/redirect/requirements.rs b/crates/socket-patch-core/src/patch/redirect/requirements.rs index cdbd9eb6d..91ebddd00 100644 --- a/crates/socket-patch-core/src/patch/redirect/requirements.rs +++ b/crates/socket-patch-core/src/patch/redirect/requirements.rs @@ -265,7 +265,9 @@ pub(super) fn rewrite( } } matched = true; - result.confirmed_requirements_uuids.insert(dep.patch_uuid.clone()); + result + .confirmed_requirements_uuids + .insert(dep.patch_uuid.clone()); let options = requirement_tokens(specifier) .into_iter() .skip_while(|token| !token.starts_with("--")) diff --git a/crates/socket-patch-core/src/patch/redirect/state.rs b/crates/socket-patch-core/src/patch/redirect/state.rs index 6d1b2f5d0..98d0b620e 100644 --- a/crates/socket-patch-core/src/patch/redirect/state.rs +++ b/crates/socket-patch-core/src/patch/redirect/state.rs @@ -56,7 +56,6 @@ impl RedirectState { records: BTreeMap::new(), } } - } impl Default for RedirectState { @@ -518,5 +517,4 @@ mod tests { "changed bytes still go through the (here refused) atomic write" ); } - } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs b/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs index f51142f69..87c49a542 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs @@ -332,7 +332,10 @@ mod tests { let package_start = u64::from_le_bytes(lock[110..118].try_into().unwrap()) as usize; // The root resolution's flag byte (its last). let flags_at = package_start + count * 16 + 63; - assert_eq!(lock[flags_at], crate::vendor::bun_lockb::NORMALIZED_FORMAT_1); + assert_eq!( + lock[flags_at], + crate::vendor::bun_lockb::NORMALIZED_FORMAT_1 + ); lock[flags_at] |= 0x40; BunLockb::parse(&lock).unwrap().validate_mutation().unwrap(); let (outcome, after) = run(&lock, &vendor_opts()).await; diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs index c44d7919e..70ca86a6d 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs @@ -97,7 +97,10 @@ pub(crate) async fn restore( ) }); let cksums: BTreeMap> = - futures_util::future::join_all(lookups).await.into_iter().collect(); + futures_util::future::join_all(lookups) + .await + .into_iter() + .collect(); let mut changed = false; let mut restored: Vec<(&LockHit, String)> = Vec::new(); for hit in &hits { @@ -116,7 +119,9 @@ pub(crate) async fn restore( } // The entries' own source + checksum values, spliced at the parse's // spans (every hit is a distinct block: its source names its uuid). - let spans = model.spans().expect("a lock parsed from text carries spans"); + let spans = model + .spans() + .expect("a lock parsed from text carries spans"); let mut splices: Vec<(std::ops::Range, String)> = Vec::new(); for (hit, cksum) in &restored { let at = &spans.packages[hit.index]; @@ -133,7 +138,10 @@ pub(crate) async fn restore( } for (hit, cksum) in &restored { // Dependents' full-id references and the v1 `[metadata]` key. - lock = lock.replace(&format!("({})", hit.source), &format!("({CRATES_IO_SOURCE})")); + lock = lock.replace( + &format!("({})", hit.source), + &format!("({CRATES_IO_SOURCE})"), + ); let metadata_key = format!( "\"checksum {} {} ({CRATES_IO_SOURCE})\" = \"", hit.name, hit.version @@ -152,7 +160,11 @@ pub(crate) async fn restore( if changed { view.write( "Cargo.lock", - if crlf { lock.replace('\n', "\r\n") } else { lock }, + if crlf { + lock.replace('\n', "\r\n") + } else { + lock + }, ); } } @@ -317,11 +329,10 @@ fn remove_registry_block(config: &str, reg: &str) -> Option { end -= 1; } let fragment = format!("{}\n", lines[i..end].join("\n")); - let removed = remove_appended_cargo_block(&lf, &fragment) - .or_else(|| { - // The block ends the file with no final newline. - remove_appended_cargo_block(&lf, fragment.trim_end_matches('\n')) - })?; + let removed = remove_appended_cargo_block(&lf, &fragment).or_else(|| { + // The block ends the file with no final newline. + remove_appended_cargo_block(&lf, fragment.trim_end_matches('\n')) + })?; Some(if crlf { removed.replace('\n', "\r\n") } else { @@ -388,7 +399,10 @@ mod tests { #[test] fn table_form_line_is_dropped() { - assert_eq!(unpin_line(&format!("registry = \"{REG}\""), REG), Some(None)); + assert_eq!( + unpin_line(&format!("registry = \"{REG}\""), REG), + Some(None) + ); } #[test] @@ -397,7 +411,10 @@ mod tests { let hosted = format!( "{original}\n[registries.{REG}]\nindex = \"sparse+https://patch.socket.dev/x/index/\"\n" ); - assert_eq!(remove_registry_block(&hosted, REG).as_deref(), Some(original)); + assert_eq!( + remove_registry_block(&hosted, REG).as_deref(), + Some(original) + ); let created = format!("[registries.{REG}]\nindex = \"sparse+https://x/\"\n"); assert_eq!(remove_registry_block(&created, REG).as_deref(), Some("")); } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs b/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs index b804342c6..49c9b7ee2 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs @@ -27,11 +27,11 @@ use std::collections::BTreeMap; use serde_json::Value; +use super::{Ctx, FormatResult, HostedPin, View}; +use crate::crawlers::composer_crawler::normalize_version; use crate::formats::composer::hosted::{ find_composer_entry, json_object_end_from, json_string_field, ComposerEntry, }; -use super::{Ctx, FormatResult, HostedPin, View}; -use crate::crawlers::composer_crawler::normalize_version; const COMPOSER_LOCK: &str = "composer.lock"; const DIST_KEY: &str = "\"dist\": {"; diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs index e42e183c8..af25a9b35 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs @@ -56,11 +56,11 @@ use std::collections::{BTreeMap, BTreeSet}; use regex::Regex; use super::{Ctx, FormatResult, HostedPin, View}; -use crate::utils::line_endings::{to_lf, LineEndings}; -use crate::vendor::gem::{gem_declaration_any, quoted_literal}; use crate::formats::gem::{ bundler_manifest_for, parse_spec, same_remote, split_checksum_entry, BUNDLER_LOCKS, }; +use crate::utils::line_endings::{to_lf, LineEndings}; +use crate::vendor::gem::{gem_declaration_any, quoted_literal}; /// The default upstream `GEM` remote. const RUBYGEMS_REMOTE: &str = "https://rubygems.org/"; @@ -249,17 +249,14 @@ fn choose_upstream( /// The line after which a spec named `name-version` sorts into `sec` /// (bundler writes specs sorted by full name). fn insertion_point(sec: &GemSec, full_name: &str) -> Option { - let pred = sec - .entries - .iter() - .rfind(|e| { - let full = if e.version.is_empty() { - e.name.clone() - } else { - format!("{}-{}", e.name, e.version) - }; - full.as_str() < full_name - }); + let pred = sec.entries.iter().rfind(|e| { + let full = if e.version.is_empty() { + e.name.clone() + } else { + format!("{}-{}", e.name, e.version) + }; + full.as_str() < full_name + }); pred.map(|e| e.last).or(sec.specs_line) } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs b/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs index 4ce16051f..cee422040 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs @@ -65,7 +65,10 @@ pub(crate) async fn restore( (uuid.clone(), ctx.client.go_sums(module, version).await) }); let sums: std::collections::BTreeMap> = - futures_util::future::join_all(lookups).await.into_iter().collect(); + futures_util::future::join_all(lookups) + .await + .into_iter() + .collect(); let mut go_mod_next = go_mod.clone(); let mut go_sum = view.read("go.sum").await.ok().flatten(); @@ -88,8 +91,8 @@ pub(crate) async fn restore( } } if let Some(text) = go_sum.as_deref() { - let mut next = remove_module_prefix_lines(text, socket_module) - .unwrap_or_else(|| text.to_string()); + let mut next = + remove_module_prefix_lines(text, socket_module).unwrap_or_else(|| text.to_string()); let upstream = format!( "{module} {version} {}\n{module} {version}/go.mod {}\n", sums.zip_h1, sums.mod_h1 diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs index 4e0eaf2ac..c3aa0733f 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs @@ -344,9 +344,7 @@ pub struct RestoreOutcome { impl RestoreOutcome { pub fn restored(&self) -> impl Iterator { - self.pins - .iter() - .filter(|p| p.status == PinStatus::Restored) + self.pins.iter().filter(|p| p.status == PinStatus::Restored) } pub fn refused(&self) -> impl Iterator { @@ -673,9 +671,7 @@ async fn restore_pass(view: &mut View<'_>, active: &[&HostedPin], ctx: &Ctx<'_>) Format::YarnLock => npm::restore_yarn_locks(view, &pins, &files, ctx).await, Format::PnpmLock => npm::restore_pnpm_locks(view, &pins, &files, ctx).await, Format::BunLock => npm::restore_bun_locks(view, &pins, &files, ctx).await, - Format::BunLockb if ctx.bun_lockb => { - bun_lockb::restore(view, &pins, &files, ctx).await - } + Format::BunLockb if ctx.bun_lockb => bun_lockb::restore(view, &pins, &files, ctx).await, Format::Cargo => cargo::restore(view, &pins, &files, ctx).await, Format::Golang => golang::restore(view, &pins, &files, ctx).await, Format::Gem => gem::restore(view, &pins, &files, ctx).await, diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs b/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs index ac105569f..8530ddf48 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs @@ -57,7 +57,16 @@ fn files_value(release: &[PypiFile], by_url: bool) -> Option { let key = if by_url { "url" } else { "file" }; let mut located: Vec<(&str, &PypiFile)> = release .iter() - .map(|f| (if by_url { f.url.as_str() } else { f.filename.as_str() }, f)) + .map(|f| { + ( + if by_url { + f.url.as_str() + } else { + f.filename.as_str() + }, + f, + ) + }) .collect(); // PDM orders each entry's files by the location it writes: a `static_urls` // lock by URL (so an sdist under `0c/…` precedes a wheel under `b0/…`), diff --git a/crates/socket-patch-core/src/patch/redirect/vlt.rs b/crates/socket-patch-core/src/patch/redirect/vlt.rs index 149868520..c3561b44a 100644 --- a/crates/socket-patch-core/src/patch/redirect/vlt.rs +++ b/crates/socket-patch-core/src/patch/redirect/vlt.rs @@ -985,5 +985,4 @@ mod tests { ); assert_eq!(carried_pin_original(&relocked, &old), None); } - } diff --git a/crates/socket-patch-core/src/policy/mod.rs b/crates/socket-patch-core/src/policy/mod.rs index 15778f264..605dfd9ce 100644 --- a/crates/socket-patch-core/src/policy/mod.rs +++ b/crates/socket-patch-core/src/policy/mod.rs @@ -456,7 +456,8 @@ fn compile(file: &str, lists: &[(&'static str, &[String])]) -> Result &'static SelectionPolicy { - static DEFAULTS: std::sync::LazyLock = std::sync::LazyLock::new(SelectionPolicy::unrestricted); + static DEFAULTS: std::sync::LazyLock = + std::sync::LazyLock::new(SelectionPolicy::unrestricted); &DEFAULTS } @@ -805,7 +806,9 @@ fn ceiling_dirs() -> Vec { #[cfg(unix)] fn trusted_owner(meta: &std::fs::Metadata) -> bool { use std::os::unix::fs::MetadataExt; - let sudo_uid = std::env::var("SUDO_UID").ok().and_then(|v| v.trim().parse::().ok()); + let sudo_uid = std::env::var("SUDO_UID") + .ok() + .and_then(|v| v.trim().parse::().ok()); // SAFETY: geteuid has no preconditions and cannot fail. owner_trusted(meta.uid(), unsafe { libc::geteuid() }, sudo_uid) } diff --git a/crates/socket-patch-core/src/policy/report.rs b/crates/socket-patch-core/src/policy/report.rs index ba8ff4221..0d095c7dc 100644 --- a/crates/socket-patch-core/src/policy/report.rs +++ b/crates/socket-patch-core/src/policy/report.rs @@ -48,7 +48,9 @@ pub fn policy_block( let (floor, floor_source) = policy.min_severity(); // Sorted: crawl order is filesystem order, and the two engines differ. let mut filtered: Vec<&FilteredEntry> = filtered.iter().collect(); - filtered.sort_by(|a, b| (&a.project, &a.purl, a.reason.code()).cmp(&(&b.project, &b.purl, b.reason.code()))); + filtered.sort_by(|a, b| { + (&a.project, &a.purl, a.reason.code()).cmp(&(&b.project, &b.purl, b.reason.code())) + }); let mut retained: Vec<&RetainedEntry> = retained.iter().collect(); retained.sort_by(|a, b| (&a.project, &a.purl).cmp(&(&b.project, &b.purl))); let filtered: Vec = filtered diff --git a/crates/socket-patch-core/src/policy/socket_yml.rs b/crates/socket-patch-core/src/policy/socket_yml.rs index 30a99499c..103fe20bd 100644 --- a/crates/socket-patch-core/src/policy/socket_yml.rs +++ b/crates/socket-patch-core/src/policy/socket_yml.rs @@ -486,7 +486,11 @@ pub(crate) fn package_spec_error(spec: &str) -> Option<&'static str> { if spec.is_empty() { return Some("package spec is empty"); } - if let Some(rest) = spec.get(..4).filter(|p| p.eq_ignore_ascii_case("pkg:")).map(|_| &spec[4..]) { + if let Some(rest) = spec + .get(..4) + .filter(|p| p.eq_ignore_ascii_case("pkg:")) + .map(|_| &spec[4..]) + { let valid = rest.split_once('/').is_some_and(|(ty, name)| { !ty.is_empty() && !name.trim_matches('/').is_empty() && !name.starts_with('@') }); @@ -785,7 +789,9 @@ pub(crate) fn parse_file( Some(Err((key, message))) => { warnings.push(PolicyWarning { code: super::SOCKET_YML_IGNORED_VALUE, - detail: super::strip_unsafe(&format!("{file}: {key} {message}; the key is ignored")), + detail: super::strip_unsafe(&format!( + "{file}: {key} {message}; the key is ignored" + )), }); Vec::new() } @@ -916,8 +922,12 @@ mod tests { // YAML beats everything; the case variant beats the version gate; // the version gate beats the keys. assert_eq!(err_key("patches: {minSeverty: x}\n").0, "version"); - assert!(err_key("Patches: {}\npatches: {minSeverty: x}\n").1.contains("misspelled")); - assert!(err_key("patches: {minSeverty: x\n").1.contains("invalid YAML")); + assert!(err_key("Patches: {}\npatches: {minSeverty: x}\n") + .1 + .contains("misspelled")); + assert!(err_key("patches: {minSeverty: x\n") + .1 + .contains("invalid YAML")); } #[test] @@ -986,12 +996,9 @@ mod tests { let (key, message) = err_key(text); assert_eq!(key, "", "{text:?}"); assert!( - [ - "invalid YAML", - "top level must be a mapping", - ] - .iter() - .any(|m| message.contains(m)), + ["invalid YAML", "top level must be a mapping",] + .iter() + .any(|m| message.contains(m)), "{text:?}: {message}" ); } diff --git a/crates/socket-patch-core/src/policy/tests.rs b/crates/socket-patch-core/src/policy/tests.rs index 5e03be9d7..2c18534f4 100644 --- a/crates/socket-patch-core/src/policy/tests.rs +++ b/crates/socket-patch-core/src/policy/tests.rs @@ -417,8 +417,14 @@ fn composer_package_filters_match_release_identity_and_preserve_branch_case() { Err(FilterReason::PackageIgnored { .. }) )); assert!(policy.admits_purl("pkg:composer/psr/log@3.0.3").is_ok()); - assert!(package_spec_matches("pkg:composer/PSR/Log@3.0.2.0", "pkg:composer/psr/log@3.0.2")); - assert!(!package_spec_matches("pkg:composer/psr/log@dev-Feature", "pkg:composer/psr/log@dev-feature")); + assert!(package_spec_matches( + "pkg:composer/PSR/Log@3.0.2.0", + "pkg:composer/psr/log@3.0.2" + )); + assert!(!package_spec_matches( + "pkg:composer/psr/log@dev-Feature", + "pkg:composer/psr/log@dev-feature" + )); } #[test] @@ -576,7 +582,10 @@ mod disk { assert!(owner_trusted(1000, 1000, None)); assert!(owner_trusted(0, 1000, None)); assert!(!owner_trusted(1001, 1000, None)); - assert!(owner_trusted(1001, 1000, Some(1001)), "sudo's invoking user"); + assert!( + owner_trusted(1001, 1000, Some(1001)), + "sudo's invoking user" + ); assert!(owner_trusted(1001, 0, None), "root trusts every owner"); } @@ -597,13 +606,21 @@ mod disk { fn this_repos_socket_yml_loads_and_excludes_its_fixtures() { let repo = Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); let (policy, warnings) = - SelectionPolicy::load(&DiskPolicyFs::new(&repo), &PolicyOverrides::default()).expect("valid"); + SelectionPolicy::load(&DiskPolicyFs::new(&repo), &PolicyOverrides::default()) + .expect("valid"); assert!(warnings.is_empty(), "{warnings:?}"); assert!(matches!(policy.source(), PolicySource::File { path, .. } if path == "socket.yml")); let lock = strings(&["package-lock.json"]); let err = policy - .admits_root(&root("crates/socket-patch-core/tests/fixtures/redirect/npm", &lock, true)) + .admits_root(&root( + "crates/socket-patch-core/tests/fixtures/redirect/npm", + &lock, + true, + )) .unwrap_err(); - assert_eq!(err.detail(), "crates/socket-patch-core/tests/fixtures/** (projectIgnorePaths)"); + assert_eq!( + err.detail(), + "crates/socket-patch-core/tests/fixtures/** (projectIgnorePaths)" + ); assert!(policy.admits_root(&root("", &lock, true)).is_ok()); } diff --git a/crates/socket-patch-core/src/rollout/stage.rs b/crates/socket-patch-core/src/rollout/stage.rs index 9ebd7e7ac..7c24ebadf 100644 --- a/crates/socket-patch-core/src/rollout/stage.rs +++ b/crates/socket-patch-core/src/rollout/stage.rs @@ -394,7 +394,11 @@ impl Stage { "a patch lookup failed for a package that could get its first patch, so \ no new patches were added this run ({} deferred) and none can take the \ missing package's place; re-run once the API answers", - if deferred == 1 { "1 package".to_string() } else { format!("{deferred} packages") } + if deferred == 1 { + "1 package".to_string() + } else { + format!("{deferred} packages") + } ), )); } @@ -415,7 +419,9 @@ impl Stage { purl: c.purl.clone(), uuid: c.uuid.clone(), reason: ROLLOUT_DEFERRED.to_string(), - detail: Some(format!("rank {rank} in the rollout queue; a later scan adds it")), + detail: Some(format!( + "rank {rank} in the rollout queue; a later scan adds it" + )), }) .collect() } @@ -502,4 +508,3 @@ pub fn rollout_json(configured: &MaxNew, plan: Option<&RolloutPlan>) -> serde_js "deferred": deferred, }) } - diff --git a/crates/socket-patch-core/src/telemetry.rs b/crates/socket-patch-core/src/telemetry.rs index d49aaa5c6..5f0eccb8d 100644 --- a/crates/socket-patch-core/src/telemetry.rs +++ b/crates/socket-patch-core/src/telemetry.rs @@ -4,9 +4,7 @@ use once_cell::sync::Lazy; use uuid::Uuid; use crate::constants::USER_AGENT; -use crate::utils::env_compat::{ - is_debug_enabled, is_offline_env, proxy_url_from_env, -}; +use crate::utils::env_compat::{is_debug_enabled, is_offline_env, proxy_url_from_env}; use crate::utils::fs::home_dir; use crate::vex::time::unix_to_ymdhms; diff --git a/crates/socket-patch-core/src/update/download.rs b/crates/socket-patch-core/src/update/download.rs index f176426ce..be1476b19 100644 --- a/crates/socket-patch-core/src/update/download.rs +++ b/crates/socket-patch-core/src/update/download.rs @@ -741,7 +741,10 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let missing = tmp.path().join("never-existed"); sweep_stale_stages(&missing); - assert!(!missing.exists(), "sweep must not create the destination dir"); + assert!( + !missing.exists(), + "sweep must not create the destination dir" + ); } /// A write failure AFTER a successful open (EFBIG here, standing in @@ -757,8 +760,7 @@ mod tests { #[test] fn stage_write_failure_cleans_up_stage_file() { const CHILD_ENV: &str = "SOCKET_PATCH_CORE_TEST_STAGE_FSIZE_CHILD"; - const TEST_NAME: &str = - "update::download::tests::stage_write_failure_cleans_up_stage_file"; + const TEST_NAME: &str = "update::download::tests::stage_write_failure_cleans_up_stage_file"; if std::env::var_os(CHILD_ENV).is_none() { let exe = std::env::current_exe().expect("test binary path must resolve"); let output = std::process::Command::new(exe) @@ -824,7 +826,10 @@ mod tests { matches!(err, UpdateError::SwapFailed(_)), "expected SwapFailed, got: {err}" ); - assert!(err.to_string().contains("error writing staged binary"), "{err}"); + assert!( + err.to_string().contains("error writing staged binary"), + "{err}" + ); let leftovers: Vec = std::fs::read_dir(tmp.path()) .unwrap() .map(|e| e.unwrap().file_name().to_string_lossy().into_owned()) diff --git a/crates/socket-patch-core/src/update/release.rs b/crates/socket-patch-core/src/update/release.rs index 5b2869012..7c3b04c29 100644 --- a/crates/socket-patch-core/src/update/release.rs +++ b/crates/socket-patch-core/src/update/release.rs @@ -751,9 +751,11 @@ mod tests { .mount(&server) .await; - let client = - metadata_client(&short_timeouts(), follow_redirect_policy(&default_endpoints())) - .unwrap(); + let client = metadata_client( + &short_timeouts(), + follow_redirect_policy(&default_endpoints()), + ) + .unwrap(); let err = client .get(format!("{}/start", server.uri())) .send() @@ -786,9 +788,11 @@ mod tests { .mount(&server) .await; - let client = - metadata_client(&short_timeouts(), follow_redirect_policy(&default_endpoints())) - .unwrap(); + let client = metadata_client( + &short_timeouts(), + follow_redirect_policy(&default_endpoints()), + ) + .unwrap(); let err = client .get(format!("{}/start", server.uri())) .send() @@ -864,7 +868,10 @@ mod tests { .unwrap_err(); assert!(matches!(err, UpdateError::CheckFailed(_)), "{err:?}"); let msg = err.to_string(); - assert!(msg.contains("expected a redirect to the latest tag"), "{msg}"); + assert!( + msg.contains("expected a redirect to the latest tag"), + "{msg}" + ); assert!(msg.contains("API fallback:"), "{msg}"); assert!(msg.contains("returned 500"), "{msg}"); } @@ -945,8 +952,14 @@ mod tests { #[test] fn url_host_keeps_explicit_ports() { - assert_eq!(url_host("http://127.0.0.1:9/x").as_deref(), Some("127.0.0.1:9")); - assert_eq!(url_host("https://github.com/a").as_deref(), Some("github.com")); + assert_eq!( + url_host("http://127.0.0.1:9/x").as_deref(), + Some("127.0.0.1:9") + ); + assert_eq!( + url_host("https://github.com/a").as_deref(), + Some("github.com") + ); assert_eq!(url_host("not a url"), None); } @@ -959,7 +972,9 @@ mod tests { // code stays `check_failed` (stable contract). let server = MockServer::start().await; Mock::given(method("GET")) - .and(path("/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS")) + .and(path( + "/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS", + )) .respond_with(ResponseTemplate::new(404)) .mount(&server) .await; @@ -992,7 +1007,9 @@ mod tests { // silently. let server = MockServer::start().await; Mock::given(method("GET")) - .and(path("/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS")) + .and(path( + "/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS", + )) .respond_with(ResponseTemplate::new(500)) .mount(&server) .await; diff --git a/crates/socket-patch-core/src/utils/group_commit.rs b/crates/socket-patch-core/src/utils/group_commit.rs index e8f2284fe..973c02877 100644 --- a/crates/socket-patch-core/src/utils/group_commit.rs +++ b/crates/socket-patch-core/src/utils/group_commit.rs @@ -304,9 +304,9 @@ where // write the lock edits beside the pre-run ledger. Put the caller's value // back before the unwind continues — the same value a caught-and- // continued caller holds. - if let Err(panic) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - edit(Arc::make_mut(value)) - })) { + if let Err(panic) = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| edit(Arc::make_mut(value)))) + { files.insert(key, captured(value)); drop(files); std::panic::resume_unwind(panic); @@ -1608,7 +1608,10 @@ mod tests { .unwrap(); remove_dir_after_commit(&dir).await; drop(dropped); - assert!(dir.join("config.toml").exists(), "an abandoned commit removes nothing"); + assert!( + dir.join("config.toml").exists(), + "an abandoned commit removes nothing" + ); let group = GroupCommit::begin(root); super::super::fs::remove_file(&dir.join("config.toml")) @@ -1617,7 +1620,10 @@ mod tests { remove_dir_after_commit(&dir).await; assert!(dir.join("config.toml").exists(), "captured, still on disk"); group.commit().await.unwrap(); - assert!(!dir.exists(), "the emptied directory is removed after the commit"); + assert!( + !dir.exists(), + "the emptied directory is removed after the commit" + ); std::fs::create_dir_all(&dir).unwrap(); std::fs::write(dir.join("config.toml"), b"[patch]\n").unwrap(); @@ -1629,7 +1635,10 @@ mod tests { remove_dir_after_commit(&dir).await; group.commit().await.unwrap(); assert!(!dir.join("config.toml").exists()); - assert!(dir.join("credentials.toml").exists(), "a non-empty directory is kept"); + assert!( + dir.join("credentials.toml").exists(), + "a non-empty directory is kept" + ); remove_dir_after_commit(&root.join("gone")).await; std::fs::remove_file(dir.join("credentials.toml")).unwrap(); diff --git a/crates/socket-patch-core/src/utils/hatch.rs b/crates/socket-patch-core/src/utils/hatch.rs index a08d79a97..756dacd1f 100644 --- a/crates/socket-patch-core/src/utils/hatch.rs +++ b/crates/socket-patch-core/src/utils/hatch.rs @@ -265,8 +265,7 @@ fn rewrite_environments( .is_some_and(|kind| kind != "virtual") { return Err( - "Hatch sources, overrides and custom environments require agent mode" - .into(), + "Hatch sources, overrides and custom environments require agent mode".into(), ); } for key in ["dependencies", "extra-dependencies"] { diff --git a/crates/socket-patch-core/src/utils/line_endings.rs b/crates/socket-patch-core/src/utils/line_endings.rs index 889f6ad32..c6f257359 100644 --- a/crates/socket-patch-core/src/utils/line_endings.rs +++ b/crates/socket-patch-core/src/utils/line_endings.rs @@ -119,5 +119,4 @@ mod tests { assert_eq!(majority_terminator("a\r\nb\n"), "\n", "a tie is LF"); assert_eq!(majority_terminator("{}"), "\n", "no break: LF, not os.EOL"); } - } diff --git a/crates/socket-patch-core/src/utils/mod.rs b/crates/socket-patch-core/src/utils/mod.rs index ee47573e8..1fa8d934b 100644 --- a/crates/socket-patch-core/src/utils/mod.rs +++ b/crates/socket-patch-core/src/utils/mod.rs @@ -7,9 +7,9 @@ pub mod env_compat; pub mod failpoint; pub mod fs; pub mod group_commit; -pub mod notice; pub(crate) mod http; pub(crate) mod line_endings; +pub mod notice; pub mod pdm_lock; pub mod pipenv; pub mod poetry_lock; diff --git a/crates/socket-patch-core/src/utils/python_script.rs b/crates/socket-patch-core/src/utils/python_script.rs index 2ca51e107..5eb997005 100644 --- a/crates/socket-patch-core/src/utils/python_script.rs +++ b/crates/socket-patch-core/src/utils/python_script.rs @@ -627,7 +627,12 @@ mod rendering_tests { "{direct}" ); assert!(uv_line.ends_with('}'), "{direct}"); - assert!(direct.starts_with("[project]\nname = \"p\"\ndependencies = [\"alpha==1.0.0\"]\n\n[tool]\n"), "{direct}"); + assert!( + direct.starts_with( + "[project]\nname = \"p\"\ndependencies = [\"alpha==1.0.0\"]\n\n[tool]\n" + ), + "{direct}" + ); assert_settled(&direct); let transitive = rewrite_project_metadata( diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index 2dbe53d91..546250c0d 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -516,5 +516,4 @@ mod tests { ); } } - } diff --git a/crates/socket-patch-core/src/vendor/bun_lockb.rs b/crates/socket-patch-core/src/vendor/bun_lockb.rs index 27751bcd0..21f5ca832 100644 --- a/crates/socket-patch-core/src/vendor/bun_lockb.rs +++ b/crates/socket-patch-core/src/vendor/bun_lockb.rs @@ -1779,7 +1779,10 @@ mod tests { lock.set_package(package.id, &repin, &digest()).unwrap(); assert_eq!(lock.bytes().len(), first.len(), "{version}"); assert!( - !lock.bytes().windows(token.len()).any(|w| w == token.as_bytes()), + !lock + .bytes() + .windows(token.len()) + .any(|w| w == token.as_bytes()), "{version}: the superseded URL is gone" ); // A remote tarball keeps the registry record's inactive bytes; a @@ -1822,7 +1825,9 @@ mod tests { .set_package(1, ".socket/vendor/npm/x/minimist-1.2.2.tgz", &digest()) .unwrap(); let at = local.resolution_at(1); - assert!(local.data[at + 16..at + local.resolution_size].iter().all(|b| *b == 0)); + assert!(local.data[at + 16..at + local.resolution_size] + .iter() + .all(|b| *b == 0)); } #[test] diff --git a/crates/socket-patch-core/src/vendor/cargo_lock.rs b/crates/socket-patch-core/src/vendor/cargo_lock.rs index 7e50bccaf..73bdf8275 100644 --- a/crates/socket-patch-core/src/vendor/cargo_lock.rs +++ b/crates/socket-patch-core/src/vendor/cargo_lock.rs @@ -64,11 +64,9 @@ use std::sync::Arc; use toml_edit::{DocumentMut, Item, Table}; use super::cargo_tag; -use crate::formats::cargo::{ - locked_packages, metadata_checksum_key, parse_ref, LockedPackage, -}; use super::parse_memo::ParseMemo; use super::state::CargoLockOriginal; +use crate::formats::cargo::{locked_packages, metadata_checksum_key, parse_ref, LockedPackage}; use crate::utils::fs::{atomic_write_bytes_preserving_mode, read_regular_to_string}; /// Why a lock edit could not be performed. diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs b/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs index 4960652e4..09af505f5 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs @@ -594,9 +594,15 @@ async fn inventory_requirements_txt(view: &ProjectView<'_>) -> Option None, - CopyClaim::OtherTag(other) => Some(format!( - "{CARGO_LOCK} builds the copy tagged for patch {other} ({name} {})", - cargo_tag::tag_version(version, other) - )), - CopyClaim::UntaggedOverride => Some(format!( - "{CARGO_LOCK} builds an untagged {name} {version}, not the copy (which \ + let why = match lock.vendored_in_use(name, version, &vref.uuid, copy_tagged) { + CopyClaim::Consumed => None, + CopyClaim::OtherTag(other) => Some(format!( + "{CARGO_LOCK} builds the copy tagged for patch {other} ({name} {})", + cargo_tag::tag_version(version, other) + )), + CopyClaim::UntaggedOverride => Some(format!( + "{CARGO_LOCK} builds an untagged {name} {version}, not the copy (which \ cargo would lock as {}): another [patch] or path dependency overrides it", - cargo_tag::tag_version(version, &vref.uuid) - )), - CopyClaim::NotConsumed => Some(format!( - "{CARGO_LOCK} does not build {name}@{version} from it (an unused patch, \ + cargo_tag::tag_version(version, &vref.uuid) + )), + CopyClaim::NotConsumed => Some(format!( + "{CARGO_LOCK} does not build {name}@{version} from it (an unused patch, \ or the lock resolves it from a registry)" - )), - }; + )), + }; if let Some(why) = why { out.diag( DIAG_REF_INVALID, diff --git a/crates/socket-patch-core/src/vex/discover/gem.rs b/crates/socket-patch-core/src/vex/discover/gem.rs index 77f7388a8..3e0718864 100644 --- a/crates/socket-patch-core/src/vex/discover/gem.rs +++ b/crates/socket-patch-core/src/vex/discover/gem.rs @@ -125,10 +125,10 @@ use super::{ names_vendor_dir, simple_purl, vendor_ref, vendored_leaf_purl, DiscoverCtx, Discovery, PatchedRef, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; -use crate::vendor::gem::{gem_declaration_any, quoted_literal}; use crate::formats::gem::{ bundler_manifest_for, same_remote, GemfileLock, Section, SpecLine, BUNDLER_LOCKS, }; +use crate::vendor::gem::{gem_declaration_any, quoted_literal}; pub(crate) async fn extract(ctx: &DiscoverCtx<'_>, out: &mut Discovery) { // Both locks, legacy spelling first (order only affects diagnostics). diff --git a/crates/socket-patch-core/src/vex/discover/maven.rs b/crates/socket-patch-core/src/vex/discover/maven.rs index 734f3e61d..fc9e1fdb0 100644 --- a/crates/socket-patch-core/src/vex/discover/maven.rs +++ b/crates/socket-patch-core/src/vex/discover/maven.rs @@ -88,15 +88,15 @@ use super::{ Discovery, PatchedRef, WiringMode, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; +use crate::formats::maven::{ + is_maven_coordinate, is_maven_version_text, parse_pom, split_socket_version, Pom, PomDep, + PomRepo, +}; use crate::patch::redirect::{ local_repo_artifact_path, MVN_CHECKSUMS, MVN_CONFIG, TRUSTED_CHECKSUMS_ON, }; use crate::utils::digest::sha256_hex; use crate::vendor::lock_inventory::LockIntegrity; -use crate::formats::maven::{ - is_maven_coordinate, is_maven_version_text, parse_pom, split_socket_version, Pom, PomDep, - PomRepo, -}; use crate::vendor::maven_repo::{sha1_sidecar_matches, VENDOR_REPO_URL_PREFIX}; use crate::vendor::path::{sweep_vendor_dirs, VENDOR_DIR}; diff --git a/crates/socket-patch-core/src/vex/discover/nuget.rs b/crates/socket-patch-core/src/vex/discover/nuget.rs index d7f3cd95d..3f608233f 100644 --- a/crates/socket-patch-core/src/vex/discover/nuget.rs +++ b/crates/socket-patch-core/src/vex/discover/nuget.rs @@ -73,8 +73,8 @@ use super::{ Discovery, PatchedRef, UnlockedPin, WiringMode, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; -use crate::vendor::lock_inventory::LockIntegrity; use crate::formats::nuget::{parse_config, NugetConfig}; +use crate::vendor::lock_inventory::LockIntegrity; use crate::vendor::nuget_config::{same_file, CONFIG_NAMES}; use crate::vendor::nuget_feed::{is_plain_nuget_token, nuget_lock_entries, nupkg_leaf}; use crate::vendor::path::VENDOR_DIR; diff --git a/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs b/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs index 1e0bc6149..de8dc2e67 100644 --- a/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs +++ b/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs @@ -569,5 +569,8 @@ async fn fetch_missing_blobs_mixed_outcomes_aggregate_and_format() { // End-to-end formatter exercise with a genuinely mixed result. let rendered = format_fetch_result(&result); assert!(rendered.contains("Downloaded 1 blob\n"), "{rendered}"); - assert!(rendered.contains("Failed to download 2 blobs"), "{rendered}"); + assert!( + rendered.contains("Failed to download 2 blobs"), + "{rendered}" + ); } diff --git a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs index 24a50d9b9..fbbda6290 100644 --- a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs +++ b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs @@ -95,7 +95,11 @@ async fn get_vendor_paths_global_nonexistent_composer_home_falls_back() { fn write_composer_shim(dir: &Path, echo_path: &Path) { use std::os::unix::fs::PermissionsExt; let shim = dir.join("composer"); - std::fs::write(&shim, format!("#!/bin/sh\necho '{}'\n", echo_path.display())).unwrap(); + std::fs::write( + &shim, + format!("#!/bin/sh\necho '{}'\n", echo_path.display()), + ) + .unwrap(); std::fs::set_permissions(&shim, std::fs::Permissions::from_mode(0o755)).unwrap(); } diff --git a/crates/socket-patch-core/tests/crawler_python_e2e.rs b/crates/socket-patch-core/tests/crawler_python_e2e.rs index 9e1addf9f..2ad70b423 100644 --- a/crates/socket-patch-core/tests/crawler_python_e2e.rs +++ b/crates/socket-patch-core/tests/crawler_python_e2e.rs @@ -518,6 +518,191 @@ async fn get_global_python_site_packages_discovers_uv_python_install() { ); } +// ── pipx venv discovery ─────────────────────────────────────── + +/// Run `get_global_python_site_packages` with HOME, PIPX_HOME and +/// XDG_DATA_HOME rebound (`None` unsets), restoring all three after. +/// pipx resolves its home from these, so every pipx test has to pin +/// them or an ambient value on the host would decide the result. +async fn global_site_packages_with_env( + home: &Path, + pipx_home: Option<&Path>, + xdg_data_home: Option<&Path>, +) -> Vec { + let saved: Vec<(&str, Option)> = ["HOME", "PIPX_HOME", "XDG_DATA_HOME"] + .into_iter() + .map(|k| (k, std::env::var(k).ok())) + .collect(); + std::env::set_var("HOME", home); + for (key, value) in [("PIPX_HOME", pipx_home), ("XDG_DATA_HOME", xdg_data_home)] { + match value { + Some(v) => std::env::set_var(key, v), + None => std::env::remove_var(key), + } + } + let result = get_global_python_site_packages().await; + for (key, value) in saved { + match value { + Some(v) => std::env::set_var(key, v), + None => std::env::remove_var(key), + } + } + result +} + +/// The site-packages of a pipx app venv under `pipx_home`, in the +/// platform's venv layout (`lib/python3.X/site-packages` on Unix, +/// `Lib\site-packages` on Windows). +fn pipx_venv_site_packages(pipx_home: &Path, app: &str) -> std::path::PathBuf { + let venv = pipx_home.join("venvs").join(app); + if cfg!(windows) { + venv.join("Lib").join("site-packages") + } else { + venv.join("lib").join("python3.11").join("site-packages") + } +} + +/// `pipx install hatch` on Linux (pipx >= 1.3) puts the app venv at +/// `~/.local/share/pipx/venvs/hatch` (#415). Every app venv must surface, +/// not just the first. +#[cfg(all(not(target_os = "macos"), not(windows)))] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_linux() { + let tmp = tempfile::tempdir().unwrap(); + let pipx_home = tmp.path().join(".local").join("share").join("pipx"); + let staged: Vec<_> = ["hatch", "black"] + .iter() + .map(|app| pipx_venv_site_packages(&pipx_home, app)) + .collect(); + for sp in &staged { + tokio::fs::create_dir_all(sp).await.unwrap(); + } + + let result = global_site_packages_with_env(tmp.path(), None, None).await; + for sp in &staged { + assert!( + result.iter().any(|p| p == sp), + "pipx venv {} must surface; got {result:?}", + sp.display() + ); + } +} + +/// pipx's Linux default follows `$XDG_DATA_HOME` (platformdirs' +/// `user_data_dir`), so a relocated data home moves the venvs too. +#[cfg(all(not(target_os = "macos"), not(windows)))] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_under_xdg_data_home() { + let tmp = tempfile::tempdir().unwrap(); + let xdg = tmp.path().join("xdg-data"); + let sp = pipx_venv_site_packages(&xdg.join("pipx"), "hatch"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), None, Some(&xdg)).await; + assert!( + result.iter().any(|p| p == &sp), + "pipx venv under XDG_DATA_HOME must surface; got {result:?}" + ); +} + +/// Native (C-extension) packages land in `lib64` on RHEL/Fedora/SUSE +/// venvs, the same split the other well-known scans already handle. +#[cfg(not(windows))] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venv_lib64() { + let tmp = tempfile::tempdir().unwrap(); + let pipx_home = tmp.path().join("pipx-home"); + let sp = pipx_home + .join("venvs") + .join("hatch") + .join("lib64") + .join("python3.11") + .join("site-packages"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), Some(&pipx_home), None).await; + assert!( + result.iter().any(|p| p == &sp), + "pipx venv lib64 site-packages must surface; got {result:?}" + ); +} + +/// pipx's legacy home `~/.local/pipx` is still used when it exists +/// (pipx < 1.3 installs, and pipx's fallback on every OS), and is +/// what macOS runners use in the #415 probe. +#[cfg(not(windows))] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_legacy_home() { + let tmp = tempfile::tempdir().unwrap(); + let sp = pipx_venv_site_packages(&tmp.path().join(".local").join("pipx"), "hatch"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), None, None).await; + assert!( + result.iter().any(|p| p == &sp), + "legacy ~/.local/pipx venv must surface; got {result:?}" + ); +} + +/// platformdirs' macOS data dir is `~/Library/Application Support`, +/// which pipx 1.3–1.4 used as its default home. +#[cfg(target_os = "macos")] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_macos_app_support() { + let tmp = tempfile::tempdir().unwrap(); + let pipx_home = tmp + .path() + .join("Library") + .join("Application Support") + .join("pipx"); + let sp = pipx_venv_site_packages(&pipx_home, "hatch"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), None, None).await; + assert!( + result.iter().any(|p| p == &sp), + "macOS Application Support pipx venv must surface; got {result:?}" + ); +} + +/// pipx's Windows default home is `%USERPROFILE%\pipx`, with venvs in +/// the Windows layout `venvs\\Lib\site-packages`. +#[cfg(windows)] +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_windows() { + let tmp = tempfile::tempdir().unwrap(); + let sp = pipx_venv_site_packages(&tmp.path().join("pipx"), "hatch"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), None, None).await; + assert!( + result.iter().any(|p| p == &sp), + "%USERPROFILE%\\pipx venv must surface; got {result:?}" + ); +} + +/// An explicit `PIPX_HOME` relocates every pipx venv, on every OS. +#[tokio::test] +#[serial] +async fn get_global_python_site_packages_discovers_pipx_venvs_under_pipx_home() { + let tmp = tempfile::tempdir().unwrap(); + let pipx_home = tmp.path().join("custom pipx"); + let sp = pipx_venv_site_packages(&pipx_home, "hatch"); + tokio::fs::create_dir_all(&sp).await.unwrap(); + + let result = global_site_packages_with_env(tmp.path(), Some(&pipx_home), None).await; + assert!( + result.iter().any(|p| p == &sp), + "pipx venv under PIPX_HOME must surface; got {result:?}" + ); +} + // ── project-marker fallback in get_site_packages_paths ──────── /// A project with `pyproject.toml` but no `.venv` must fall through diff --git a/crates/socket-patch-core/tests/hosted_inventory.rs b/crates/socket-patch-core/tests/hosted_inventory.rs index 1bb3772d2..db1ecd6ae 100644 --- a/crates/socket-patch-core/tests/hosted_inventory.rs +++ b/crates/socket-patch-core/tests/hosted_inventory.rs @@ -51,9 +51,15 @@ async fn contradicted_hosted_lock_is_contested_not_absent() { assert!(!inv.is_empty(), "contested wiring is hosted state: {inv:?}"); let refusal = inv.contested_refusal().expect("a refusal"); assert!(refusal.contains("npm-shrinkwrap.json"), "{refusal}"); - assert!(refusal.contains("git checkout -- npm-shrinkwrap.json"), "{refusal}"); + assert!( + refusal.contains("git checkout -- npm-shrinkwrap.json"), + "{refusal}" + ); assert!(refusal.contains("patched_ref_unattributable"), "{refusal}"); - assert!(!refusal.contains(GRANT), "the grant token is not a patch: {refusal}"); + assert!( + !refusal.contains(GRANT), + "the grant token is not a patch: {refusal}" + ); } #[tokio::test] diff --git a/crates/socket-patch-core/tests/poetry_hosted.rs b/crates/socket-patch-core/tests/poetry_hosted.rs index bd3ca3c83..2d2e17d5d 100644 --- a/crates/socket-patch-core/tests/poetry_hosted.rs +++ b/crates/socket-patch-core/tests/poetry_hosted.rs @@ -1,6 +1,4 @@ -use socket_patch_core::patch::redirect::{ - rewrite_registry_redirect, DepOverride, Integrity, -}; +use socket_patch_core::patch::redirect::{rewrite_registry_redirect, DepOverride, Integrity}; use socket_patch_core::utils::poetry_lock::rewrite_poetry_lock; use std::collections::BTreeMap; @@ -63,7 +61,11 @@ fn native_lock_generations_redirect_idempotently() { let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); assert_eq!( codes, - if pre_1_4 { vec!["redirect_poetry_stale_install_risk"] } else { vec![] }, + if pre_1_4 { + vec!["redirect_poetry_stale_install_risk"] + } else { + vec![] + }, "{version}: {:?}", result.warnings ); @@ -92,12 +94,24 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert!(lock10.contains(&format!("url = \"{URL}#sha256={sha}&\"")), "{lock10}"); + assert!( + lock10.contains(&format!("url = \"{URL}#sha256={sha}&\"")), + "{lock10}" + ); assert!(lock10.contains("reference = \"\""), "{lock10}"); - assert!(lock10.contains(&format!("urllib3 = [{{ file = \"{WHEEL}\", hash = \"sha256:{sha}\" }}]")), "{lock10}"); + assert!( + lock10.contains(&format!( + "urllib3 = [{{ file = \"{WHEEL}\", hash = \"sha256:{sha}\" }}]" + )), + "{lock10}" + ); // Poetry >= 1.2 consuming this 1.0 lock verifies the package `files` // entry, so it is written too (1.0 ignores the extra key). - assert_eq!(lock10.matches(&format!("sha256:{sha}")).count(), 2, "{lock10}"); + assert_eq!( + lock10.matches(&format!("sha256:{sha}")).count(), + 2, + "{lock10}" + ); let doc: toml_edit::DocumentMut = lock10.parse().unwrap(); assert!(doc["package"][0]["files"].is_array(), "{lock10}"); @@ -124,7 +138,11 @@ fn hosted_shapes_match_each_lock_generations_installer() { &BTreeMap::from([("poetry.lock".to_string(), lock10_populated)]), &[patch()], ); - assert!(rerun.files.is_empty() && rerun.warnings.is_empty(), "{:?}", rerun.warnings); + assert!( + rerun.files.is_empty() && rerun.warnings.is_empty(), + "{:?}", + rerun.warnings + ); let lock11 = rewrite_registry_redirect( &BTreeMap::from([("poetry.lock".to_string(), original("1.2.2"))]), @@ -132,8 +150,15 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert_eq!(lock11.matches(&format!("sha256:{sha}")).count(), 2, "package files + metadata.files:\n{lock11}"); - assert!(lock11.contains(&format!("url = \"{URL}\"")), "no fragment on 1.1"); + assert_eq!( + lock11.matches(&format!("sha256:{sha}")).count(), + 2, + "package files + metadata.files:\n{lock11}" + ); + assert!( + lock11.contains(&format!("url = \"{URL}\"")), + "no fragment on 1.1" + ); assert!(!lock11.contains("reference"), "{lock11}"); let doc: toml_edit::DocumentMut = lock11.parse().unwrap(); assert!(doc["package"][0]["files"].is_array()); @@ -145,11 +170,19 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert_eq!(lock21.matches(&format!("sha256:{sha}")).count(), 1, "{lock21}"); + assert_eq!( + lock21.matches(&format!("sha256:{sha}")).count(), + 1, + "{lock21}" + ); assert!(!lock21.contains("reference")); let pristine: toml_edit::DocumentMut = original("2.4.3").parse().unwrap(); let doc: toml_edit::DocumentMut = lock21.parse().unwrap(); - assert_eq!(doc["metadata"].to_string(), pristine["metadata"].to_string(), "[metadata] untouched on 2.x"); + assert_eq!( + doc["metadata"].to_string(), + pristine["metadata"].to_string(), + "[metadata] untouched on 2.x" + ); } #[test] @@ -248,14 +281,21 @@ fn absent_entries_warn_once_and_missing_sha256_is_gated_once_per_dep() { let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); assert_eq!( codes, - vec!["redirect_poetry_entry_not_found", "redirect_poetry_entry_not_found"] + vec![ + "redirect_poetry_entry_not_found", + "redirect_poetry_entry_not_found" + ] ); let mut missing_hash = patch(); missing_hash.integrity.sha256 = None; let result = rewrite_registry_redirect(&files, &[missing_hash]); assert!(result.files.is_empty()); let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); - assert_eq!(codes, vec!["redirect_poetry_missing_sha256"], "gated once, not once per lock"); + assert_eq!( + codes, + vec!["redirect_poetry_missing_sha256"], + "gated once, not once per lock" + ); } /// A future Poetry that bumps the lock minor (2.2) is rewritten like 2.1 in @@ -278,11 +318,20 @@ fn rotated_grant_token_supersedes_the_prior_hosted_url() { let first = rewrite_registry_redirect(&files, &[patch()]); let mut rotated = patch(); rotated.token = "00000000-0000-4000-8000-000000000000".into(); - rotated.artifact_url = URL.replace("7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e", "00000000-0000-4000-8000-000000000000"); + rotated.artifact_url = URL.replace( + "7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e", + "00000000-0000-4000-8000-000000000000", + ); let second = rewrite_registry_redirect(&first.files, &[rotated.clone()]); assert!(second.warnings.is_empty(), "{:?}", second.warnings); let lock = &second.files["poetry.lock"]; assert!(lock.contains(&rotated.artifact_url) && !lock.contains(URL)); assert_eq!(second.edits.len(), 1); - assert!(second.edits[0].original.as_ref().unwrap().as_str().unwrap().contains(URL)); + assert!(second.edits[0] + .original + .as_ref() + .unwrap() + .as_str() + .unwrap() + .contains(URL)); } diff --git a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs index 33c34297c..abde352bb 100644 --- a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs +++ b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs @@ -18,11 +18,7 @@ use socket_patch_core::telemetry::{is_telemetry_disabled, sanitize_error_message /// Every environment variable that can independently disable telemetry. /// Scrubbing the full set is what makes the per-var causation asserts honest. -const DISABLE_VARS: &[&str] = &[ - "SOCKET_TELEMETRY_DISABLED", - "VITEST", - "SOCKET_OFFLINE", -]; +const DISABLE_VARS: &[&str] = &["SOCKET_TELEMETRY_DISABLED", "VITEST", "SOCKET_OFFLINE"]; /// Run `f` with all telemetry-disabling vars removed, restoring the prior /// values afterward even if `f` panics (so one failing assert can't poison diff --git a/crates/socket-patch-core/tests/upstream_restore_golden.rs b/crates/socket-patch-core/tests/upstream_restore_golden.rs index 373e0bc04..9885db0a9 100644 --- a/crates/socket-patch-core/tests/upstream_restore_golden.rs +++ b/crates/socket-patch-core/tests/upstream_restore_golden.rs @@ -13,10 +13,12 @@ use std::fs; use std::path::{Path, PathBuf}; use serial_test::serial; -use socket_patch_core::patch::redirect::{rewrite_registry_redirect_with_pipenv_version, DepOverride}; use socket_patch_core::patch::redirect::upstream::{ restore_upstream, HostedPin, PinStatus, RestoreOptions, }; +use socket_patch_core::patch::redirect::{ + rewrite_registry_redirect_with_pipenv_version, DepOverride, +}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -29,7 +31,10 @@ fn walk(dir: &Path) -> BTreeMap { if !dir.is_dir() { return out; } - for entry in walkdir::WalkDir::new(dir).into_iter().filter_map(Result::ok) { + for entry in walkdir::WalkDir::new(dir) + .into_iter() + .filter_map(Result::ok) + { if entry.file_type().is_file() { let rel = entry .path() @@ -45,16 +50,27 @@ fn walk(dir: &Path) -> BTreeMap { /// Tokens of `pattern` that `input` holds and `expected` does not: the /// upstream values the hosted rewrite replaced. -fn vanished(input: &BTreeMap, expected: &BTreeMap, re: &str) -> Vec { +fn vanished( + input: &BTreeMap, + expected: &BTreeMap, + re: &str, +) -> Vec { let re = regex::Regex::new(re).unwrap(); let all = |files: &BTreeMap| -> BTreeSet { files .values() - .flat_map(|t| re.captures_iter(t).map(|c| c[1].to_string()).collect::>()) + .flat_map(|t| { + re.captures_iter(t) + .map(|c| c[1].to_string()) + .collect::>() + }) .collect() }; let after = all(expected); - let mut out: Vec = all(input).into_iter().filter(|t| !after.contains(t)).collect(); + let mut out: Vec = all(input) + .into_iter() + .filter(|t| !after.contains(t)) + .collect(); out.sort(); out } @@ -80,10 +96,9 @@ fn load(flavor: &str) -> Vec { // `expected/` holds only the files the rewrite changed. let mut expected = input.clone(); expected.extend(walk(&dir.join("expected"))); - let overrides = serde_json::from_str( - &fs::read_to_string(dir.join("overrides.json")).unwrap(), - ) - .unwrap(); + let overrides = + serde_json::from_str(&fs::read_to_string(dir.join("overrides.json")).unwrap()) + .unwrap(); Case { dir, input, @@ -132,10 +147,23 @@ async fn run_case_with( (walk(tmp.path()), statuses) } -fn assert_round_trip(case: &Case, after: &BTreeMap, statuses: &[(String, PinStatus)]) { - assert!(!statuses.is_empty(), "{}: discovery found no hosted pin", case.dir.display()); +fn assert_round_trip( + case: &Case, + after: &BTreeMap, + statuses: &[(String, PinStatus)], +) { + assert!( + !statuses.is_empty(), + "{}: discovery found no hosted pin", + case.dir.display() + ); for (purl, status) in statuses { - assert_eq!(*status, PinStatus::Restored, "{}: {purl}", case.dir.display()); + assert_eq!( + *status, + PinStatus::Restored, + "{}: {purl}", + case.dir.display() + ); } for (rel, want) in &case.input { assert_eq!( @@ -145,8 +173,15 @@ fn assert_round_trip(case: &Case, after: &BTreeMap, statuses: &[ case.dir.display() ); } - let extra: Vec<&String> = after.keys().filter(|k| !case.input.contains_key(*k)).collect(); - assert!(extra.is_empty(), "{}: left behind {extra:?}", case.dir.display()); + let extra: Vec<&String> = after + .keys() + .filter(|k| !case.input.contains_key(*k)) + .collect(); + assert!( + extra.is_empty(), + "{}: left behind {extra:?}", + case.dir.display() + ); } /// Sets env vars for the guard's lifetime (tests using it are `#[serial]`). @@ -207,10 +242,9 @@ async fn npm_mock(case: &Case) -> MockServer { } Mock::given(method("GET")) .and(path(format!("/{}/{version}", name.replace('/', "%2f")))) - .respond_with( - ResponseTemplate::new(200) - .set_body_json(serde_json::json!({ "name": name, "version": version, "dist": dist })), - ) + .respond_with(ResponseTemplate::new(200).set_body_json( + serde_json::json!({ "name": name, "version": version, "dist": dist }), + )) .mount(&server) .await; } @@ -416,7 +450,12 @@ fn assert_refused( } other => panic!("{}: expected a refusal, got {other:?}", case.dir.display()), } - assert_eq!(after, &case.expected, "{}: a refused pin must change nothing", case.dir.display()); + assert_eq!( + after, + &case.expected, + "{}: a refused pin must change nothing", + case.dir.display() + ); } fn offline() -> RestoreOptions { @@ -508,7 +547,8 @@ fn transitive_lock() -> String { #[serial] async fn gem_edge_shapes_round_trip() { let gemfile = "source \"https://rubygems.org\"\n\ngem \"puma\"\n\ngroup :test do\n gem \"rails\", \"7.0.0\", require: false\nend\n"; - let crlf_gemfile = "source \"https://rubygems.org\"\r\n\r\ngem \"rails\", \"7.0.0\"\r\ngem \"puma\"\r\n"; + let crlf_gemfile = + "source \"https://rubygems.org\"\r\n\r\ngem \"rails\", \"7.0.0\"\r\ngem \"puma\"\r\n"; let two_sources_gemfile = "source \"https://rubygems.org\"\n\ngem \"rails\", \"7.0.0\"\nsource \"https://gems.example.com\" do\n gem \"private-gem\"\nend\n"; let two_sources_lock = "GEM\n remote: https://gems.example.com/\n specs:\n private-gem (1.0.0)\n\nGEM\n remote: https://rubygems.org/\n specs:\n rails (7.0.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n private-gem!\n rails (= 7.0.0)\n\nCHECKSUMS\n private-gem (1.0.0) sha256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n rails (7.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222\n\nBUNDLED WITH\n 2.6.2\n"; // Provably transitive: the rewriter appended after a trailing blank @@ -536,12 +576,18 @@ async fn gem_edge_shapes_round_trip() { ), synthetic( "multiple-gem-sections", - &[("Gemfile", two_sources_gemfile), ("Gemfile.lock", two_sources_lock)], + &[ + ("Gemfile", two_sources_gemfile), + ("Gemfile.lock", two_sources_lock), + ], gem_override("rails", "7.0.0"), ), synthetic( "transitive-appended", - &[("Gemfile", transitive_gemfile), ("Gemfile.lock", &transitive)], + &[ + ("Gemfile", transitive_gemfile), + ("Gemfile.lock", &transitive), + ], gem_override("zeitwerk", "2.6.0"), ), ]; @@ -600,7 +646,10 @@ async fn gem_pre_checksums_states() { .replace(" rails (~> 7.0)\n", " rails (= 7.0.0)!\n"); mixed.expected.insert("Gemfile.lock".into(), converged); let (after, statuses) = run_case_with(&mixed, None, &offline()).await; - assert_eq!(statuses, vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)]); + assert_eq!( + statuses, + vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)] + ); assert_eq!(after["Gemfile.lock"], restored_lock); assert_eq!(after["Gemfile"], restored_gemfile); @@ -613,7 +662,10 @@ async fn gem_pre_checksums_states() { .replace(" rails (~> 7.0)\n", " rails (= 7.0.0)!\n"); mixed.expected.insert("Gemfile.lock".into(), merged); let (after, statuses) = run_case_with(&mixed, None, &offline()).await; - assert_eq!(statuses, vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)]); + assert_eq!( + statuses, + vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)] + ); assert_eq!(after["Gemfile.lock"], restored_lock); assert_eq!(after["Gemfile"], restored_gemfile); } @@ -632,7 +684,10 @@ async fn gem_transitive_without_proof_stays_declared() { ); let (after, statuses) = gem_run(&case).await; assert_eq!(statuses[0].1, PinStatus::Restored); - assert_eq!(after["Gemfile"], format!("{gemfile}gem \"zeitwerk\", \"2.6.0\"\n")); + assert_eq!( + after["Gemfile"], + format!("{gemfile}gem \"zeitwerk\", \"2.6.0\"\n") + ); assert_eq!( after["Gemfile.lock"], lock.replace(" puma\n", " puma\n zeitwerk (= 2.6.0)\n") @@ -661,10 +716,19 @@ async fn gem_refusals_leave_everything_hosted() { // The upstream section is another registry's. let mut foreign = case.clone_with("foreign-upstream"); foreign.edit_both("Gemfile.lock", |t| { - t.replace("remote: https://rubygems.org/", "remote: https://gems.example.com/") + t.replace( + "remote: https://rubygems.org/", + "remote: https://gems.example.com/", + ) }); let (after, statuses) = gem_run(&foreign).await; - assert_refused(&foreign, &after, &statuses, "Gemfile.lock", "not rubygems.org"); + assert_refused( + &foreign, + &after, + &statuses, + "Gemfile.lock", + "not rubygems.org", + ); // Two upstream sections, neither singled out. let mut ambiguous = case.clone_with("ambiguous-upstream"); ambiguous.edit_both("Gemfile.lock", |t| { @@ -673,18 +737,38 @@ async fn gem_refusals_leave_everything_hosted() { "GEM\n remote: https://gems.example.com/\n specs:\n other (1.0.0)\n\nPLATFORMS", ) }); - ambiguous.edit_both("Gemfile", |t| t.replace("source \"https://rubygems.org\"\n", "")); - ambiguous.edit_both("Gemfile.lock", |t| t.replace("remote: https://rubygems.org/", "remote: https://mirror.example.com/")); + ambiguous.edit_both("Gemfile", |t| { + t.replace("source \"https://rubygems.org\"\n", "") + }); + ambiguous.edit_both("Gemfile.lock", |t| { + t.replace( + "remote: https://rubygems.org/", + "remote: https://mirror.example.com/", + ) + }); let (after, statuses) = gem_run(&ambiguous).await; - assert_refused(&ambiguous, &after, &statuses, "Gemfile.lock", "upstream GEM sections"); + assert_refused( + &ambiguous, + &after, + &statuses, + "Gemfile.lock", + "upstream GEM sections", + ); // The Gemfile block was hand-edited. let mut edited = case.clone_with("edited-block"); edited.expected.insert( "Gemfile".into(), - edited.expected["Gemfile"].replace(" gem \"rails\", \"7.0.0\"", " gem \"rails\", \"~> 7.0\""), + edited.expected["Gemfile"] + .replace(" gem \"rails\", \"7.0.0\"", " gem \"rails\", \"~> 7.0\""), ); let (after, statuses) = gem_run(&edited).await; - assert_refused(&edited, &after, &statuses, "Gemfile.lock", "shape other than the source block"); + assert_refused( + &edited, + &after, + &statuses, + "Gemfile.lock", + "shape other than the source block", + ); } // ── composer ──────────────────────────────────────────────────────────────── @@ -853,7 +937,11 @@ async fn composer_edge_shapes_round_trip() { &[("composer.lock", &escaped)], composer_override("acme/tool", "dev-main"), ), - synthetic("crlf", &[("composer.lock", &crlf)], composer_override("psr/log", "1.1.4")), + synthetic( + "crlf", + &[("composer.lock", &crlf)], + composer_override("psr/log", "1.1.4"), + ), ]; for case in &cases { let (after, statuses) = composer_run(case, |_| {}).await; @@ -872,20 +960,42 @@ async fn composer_refusals_leave_everything_hosted() { // Packagist now serves another commit for the version. let (after, statuses) = composer_run(&case, |d| d["dist"]["reference"] = "feedface".into()).await; - assert_refused(&case, &after, &statuses, "composer.lock", "packagist now serves"); + assert_refused( + &case, + &after, + &statuses, + "composer.lock", + "packagist now serves", + ); // Packagist does not list the version. let (after, statuses) = composer_run(&case, |d| d["version"] = "0.0.1".into()).await; - assert_refused(&case, &after, &statuses, "composer.lock", "does not list version 1.1.4"); + assert_refused( + &case, + &after, + &statuses, + "composer.lock", + "does not list version 1.1.4", + ); // Offline. let (after, statuses) = run_case_with(&case, None, &offline()).await; assert_refused(&case, &after, &statuses, "composer.lock", "offline"); // Locked from another repository. let mut foreign = case.clone_with("foreign"); foreign.edit_both("composer.lock", |t| { - t.replacen("https://packagist.org/downloads/", "https://repo.example.com/downloads/", 1) + t.replacen( + "https://packagist.org/downloads/", + "https://repo.example.com/downloads/", + 1, + ) }); let (after, statuses) = composer_run(&foreign, |_| {}).await; - assert_refused(&foreign, &after, &statuses, "composer.lock", "not packagist"); + assert_refused( + &foreign, + &after, + &statuses, + "composer.lock", + "not packagist", + ); // No notification-url, and composer.json names custom repositories. let mut custom = case.clone_with("custom-repos"); custom.edit_both("composer.lock", |t| { @@ -902,7 +1012,13 @@ async fn composer_refusals_leave_everything_hosted() { ); } let (after, statuses) = composer_run(&custom, |_| {}).await; - assert_refused(&custom, &after, &statuses, "composer.lock", "custom repositories"); + assert_refused( + &custom, + &after, + &statuses, + "composer.lock", + "custom repositories", + ); } // ── PyPI ───────────────────────────────────────────────────────────────────── @@ -940,7 +1056,11 @@ fn urllib3_dep() -> DepOverride { /// PyPI's blake2b-bucketed file URLs, with real urllib3 1.26.18's buckets: the /// sdist sorts before the wheel by URL, the reverse of filename order. fn pypi_file_url(filename: &str) -> String { - let bucket = if filename.ends_with(".tar.gz") { "0c/39" } else { "b0/53" }; + let bucket = if filename.ends_with(".tar.gz") { + "0c/39" + } else { + "b0/53" + }; format!("https://files.pythonhosted.org/packages/{bucket}/{filename}") } @@ -953,8 +1073,18 @@ fn urllib3_release() -> Release<'static> { "urllib3", "1.26.18", vec![ - (URLLIB3_WHEEL, URLLIB3_WHEEL_SHA, 143835, "2023-10-17T17:46:21.184066Z"), - (URLLIB3_SDIST, URLLIB3_SDIST_SHA, 305687, "2023-10-17T17:46:24.000000Z"), + ( + URLLIB3_WHEEL, + URLLIB3_WHEEL_SHA, + 143835, + "2023-10-17T17:46:21.184066Z", + ), + ( + URLLIB3_SDIST, + URLLIB3_SDIST_SHA, + 305687, + "2023-10-17T17:46:24.000000Z", + ), ], ) } @@ -989,7 +1119,10 @@ async fn pypi_mock(releases: &[Release<'_>]) -> (MockServer, EnvGuard) { } fn tree(files: &[(&str, String)]) -> BTreeMap { - files.iter().map(|(k, v)| (k.to_string(), v.clone())).collect() + files + .iter() + .map(|(k, v)| (k.to_string(), v.clone())) + .collect() } /// `input` as the real hosted rewriter leaves it. @@ -1036,14 +1169,24 @@ async fn assert_pypi_round_trip( pipenv: Option, ) { let rewritten = hosted(input, deps, pipenv); - assert_ne!(&rewritten, input, "{label}: the hosted rewrite changed nothing"); + assert_ne!( + &rewritten, input, + "{label}: the hosted rewrite changed nothing" + ); let (after, statuses) = restore_tree(&rewritten, &RestoreOptions::default()).await; - assert!(!statuses.is_empty(), "{label}: discovery found no hosted pin"); + assert!( + !statuses.is_empty(), + "{label}: discovery found no hosted pin" + ); for (purl, status) in &statuses { assert_eq!(*status, PinStatus::Restored, "{label}: {purl}"); } for (rel, want) in input { - assert_eq!(after.get(rel), Some(want), "{label}: {rel} did not round-trip"); + assert_eq!( + after.get(rel), + Some(want), + "{label}: {rel} did not round-trip" + ); } let extra: Vec<&String> = after.keys().filter(|k| !input.contains_key(*k)).collect(); assert!(extra.is_empty(), "{label}: left behind {extra:?}"); @@ -1066,13 +1209,20 @@ async fn pypi_refusal( PinStatus::Restored => None, }) .collect(); - assert!(!refusals.is_empty() && refusals.len() == statuses.len(), "{statuses:?}"); + assert!( + !refusals.is_empty() && refusals.len() == statuses.len(), + "{statuses:?}" + ); (refusals.join("\n"), rewritten, after) } fn fixture(rel: &str) -> String { - fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(rel)) - .unwrap() + fs::read_to_string( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures") + .join(rel), + ) + .unwrap() } #[tokio::test] @@ -1085,7 +1235,12 @@ async fn requirements_golden_restores_modulo_name_casing() { let (after, statuses) = run_case(&case).await; assert!(!statuses.is_empty()); for (purl, status) in &statuses { - assert_eq!(*status, PinStatus::Restored, "{}: {purl}", case.dir.display()); + assert_eq!( + *status, + PinStatus::Restored, + "{}: {purl}", + case.dir.display() + ); } assert_eq!( after["requirements.txt"].to_ascii_lowercase(), @@ -1105,7 +1260,12 @@ async fn uv_golden_without_a_registry_sibling_is_refused() { let (_server, _env) = pypi_mock(&[( "click", "8.1.7", - vec![("click-8.1.7-py3-none-any.whl", URLLIB3_WHEEL_SHA, 1, "2023-08-17T17:29:10Z")], + vec![( + "click-8.1.7-py3-none-any.whl", + URLLIB3_WHEEL_SHA, + 1, + "2023-08-17T17:29:10Z", + )], )]) .await; let mut ran = 0; @@ -1120,7 +1280,10 @@ async fn uv_golden_without_a_registry_sibling_is_refused() { case.dir.display() ); } - assert_eq!(after, case.expected, "a refused pin must leave the files untouched"); + assert_eq!( + after, case.expected, + "a refused pin must leave the files untouched" + ); ran += 1; } assert!(ran > 0); @@ -1227,9 +1390,14 @@ async fn pdm_static_urls_round_trip() { &format!("{{url = \"{}\"", pypi_file_url(URLLIB3_SDIST)), ); // PDM writes a static_urls entry's files in URL order (sdist first here). - let wheel_line = format!(" {{url = \"{}\", hash = \"sha256:{URLLIB3_WHEEL_SHA}\"}},\n", pypi_file_url(URLLIB3_WHEEL)); + let wheel_line = format!( + " {{url = \"{}\", hash = \"sha256:{URLLIB3_WHEEL_SHA}\"}},\n", + pypi_file_url(URLLIB3_WHEEL) + ); assert!(lock.contains(&wheel_line), "{lock}"); - let lock = lock.replacen(&wheel_line, "", 1).replacen("\n]\n", &format!("\n{wheel_line}]\n"), 1); + let lock = + lock.replacen(&wheel_line, "", 1) + .replacen("\n]\n", &format!("\n{wheel_line}]\n"), 1); assert!( lock.find(URLLIB3_SDIST).unwrap() < lock.find(URLLIB3_WHEEL).unwrap(), "{lock}" @@ -1243,12 +1411,17 @@ async fn pdm_static_urls_round_trip() { async fn pdm_narrowed_lock_with_platform_wheels_is_refused() { let wheel = "urllib3-1.26.18-cp311-cp311-manylinux_2_17_x86_64.whl"; let mut release = urllib3_release(); - release.2.push((wheel, URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); + release + .2 + .push((wheel, URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); let (_server, _env) = pypi_mock(&[release]).await; let input = tree(&[("pdm.lock", fixture("pdm-native/2.29.2.lock"))]); let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!(why.contains("not derivable") && why.contains("cross_platform"), "{why}"); + assert!( + why.contains("not derivable") && why.contains("cross_platform"), + "{why}" + ); assert!(why.contains("git checkout -- pdm.lock"), "{why}"); assert_eq!(after, rewritten); // A cross-platform lock records every file, whatever its tags. @@ -1308,7 +1481,9 @@ async fn pipfile_lock_fixture_and_every_category_round_trip() { assert_pypi_round_trip(&label, &input, &[urllib3_dep()], None).await; } // Pipenv 7.x–2017 writes `path` (and, before 2018, no `index`). - let old = text.replace(",\n \"index\": \"pypi\"", "").replace("\"index\": \"pypi\",\n ", ""); + let old = text + .replace(",\n \"index\": \"pypi\"", "") + .replace("\"index\": \"pypi\",\n ", ""); assert!(!old.contains("\"index\""), "{old}"); let input = tree(&[("Pipfile.lock", old), ("Pipfile", "[packages]\n".into())]); assert_pypi_round_trip("pipenv 2017", &input, &[urllib3_dep()], Some(11)).await; @@ -1342,7 +1517,9 @@ async fn pipfile_lock_real_pipenv_shapes_round_trip_their_index() { let pipfile = fixture(&format!("{dir}/Pipfile")); let pristine: serde_json::Value = serde_json::from_str(&lock).unwrap(); assert_eq!( - pristine["default"]["urllib3"].get("index").and_then(|v| v.as_str()), + pristine["default"]["urllib3"] + .get("index") + .and_then(|v| v.as_str()), index, "{dir}: fixture drifted from what Pipenv writes" ); @@ -1357,9 +1534,16 @@ async fn pipfile_lock_real_pipenv_shapes_round_trip_their_index() { let hosted_lock = hosted(&input, &[urllib3_dep()], major)["Pipfile.lock"].clone(); let entry: serde_json::Value = serde_json::from_str(&hosted_lock).unwrap(); let entry = &entry["default"]["urllib3"]; - assert!(entry.get("file").is_some() && entry.get("version").is_none(), "{label}: {entry}"); + assert!( + entry.get("file").is_some() && entry.get("version").is_none(), + "{label}: {entry}" + ); for key in ["index", "markers", "extras"] { - assert_eq!(entry.get(key), pristine["default"]["urllib3"].get(key), "{label}: {key}"); + assert_eq!( + entry.get(key), + pristine["default"]["urllib3"].get(key), + "{label}: {key}" + ); } assert_pypi_round_trip(&label, &input, &[urllib3_dep()], major).await; } @@ -1383,12 +1567,17 @@ async fn pipfile_lock_marker_excluded_relock_hybrid_restores_the_original() { ("Pipfile", fixture(&format!("{dir}/Pipfile"))), ]); let rewritten = hosted(&input, &[urllib3_dep()], Some(2026)); - let mut relocked: serde_json::Value = - serde_json::from_str(&rewritten["Pipfile.lock"]).unwrap(); + let mut relocked: serde_json::Value = serde_json::from_str(&rewritten["Pipfile.lock"]).unwrap(); let pristine: serde_json::Value = serde_json::from_str(&lock).unwrap(); let entry = relocked["default"]["urllib3"].as_object_mut().unwrap(); - assert!(entry.contains_key("file") && !entry.contains_key("index"), "{entry:?}"); - entry.insert("hashes".into(), pristine["default"]["urllib3"]["hashes"].clone()); + assert!( + entry.contains_key("file") && !entry.contains_key("index"), + "{entry:?}" + ); + entry.insert( + "hashes".into(), + pristine["default"]["urllib3"]["hashes"].clone(), + ); entry.insert("version".into(), serde_json::json!("==1.26.18")); relocked.sort_all_objects(); let hybrid = reindent4(&serde_json::to_string_pretty(&relocked).unwrap()) + "\n"; @@ -1399,7 +1588,10 @@ async fn pipfile_lock_marker_excluded_relock_hybrid_restores_the_original() { for (purl, status) in &statuses { assert_eq!(*status, PinStatus::Restored, "{purl}"); } - assert_eq!(after["Pipfile.lock"], lock, "the hybrid restores the pristine bytes"); + assert_eq!( + after["Pipfile.lock"], lock, + "the hybrid restores the pristine bytes" + ); } #[tokio::test] @@ -1417,7 +1609,10 @@ async fn pipfile_lock_refusals() { ..Default::default() }; let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &offline).await; - assert!(why.contains("offline") && why.contains("git checkout -- Pipfile.lock"), "{why}"); + assert!( + why.contains("offline") && why.contains("git checkout -- Pipfile.lock"), + "{why}" + ); assert_eq!(after, rewritten); // A mirror as the only source. let mirror = lock.replace("https://pypi.org/simple", "https://mirror.example/simple"); @@ -1470,7 +1665,10 @@ async fn requirements_hash_mode_ambiguity_is_refused() { let (_server, _env) = pypi_mock(&[urllib3_release()]).await; let input = tree(&[("requirements.txt", "urllib3==1.26.18\n".into())]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!(why.contains("hash-checking mode") && why.contains("not derivable"), "{why}"); + assert!( + why.contains("hash-checking mode") && why.contains("not derivable"), + "{why}" + ); let input = tree(&[( "requirements.txt", "idna==3.4 --hash=sha256:aaaa\nsix==1.16.0\nurllib3==1.26.18\n".into(), @@ -1488,7 +1686,10 @@ async fn requirements_hash_mode_ambiguity_is_refused() { offline: true, ..Default::default() }; - let input = tree(&[("requirements.txt", "flask==2.0.1\nurllib3==1.26.18\n".into())]); + let input = tree(&[( + "requirements.txt", + "flask==2.0.1\nurllib3==1.26.18\n".into(), + )]); let rewritten = hosted(&input, &[urllib3_dep()], None); let (after, statuses) = restore_tree(&rewritten, &offline).await; assert_eq!(statuses[0].1, PinStatus::Restored); @@ -1496,7 +1697,9 @@ async fn requirements_hash_mode_ambiguity_is_refused() { // …and is refused in hash mode. let input = tree(&[( "requirements.txt", - format!("idna==3.4 --hash=sha256:aaaa\nurllib3==1.26.18 --hash=sha256:{URLLIB3_WHEEL_SHA}\n"), + format!( + "idna==3.4 --hash=sha256:aaaa\nurllib3==1.26.18 --hash=sha256:{URLLIB3_WHEEL_SHA}\n" + ), )]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &offline).await; assert!(why.contains("offline"), "{why}"); @@ -1507,7 +1710,12 @@ async fn requirements_hash_mode_ambiguity_is_refused() { async fn a_refused_pin_leaves_the_other_pins_restored() { // PyPI knows urllib3 only: idna's hashes cannot be re-derived. let (_server, _env) = pypi_mock(&[urllib3_release()]).await; - let idna = pypi_dep("idna", "3.4", "idna-3.4-py3-none-any.whl", "44444444-4444-4444-4444-444444444444"); + let idna = pypi_dep( + "idna", + "3.4", + "idna-3.4-py3-none-any.whl", + "44444444-4444-4444-4444-444444444444", + ); let input = tree(&[( "requirements.txt", format!( @@ -1521,7 +1729,10 @@ async fn a_refused_pin_leaves_the_other_pins_restored() { assert!(matches!(status("pkg:pypi/idna@3.4"), PinStatus::Refused(why) if why.contains("404"))); let lines: Vec<&str> = after["requirements.txt"].lines().collect(); assert_eq!(lines[0], "six==1.16.0 --hash=sha256:aaaa"); - assert!(lines[1].starts_with("idna @ https://patch.socket.dev/"), "{lines:?}"); + assert!( + lines[1].starts_with("idna @ https://patch.socket.dev/"), + "{lines:?}" + ); assert_eq!(lines[2], input["requirements.txt"].lines().nth(2).unwrap()); } @@ -1600,7 +1811,13 @@ async fn uv_project_locks_round_trip() { ("uv.lock", lock.replace('\n', eol)), ("pyproject.toml", pyproject.replace('\n', eol)), ]); - assert_pypi_round_trip(&format!("uv direct {eol:?}"), &input, &[urllib3_dep()], None).await; + assert_pypi_round_trip( + &format!("uv direct {eol:?}"), + &input, + &[urllib3_dep()], + None, + ) + .await; } // A transitive dependency: the override the rewrite pins in the // pyproject and the lock's `[manifest]` both go again. @@ -1673,7 +1890,10 @@ async fn uv_refusals() { { let (_server, _env) = pypi_mock(&[urllib3_release()]).await; // No other entry shows how this uv joins specifier clauses. - let input = tree(&[("uv.lock", direct.clone()), ("pyproject.toml", pyproject.into())]); + let input = tree(&[ + ("uv.lock", direct.clone()), + ("pyproject.toml", pyproject.into()), + ]); let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; assert!(why.contains("multi-clause"), "{why}"); @@ -1711,7 +1931,12 @@ async fn uv_refusals() { } // A release with interpreter-specific wheels. let mut release = urllib3_release(); - release.2.push(("urllib3-1.26.18-cp311-cp311-win_amd64.whl", URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); + release.2.push(( + "urllib3-1.26.18-cp311-cp311-win_amd64.whl", + URLLIB3_WHEEL_SHA, + 1, + "2023-10-17T17:46:21Z", + )); let (_server, _env) = pypi_mock(&[release]).await; let input = tree(&[("uv.lock", direct)]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; @@ -1765,7 +1990,10 @@ async fn vlt_goldens_round_trip() { // refused a package whose package-lock.json entry the rewrite still // pinned; with vlt-lock.json upstream that pin is not live wiring, so // discovery (rightly) reports no pin to restore there. - let not_invertible = ["sibling-package-lock-vlt-installed", "sibling-refused-in-vlt"]; + let not_invertible = [ + "sibling-package-lock-vlt-installed", + "sibling-refused-in-vlt", + ]; let mut ran = 0; for case in load("npm/vlt") { let name = case.dir.file_name().unwrap().to_string_lossy().into_owned(); @@ -1810,7 +2038,10 @@ async fn maven_config_merge_keeps_the_resolver_lines() { .find(|c| c.dir.ends_with("mvn-config-merge")) .unwrap(); let (after, statuses) = run_case(&case).await; - assert!(matches!(statuses[..], [(_, PinStatus::Restored)]), "{statuses:?}"); + assert!( + matches!(statuses[..], [(_, PinStatus::Restored)]), + "{statuses:?}" + ); for rel in ["pom.xml", ".mvn/checksums/checksums.sha256"] { assert_eq!(after.get(rel), case.input.get(rel), "{rel}"); } @@ -1831,7 +2062,9 @@ async fn nuget_mock(case: &Case) -> MockServer { let (id, version) = (id.to_lowercase(), entry["resolved"].as_str().unwrap()); let catalog = format!("{}/catalog0/data/{id}.{version}.json", server.uri()); Mock::given(method("GET")) - .and(path(format!("/v3/registration5-gz-semver2/{id}/{version}.json"))) + .and(path(format!( + "/v3/registration5-gz-semver2/{id}/{version}.json" + ))) .respond_with( ResponseTemplate::new(200) .set_body_json(serde_json::json!({ "catalogEntry": catalog })), @@ -1901,11 +2134,17 @@ async fn nuget_non_invertible_goldens_restore_or_refuse_as_documented() { let PinStatus::Refused(why) = status else { panic!("{name}: {status:?}"); }; - assert!(why.contains("corp-feed") && why.contains("git checkout"), "{why}"); + assert!( + why.contains("corp-feed") && why.contains("git checkout"), + "{why}" + ); assert_eq!(after, case.expected, "{name}: a refusal changes nothing"); } else { assert_eq!(*status, PinStatus::Restored, "{name}"); - assert_eq!(after.get("packages.lock.json"), case.input.get("packages.lock.json")); + assert_eq!( + after.get("packages.lock.json"), + case.input.get("packages.lock.json") + ); let config = &after["nuget.config"]; assert!(!config.contains("socket-patch") && !config.contains("packageSourceMapping")); } diff --git a/crates/socket-patch-core/tests/uv_hosted.rs b/crates/socket-patch-core/tests/uv_hosted.rs index 9a2526cd7..81696f5dc 100644 --- a/crates/socket-patch-core/tests/uv_hosted.rs +++ b/crates/socket-patch-core/tests/uv_hosted.rs @@ -1,8 +1,6 @@ use std::collections::BTreeMap; -use socket_patch_core::patch::redirect::{ - rewrite_registry_redirect, DepOverride, Integrity, -}; +use socket_patch_core::patch::redirect::{rewrite_registry_redirect, DepOverride, Integrity}; fn patch(name: &str) -> DepOverride { DepOverride { diff --git a/crates/socket-patch-node/src/lib.rs b/crates/socket-patch-node/src/lib.rs index d83403b84..29fa8e6ae 100644 --- a/crates/socket-patch-node/src/lib.rs +++ b/crates/socket-patch-node/src/lib.rs @@ -15,11 +15,11 @@ use std::sync::Arc; use napi::bindgen_prelude::{Buffer, External, Function, JsObjectValue, Object, PromiseRaw}; use napi::{Env, Status}; use napi_derive::napi; +use socket_patch_core::api::client::PatchApi; use socket_patch_core::hosted::memory::{ - self as hosted_memory, EngineError, HostedScanOptions, HostedScanOutput, PresentKind, SelectOptions, - SessionBuilder, TreeEntryInput, + self as hosted_memory, EngineError, HostedScanOptions, HostedScanOutput, PresentKind, + SelectOptions, SessionBuilder, TreeEntryInput, }; -use socket_patch_core::api::client::PatchApi; use tokio_util::sync::CancellationToken; use provider::{JsPatchApi, ProviderRefs}; From d391f14fb46edd4303bb55a1bb4ddbb74da0a202 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 05:07:45 +0000 Subject: [PATCH 3/3] Drop unrelated formatting from the pipx fix The previous commit ran rustfmt over the whole workspace, which reformatted 126 files the fix doesn't touch. Restore them to main so the PR only carries the pipx discovery change and its tests. Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/src/commands/apply.rs | 4 +- crates/socket-patch-cli/src/commands/list.rs | 15 +- crates/socket-patch-cli/src/commands/mod.rs | 22 +- .../socket-patch-cli/src/commands/remove.rs | 2 +- .../socket-patch-cli/src/commands/rollback.rs | 11 +- .../src/commands/scan/hosted.rs | 54 +- .../socket-patch-cli/src/commands/scan/mod.rs | 89 ++-- .../src/commands/scan/policy.rs | 68 +-- .../src/commands/scan/render.rs | 5 +- .../src/commands/scan/rollout.rs | 20 +- .../src/commands/scan/rollout_args.rs | 2 + .../tests/apply/apply_network.rs | 10 +- .../apply/in_process_gem_config_warning.rs | 4 +- .../tests/cli/covgap_output.rs | 10 +- .../tests/cli/interactive_prompts_e2e.rs | 5 +- .../tests/cli_config_fallback.rs | 7 +- .../socket-patch-cli/tests/cli_get_silent.rs | 5 +- .../socket-patch-cli/tests/cli_parse_list.rs | 11 +- .../tests/cli_parse_rollback.rs | 6 +- .../socket-patch-cli/tests/cli_parse_scan.rs | 29 +- .../coverage_fix_apply_silent_mute_exit.rs | 4 +- .../tests/covgap_commands_scan_hosted.rs | 42 +- .../tests/covgap_commands_scan_mod.rs | 9 +- crates/socket-patch-cli/tests/e2e_cargo.rs | 6 +- crates/socket-patch-cli/tests/e2e_gem.rs | 6 +- crates/socket-patch-cli/tests/e2e_maven.rs | 3 +- crates/socket-patch-cli/tests/e2e_npm.rs | 6 +- crates/socket-patch-cli/tests/e2e_nuget.rs | 6 +- crates/socket-patch-cli/tests/e2e_pypi.rs | 6 +- .../tests/e2e_redirect_gem_stale_install.rs | 3 +- .../tests/e2e_safety_cargo_build.rs | 6 +- .../socket-patch-cli/tests/e2e_safety_pnpm.rs | 18 +- .../tests/e2e_socket_yml_policy.rs | 471 ++++-------------- .../tests/e2e_vex_lockfile/common_selftest.rs | 6 +- .../tests/get/get_edge_cases_e2e.rs | 12 +- .../tests/get/global_packages_e2e.rs | 5 +- .../tests/help_text_hygiene.rs | 31 +- .../tests/hosted_memory_engine.rs | 3 +- .../tests/hosted_memory_parity.rs | 183 ++----- .../tests/hosted_memory_rollout.rs | 42 +- .../tests/in_process_get_hosted_ecosystems.rs | 12 +- .../tests/in_process_redirect/vlt.rs | 10 +- .../tests/in_process_redirect_pdm.rs | 30 +- .../tests/in_process_redirect_pipenv.rs | 73 +-- .../tests/in_process_redirect_pnpm.rs | 11 +- .../tests/repair_vendor_flavors_e2e/vlt.rs | 5 +- .../rollback/rollback_duality_invariants.rs | 3 +- .../tests/scan/covgap_ecosystem_dispatch.rs | 8 +- .../tests/scan/scan_invariants.rs | 20 +- .../tests/scan/scan_paths_e2e.rs | 12 +- .../tests/update/covgap_commands_update.rs | 8 +- .../tests/yarn_berry_common/mod.rs | 4 +- crates/socket-patch-core/src/api/ranking.rs | 17 +- .../src/crawlers/npm_crawler.rs | 6 +- .../src/crawlers/npm_crawler/oracle.rs | 6 +- .../src/crawlers/python_crawler.rs | 6 +- .../src/formats/cargo/mod.rs | 12 +- .../src/formats/composer/hosted.rs | 2 +- .../src/formats/composer/mod.rs | 3 + .../src/formats/gem/hosted.rs | 1 + .../socket-patch-core/src/formats/gem/mod.rs | 2 + crates/socket-patch-core/src/formats/mod.rs | 8 +- .../socket-patch-core/src/formats/pnpm/mod.rs | 76 +-- .../socket-patch-core/src/formats/registry.rs | 18 +- .../socket-patch-core/src/formats/yarn/mod.rs | 5 +- .../socket-patch-core/src/hosted/guidance.rs | 10 +- .../src/hosted/memory/discover.rs | 4 +- .../src/hosted/memory/limits.rs | 12 +- .../src/hosted/memory/mod.rs | 89 ++-- .../src/hosted/memory/roots.rs | 22 +- .../src/hosted/memory/select.rs | 21 +- .../src/hosted/memory/types.rs | 4 +- crates/socket-patch-core/src/ledgers.rs | 1 + crates/socket-patch-core/src/lib.rs | 1 + .../socket-patch-core/src/manifest/records.rs | 5 +- .../redirect/cargo_lock_equivalence_tests.rs | 4 +- .../redirect/golang_equivalence_tests.rs | 6 +- .../patch/redirect/group_equivalence_tests.rs | 7 +- .../src/patch/redirect/mod.rs | 52 +- .../src/patch/redirect/npmrc.rs | 3 + .../src/patch/redirect/pdm.rs | 21 +- .../src/patch/redirect/pipenv.rs | 45 +- .../src/patch/redirect/poetry.rs | 22 +- .../src/patch/redirect/requirements.rs | 4 +- .../src/patch/redirect/state.rs | 2 + .../src/patch/redirect/upstream/bun_lockb.rs | 5 +- .../src/patch/redirect/upstream/cargo.rs | 39 +- .../src/patch/redirect/upstream/composer.rs | 4 +- .../src/patch/redirect/upstream/gem.rs | 23 +- .../src/patch/redirect/upstream/golang.rs | 9 +- .../src/patch/redirect/upstream/mod.rs | 8 +- .../src/patch/redirect/upstream/pypi_locks.rs | 11 +- .../src/patch/redirect/vlt.rs | 1 + crates/socket-patch-core/src/policy/mod.rs | 7 +- crates/socket-patch-core/src/policy/report.rs | 4 +- .../src/policy/socket_yml.rs | 27 +- crates/socket-patch-core/src/policy/tests.rs | 29 +- crates/socket-patch-core/src/rollout/stage.rs | 11 +- crates/socket-patch-core/src/telemetry.rs | 4 +- .../socket-patch-core/src/update/download.rs | 13 +- .../socket-patch-core/src/update/release.rs | 39 +- .../src/utils/group_commit.rs | 21 +- crates/socket-patch-core/src/utils/hatch.rs | 3 +- .../src/utils/line_endings.rs | 1 + crates/socket-patch-core/src/utils/mod.rs | 2 +- .../src/utils/python_script.rs | 7 +- .../src/vendor/bun_lock_text.rs | 1 + .../socket-patch-core/src/vendor/bun_lockb.rs | 9 +- .../src/vendor/cargo_lock.rs | 4 +- .../src/vendor/lock_inventory/pypi.rs | 12 +- .../src/vendor/lock_inventory/view.rs | 4 +- .../src/vendor/lock_inventory/vlt.rs | 2 +- .../src/vendor/lock_inventory/wired.rs | 2 +- .../socket-patch-core/src/vendor/prestage.rs | 5 +- .../src/vendor/toml_surgery.rs | 3 +- .../src/vex/discover/cargo.rs | 29 +- .../socket-patch-core/src/vex/discover/gem.rs | 2 +- .../src/vex/discover/maven.rs | 8 +- .../src/vex/discover/nuget.rs | 2 +- .../tests/covgap_api_blob_fetcher.rs | 5 +- .../tests/covgap_crawlers_composer_crawler.rs | 6 +- .../tests/hosted_inventory.rs | 10 +- .../socket-patch-core/tests/poetry_hosted.rs | 81 +-- .../tests/telemetry_helpers_e2e.rs | 6 +- .../tests/upstream_restore_golden.rs | 395 +++------------ crates/socket-patch-core/tests/uv_hosted.rs | 4 +- crates/socket-patch-node/src/lib.rs | 6 +- 127 files changed, 756 insertions(+), 2048 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/apply.rs b/crates/socket-patch-cli/src/commands/apply.rs index b2207e43c..761d830cf 100644 --- a/crates/socket-patch-cli/src/commands/apply.rs +++ b/crates/socket-patch-cli/src/commands/apply.rs @@ -2,7 +2,9 @@ use clap::Args; use socket_patch_core::api::blob_fetcher::get_missing_blobs; use socket_patch_core::api::client::{get_api_client_with_overrides, ApiClient}; use socket_patch_core::crawlers::ruby_crawler::config_path_ignored_warning; -use socket_patch_core::crawlers::{detect_npm_pkg_manager, Ecosystem, NpmPkgManager, RubyCrawler}; +use socket_patch_core::crawlers::{ + detect_npm_pkg_manager, Ecosystem, NpmPkgManager, RubyCrawler, +}; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::{PatchFileInfo, PatchManifest, PatchRecord}; use socket_patch_core::patch::apply::{ diff --git a/crates/socket-patch-cli/src/commands/list.rs b/crates/socket-patch-cli/src/commands/list.rs index 44c719038..8fc77fab1 100644 --- a/crates/socket-patch-cli/src/commands/list.rs +++ b/crates/socket-patch-cli/src/commands/list.rs @@ -431,10 +431,7 @@ pub async fn run(args: ListArgs) -> i32 { detail: detail.clone(), }); } else if !args.common.silent { - eprintln!( - "Warning: {}", - crate::commands::rollback::capitalize_first(detail) - ); + eprintln!("Warning: {}", crate::commands::rollback::capitalize_first(detail)); } } let vendor_state = crate::commands::vendor_state_lenient(&loaded.vendor, args.common.silent); @@ -776,18 +773,12 @@ mod tests { let listings = HostedListing::from_pins( &[ pin("pkg:npm/minimist@1.2.2", &record.uuid), - pin( - "pkg:npm/other@1.0.0", - "33333333-3333-4333-8333-333333333333", - ), + pin("pkg:npm/other@1.0.0", "33333333-3333-4333-8333-333333333333"), ], Some(&legacy), ); assert_eq!(listings[0].record, record); - assert_eq!( - listings[1].record.uuid, - "33333333-3333-4333-8333-333333333333" - ); + assert_eq!(listings[1].record.uuid, "33333333-3333-4333-8333-333333333333"); assert!(listings[1].record.vulnerabilities.is_empty()); assert_eq!(listings[1].lockfiles, vec!["yarn.lock".to_string()]); } diff --git a/crates/socket-patch-cli/src/commands/mod.rs b/crates/socket-patch-cli/src/commands/mod.rs index c9750247a..7099e4a60 100644 --- a/crates/socket-patch-cli/src/commands/mod.rs +++ b/crates/socket-patch-cli/src/commands/mod.rs @@ -1,7 +1,7 @@ pub mod apply; pub(crate) mod bun_preflight; -pub(crate) mod composer_hints; pub(crate) mod context; +pub(crate) mod composer_hints; pub(crate) mod fetch_stage; pub mod get; pub mod hosted_bundle; @@ -9,11 +9,11 @@ pub mod list; pub(crate) mod lock_cli; pub mod remove; pub mod repair; +pub(crate) mod vendored_backend; pub mod rollback; pub mod scan; pub mod update; pub mod vendor; -pub(crate) mod vendored_backend; pub mod vex; pub(crate) mod vex_consumed; pub(crate) mod vex_sources; @@ -97,11 +97,9 @@ pub(crate) async fn hosted_state_from_lockfiles( common: &crate::args::GlobalArgs, root: &Path, ) -> socket_patch_core::patch::redirect::RedirectState { - hosted_state_from_pins( - &socket_patch_core::patch::redirect::upstream::HostedPin::all( - &discover_wiring(common, root).await, - ), - ) + hosted_state_from_pins(&socket_patch_core::patch::redirect::upstream::HostedPin::all( + &discover_wiring(common, root).await, + )) } /// [`hosted_state_from_lockfiles`] over already-discovered pins. A purl @@ -111,8 +109,10 @@ pub(crate) fn hosted_state_from_pins( ) -> socket_patch_core::patch::redirect::RedirectState { let mut state = socket_patch_core::patch::redirect::RedirectState::new(); for pin in pins { - state.records.entry(pin.purl.clone()).or_insert_with(|| { - socket_patch_core::manifest::schema::PatchRecord { + state + .records + .entry(pin.purl.clone()) + .or_insert_with(|| socket_patch_core::manifest::schema::PatchRecord { uuid: pin.uuid.clone(), exported_at: String::new(), files: Default::default(), @@ -120,8 +120,7 @@ pub(crate) fn hosted_state_from_pins( description: String::new(), license: String::new(), tier: String::new(), - } - }); + }); } state } @@ -148,3 +147,4 @@ pub(crate) fn vendor_state_lenient( } } } + diff --git a/crates/socket-patch-cli/src/commands/remove.rs b/crates/socket-patch-cli/src/commands/remove.rs index c7bae9247..3287aa81a 100644 --- a/crates/socket-patch-cli/src/commands/remove.rs +++ b/crates/socket-patch-cli/src/commands/remove.rs @@ -17,9 +17,9 @@ use super::rollback::{ pin_before_hash_blobs, rollback_patches_inner, run_hosted_leg, sweep_failure, sweep_unused_artifacts, HostedLegOutcome, InnerSelection, }; +use crate::commands::vendored_backend::{RevertedEntry, VendorRevertStep, VendoredBackend}; use crate::args::{apply_env_toggles, GlobalArgs}; use crate::commands::lock_cli::acquire_or_emit; -use crate::commands::vendored_backend::{RevertedEntry, VendorRevertStep, VendoredBackend}; use crate::json_envelope::{Command, Envelope, EnvelopeError, PatchAction, PatchEvent, Status}; use crate::ui::plural; diff --git a/crates/socket-patch-cli/src/commands/rollback.rs b/crates/socket-patch-cli/src/commands/rollback.rs index 97d35e16f..05a00c7c7 100644 --- a/crates/socket-patch-cli/src/commands/rollback.rs +++ b/crates/socket-patch-cli/src/commands/rollback.rs @@ -10,13 +10,13 @@ use socket_patch_core::manifest::operations::{ }; use socket_patch_core::manifest::schema::{PatchFileInfo, PatchManifest, PatchRecord}; use socket_patch_core::patch::apply::select_installed_variants; -use socket_patch_core::patch::redirect::upstream::HostedPin; use socket_patch_core::patch::rollback::{ cannot_rollback_error, rollback_package_patch, verify_file_rollback, RollbackResult, VerifyRollbackResult, VerifyRollbackStatus, }; use socket_patch_core::telemetry::{track_patch_rollback_failed, track_patch_rolled_back}; use socket_patch_core::utils::purl::{patch_matches, strip_purl_qualifiers}; +use socket_patch_core::patch::redirect::upstream::HostedPin; use socket_patch_core::vendor::{purl_keys_cover, RevertOpts, VendorState}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; @@ -1026,8 +1026,7 @@ pub(crate) async fn run_hosted_leg(common: &GlobalArgs, pins: &[HostedPin]) -> H .iter() .map(|(code, detail)| (code.to_string(), detail.clone())), ); - out.edited_files - .extend(outcome.reverted_files.iter().cloned()); + out.edited_files.extend(outcome.reverted_files.iter().cloned()); let unwound: Vec<_> = vlt_targets .into_iter() .filter(|t| out.reverted.iter().any(|p| p == &t.purl)) @@ -1158,11 +1157,7 @@ pub async fn run(args: RollbackArgs) -> i32 { } else if !args.common.silent { println!( "{} the pre-v5 hosted ledger {}: no lockfile pins a hosted patch.", - if args.common.dry_run { - "Would remove" - } else { - "Removed" - }, + if args.common.dry_run { "Would remove" } else { "Removed" }, socket_patch_core::patch::redirect::REDIRECT_STATE_REL ); } diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index 290598f28..d6031e784 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -934,8 +934,7 @@ pub(crate) async fn run_redirect_selected( socket_patch_core::utils::fs::read_regular_to_string_sync(path).ok() }) }; - let rewrite_options = || { - RewriteOptions { + let rewrite_options = || RewriteOptions { dry_run: common.dry_run, targets_pipenv_lock, pipenv_major, @@ -947,7 +946,6 @@ pub(crate) async fn run_redirect_selected( npm_allow_remote_config: !common.no_npm_allow_remote_config, npm_outer: &npm_outer, blocking: true, - } }; // The rollout gate plans again without its deferred rows: keep what // the second pass needs. @@ -2173,19 +2171,13 @@ fn join_names(names: &[String], max: usize) -> String { /// artifacts, then verify with `vex`. After a vendored→hosted takeover /// (`vendored_removed`) the commit also has to carry the deleted vendored /// ledger entries and artifacts. -fn format_next_steps( - files: &[String], - edits: &[socket_patch_core::patch::redirect::FileEdit], - vendored_removed: bool, -) -> Vec { +fn format_next_steps(files: &[String], edits: &[socket_patch_core::patch::redirect::FileEdit], vendored_removed: bool) -> Vec { if files.is_empty() && !vendored_removed { return Vec::new(); } let mut commit: Vec = Vec::new(); if vendored_removed { - commit.push( - ".socket/vendor/ (the removed vendored ledger entries and artifacts)".to_string(), - ); + commit.push(".socket/vendor/ (the removed vendored ledger entries and artifacts)".to_string()); } commit.extend(files.iter().cloned()); let npm = files @@ -4103,43 +4095,19 @@ mod tests { use super::npm_allow_remote_one_line; let hosts = ["patch.socket.dev"]; let cases = [ - ( - npm_allow_remote_configured_detail(&hosts, true, false), - "Note: set", - ), - ( - npm_allow_remote_configured_detail(&hosts, false, false), - "Note: set", - ), - ( - npm_allow_remote_configured_detail(&hosts, true, true), - "Note: would set", - ), - ( - npm_allow_remote_already_detail(&hosts), - "Note: .npmrc already", - ), - ( - npm_allow_remote_user_set_detail(&hosts, "none"), - "Warning: npm >=12", - ), - ( - npm_allow_remote_env_set_detail(&hosts, "npm_config_allow_remote", "none"), - "Warning: npm >=12", - ), + (npm_allow_remote_configured_detail(&hosts, true, false), "Note: set"), + (npm_allow_remote_configured_detail(&hosts, false, false), "Note: set"), + (npm_allow_remote_configured_detail(&hosts, true, true), "Note: would set"), + (npm_allow_remote_already_detail(&hosts), "Note: .npmrc already"), + (npm_allow_remote_user_set_detail(&hosts, "none"), "Warning: npm >=12"), + (npm_allow_remote_env_set_detail(&hosts, "npm_config_allow_remote", "none"), "Warning: npm >=12"), (npm_allow_remote_manual_detail(&hosts), "Warning: npm >=12"), - ( - npm_allow_remote_unreadable_detail(&hosts, "is a symlink"), - "Warning: npm >=12", - ), + (npm_allow_remote_unreadable_detail(&hosts, "is a symlink"), "Warning: npm >=12"), ]; for (detail, start) in cases { let line = npm_allow_remote_one_line(&detail); assert!(line.starts_with(start), "{line}"); - assert!( - !line.contains('\n') && line.ends_with("(details: --verbose)."), - "{line}" - ); + assert!(!line.contains('\n') && line.ends_with("(details: --verbose)."), "{line}"); } } } diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index e1482bddf..26a1282d7 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -35,17 +35,17 @@ use crate::ui::{self, plural, print_json, StatusLine}; use super::get::{download_and_apply_patches_with, DownloadParams, DownloadRun}; -use self::policy::{load_invocation_policy, InvocationPolicy, PolicyLoadError, ScanPolicy}; pub use self::socket_yml_args::{SocketYmlArgs, MIN_SEVERITY_ENV}; +use self::policy::{load_invocation_policy, InvocationPolicy, PolicyLoadError, ScanPolicy}; mod discovery; mod gc; pub(crate) mod hosted; pub(crate) mod policy; +mod socket_yml_args; pub(crate) mod render; pub(crate) mod rollout; pub mod rollout_args; -mod socket_yml_args; pub(crate) mod vendor_flow; use self::discovery::{ @@ -65,13 +65,13 @@ use self::gc::gc_json; pub(crate) use self::hosted::boxed_run_redirect_selected; use self::hosted::run_redirect; pub(crate) use self::hosted::{vlt_rollback_heal, vlt_takeover_heal}; +pub(crate) use self::vendor_flow::{ + boxed_vendor_step, preview_vendor_json, print_dry_run_refusals, VendorStep, +}; use self::vendor_flow::{ boxed_vendor_interactive_path, boxed_vendor_json_path, fold_vendored_skips_into_apply, partition_skipped_selected, }; -pub(crate) use self::vendor_flow::{ - boxed_vendor_step, preview_vendor_json, print_dry_run_refusals, VendorStep, -}; /// Packages per batch request on the authenticated API when `--batch-size` /// is not given: the server's own per-request maximum @@ -234,7 +234,9 @@ pub fn resolve_mode_flags(args: &mut ScanArgs) -> Result<(), String> { // stays report-only (neither has a project lockfile to rewire). args.mode = Some(ScanMode::Hosted); } - if args.mode == Some(ScanMode::Hosted) && args.common.is_global() { + if args.mode == Some(ScanMode::Hosted) + && args.common.is_global() + { // Global installs have no project lockfile to repoint: the hosted // flow would "redirect 0 packages" and exit 0, a silent no-op. return Err(format!( @@ -323,7 +325,11 @@ pub struct ScanArgs { /// `requests`), or a purl with or without its version /// (`pkg:npm/lodash`, `pkg:pypi/requests@2.31.0`). Repeat the flag or /// separate with commas - #[arg(long = "package", env = "SOCKET_SCAN_PACKAGES", value_delimiter = ',')] + #[arg( + long = "package", + env = "SOCKET_SCAN_PACKAGES", + value_delimiter = ',' + )] pub packages: Vec, /// On a successful scan, also generate an OpenVEX 0.2.0 document. @@ -501,10 +507,9 @@ async fn discover_selected( telemetry.flush().await; let error_count = failures.len(); if error_count > 0 && error_count == packages.len() { - let err = failures.last().map_or_else( - || "all patch-detail queries failed".to_string(), - |(_, e)| e.clone(), - ); + let err = failures + .last() + .map_or_else(|| "all patch-detail queries failed".to_string(), |(_, e)| e.clone()); let message = format!("all {error_count} patch-detail queries failed: {err}"); if detail_error_line { eprintln!("{}", render::fetch_details_failed(&failures)); @@ -570,11 +575,7 @@ fn classified_rows( packages: &[BatchPackagePatches], result: Option<&mut serde_json::Value>, ) -> Vec { - let failed: Vec = discovered - .failed - .iter() - .map(|(purl, _)| purl.clone()) - .collect(); + let failed: Vec = discovered.failed.iter().map(|(purl, _)| purl.clone()).collect(); stage.incomplete = rollout::lookup_incomplete(&recorded.index, &failed, batch_failed); let rows = rollout::classify(&discovered.offers, &recorded.index, &stage.project); if let Some(result) = result { @@ -1323,8 +1324,7 @@ fn project_dirs(cwd: &Path, paths: &[String]) -> Result, St let joined = cwd.join(raw); if raw.contains(['*', '?', '[']) { let pattern = joined.to_string_lossy().into_owned(); - let matches = - glob::glob(&pattern).map_err(|e| format!("invalid path pattern `{raw}`: {e}"))?; + let matches = glob::glob(&pattern).map_err(|e| format!("invalid path pattern `{raw}`: {e}"))?; let before = dirs.len(); dirs.extend( matches @@ -1397,10 +1397,7 @@ async fn run_project_dirs( } // One budget per invocation (§5.2): the directories spend it in sorted // order, and a package admitted in one is admitted free in the next. - let configured = match args - .rollout - .resolve_from_env(invocation.policy.max_new_patches()) - { + let configured = match args.rollout.resolve_from_env(invocation.policy.max_new_patches()) { Ok(max) => max, Err(message) => { eprintln!("Error: {message}"); @@ -1501,10 +1498,7 @@ async fn run_scan( // error. let configured_cap = match args.rollout.carry.as_ref() { Some(carry) => carry.lock().configured, - None => match args - .rollout - .resolve_from_env(invocation.policy.max_new_patches()) - { + None => match args.rollout.resolve_from_env(invocation.policy.max_new_patches()) { Ok(max) => max, Err(message) => { eprintln!("Error: {message}"); @@ -1512,8 +1506,11 @@ async fn run_scan( } }, }; - let mut stage = - rollout::Stage::new(configured_cap, args.rollout.carry.clone(), &args.common.cwd); + let mut stage = rollout::Stage::new( + configured_cap, + args.rollout.carry.clone(), + &args.common.cwd, + ); // Strict airgap (CLI_CONTRACT.md `--offline`): scan's patch discovery // is remote data, so refuse before the crawl and before the API client @@ -1690,11 +1687,8 @@ async fn run_scan( .filter(|pkg| args.common.purl_ecosystem_selected(&pkg.purl)) .collect(); - let package_specs: Vec<&String> = args - .packages - .iter() - .filter(|s| !s.trim().is_empty()) - .collect(); + let package_specs: Vec<&String> = + args.packages.iter().filter(|s| !s.trim().is_empty()).collect(); let filtered_crawled: Vec<_> = if package_specs.is_empty() { filtered_crawled } else { @@ -1832,12 +1826,13 @@ async fn run_scan( // `redirectState` rides the empty-discovery envelope too // (same rule as the ≥1-package path). `wiringLive` is empty // by construction: this run covered zero packages. - let redirect_state = - (!args.common.is_global()).then_some(crate::commands::hosted_state_from_pins( + let redirect_state = (!args.common.is_global()).then_some( + crate::commands::hosted_state_from_pins( &socket_patch_core::patch::redirect::upstream::HostedPin::all( ctx.discovery().await, ), - )); + ), + ); if let Some(state) = redirect_state_json(redirect_state.as_ref(), &[]) { result["redirectState"] = state; } @@ -2193,8 +2188,7 @@ async fn run_scan( // A report-only run selects nothing, but a severity floor or // `enabled: false` still hides candidates; report them like the // human arm does (the detail fetch runs only then). - if !apply && !vendor && policy.reports_selection() && !all_packages_with_patches.is_empty() - { + if !apply && !vendor && policy.reports_selection() && !all_packages_with_patches.is_empty() { if let Err((code, message)) = discover_selected( &api_client, &all_packages_with_patches, @@ -2487,7 +2481,12 @@ async fn run_scan( &all_packages_with_patches, None, ); - updates = offer_updates(&rows, &discovered, &recorded, &all_packages_with_patches); + updates = offer_updates( + &rows, + &discovered, + &recorded, + &all_packages_with_patches, + ); rows } // `discover_selected` already printed the failure to stderr. @@ -2949,20 +2948,14 @@ mod tests { dirs.iter() .map(|(d, explicit)| { ( - d.strip_prefix(tmp.path()) - .unwrap() - .to_string_lossy() - .replace('\\', "/"), + d.strip_prefix(tmp.path()).unwrap().to_string_lossy().replace('\\', "/"), *explicit, ) }) .collect() }; - let got = project_dirs( - tmp.path(), - &["apps/*".into(), "libs/core".into(), "apps/web".into()], - ) - .unwrap(); + let got = project_dirs(tmp.path(), &["apps/*".into(), "libs/core".into(), "apps/web".into()]) + .unwrap(); // Named literally = explicit (also when a glob matches it too). assert_eq!( rel(got), diff --git a/crates/socket-patch-cli/src/commands/scan/policy.rs b/crates/socket-patch-cli/src/commands/scan/policy.rs index 21a0e51a6..0cd466bf5 100644 --- a/crates/socket-patch-cli/src/commands/scan/policy.rs +++ b/crates/socket-patch-cli/src/commands/scan/policy.rs @@ -11,9 +11,9 @@ use socket_patch_core::api::ranking::cmp_search_results; use socket_patch_core::api::types::PatchSearchResult; use socket_patch_core::manifest::schema::PatchManifest; use socket_patch_core::policy::{ - canon, find_repo_root_with_warnings, patch_severity_order, policy_block, repo_relative_checked, - sanitize, severity_name, DiskPolicyFs, FilterReason, FilteredEntry, Offers, PolicyError, - PolicySource, PolicyWarning, RetainedEntry, Root, SelectionPolicy, PATCHES_DISABLED, + canon, find_repo_root_with_warnings, policy_block, FilteredEntry, RetainedEntry, patch_severity_order, repo_relative_checked, sanitize, severity_name, + DiskPolicyFs, FilterReason, Offers, PolicyError, PolicySource, PolicyWarning, Root, SelectionPolicy, + PATCHES_DISABLED, }; use socket_patch_core::utils::purl::normalize_purl; @@ -42,18 +42,12 @@ pub(crate) struct InvocationPolicy { /// Load the policy for `args` (4.5): `--global` scans have no repo and read /// no file; everything else reads the repo root's socket.yml. pub(crate) fn load_invocation_policy(args: &ScanArgs) -> Result { - let overrides = args - .socket_yml - .overrides() - .map_err(PolicyLoadError::Usage)?; + let overrides = args.socket_yml.overrides().map_err(PolicyLoadError::Usage)?; let cwd = std::fs::canonicalize(&args.common.cwd).unwrap_or_else(|_| args.common.cwd.clone()); if args.common.is_global() { - let policy = SelectionPolicy::load( - &socket_patch_core::policy::MemoryPolicyFs::default(), - &overrides, - ) - .map_err(PolicyLoadError::Policy)? - .0; + let policy = SelectionPolicy::load(&socket_patch_core::policy::MemoryPolicyFs::default(), &overrides) + .map_err(PolicyLoadError::Policy)? + .0; return Ok(InvocationPolicy { policy, repo_root: cwd, @@ -62,8 +56,8 @@ pub(crate) fn load_invocation_policy(args: &ScanArgs) -> Result Self { + pub(crate) fn for_root(invocation: &InvocationPolicy, root_dir: &Path, explicit: bool, global: bool) -> Self { let root_dir = std::fs::canonicalize(root_dir).unwrap_or_else(|_| root_dir.to_path_buf()); let project = repo_relative_checked(&invocation.repo_root, &root_dir).unwrap_or_default(); let root_verdict = if global { @@ -182,9 +171,7 @@ impl ScanPolicy { severity: None, }); } - let announce_warnings = !invocation - .warned - .swap(true, std::sync::atomic::Ordering::Relaxed); + let announce_warnings = !invocation.warned.swap(true, std::sync::atomic::Ordering::Relaxed); Self { policy: invocation.policy.clone(), warnings, @@ -237,10 +224,7 @@ impl ScanPolicy { /// exclude stays in the query (so `upgradeAvailable` can be reported) /// but joins the retained set, which never reaches a writer. pub(crate) fn admit_crawled(&self, purl: &str) -> bool { - let verdict = self - .root_verdict - .clone() - .and_then(|()| self.policy.admits_purl(purl)); + let verdict = self.root_verdict.clone().and_then(|()| self.policy.admits_purl(purl)); let reason = match verdict { Ok(()) => return true, Err(reason) => reason, @@ -350,8 +334,7 @@ impl ScanPolicy { // (not when a lower-ranked admitted patch simply wins). let top_withheld = self.policy.admits_severity(patch_severity_order(&group[0])); if let Err(reason) = top_withheld { - let upgrade_withheld = - chosen.is_some() && chosen == recorded_at && recorded_at != Some(0); + let upgrade_withheld = chosen.is_some() && chosen == recorded_at && recorded_at != Some(0); if chosen.is_none() || upgrade_withheld { report.filtered.push(FilteredEntry { purl: Some(canon(&purl)), @@ -539,20 +522,17 @@ pub(crate) fn policy_bypass_warnings( let verdict = if !policy.enabled() { Err(FilterReason::Disabled) } else { - root_verdict - .clone() - .and_then(|()| policy.admits_purl(purl)) - .and_then(|()| { - // The floor only hides a package when none of its patches pass. - match group - .iter() - .map(|p| policy.admits_severity(patch_severity_order(p))) - .find(Result::is_ok) - { - Some(ok) => ok, - None => policy.admits_severity(patch_severity_order(group[0])), - } - }) + root_verdict.clone().and_then(|()| policy.admits_purl(purl)).and_then(|()| { + // The floor only hides a package when none of its patches pass. + match group + .iter() + .map(|p| policy.admits_severity(patch_severity_order(p))) + .find(Result::is_ok) + { + Some(ok) => ok, + None => policy.admits_severity(patch_severity_order(group[0])), + } + }) }; if let Err(reason) = verdict { out.push(( diff --git a/crates/socket-patch-cli/src/commands/scan/render.rs b/crates/socket-patch-cli/src/commands/scan/render.rs index 29215f69d..12bfa5e8d 100644 --- a/crates/socket-patch-cli/src/commands/scan/render.rs +++ b/crates/socket-patch-cli/src/commands/scan/render.rs @@ -726,10 +726,7 @@ mod tests { #[test] fn report_only_hint_names_agent_mode() { - assert_eq!( - report_only_hint()[0], - "To apply these patches in place, run:" - ); + assert_eq!(report_only_hint()[0], "To apply these patches in place, run:"); assert!(report_only_hint()[1].contains("--mode agent")); } diff --git a/crates/socket-patch-cli/src/commands/scan/rollout.rs b/crates/socket-patch-cli/src/commands/scan/rollout.rs index fe7470a83..82ef99e17 100644 --- a/crates/socket-patch-cli/src/commands/scan/rollout.rs +++ b/crates/socket-patch-cli/src/commands/scan/rollout.rs @@ -4,10 +4,8 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; +use socket_patch_core::rollout::{canonical_base_purl, severity_label, MaxNew, MaxNewSource, Recorded, RolloutPlan}; pub(crate) use socket_patch_core::rollout::stage::*; -use socket_patch_core::rollout::{ - canonical_base_purl, severity_label, MaxNew, MaxNewSource, Recorded, RolloutPlan, -}; use super::discovery::UpdateInfo; @@ -210,11 +208,11 @@ pub(crate) fn human_lines( mod tests { use super::*; use socket_patch_core::api::types::PatchSearchResult; - use socket_patch_core::api::types::VulnerabilityResponse; use socket_patch_core::manifest::schema::PatchManifest; + use std::path::Path; + use socket_patch_core::api::types::VulnerabilityResponse; use socket_patch_core::manifest::schema::PatchRecord; use std::collections::HashMap; - use std::path::Path; fn offer(purl: &str, uuid: &str, published: &str, severities: &[&str]) -> PatchSearchResult { PatchSearchResult { @@ -359,21 +357,13 @@ mod tests { let stored = manifest(&[("pkg:composer/psr/log@3.0.2.0", "old")]); let recorded = RecordedIndex::new(Some(&stored), &[]); let offers = offers_from_results( - &[offer( - "pkg:composer/psr/log@v3.0.2", - "new", - "2026-02-01T00:00:00Z", - &["high"], - )], + &[offer("pkg:composer/psr/log@v3.0.2", "new", "2026-02-01T00:00:00Z", &["high"])], false, ); let rows = classify(&offers, &recorded, ""); let plan = socket_patch_core::rollout::plan_rollout( rows.into_iter().map(|row| row.candidate).collect(), - &MaxNew { - value: Some(0), - source: MaxNewSource::Flag, - }, + &MaxNew { value: Some(0), source: MaxNewSource::Flag }, false, &BTreeSet::new(), ); diff --git a/crates/socket-patch-cli/src/commands/scan/rollout_args.rs b/crates/socket-patch-cli/src/commands/scan/rollout_args.rs index f83636045..e4d251e98 100644 --- a/crates/socket-patch-cli/src/commands/scan/rollout_args.rs +++ b/crates/socket-patch-cli/src/commands/scan/rollout_args.rs @@ -1,6 +1,7 @@ //! `scan --max-new-patches` (see the rollout guide, //! `docs/configuration.md#gradual-rollout`). + use clap::Args; pub(crate) use socket_patch_core::rollout::stage::RolloutCarry; use socket_patch_core::rollout::{resolve_max_new, MaxNew}; @@ -76,6 +77,7 @@ impl RolloutArgs { } } + #[cfg(test)] mod tests { use super::*; diff --git a/crates/socket-patch-cli/tests/apply/apply_network.rs b/crates/socket-patch-cli/tests/apply/apply_network.rs index 7284a5e2f..837057e18 100644 --- a/crates/socket-patch-cli/tests/apply/apply_network.rs +++ b/crates/socket-patch-cli/tests/apply/apply_network.rs @@ -940,10 +940,7 @@ async fn apply_online_ignores_legacy_package_archive_when_downloads_fail() { "a legacy package archive must not cover the patch; stdout={stdout}\nstderr={stderr}" ); let content = std::fs::read(tmp.path().join("node_modules/pkgcache/index.js")).unwrap(); - assert_eq!( - content, before, - "the file must not be patched from the legacy archive" - ); + assert_eq!(content, before, "the file must not be patched from the legacy archive"); let requests = mock.received_requests().await.unwrap_or_default(); let blob_path = format!("/v0/orgs/{ORG_SLUG}/patches/blob/{after_hash}"); @@ -1046,7 +1043,10 @@ async fn mismatch_blob_topup_probes_every_copy_of_a_duplicated_package() { v["summary"]["applied"], 1, "the drifted nested copy must be warn-overwritten.\nstdout={v:#}" ); - assert_eq!(v["summary"]["failed"], 0, "no copy may fail.\nstdout={v:#}"); + assert_eq!( + v["summary"]["failed"], 0, + "no copy may fail.\nstdout={v:#}" + ); // The nested copy's blob was fetched on demand… let requests = mock.received_requests().await.unwrap(); diff --git a/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs b/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs index 5bc4eacd7..6e849f90c 100644 --- a/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs +++ b/crates/socket-patch-cli/tests/apply/in_process_gem_config_warning.rs @@ -201,9 +201,7 @@ fn apply_stderr_warning_gates_on_silent() { "non-silent stderr must carry the {CODE} warning; got:\n{stderr}" ); assert_eq!( - stderr - .matches("Warning: bundler app config BUNDLE_PATH") - .count(), + stderr.matches("Warning: bundler app config BUNDLE_PATH").count(), 1, "exactly ONE warning line (not one per discovery call); got:\n{stderr}" ); diff --git a/crates/socket-patch-cli/tests/cli/covgap_output.rs b/crates/socket-patch-cli/tests/cli/covgap_output.rs index 1f5e1c860..65cf0b67f 100644 --- a/crates/socket-patch-cli/tests/cli/covgap_output.rs +++ b/crates/socket-patch-cli/tests/cli/covgap_output.rs @@ -168,8 +168,9 @@ fn run_in_pty_inner( .expect("spawn socket-patch in PTY"); drop(pair.slave); - let reader_handle = - crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); + let reader_handle = crate::pty_io::PtyOutput::spawn( + pair.master.try_clone_reader().expect("clone reader"), + ); // Watchdog: detached kill after `timeout`; a no-op if the child exits // naturally first. @@ -260,10 +261,7 @@ fn remove_interactive_bare_enter_proceeds_with_default_yes() { "\n", Duration::from_secs(15), ); - assert_eq!( - code, 0, - "remove with bare Enter must succeed; got: {output}" - ); + assert_eq!(code, 0, "remove with bare Enter must succeed; got: {output}"); // The interactive confirm MUST have run — otherwise this test passes // vacuously against a regression that drops the TTY gate and // auto-proceeds. Match the distinctive prompt verbatim (the loose diff --git a/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs b/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs index a7387e225..6f744bfe4 100644 --- a/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs +++ b/crates/socket-patch-cli/tests/cli/interactive_prompts_e2e.rs @@ -112,8 +112,9 @@ fn run_in_pty_bytes(args: &[&str], cwd: &Path, input: &[u8], timeout: Duration) // closed. The previous design used a chunked read+mpsc loop // because it interleaved with a try_wait poll; the simplified // design serializes wait → drop master → read_to_end joins. - let reader_handle = - crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); + let reader_handle = crate::pty_io::PtyOutput::spawn( + pair.master.try_clone_reader().expect("clone reader"), + ); // Watchdog: detach a thread that kills the child after `timeout`. // The cloned ChildKiller is independent of the main `child` diff --git a/crates/socket-patch-cli/tests/cli_config_fallback.rs b/crates/socket-patch-cli/tests/cli_config_fallback.rs index 530a53c5f..df19585db 100644 --- a/crates/socket-patch-cli/tests/cli_config_fallback.rs +++ b/crates/socket-patch-cli/tests/cli_config_fallback.rs @@ -59,7 +59,8 @@ fn scan_cmd(project: &Path, data_dir: &Path) -> Command { let mut cmd = Command::new(BINARY); // Human mode: core's proxy advisory (the oracle below) is muted under // `--json`/`--silent`. - cmd.args(["scan", "-e", "npm", "--cwd"]).arg(project); + cmd.args(["scan", "-e", "npm", "--cwd"]) + .arg(project); for (key, _) in std::env::vars_os() { let name = key.to_string_lossy(); if name.starts_with("SOCKET_") { @@ -297,9 +298,7 @@ async fn corrupt_config_warns_and_keeps_json_stdout_clean() { json_cmd.arg("--json"); let json_out = run(json_cmd); assert!( - json_out - .stderr - .contains("could not parse socket-cli config"), + json_out.stderr.contains("could not parse socket-cli config"), "the parse warning must reach stderr under --json too; got:\n{}", json_out.stderr ); diff --git a/crates/socket-patch-cli/tests/cli_get_silent.rs b/crates/socket-patch-cli/tests/cli_get_silent.rs index 72f454a6a..4e43c353d 100644 --- a/crates/socket-patch-cli/tests/cli_get_silent.rs +++ b/crates/socket-patch-cli/tests/cli_get_silent.rs @@ -25,7 +25,10 @@ fn run_get(cwd: &Path, args: &[&str]) -> (i32, String) { for var in GLOBAL_ARG_ENV_VARS { cmd.env_remove(var); } - for var in ["SOCKET_SAVE_ONLY", "SOCKET_ALL_RELEASES"] { + for var in [ + "SOCKET_SAVE_ONLY", + "SOCKET_ALL_RELEASES", + ] { cmd.env_remove(var); } cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); diff --git a/crates/socket-patch-cli/tests/cli_parse_list.rs b/crates/socket-patch-cli/tests/cli_parse_list.rs index 9a850490d..8c997686f 100644 --- a/crates/socket-patch-cli/tests/cli_parse_list.rs +++ b/crates/socket-patch-cli/tests/cli_parse_list.rs @@ -370,11 +370,7 @@ fn missing_manifest_under_valid_cwd_is_not_an_error_via_binary() { let out = run_list_binary(tmp.path(), &["--json"]); let v: serde_json::Value = serde_json::from_str(String::from_utf8_lossy(&out.stdout).trim()) .expect("stdout must be valid JSON envelope"); - assert_eq!( - out.status.code(), - Some(0), - "missing manifest is an empty list" - ); + assert_eq!(out.status.code(), Some(0), "missing manifest is an empty list"); assert_eq!(v["status"], "success", "envelope: {v}"); assert_eq!(v["summary"]["discovered"], 0, "envelope: {v}"); } @@ -1317,10 +1313,7 @@ fn missing_manifest_with_corrupt_ledger_keeps_warning_in_the_envelope_via_binary assert_eq!(v["status"], "success", "envelope={v}"); let warnings = v["warnings"].as_array().expect("warnings[] present"); assert_eq!(warnings.len(), 1, "envelope={v}"); - assert_eq!( - warnings[0]["code"], "redirect_ledger_corrupt", - "envelope={v}" - ); + assert_eq!(warnings[0]["code"], "redirect_ledger_corrupt", "envelope={v}"); assert!( out.stderr.is_empty(), "--json must keep stderr clean: {}", diff --git a/crates/socket-patch-cli/tests/cli_parse_rollback.rs b/crates/socket-patch-cli/tests/cli_parse_rollback.rs index f1590292e..c8b77af5e 100644 --- a/crates/socket-patch-cli/tests/cli_parse_rollback.rs +++ b/crates/socket-patch-cli/tests/cli_parse_rollback.rs @@ -366,11 +366,7 @@ fn bare_bool_does_not_consume_next_token() { /// relied on the rejection get a test-visible flip instead of a silent one. #[test] fn multiple_targets_parse_in_order() { - let args = parse_rollback(&[ - "pkg:npm/foo@1", - "packages/api/**", - "b0630680-4da6-45f9-bba8-b888e0ffd58c", - ]); + let args = parse_rollback(&["pkg:npm/foo@1", "packages/api/**", "b0630680-4da6-45f9-bba8-b888e0ffd58c"]); assert_eq!( args.targets, vec![ diff --git a/crates/socket-patch-cli/tests/cli_parse_scan.rs b/crates/socket-patch-cli/tests/cli_parse_scan.rs index eff55ee79..ab81fa6eb 100644 --- a/crates/socket-patch-cli/tests/cli_parse_scan.rs +++ b/crates/socket-patch-cli/tests/cli_parse_scan.rs @@ -898,11 +898,7 @@ fn max_new_patches_takes_a_count_or_none() { ("NONE", None), ] { let args = parse_scan(&["--max-new-patches", raw]); - assert_eq!( - args.rollout.max_new_patches, - Some(MaxNewPatches(want)), - "{raw}" - ); + assert_eq!(args.rollout.max_new_patches, Some(MaxNewPatches(want)), "{raw}"); } } @@ -993,33 +989,20 @@ fn min_severity_flag_and_env() { assert_eq!(parse_scan(&[]).socket_yml.min_severity, None); assert_eq!(overrides(&[], &[]).unwrap().min_severity, None); assert_eq!( - overrides(&["--min-severity", "High"], &[]) - .unwrap() - .min_severity, + overrides(&["--min-severity", "High"], &[]).unwrap().min_severity, Some((Some(1), OverrideSource::Flag)) ); assert_eq!( - overrides( - &["--min-severity", "none"], - &[("SOCKET_MIN_SEVERITY", "critical")] - ) - .unwrap() - .min_severity, + overrides(&["--min-severity", "none"], &[("SOCKET_MIN_SEVERITY", "critical")]).unwrap().min_severity, Some((None, OverrideSource::Flag)) ); assert_eq!( - overrides(&[], &[("SOCKET_MIN_SEVERITY", "moderate")]) - .unwrap() - .min_severity, + overrides(&[], &[("SOCKET_MIN_SEVERITY", "moderate")]).unwrap().min_severity, Some((Some(2), OverrideSource::Env)) ); - assert_eq!( - overrides(&[], &[("SOCKET_MIN_SEVERITY", "")]) - .unwrap() - .min_severity, - None - ); + assert_eq!(overrides(&[], &[("SOCKET_MIN_SEVERITY", "")]).unwrap().min_severity, None); assert!(overrides(&[], &[("SOCKET_MIN_SEVERITY", "severe")]).is_err()); assert!(try_parse_scan(&["--min-severity", "severe"]).is_err()); assert!(overrides(&["--no-socket-yml"], &[]).unwrap().bypass); } + diff --git a/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs b/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs index 049d8356b..444dd2a3b 100644 --- a/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs +++ b/crates/socket-patch-cli/tests/coverage_fix_apply_silent_mute_exit.rs @@ -149,9 +149,7 @@ fn apply_silent_online_download_failure_keeps_error_output() { ); let chatter = stderr_chatter(&stderr); assert!( - chatter - .iter() - .any(|l| l.contains("could not be downloaded")), + chatter.iter().any(|l| l.contains("could not be downloaded")), "--silent must keep the download-failure error (errors only, \ never nothing); stderr was: {stderr:?}" ); diff --git a/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs b/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs index df8e61626..35577532a 100644 --- a/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs +++ b/crates/socket-patch-cli/tests/covgap_commands_scan_hosted.rs @@ -566,7 +566,8 @@ async fn wet_takeover_refuses_unrevertable_vendored_flavor_fail_closed() { "the human skipped line must name purl + reason; stderr=\n{stderr}" ); assert!( - stderr.contains("Warning: ") && stderr.contains("could not be reverted"), + stderr.contains("Warning: ") + && stderr.contains("could not be reverted"), "the takeover pre-warning must reach human stderr; stderr=\n{stderr}" ); } @@ -813,10 +814,7 @@ async fn zero_grant_wet_run_ignores_a_malformed_pre_v5_ledger() { let lock_before = std::fs::read(root.join("package-lock.json")).unwrap(); let assert_ignored = |code: i32, doc: &Value, label: &str| { - assert_eq!( - code, 0, - "{label}: a pre-v5 ledger is never an error: {doc:#}" - ); + assert_eq!(code, 0, "{label}: a pre-v5 ledger is never an error: {doc:#}"); assert_eq!(doc["status"], "success", "{label}: {doc:#}"); assert!( !doc.to_string().contains("redirect-state.json"), @@ -1019,10 +1017,7 @@ async fn hosted_human_empty_discovery_ignores_a_malformed_pre_v5_ledger() { for extra in [&[][..], &["--silent"][..]] { let (code, stdout, stderr) = scan_hosted(root, &server.uri(), extra, &[]); - assert_eq!( - code, 0, - "{extra:?}: an empty discovery exits 0; stderr=\n{stderr}" - ); + assert_eq!(code, 0, "{extra:?}: an empty discovery exits 0; stderr=\n{stderr}"); if extra.is_empty() { assert!( stdout.contains("No patches available for installed packages."), @@ -1409,22 +1404,16 @@ async fn native_bun_lockb_hosting_dry_run_rerun_and_rollback_without_bun() { ], &env, ); - assert_eq!( - code, 1, - "a binary bun.lockb pin is refused: {stdout}\n{stderr}" - ); + assert_eq!(code, 1, "a binary bun.lockb pin is refused: {stdout}\n{stderr}"); let doc: Value = serde_json::from_str(&stdout).unwrap_or_else(|e| panic!("{e}: {stdout}")); assert_eq!(doc["status"], "partial_failure", "{doc:#}"); - let failed = doc["hosted"]["failed"] - .as_array() - .unwrap_or_else(|| panic!("{doc:#}")); + let failed = doc["hosted"]["failed"].as_array().unwrap_or_else(|| panic!("{doc:#}")); assert_eq!(failed.len(), 1, "{doc:#}"); assert_eq!(failed[0]["purl"], purl, "{doc:#}"); let error = failed[0]["error"].as_str().unwrap_or_default(); assert!( - error.starts_with(&format!( - "cannot restore {purl} to its upstream registry entry: " - )) && error.contains("bun.lockb") + error.starts_with(&format!("cannot restore {purl} to its upstream registry entry: ")) + && error.contains("bun.lockb") && error.contains("git checkout"), "{error}" ); @@ -1830,9 +1819,7 @@ async fn unreadable_pnpm_workspace_gets_warning_only_guidance_in_a_live_run() { "the unreadable workspace file must be left byte-identical" ); assert!( - !tmp.path() - .join(".socket/vendor/redirect-state.json") - .exists(), + !tmp.path().join(".socket/vendor/redirect-state.json").exists(), "v5 hosted mode writes no redirect ledger" ); } @@ -1944,8 +1931,9 @@ async fn live_hosted_overlap_fires_redirect_supersedes_vendored() { let (code, _stdout, stderr) = scan_hosted(root, &server.uri(), &psu, &[]); assert_eq!(code, 0, "human overlap run exits 0; stderr=\n{stderr}"); assert!( - stderr.contains("Warning: Hosted wiring superseded the vendored ledger for:") - && stderr.contains(XPURL), + stderr.contains( + "Warning: Hosted wiring superseded the vendored ledger for:" + ) && stderr.contains(XPURL), "the supersedes warning must reach human stderr; stderr=\n{stderr}" ); } @@ -1993,7 +1981,8 @@ async fn human_dry_run_prints_would_rewrite_pnpm_guidance_and_vex_skip() { "the requested-but-skipped VEX must be announced; stderr=\n{stderr}" ); assert!( - stderr.contains("Warning: ") && stderr.contains("trustLockfile"), + stderr.contains("Warning: ") + && stderr.contains("trustLockfile"), "the pnpm trust guidance must reach human stderr; stderr=\n{stderr}" ); assert!( @@ -2431,8 +2420,7 @@ async fn human_pnpm_rerun_prints_only_the_reminder_and_heal_restores_guidance() let (code, stdout, stderr) = scan_hosted(root, &server.uri(), &[], &[]); assert_eq!(code, 0, "stdout=\n{stdout}\nstderr=\n{stderr}"); assert!( - engine_stdout(&stdout) - .starts_with("Switched 1 package to hosted patches; rewrote 2 files.\n"), + engine_stdout(&stdout).starts_with("Switched 1 package to hosted patches; rewrote 2 files.\n"), "{stdout}" ); // Everything from the pnpm warning on (the lines above it are the diff --git a/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs b/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs index a15170613..47fa71669 100644 --- a/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs +++ b/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs @@ -1476,13 +1476,7 @@ async fn scan_hosted_paths_run_once_per_project_directory() { let header = format!("== {} ==", Path::new("apps").join(app).display()); assert!(stdout.contains(&header), "missing {header:?}: {stdout}"); } - assert_eq!( - stdout - .matches("Switched 0 packages to hosted patches") - .count(), - 2, - "{stdout}" - ); + assert_eq!(stdout.matches("Switched 0 packages to hosted patches").count(), 2, "{stdout}"); let reqs = recorded(&mock).await; assert_eq!(batch_bodies(&reqs).len(), 2, "one discovery per directory"); } @@ -1921,6 +1915,7 @@ mod pty { screen.join("\n") ); } + } // --------------------------------------------------------------------------- diff --git a/crates/socket-patch-cli/tests/e2e_cargo.rs b/crates/socket-patch-cli/tests/e2e_cargo.rs index 73c6acaef..3978aff96 100644 --- a/crates/socket-patch-cli/tests/e2e_cargo.rs +++ b/crates/socket-patch-cli/tests/e2e_cargo.rs @@ -204,7 +204,8 @@ async fn scan_discovers_fake_registry_crates() { "Expected human scan to report exactly 'Found 2 packages (2 cargo)', got:\n{combined}" ); assert!( - !combined.contains("No packages found") && !combined.contains("No packages found"), + !combined.contains("No packages found") + && !combined.contains("No packages found"), "scan reported no packages despite a populated registry:\n{combined}" ); @@ -261,7 +262,8 @@ async fn scan_discovers_vendor_crates() { "Expected human scan to report exactly 'Found 1 package (1 cargo)', got:\n{combined}" ); assert!( - !combined.contains("No packages found") && !combined.contains("No packages found"), + !combined.contains("No packages found") + && !combined.contains("No packages found"), "scan reported no packages despite a populated vendor dir:\n{combined}" ); diff --git a/crates/socket-patch-cli/tests/e2e_gem.rs b/crates/socket-patch-cli/tests/e2e_gem.rs index f6f189113..db8af0af8 100644 --- a/crates/socket-patch-cli/tests/e2e_gem.rs +++ b/crates/socket-patch-cli/tests/e2e_gem.rs @@ -583,11 +583,7 @@ fn test_gem_dry_run() { let gem_dir = find_gem_dir(cwd); // Download without applying. - assert_run_ok( - cwd, - &["get", GEM_UUID, "--mode", "agent", "--no-apply"], - "get --no-apply", - ); + assert_run_ok(cwd, &["get", GEM_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); // Read manifest to get file list and expected hashes. let manifest_path = cwd.join(".socket/manifest.json"); diff --git a/crates/socket-patch-cli/tests/e2e_maven.rs b/crates/socket-patch-cli/tests/e2e_maven.rs index b6c650cad..6f4474404 100644 --- a/crates/socket-patch-cli/tests/e2e_maven.rs +++ b/crates/socket-patch-cli/tests/e2e_maven.rs @@ -177,7 +177,8 @@ async fn scan_discovers_maven_artifacts() { // Must NOT have hit the empty-crawl path — that line *also* contains // the word "packages". assert!( - !combined.contains("No packages found") && !combined.contains("No packages found"), + !combined.contains("No packages found") + && !combined.contains("No packages found"), "scan reported zero packages — Maven discovery did not run:\n{combined}" ); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_npm.rs b/crates/socket-patch-cli/tests/e2e_npm.rs index 7486a85c9..89f40f9a5 100644 --- a/crates/socket-patch-cli/tests/e2e_npm.rs +++ b/crates/socket-patch-cli/tests/e2e_npm.rs @@ -286,11 +286,7 @@ fn test_npm_dry_run() { assert_eq!(git_sha256_file(&index_js), BEFORE_HASH); // Download the patch *without* applying. - assert_run_ok( - cwd, - &["get", NPM_UUID, "--mode", "agent", "--no-apply"], - "get --no-apply", - ); + assert_run_ok(cwd, &["get", NPM_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); // File should still be original. assert_eq!( diff --git a/crates/socket-patch-cli/tests/e2e_nuget.rs b/crates/socket-patch-cli/tests/e2e_nuget.rs index f8ec8eb1e..ce4cc4998 100644 --- a/crates/socket-patch-cli/tests/e2e_nuget.rs +++ b/crates/socket-patch-cli/tests/e2e_nuget.rs @@ -227,8 +227,7 @@ async fn scan_discovers_global_cache_packages() { // "packages" substring check would also match). assert!( !combined.contains("No packages found") - && !combined.contains("No packages found") - && !combined.contains("No global packages found"), + && !combined.contains("No packages found") && !combined.contains("No global packages found"), "scan failed to discover the fake global cache:\n{combined}" ); // Exactly the two packages we planted (Newtonsoft.Json, System.Text.Json), @@ -286,8 +285,7 @@ async fn scan_discovers_legacy_packages() { ); assert!( !combined.contains("No packages found") - && !combined.contains("No packages found") - && !combined.contains("No global packages found"), + && !combined.contains("No packages found") && !combined.contains("No global packages found"), "scan failed to discover the legacy packages/ layout:\n{combined}" ); // Exactly the single legacy package we planted (Newtonsoft.Json.13.0.3), diff --git a/crates/socket-patch-cli/tests/e2e_pypi.rs b/crates/socket-patch-cli/tests/e2e_pypi.rs index d84c6db20..4531d1173 100644 --- a/crates/socket-patch-cli/tests/e2e_pypi.rs +++ b/crates/socket-patch-cli/tests/e2e_pypi.rs @@ -426,11 +426,7 @@ fn test_pypi_dry_run() { let original_hash = git_sha256_file(&messages_py); // Download without applying. - assert_run_ok( - cwd, - &["get", PYPI_UUID, "--mode", "agent", "--no-apply"], - "get --no-apply", - ); + assert_run_ok(cwd, &["get", PYPI_UUID, "--mode", "agent", "--no-apply"], "get --no-apply"); // File should be unchanged. assert_eq!( diff --git a/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs b/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs index ac6c8b31d..4c2aa5327 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs @@ -327,8 +327,7 @@ async fn gem_hosted_redirect_over_stale_install_warns_loudly() { ); assert_eq!(code, 0, "human re-scan must succeed:\n{stderr}"); assert!( - stderr.contains("Warning: ") - && stderr.contains("was switched to its hosted patch, but a stale"), + stderr.contains("Warning: ") && stderr.contains("was switched to its hosted patch, but a stale"), "human mode must print the stale-install warning on stderr:\n{stderr}" ); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs b/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs index 29e90c39d..62e9ef05d 100644 --- a/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs +++ b/crates/socket-patch-cli/tests/e2e_safety_cargo_build.rs @@ -419,11 +419,7 @@ fn manifestless_agent_patch_is_not_attested(consumer: &Path, cargo_home: &Path) "description": "d" } }); - std::fs::write( - &manifest_path, - serde_json::to_vec_pretty(&manifest).unwrap(), - ) - .unwrap(); + std::fs::write(&manifest_path, serde_json::to_vec_pretty(&manifest).unwrap()).unwrap(); let out = run_vex(&bin, consumer, &run); assert_eq!(out.code, Some(0), "manifest-backed vex:\n{out}"); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs index 783e7337a..7af958619 100644 --- a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs +++ b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs @@ -293,11 +293,7 @@ fn apply_in_a_does_not_mutate_b_or_store() { }; // -- get + apply in proj_a only ---------------------------------- - assert_run_ok( - &fx.proj_a, - &["get", NPM_UUID, "--mode", "agent"], - "socket-patch get", - ); + assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); // proj_a is patched. assert_eq!( @@ -401,11 +397,7 @@ fn pnpm_install_in_b_does_not_revert_a() { store_id }; - assert_run_ok( - &fx.proj_a, - &["get", NPM_UUID, "--mode", "agent"], - "socket-patch get", - ); + assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); assert_eq!(git_sha256_file(&index_a), AFTER_HASH); // Re-run pnpm install in proj_b with frozen lockfile — this @@ -483,11 +475,7 @@ fn apply_in_pnpm_project_emits_layout_note() { let root = tempfile::tempdir().unwrap(); let fx = setup_two_pnpm_projects(root.path()); - let (_stdout, stderr) = assert_run_ok( - &fx.proj_a, - &["get", NPM_UUID, "--mode", "agent"], - "socket-patch get", - ); + let (_stdout, stderr) = assert_run_ok(&fx.proj_a, &["get", NPM_UUID, "--mode", "agent"], "socket-patch get"); // The exact phrasing is a stable contract. A bare `contains("pnpm")` // is worthless here — every pnpm store path printed on stderr diff --git a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs index 9a02495b9..50018d6a9 100644 --- a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs +++ b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs @@ -61,11 +61,7 @@ impl Patch { "low" => 3, _ => 4, }; - self.severities - .iter() - .copied() - .min_by_key(|s| rank(s)) - .unwrap_or("unknown") + self.severities.iter().copied().min_by_key(|s| rank(s)).unwrap_or("unknown") } } @@ -204,9 +200,7 @@ async fn mount_api(server: &MockServer, patches: Vec) { .await; let detail_map = by_purl.clone(); Mock::given(method("GET")) - .and(path_regex(format!( - "^/v0/orgs/{ORG}/patches/by-package/.+$" - ))) + .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) .respond_with(move |req: &Request| { let raw = req.url.path().rsplit('/').next().unwrap(); let purl = percent_decode(raw); @@ -222,15 +216,11 @@ async fn mount_api(server: &MockServer, patches: Vec) { }) }) .collect(); - ResponseTemplate::new(200) - .set_body_json(json!({"patches": list, "canAccessPaidPatches": false})) + ResponseTemplate::new(200).set_body_json(json!({"patches": list, "canAccessPaidPatches": false})) }) .mount(server) .await; - let by_uuid: BTreeMap = patches - .iter() - .map(|p| (p.uuid.to_string(), p.clone())) - .collect(); + let by_uuid: BTreeMap = patches.iter().map(|p| (p.uuid.to_string(), p.clone())).collect(); let refs = by_uuid.clone(); Mock::given(method("POST")) .and(path(format!("/v0/orgs/{ORG}/patches/package"))) @@ -287,10 +277,8 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { let dep_map: BTreeMap<&str, &str> = deps.iter().map(|d| (*d, "1.0.0")).collect(); std::fs::write( dir.join("package.json"), - serde_json::to_string_pretty( - &json!({"name": "consumer", "version": "0.0.0", "dependencies": dep_map}), - ) - .unwrap(), + serde_json::to_string_pretty(&json!({"name": "consumer", "version": "0.0.0", "dependencies": dep_map})) + .unwrap(), ) .unwrap(); let mut packages = serde_json::Map::new(); @@ -301,11 +289,7 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { for name in deps { let pkg = dir.join("node_modules").join(name); std::fs::create_dir_all(&pkg).unwrap(); - std::fs::write( - pkg.join("package.json"), - format!(r#"{{ "name": "{name}", "version": "1.0.0" }}"#), - ) - .unwrap(); + std::fs::write(pkg.join("package.json"), format!(r#"{{ "name": "{name}", "version": "1.0.0" }}"#)).unwrap(); std::fs::write(pkg.join("index.js"), orig_index(name)).unwrap(); packages.insert( format!("node_modules/{name}"), @@ -320,20 +304,12 @@ fn write_npm_root(dir: &Path, deps: &[&str]) { "name": "consumer", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": packages }); - std::fs::write( - dir.join("package-lock.json"), - serde_json::to_string_pretty(&lock).unwrap() + "\n", - ) - .unwrap(); + std::fs::write(dir.join("package-lock.json"), serde_json::to_string_pretty(&lock).unwrap() + "\n").unwrap(); } fn write_gem(dir: &Path, name: &str, version: &str) { - std::fs::create_dir_all( - dir.join("vendor/bundle/ruby/3.0.0/gems") - .join(format!("{name}-{version}")) - .join("lib"), - ) - .unwrap(); + std::fs::create_dir_all(dir.join("vendor/bundle/ruby/3.0.0/gems").join(format!("{name}-{version}")).join("lib")) + .unwrap(); } /// The monorepo: `services/web` (alpha, beta, left-pad + a gem), @@ -373,11 +349,7 @@ impl Repo { if entry.file_type().unwrap().is_dir() { walk(&path, root, out); } else { - let rel = path - .strip_prefix(root) - .unwrap() - .to_string_lossy() - .into_owned(); + let rel = path.strip_prefix(root).unwrap().to_string_lossy().into_owned(); out.insert(rel, std::fs::read(&path).unwrap()); } } @@ -397,8 +369,7 @@ fn run_cli(cwd: &Path, args: &[&str], env: &[(&str, &str)]) -> (i32, String, Str cmd.env_remove(key); } } - cmd.env_remove("GIT_CEILING_DIRECTORIES") - .env_remove("VIRTUAL_ENV"); + cmd.env_remove("GIT_CEILING_DIRECTORIES").env_remove("VIRTUAL_ENV"); cmd.env("SOCKET_TELEMETRY_DISABLED", "1"); // The fixture's hosted pins name this origin; it makes them recorded. cmd.env("SOCKET_PATCH_SERVER_URL", "http://patch.test"); @@ -412,8 +383,7 @@ fn run_cli(cwd: &Path, args: &[&str], env: &[(&str, &str)]) -> (i32, String, Str ] { cmd.env(var, &absent); } - cmd.env("NPM_CONFIG_ALLOW_REMOTE", "") - .env("npm_config_allow_remote", ""); + cmd.env("NPM_CONFIG_ALLOW_REMOTE", "").env("npm_config_allow_remote", ""); for (k, v) in env { cmd.env(k, v); } @@ -448,9 +418,8 @@ fn scan_json(cwd: &Path, api: &str, extra: &[&str], env: &[(&str, &str)]) -> (i3 let mut args = vec!["--json"]; args.extend_from_slice(extra); let (code, stdout, stderr) = scan(cwd, api, &args, env); - let doc: Value = serde_json::from_str(&stdout).unwrap_or_else(|e| { - panic!("stdout must be JSON ({e})\nstdout=\n{stdout}\nstderr=\n{stderr}") - }); + let doc: Value = serde_json::from_str(&stdout) + .unwrap_or_else(|e| panic!("stdout must be JSON ({e})\nstdout=\n{stdout}\nstderr=\n{stderr}")); (code, doc) } @@ -459,12 +428,7 @@ fn filtered(doc: &Value) -> Vec<(Option, String)> { .as_array() .unwrap() .iter() - .map(|f| { - ( - f["purl"].as_str().map(str::to_string), - f["reason"].as_str().unwrap().to_string(), - ) - }) + .map(|f| (f["purl"].as_str().map(str::to_string), f["reason"].as_str().unwrap().to_string())) .collect() } @@ -480,11 +444,7 @@ fn filtered_reason<'a>(doc: &'a Value, purl: &str) -> &'a Value { fn warning_codes(doc: &Value) -> Vec { doc["warnings"] .as_array() - .map(|w| { - w.iter() - .filter_map(|e| e["code"].as_str().map(str::to_string)) - .collect() - }) + .map(|w| w.iter().filter_map(|e| e["code"].as_str().map(str::to_string)).collect()) .unwrap_or_default() } @@ -504,28 +464,16 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { assert_eq!(code, 0, "{doc:#}"); let lock = repo.lock("services/web"); - assert!( - lock.contains(&P_ALPHA.hosted_url()), - "alpha is patched:\n{lock}" - ); - assert!( - !lock.contains(P_BETA.uuid), - "beta is below the floor:\n{lock}" - ); - assert!( - !lock.contains(P_LEFTPAD.uuid), - "left-pad is ignored:\n{lock}" - ); + assert!(lock.contains(&P_ALPHA.hosted_url()), "alpha is patched:\n{lock}"); + assert!(!lock.contains(P_BETA.uuid), "beta is below the floor:\n{lock}"); + assert!(!lock.contains(P_LEFTPAD.uuid), "left-pad is ignored:\n{lock}"); let policy = &doc["policy"]; assert_eq!(policy["source"], "file"); assert_eq!(policy["path"], "socket.yml"); assert_eq!(policy["sha256"].as_str().unwrap().len(), 64); assert_eq!(policy["enabled"], true); - assert_eq!( - policy["minSeverity"], - json!({"value": "high", "source": "file"}) - ); + assert_eq!(policy["minSeverity"], json!({"value": "high", "source": "file"})); let beta = filtered_reason(&doc, "pkg:npm/beta@1.0.0"); assert_eq!(beta["reason"], "policy_severity"); assert_eq!(beta["detail"], "low < high"); @@ -533,15 +481,8 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { assert_eq!(beta["project"], "services/web"); let left_pad = filtered_reason(&doc, "pkg:npm/left-pad@1.0.0"); assert_eq!(left_pad["reason"], "policy_package_ignored"); - assert_eq!( - left_pad["uuid"], - Value::Null, - "filtered before any patch lookup" - ); - assert_eq!( - left_pad["detail"], - "pkg:npm/left-pad (patches.ignorePackages)" - ); + assert_eq!(left_pad["uuid"], Value::Null, "filtered before any patch lookup"); + assert_eq!(left_pad["detail"], "pkg:npm/left-pad (patches.ignorePackages)"); let rack = filtered_reason(&doc, "pkg:gem/rack@1.0.0"); assert_eq!(rack["reason"], "policy_ecosystem"); assert_eq!(policy["counts"]["filtered"], 3); @@ -551,10 +492,7 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { for r in &reqs { if r.url.path().ends_with("/patches/batch") { let body = String::from_utf8_lossy(&r.body); - assert!( - !body.contains("left-pad") && !body.contains("rack"), - "{body}" - ); + assert!(!body.contains("left-pad") && !body.contains("rack"), "{body}"); } } assert_eq!(doc["redirect"]["redirected"], 1, "{:#}", doc["redirect"]); @@ -565,27 +503,13 @@ async fn hosted_filters_by_ecosystem_package_and_severity() { async fn hosted_dry_run_makes_the_same_decisions_and_writes_nothing() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some( - "version: 2\npatches:\n minSeverity: high\n ecosystems: [npm]\n", - )); + let repo = Repo::new(Some("version: 2\npatches:\n minSeverity: high\n ecosystems: [npm]\n")); let before = repo.snapshot(); - let (code, doc) = scan_json( - &repo.dir("services/web"), - &server.uri(), - &["--dry-run"], - &[], - ); + let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--dry-run"], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before, "a dry run changes no bytes"); - assert_eq!( - doc["redirect"]["redirected"], 2, - "alpha and left-pad: {:#}", - doc["redirect"] - ); - assert_eq!( - filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], - "policy_severity" - ); + assert_eq!(doc["redirect"]["redirected"], 2, "alpha and left-pad: {:#}", doc["redirect"]); + assert_eq!(filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], "policy_severity"); } #[tokio::test] @@ -593,24 +517,14 @@ async fn hosted_dry_run_makes_the_same_decisions_and_writes_nothing() { async fn path_globs_apply_default_ignores_and_ignore_paths_human() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some( - "version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n", - )); + let repo = Repo::new(Some("version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n")); let legacy = repo.lock("services/legacy"); let test_lock = repo.lock("services/test"); let (code, stdout, stderr) = scan(&repo.root, &server.uri(), &["services/*"], &[]); assert_eq!(code, 0, "stdout:\n{stdout}\nstderr:\n{stderr}"); assert!(repo.lock("services/web").contains(&P_ALPHA.hosted_url())); - assert_eq!( - repo.lock("services/legacy"), - legacy, - "ignored by patches.ignorePaths" - ); - assert_eq!( - repo.lock("services/test"), - test_lock, - "a discovered test/ root is a built-in ignore" - ); + assert_eq!(repo.lock("services/legacy"), legacy, "ignored by patches.ignorePaths"); + assert_eq!(repo.lock("services/test"), test_lock, "a discovered test/ root is a built-in ignore"); assert!(stdout.contains("Policy (socket.yml)"), "{stdout}"); // Named literally, the test/ root is explicit: defaults do not apply. @@ -624,9 +538,7 @@ async fn path_globs_apply_default_ignores_and_ignore_paths_human() { async fn include_paths_limit_roots() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some( - "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n", - )); + let repo = Repo::new(Some("version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n")); let web = repo.lock("services/web"); let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); @@ -659,10 +571,7 @@ async fn invalid_file_fails_closed_before_any_request_or_write() { assert!(message.contains("--no-socket-yml"), "{message}"); assert!(doc.get("policy").is_none()); assert_eq!(repo.snapshot(), before); - assert!( - server.received_requests().await.unwrap().is_empty(), - "no request before the policy loads" - ); + assert!(server.received_requests().await.unwrap().is_empty(), "no request before the policy loads"); // Human output names the code on stderr, same exit code. let (code, _, stderr) = scan(&repo.dir("services/web"), &server.uri(), &[], &[]); @@ -670,20 +579,10 @@ async fn invalid_file_fails_closed_before_any_request_or_write() { assert!(stderr.contains("socket_yml_invalid"), "{stderr}"); // --no-socket-yml (and its env var) skips the file. - let (code, doc) = scan_json( - &repo.dir("services/web"), - &server.uri(), - &["--no-socket-yml", "--dry-run"], - &[], - ); + let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--no-socket-yml", "--dry-run"], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(doc["policy"]["source"], "bypassed"); - let (code, doc) = scan_json( - &repo.dir("services/web"), - &server.uri(), - &["--dry-run"], - &[("SOCKET_NO_SOCKET_YML", "1")], - ); + let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--dry-run"], &[("SOCKET_NO_SOCKET_YML", "1")]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(doc["policy"]["source"], "bypassed"); } @@ -694,11 +593,7 @@ async fn both_files_disagreeing_is_ambiguous() { let server = MockServer::start().await; mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n maxNewPatches: 1\n")); - std::fs::write( - repo.root.join("socket.yaml"), - "version: 2\npatches:\n maxNewPatches: 2\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yaml"), "version: 2\npatches:\n maxNewPatches: 2\n").unwrap(); let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &[], &[]); assert_eq!(code, 1); assert_eq!(doc["errorCode"], "socket_yml_ambiguous"); @@ -711,66 +606,26 @@ async fn severity_flag_and_env_override_the_file() { mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n minSeverity: high\n")); let web = repo.dir("services/web"); - let (code, doc) = scan_json( - &web, - &server.uri(), - &["--dry-run", "--min-severity", "none"], - &[], - ); + let (code, doc) = scan_json(&web, &server.uri(), &["--dry-run", "--min-severity", "none"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - doc["policy"]["minSeverity"], - json!({"value": null, "source": "flag"}) - ); - assert_eq!( - doc["redirect"]["redirected"], 3, - "beta too once the floor is lifted" - ); + assert_eq!(doc["policy"]["minSeverity"], json!({"value": null, "source": "flag"})); + assert_eq!(doc["redirect"]["redirected"], 3, "beta too once the floor is lifted"); - let (code, doc) = scan_json( - &web, - &server.uri(), - &["--dry-run"], - &[("SOCKET_MIN_SEVERITY", "critical")], - ); + let (code, doc) = scan_json(&web, &server.uri(), &["--dry-run"], &[("SOCKET_MIN_SEVERITY", "critical")]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - doc["policy"]["minSeverity"], - json!({"value": "critical", "source": "env"}) - ); + assert_eq!(doc["policy"]["minSeverity"], json!({"value": "critical", "source": "env"})); assert_eq!(doc["redirect"]["redirected"], 1); // The flag beats the env; an empty env value is unset. - let (_, doc) = scan_json( - &web, - &server.uri(), - &["--dry-run", "--min-severity", "moderate"], - &[("SOCKET_MIN_SEVERITY", "critical")], - ); - assert_eq!( - doc["policy"]["minSeverity"], - json!({"value": "medium", "source": "flag"}) - ); - let (_, doc) = scan_json( - &web, - &server.uri(), - &["--dry-run"], - &[("SOCKET_MIN_SEVERITY", "")], - ); - assert_eq!( - doc["policy"]["minSeverity"], - json!({"value": "high", "source": "file"}) - ); + let (_, doc) = scan_json(&web, &server.uri(), &["--dry-run", "--min-severity", "moderate"], &[("SOCKET_MIN_SEVERITY", "critical")]); + assert_eq!(doc["policy"]["minSeverity"], json!({"value": "medium", "source": "flag"})); + let (_, doc) = scan_json(&web, &server.uri(), &["--dry-run"], &[("SOCKET_MIN_SEVERITY", "")]); + assert_eq!(doc["policy"]["minSeverity"], json!({"value": "high", "source": "file"})); // Malformed values are usage errors. let (code, _, stderr) = scan(&web, &server.uri(), &["--min-severity", "severe"], &[]); assert_eq!(code, 2, "{stderr}"); - let (code, _, stderr) = scan( - &web, - &server.uri(), - &[], - &[("SOCKET_MIN_SEVERITY", "severe")], - ); + let (code, _, stderr) = scan(&web, &server.uri(), &[], &[("SOCKET_MIN_SEVERITY", "severe")]); assert_eq!(code, 2, "{stderr}"); assert!(stderr.contains("SOCKET_MIN_SEVERITY"), "{stderr}"); } @@ -788,20 +643,12 @@ async fn narrowing_after_a_hosted_patch_leaves_the_pin_byte_identical() { assert!(pinned.contains(&P_ALPHA.hosted_url())); // A newer merged patch appears, and the repo now ignores alpha. - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n ignorePackages: [alpha]\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ignorePackages: [alpha]\n").unwrap(); server.reset().await; mount_api(&server, vec![P_ALPHA, P_ALPHA_MERGED_NEW]).await; let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - repo.lock("services/web"), - pinned, - "retained: not upgraded, not removed" - ); + assert_eq!(repo.lock("services/web"), pinned, "retained: not upgraded, not removed"); let retained = &doc["policy"]["retained"][0]; assert_eq!(retained["purl"], "pkg:npm/alpha@1.0.0"); assert_eq!(retained["recordedUuid"], P_ALPHA.uuid); @@ -819,11 +666,7 @@ async fn narrowing_after_a_hosted_patch_leaves_the_pin_byte_identical() { let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.lock("services/web"), pinned, "{yml}"); - assert_eq!( - doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", - "{yml}: {:#}", - doc["policy"] - ); + assert_eq!(doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", "{yml}: {:#}", doc["policy"]); } } @@ -838,15 +681,9 @@ async fn enabled_false_reports_and_writes_nothing() { assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before); assert_eq!(doc["policy"]["enabled"], false); - assert!( - warning_codes(&doc).contains(&"patches_disabled".to_string()), - "{doc:#}" - ); + assert!(warning_codes(&doc).contains(&"patches_disabled".to_string()), "{doc:#}"); let reasons: Vec = filtered(&doc).into_iter().map(|(_, r)| r).collect(); - assert!( - !reasons.is_empty() && reasons.iter().all(|r| r == "policy_disabled"), - "{reasons:?}" - ); + assert!(!reasons.is_empty() && reasons.iter().all(|r| r == "policy_disabled"), "{reasons:?}"); assert_eq!(doc["redirect"]["redirected"], 0); } @@ -855,9 +692,7 @@ async fn enabled_false_reports_and_writes_nothing() { async fn report_only_json_fails_when_every_detail_query_fails() { let server = MockServer::start().await; Mock::given(method("GET")) - .and(path_regex(format!( - "^/v0/orgs/{ORG}/patches/by-package/.+$" - ))) + .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) .respond_with(ResponseTemplate::new(500)) .with_priority(1) .mount(&server) @@ -870,10 +705,7 @@ async fn report_only_json_fails_when_every_detail_query_fails() { assert_eq!(code, 1, "{doc:#}"); assert_eq!(doc["status"], "error", "{doc:#}"); assert!( - doc["error"] - .as_str() - .unwrap_or_default() - .contains("patch-detail queries failed"), + doc["error"].as_str().unwrap_or_default().contains("patch-detail queries failed"), "{doc:#}" ); assert_eq!(repo.snapshot(), before); @@ -894,11 +726,7 @@ async fn recorded_merge_below_the_floor_is_kept_until_a_more_severe_patch_is_ava "the only available patch is pinned:\n{pinned}" ); - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n minSeverity: high\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n minSeverity: high\n").unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!( @@ -950,17 +778,11 @@ async fn floor_with_nothing_admitted_reports_the_withheld_patch() { let (code, stdout, stderr) = scan(&web, &server.uri(), &[], &[]); assert_eq!(code, 0, "{stdout}\n{stderr}"); assert_eq!(repo.lock("services/web"), lock); - assert!( - stdout.contains("Policy (socket.yml): 1 skipped by filters"), - "{stdout}" - ); + assert!(stdout.contains("Policy (socket.yml): 1 skipped by filters"), "{stdout}"); // Only critical/high are named without --verbose. assert!(!stdout.contains("skipped beta"), "{stdout}"); let (_, stdout, _) = scan(&web, &server.uri(), &["--verbose"], &[]); - assert!( - stdout.contains("skipped pkg:npm/beta@1.0.0 (low): low < critical"), - "{stdout}" - ); + assert!(stdout.contains("skipped pkg:npm/beta@1.0.0 (low): low < critical"), "{stdout}"); } #[tokio::test] @@ -971,17 +793,9 @@ async fn path_outside_the_repo_is_a_usage_error() { let repo = Repo::new(None); let outside = repo.root.parent().unwrap().join("elsewhere"); write_npm_root(&outside, &["alpha"]); - let (code, _, stderr) = scan( - &repo.dir("services"), - &server.uri(), - &["web", "../../elsewhere"], - &[], - ); + let (code, _, stderr) = scan(&repo.dir("services"), &server.uri(), &["web", "../../elsewhere"], &[]); assert_eq!(code, 2, "{stderr}"); - assert!( - stderr.contains("is outside") && stderr.contains("run one scan per repository"), - "{stderr}" - ); + assert!(stderr.contains("is outside") && stderr.contains("run one scan per repository"), "{stderr}"); } #[tokio::test] @@ -989,9 +803,7 @@ async fn path_outside_the_repo_is_a_usage_error() { async fn project_ignore_paths_is_honored_without_a_patches_block() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some( - "version: 2\nprojectIgnorePaths:\n - \"services/legacy/**\"\n", - )); + let repo = Repo::new(Some("version: 2\nprojectIgnorePaths:\n - \"services/legacy/**\"\n")); let legacy = repo.lock("services/legacy"); let (code, doc) = scan_json(&repo.dir("services/legacy"), &server.uri(), &[], &[]); assert_eq!(code, 0, "{doc:#}"); @@ -1001,22 +813,10 @@ async fn project_ignore_paths_is_honored_without_a_patches_block() { assert_eq!(entry["detail"], "services/legacy/** (projectIgnorePaths)"); // A malformed projectIgnorePaths without a patches block only warns. - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\nprojectIgnorePaths: {a: 1}\n", - ) - .unwrap(); - let (code, doc) = scan_json( - &repo.dir("services/legacy"), - &server.uri(), - &["--dry-run"], - &[], - ); + std::fs::write(repo.root.join("socket.yml"), "version: 2\nprojectIgnorePaths: {a: 1}\n").unwrap(); + let (code, doc) = scan_json(&repo.dir("services/legacy"), &server.uri(), &["--dry-run"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert!( - warning_codes(&doc).contains(&"socket_yml_ignored_value".to_string()), - "{doc:#}" - ); + assert!(warning_codes(&doc).contains(&"socket_yml_ignored_value".to_string()), "{doc:#}"); } // --------------------------------------------------------------------------- @@ -1045,18 +845,14 @@ async fn agent_mode_applies_only_admitted_patches() { let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "agent"], &[]); assert_eq!(code, 0, "{doc:#}"); let manifest: Value = - serde_json::from_str(&std::fs::read_to_string(web.join(".socket/manifest.json")).unwrap()) - .unwrap(); + serde_json::from_str(&std::fs::read_to_string(web.join(".socket/manifest.json")).unwrap()).unwrap(); let keys: Vec<&String> = manifest["patches"].as_object().unwrap().keys().collect(); assert_eq!(keys, ["pkg:npm/alpha@1.0.0"]); assert_eq!( std::fs::read_to_string(web.join("node_modules/alpha/index.js")).unwrap(), patched_index("alpha") ); - assert_eq!( - std::fs::read_to_string(web.join("node_modules/beta/index.js")).unwrap(), - orig_index("beta") - ); + assert_eq!(std::fs::read_to_string(web.join("node_modules/beta/index.js")).unwrap(), orig_index("beta")); } #[tokio::test] @@ -1071,23 +867,13 @@ async fn agent_mode_retains_a_recorded_patch_the_policy_now_excludes() { let manifest_before = std::fs::read(web.join(".socket/manifest.json")).unwrap(); let installed_before = std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(); - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n ecosystems: [pypi]\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ecosystems: [pypi]\n").unwrap(); server.reset().await; mount_api(&server, vec![P_ALPHA, P_ALPHA_MERGED_NEW]).await; let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "agent"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - std::fs::read(web.join(".socket/manifest.json")).unwrap(), - manifest_before - ); - assert_eq!( - std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(), - installed_before - ); + assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); + assert_eq!(std::fs::read(web.join("node_modules/alpha/index.js")).unwrap(), installed_before); assert_eq!(doc["policy"]["retained"][0]["reason"], "policy_ecosystem"); assert_eq!(doc["policy"]["retained"][0]["upgradeAvailable"], true); } @@ -1103,12 +889,7 @@ async fn vendored_dry_run_previews_only_admitted_patches() { mount_api(&server, catalog()).await; let repo = Repo::new(Some("version: 2\npatches:\n packages: [\"pkg:npm/beta\", \"pkg:npm/left-pad\"]\n minSeverity: medium\n")); let before = repo.snapshot(); - let (code, doc) = scan_json( - &repo.dir("services/web"), - &server.uri(), - &["--mode", "vendored", "--dry-run"], - &[], - ); + let (code, doc) = scan_json(&repo.dir("services/web"), &server.uri(), &["--mode", "vendored", "--dry-run"], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!(repo.snapshot(), before); let previewed: Vec<&str> = doc["vendor"]["patches"] @@ -1118,14 +899,8 @@ async fn vendored_dry_run_previews_only_admitted_patches() { .filter_map(|p| p["purl"].as_str()) .collect(); assert_eq!(previewed, ["pkg:npm/left-pad@1.0.0"], "{doc:#}"); - assert_eq!( - filtered_reason(&doc, "pkg:npm/alpha@1.0.0")["reason"], - "policy_package_not_listed" - ); - assert_eq!( - filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], - "policy_severity" - ); + assert_eq!(filtered_reason(&doc, "pkg:npm/alpha@1.0.0")["reason"], "policy_package_not_listed"); + assert_eq!(filtered_reason(&doc, "pkg:npm/beta@1.0.0")["reason"], "policy_severity"); } // --------------------------------------------------------------------------- @@ -1157,16 +932,9 @@ async fn get_bypasses_the_policy_with_a_warning() { let (code, stdout, stderr) = run_cli(&web, &args, &[]); assert_eq!(code, 0, "stdout:\n{stdout}\nstderr:\n{stderr}"); let doc: Value = serde_json::from_str(&stdout).unwrap(); - let warnings: Vec<&str> = doc["warnings"] - .as_array() - .unwrap() - .iter() - .filter_map(Value::as_str) - .collect(); + let warnings: Vec<&str> = doc["warnings"].as_array().unwrap().iter().filter_map(Value::as_str).collect(); assert!( - warnings - .iter() - .any(|w| w.starts_with("(policy_bypassed)") && w.contains("alpha")), + warnings.iter().any(|w| w.starts_with("(policy_bypassed)") && w.contains("alpha")), "{doc:#}" ); @@ -1192,65 +960,30 @@ async fn agent_mode_honors_path_filters_and_keeps_the_prune_universe() { // The root is excluded by path: nothing selected, and a --sync (agent // + prune) still judges the full crawl, so no entry is pruned. - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n").unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - std::fs::read(web.join(".socket/manifest.json")).unwrap(), - manifest_before - ); + assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); assert_eq!(doc["policy"]["filtered"][0]["purl"], Value::Null); - assert_eq!( - doc["policy"]["filtered"][0]["reason"], - "policy_path_not_included" - ); - assert_eq!( - doc["policy"]["counts"]["retained"], 2, - "{:#}", - doc["policy"] - ); - assert_eq!( - doc["gc"]["removed"].as_array().map_or(0, Vec::len), - 0, - "{:#}", - doc["gc"] - ); + assert_eq!(doc["policy"]["filtered"][0]["reason"], "policy_path_not_included"); + assert_eq!(doc["policy"]["counts"]["retained"], 2, "{:#}", doc["policy"]); + assert_eq!(doc["gc"]["removed"].as_array().map_or(0, Vec::len), 0, "{:#}", doc["gc"]); // A narrower ecosystem list under --sync prunes nothing either. - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n ecosystems: [pypi]\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ecosystems: [pypi]\n").unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); - assert_eq!( - std::fs::read(web.join(".socket/manifest.json")).unwrap(), - manifest_before - ); + assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); // patches.enabled: false skips the GC entirely. std::fs::remove_dir_all(web.join("node_modules/beta")).unwrap(); - let pkg_lock = repo - .lock("services/web") - .replace("\"node_modules/beta\"", "\"node_modules/gone\""); + let pkg_lock = repo.lock("services/web").replace("\"node_modules/beta\"", "\"node_modules/gone\""); std::fs::write(web.join("package-lock.json"), pkg_lock).unwrap(); - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n enabled: false\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n enabled: false\n").unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--sync"], &[]); assert_eq!(code, 0, "{doc:#}"); assert!(doc.get("gc").is_none(), "{doc:#}"); - assert_eq!( - std::fs::read(web.join(".socket/manifest.json")).unwrap(), - manifest_before - ); + assert_eq!(std::fs::read(web.join(".socket/manifest.json")).unwrap(), manifest_before); } #[tokio::test] @@ -1264,53 +997,29 @@ async fn narrowing_after_vendoring_leaves_the_vendored_package_byte_identical() let before = compute_git_sha256_from_bytes(orig_index("alpha").as_bytes()); let after = compute_git_sha256_from_bytes(patched_index("alpha").as_bytes()); std::fs::create_dir_all(web.join(".socket/blobs")).unwrap(); - std::fs::write( - web.join(".socket/blobs").join(&after), - patched_index("alpha"), - ) - .unwrap(); + std::fs::write(web.join(".socket/blobs").join(&after), patched_index("alpha")).unwrap(); let manifest = json!({"patches": {P_ALPHA.purl(): { "uuid": P_ALPHA.uuid, "exportedAt": "2026-01-01T00:00:00Z", "files": {"package/index.js": {"beforeHash": before, "afterHash": after}}, "vulnerabilities": {}, "description": "d", "license": "MIT", "tier": "free" }}}); - std::fs::write( - web.join(".socket/manifest.json"), - serde_json::to_vec_pretty(&manifest).unwrap(), - ) - .unwrap(); + std::fs::write(web.join(".socket/manifest.json"), serde_json::to_vec_pretty(&manifest).unwrap()).unwrap(); let fixture = prebuilt_common::Server::project(&web); let (code, stdout, stderr) = run_cli( &web, &["vendor", "--json", "--cwd", web.to_str().unwrap()], - &[ - ("SOCKET_VENDOR_URL", &fixture.uri), - ("SOCKET_PATCH_SERVER_URL", &fixture.uri), - ], + &[("SOCKET_VENDOR_URL", &fixture.uri), ("SOCKET_PATCH_SERVER_URL", &fixture.uri)], ); assert_eq!(code, 0, "vendor fixture: {stdout}\n{stderr}"); - assert!( - repo.lock("services/web").contains(".socket/vendor/"), - "vendored lock" - ); + assert!(repo.lock("services/web").contains(".socket/vendor/"), "vendored lock"); let snapshot = repo.snapshot(); - std::fs::write( - repo.root.join("socket.yml"), - "version: 2\npatches:\n ignorePackages: [\"pkg:npm/alpha\"]\n", - ) - .unwrap(); + std::fs::write(repo.root.join("socket.yml"), "version: 2\npatches:\n ignorePackages: [\"pkg:npm/alpha\"]\n").unwrap(); let (code, doc) = scan_json(&web, &server.uri(), &["--mode", "vendored"], &[]); assert_eq!(code, 0, "{doc:#}"); let mut after_scan = repo.snapshot(); after_scan.remove("socket.yml"); - assert_eq!( - after_scan, snapshot, - "the vendored package, its lock wiring and ledger stay byte-identical" - ); - assert_eq!( - doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", - "{:#}", - doc["policy"] - ); + assert_eq!(after_scan, snapshot, "the vendored package, its lock wiring and ledger stay byte-identical"); + assert_eq!(doc["policy"]["retained"][0]["purl"], "pkg:npm/alpha@1.0.0", "{:#}", doc["policy"]); } + diff --git a/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs b/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs index 0dd0998af..83a8de749 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_lockfile/common_selftest.rs @@ -152,11 +152,7 @@ fn committed_pre_v5_ledger_lets_a_hosted_pin_attest_offline() { ); } api.assert_no_requests(); - assert_eq!( - std::fs::read(&ledger).unwrap(), - before, - "vex never rewrites it" - ); + assert_eq!(std::fs::read(&ledger).unwrap(), before, "vex never rewrites it"); let other = "0b0b0b0b-0b0b-4b0b-8b0b-0b0b0b0b0b0b"; let mut stale = left_pad_view(); diff --git a/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs b/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs index 6cdd44ef1..e32b4ea97 100644 --- a/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs +++ b/crates/socket-patch-cli/tests/get/get_edge_cases_e2e.rs @@ -469,16 +469,8 @@ fn get_help_lists_all_identifier_flags() { ); } // Help text is for users: no implementation notes from the source. - for leak in [ - "value_parser", - "parse_bool_flag", - "No env binding", - "locally- installed", - ] { - assert!( - !stdout.contains(leak), - "get --help leaks {leak:?}: {stdout}" - ); + for leak in ["value_parser", "parse_bool_flag", "No env binding", "locally- installed"] { + assert!(!stdout.contains(leak), "get --help leaks {leak:?}: {stdout}"); } } diff --git a/crates/socket-patch-cli/tests/get/global_packages_e2e.rs b/crates/socket-patch-cli/tests/get/global_packages_e2e.rs index a86958fe8..25bdadd21 100644 --- a/crates/socket-patch-cli/tests/get/global_packages_e2e.rs +++ b/crates/socket-patch-cli/tests/get/global_packages_e2e.rs @@ -211,10 +211,7 @@ fn assert_rollback_noop(stdout: &str) { r["skipped"], "package_not_installed", "a no-op rollback may carry only not-installed markers; envelope={v}" ); - assert!( - r["path"].is_null(), - "marker path must be null; envelope={v}" - ); + assert!(r["path"].is_null(), "marker path must be null; envelope={v}"); assert!( r.get("success").is_none() && r.get("error").is_none(), "markers carry no success/error keys; envelope={v}" diff --git a/crates/socket-patch-cli/tests/help_text_hygiene.rs b/crates/socket-patch-cli/tests/help_text_hygiene.rs index 467ed46c5..9b3280e8a 100644 --- a/crates/socket-patch-cli/tests/help_text_hygiene.rs +++ b/crates/socket-patch-cli/tests/help_text_hygiene.rs @@ -61,11 +61,7 @@ fn every_help_page_has_no_developer_notes() { names.extend(cmd.get_subcommands().map(|s| s.get_name().to_string())); let mut failures = Vec::new(); for name in &names { - let path: Vec<&str> = if name.is_empty() { - vec![] - } else { - vec![name.as_str()] - }; + let path: Vec<&str> = if name.is_empty() { vec![] } else { vec![name.as_str()] }; let text = long_help(&path); let found = leaks(&text); if !found.is_empty() { @@ -151,9 +147,7 @@ fn vex_product_list_renders_one_item_per_line() { fn root_command_list_uses_the_verb_form() { let text = long_help(&[]); assert!( - text.contains( - "Undo patches: restore original files and unwind hosted or vendored lockfile wiring" - ), + text.contains("Undo patches: restore original files and unwind hosted or vendored lockfile wiring"), "{text}" ); assert!(!text.contains("Rollback patches"), "{text}"); @@ -264,24 +258,11 @@ fn short_help_lists_about_eight_options_and_long_help_lists_all() { .filter(|l| l.starts_with('-') && !l.starts_with("-h,") && !l.starts_with("-V,")) .count() }; - assert!( - count(&short) <= 9, - "{name} -h lists {} options:\n{short}", - count(&short) - ); - assert!( - count(&long) > count(&short), - "{name} --help must list more than -h" - ); - assert!( - short.contains("--json") && short.contains("--cwd"), - "{name}" - ); + assert!(count(&short) <= 9, "{name} -h lists {} options:\n{short}", count(&short)); + assert!(count(&long) > count(&short), "{name} --help must list more than -h"); + assert!(short.contains("--json") && short.contains("--cwd"), "{name}"); } let scan = cmd.find_subcommand_mut("scan").expect("scan"); let long = scan.render_long_help().to_string(); - assert!( - !long.contains("--apply") && !long.contains("--vendor "), - "{long}" - ); + assert!(!long.contains("--apply") && !long.contains("--vendor "), "{long}"); } diff --git a/crates/socket-patch-cli/tests/hosted_memory_engine.rs b/crates/socket-patch-cli/tests/hosted_memory_engine.rs index 778baf094..761d34ea9 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_engine.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_engine.rs @@ -984,8 +984,7 @@ async fn a_vlt_project_is_withheld_as_offline() { .and_then(|w| w["detail"].as_str()) .expect("the preflight warning is reported"); assert!( - detail.contains("/patch/npm//") - && detail.contains(": offline; nothing was written"), + detail.contains("/patch/npm//") && detail.contains(": offline; nothing was written"), "the offline refusal quotes the redacted URL" ); assert!(output.changed_files.is_empty()); diff --git a/crates/socket-patch-cli/tests/hosted_memory_parity.rs b/crates/socket-patch-cli/tests/hosted_memory_parity.rs index 0af392eb4..b297eaf79 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_parity.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_parity.rs @@ -754,11 +754,7 @@ fn policy_repo(socket_yml: &str) -> (Vec, BTreeMap>) { let mut patches = patches_from_overrides(&npm.join("overrides.json"), None); patches.extend(patches_from_overrides(&cargo.join("overrides.json"), None)); let mut repo: BTreeMap> = BTreeMap::new(); - for (root, dir) in [ - ("apps/web", &npm), - ("apps/legacy", &npm), - ("services/api", &cargo), - ] { + for (root, dir) in [("apps/web", &npm), ("apps/legacy", &npm), ("services/api", &cargo)] { for (rel, bytes) in fixture_files(&dir.join("input")) { repo.insert(format!("{root}/{rel}"), bytes); } @@ -777,9 +773,7 @@ fn two_phase( socket_patch_cli::hosted_memory::PathSelection, socket_patch_cli::hosted_memory::HostedScanInput, ) { - use socket_patch_cli::hosted_memory::{ - select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, - }; + use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; let entries: Vec = files .iter() .map(|(p, bytes)| TreeEntryInput { @@ -820,23 +814,15 @@ fn two_phase( (selection, input) } -fn policy_input( - files: &BTreeMap>, -) -> socket_patch_cli::hosted_memory::HostedScanInput { +fn policy_input(files: &BTreeMap>) -> socket_patch_cli::hosted_memory::HostedScanInput { let (selection, input) = two_phase(files, options(false)); - assert!( - selection.policy_error.is_none(), - "{:?}", - selection.policy_error - ); + assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); input } /// Session options as selection of `files` would hand them over, without /// going through selection (for inputs a host may get wrong). -fn policy_options( - files: &BTreeMap>, -) -> socket_patch_cli::hosted_memory::HostedScanOptions { +fn policy_options(files: &BTreeMap>) -> socket_patch_cli::hosted_memory::HostedScanOptions { let (selection, _) = two_phase(files, options(false)); let mut opts = options(false); opts.policy_paths = Some(selection.policy_paths); @@ -868,44 +854,25 @@ async fn parity_socket_yml_filters_the_same_roots_and_packages() { let server = MockServer::start().await; mount_api(&server, &patches).await; let (selection, input) = two_phase(&repo, options(false)); - assert!( - selection.policy_error.is_none(), - "{:?}", - selection.policy_error - ); + assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); let memory = run_engine(&server, input).await; assert!(memory.policy_error.is_none(), "{:?}", memory.policy_error); let roots: Vec<&str> = memory.projects.iter().map(|p| p.root.as_str()).collect(); - assert_eq!( - roots, - vec!["apps/web", "services/api"], - "the ignored root is not processed" - ); + assert_eq!(roots, vec!["apps/web", "services/api"], "the ignored root is not processed"); // Selection reports the root it excluded; nothing of it is streamed. assert!(selection .ignored_sample .iter() .any(|i| i.path == "apps/legacy/package-lock.json" && i.reason == "policy_path_excluded")); - assert!(!selection - .fetch_text - .iter() - .chain(&selection.present_only) - .any(|p| p.starts_with("apps/legacy/"))); + assert!(!selection.fetch_text.iter().chain(&selection.present_only).any(|p| p.starts_with("apps/legacy/"))); let memory_policy = memory.policy.clone().expect("policy block"); assert_eq!(memory_policy["source"], "file"); let mut disk_filtered = std::collections::BTreeSet::new(); for root in ["apps/web", "apps/legacy", "services/api"] { let disk = run_disk_in(&server, &repo, root, false); - assert_eq!( - disk.envelope["status"], "success", - "{root}: {}", - disk.stderr - ); - assert_eq!( - disk.envelope["policy"]["sha256"], memory_policy["sha256"], - "{root}" - ); + assert_eq!(disk.envelope["status"], "success", "{root}: {}", disk.stderr); + assert_eq!(disk.envelope["policy"]["sha256"], memory_policy["sha256"], "{root}"); disk_filtered.extend(filtered_set(&disk.envelope["policy"])); if let Some(project) = memory.projects.iter().find(|p| p.root == root) { assert_eq!(project.redirect, disk.envelope["redirect"], "{root}"); @@ -916,24 +883,13 @@ async fn parity_socket_yml_filters_the_same_roots_and_packages() { .collect(); assert_eq!(memory_changed, disk.changed, "{root}"); } else { - assert!( - disk.changed.is_empty(), - "{root}: an ignored root changes nothing" - ); + assert!(disk.changed.is_empty(), "{root}: an ignored root changes nothing"); } } let mut memory_filtered = filtered_set(&memory_policy); - memory_filtered.insert(( - "apps/legacy".to_string(), - None, - "policy_path_excluded".to_string(), - )); + memory_filtered.insert(("apps/legacy".to_string(), None, "policy_path_excluded".to_string())); assert_eq!(memory_filtered, disk_filtered); - assert!(disk_filtered.contains(&( - "apps/legacy".to_string(), - None, - "policy_path_excluded".to_string() - ))); + assert!(disk_filtered.contains(&("apps/legacy".to_string(), None, "policy_path_excluded".to_string()))); assert!(disk_filtered.contains(&( "services/api".to_string(), Some("pkg:cargo/serde@1.0.190".to_string()), @@ -947,17 +903,9 @@ async fn parity_socket_yml_severity_floor() { let server = MockServer::start().await; mount_api(&server, &patches).await; let memory = run_engine(&server, policy_input(&repo)).await; - let web = memory - .projects - .iter() - .find(|p| p.root == "apps/web") - .unwrap(); + let web = memory.projects.iter().find(|p| p.root == "apps/web").unwrap(); assert!(web.redirected.is_empty(), "{:#}", web.redirect); - assert!( - web.skipped.iter().any(|s| s.reason == "policy_severity"), - "{:?}", - web.skipped - ); + assert!(web.skipped.iter().any(|s| s.reason == "policy_severity"), "{:?}", web.skipped); assert!(engine_changed(&memory).is_empty()); let disk = run_disk_in(&server, &repo, "apps/web", false); assert!(disk.changed.is_empty()); @@ -983,15 +931,8 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { assert_eq!(err.code, "socket_yml_invalid"); assert!(out.projects.is_empty() && out.changed_files.is_empty() && out.policy.is_none()); // Streamed present-without-content. - let out = run_engine( - &server, - build_input(&withheld, &["socket.yml"], opts.clone()), - ) - .await; - assert_eq!( - out.policy_error.expect("policyError").code, - "socket_yml_invalid" - ); + let out = run_engine(&server, build_input(&withheld, &["socket.yml"], opts.clone())).await; + assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); // Content other than what selection read. let mut changed = repo.clone(); changed.insert("socket.yml".to_string(), b"version: 2\n".to_vec()); @@ -1002,10 +943,7 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { let mut no_sha = opts.clone(); no_sha.policy_sha256 = None; let out = run_engine(&server, build_input(&repo, &[], no_sha)).await; - assert_eq!( - out.policy_error.expect("policyError").code, - "socket_yml_invalid" - ); + assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); // Invalid content: selection refuses it before anything is fetched. let (_, bad) = policy_repo("version: 2\npatches:\n apiUrl: https://evil.example\n"); let (selection, _) = two_phase(&bad, options(false)); @@ -1020,10 +958,7 @@ async fn memory_policy_file_withheld_or_invalid_is_a_policy_error() { let mut half = opts.clone(); half.no_socket_yml = Some(true); let out = run_engine(&server, build_input(&repo, &[], half)).await; - assert_eq!( - out.policy_error.expect("policyError").code, - "socket_yml_invalid" - ); + assert_eq!(out.policy_error.expect("policyError").code, "socket_yml_invalid"); // noSocketYml skips it on both sides. let mut bypass = options(false); bypass.no_socket_yml = Some(true); @@ -1044,10 +979,7 @@ async fn memory_min_severity_option_beats_the_file() { let (_, input) = two_phase(&repo, opts); let out = run_engine(&server, input).await; let policy = out.policy.unwrap(); - assert_eq!( - policy["minSeverity"], - serde_json::json!({"value": null, "source": "flag"}) - ); + assert_eq!(policy["minSeverity"], serde_json::json!({"value": null, "source": "flag"})); assert!(out.projects.iter().any(|p| !p.redirected.is_empty())); let mut bad = options(false); bad.min_severity = Some("severe".to_string()); @@ -1056,9 +988,7 @@ async fn memory_min_severity_option_beats_the_file() { #[test] fn selection_applies_the_path_policy_and_fails_closed() { - use socket_patch_cli::hosted_memory::{ - select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, - }; + use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; let blob = |path: &str, mode: &str| TreeEntryInput { path: path.to_string(), mode: mode.to_string(), @@ -1084,34 +1014,19 @@ fn selection_applies_the_path_policy_and_fails_closed() { }; let yml = "version: 2\npatches:\n ignorePaths: [\"/apps/old/\"]\n"; let selection = select_paths(&entries, &with(vec![text("socket.yml", yml)])); - assert!( - selection.policy_error.is_none(), - "{:?}", - selection.policy_error - ); + assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); assert_eq!(selection.policy_paths, vec!["socket.yml"]); assert_eq!(selection.policy_sha256.as_ref().map(String::len), Some(64)); assert!(selection.fetch_text.contains(&"socket.yml".to_string())); assert_eq!(selection.roots, vec!["apps/web"]); // Excluded roots (file list and built-in ignores, any case) are // reported and never streamed. - for path in [ - "apps/old/yarn.lock", - "apps/web/tests/app/package-lock.json", - "Fixtures/x/yarn.lock", - ] { + for path in ["apps/old/yarn.lock", "apps/web/tests/app/package-lock.json", "Fixtures/x/yarn.lock"] { assert!( - selection - .ignored_sample - .iter() - .any(|i| i.path == path && i.reason == "policy_path_excluded"), + selection.ignored_sample.iter().any(|i| i.path == path && i.reason == "policy_path_excluded"), "{path}: {selection:?}" ); - assert!( - !selection.fetch_text.contains(&path.to_string()) - && !selection.present_only.contains(&path.to_string()), - "{path}" - ); + assert!(!selection.fetch_text.contains(&path.to_string()) && !selection.present_only.contains(&path.to_string()), "{path}"); } // Named roots are explicit: the built-in ignores do not apply. let named = select_paths( @@ -1129,16 +1044,9 @@ fn selection_applies_the_path_policy_and_fails_closed() { text: None, missing: Some(true), }; - for files in [ - vec![], - vec![missing], - vec![text("socket.yml", "version: 2\npatches:\n apiUrl: x\n")], - ] { + for files in [vec![], vec![missing], vec![text("socket.yml", "version: 2\npatches:\n apiUrl: x\n")]] { let out = select_paths(&entries, &with(files)); - assert_eq!( - out.policy_error.as_ref().map(|e| e.code.as_str()), - Some("socket_yml_invalid") - ); + assert_eq!(out.policy_error.as_ref().map(|e| e.code.as_str()), Some("socket_yml_invalid")); assert!(out.roots.is_empty() && out.fetch_text.is_empty(), "{out:?}"); assert_eq!(out.policy_paths, vec!["socket.yml"]); } @@ -1146,14 +1054,8 @@ fn selection_applies_the_path_policy_and_fails_closed() { assert!(out.policy_error.is_some()); // A symlinked policy file is never read. entries.push(blob("socket.yaml", "120000")); - let out = select_paths( - &entries, - &with(vec![text("socket.yml", yml), text("socket.yaml", yml)]), - ); - assert_eq!( - out.policy_error.map(|e| e.code), - Some("socket_yml_invalid".to_string()) - ); + let out = select_paths(&entries, &with(vec![text("socket.yml", yml), text("socket.yaml", yml)])); + assert_eq!(out.policy_error.map(|e| e.code), Some("socket_yml_invalid".to_string())); // noSocketYml: only the built-in ignores; the file need not be passed. let out = select_paths( &entries, @@ -1184,13 +1086,8 @@ async fn memory_negation_reincludes_a_default_ignored_root() { ); let (selection, input) = two_phase(&repo, options(false)); assert_eq!(selection.roots, vec!["e2e/tests"]); - assert!(selection - .fetch_text - .contains(&"e2e/tests/package-lock.json".to_string())); - assert!( - !selection.fetch_text.iter().any(|p| p.starts_with("x/")), - "{selection:?}" - ); + assert!(selection.fetch_text.contains(&"e2e/tests/package-lock.json".to_string())); + assert!(!selection.fetch_text.iter().any(|p| p.starts_with("x/")), "{selection:?}"); assert!(selection .ignored_sample .iter() @@ -1198,31 +1095,19 @@ async fn memory_negation_reincludes_a_default_ignored_root() { let memory = run_engine(&server, input).await; let roots: Vec<&str> = memory.projects.iter().map(|p| p.root.as_str()).collect(); assert_eq!(roots, vec!["e2e/tests"]); - assert!( - !memory.projects[0].redirected.is_empty(), - "{:#}", - memory.projects[0].redirect - ); + assert!(!memory.projects[0].redirected.is_empty(), "{:#}", memory.projects[0].redirect); // Given every root anyway, the session applies the same filter itself. let direct = run_engine(&server, build_input(&repo, &[], policy_options(&repo))).await; let roots: Vec<&str> = direct.projects.iter().map(|p| p.root.as_str()).collect(); assert_eq!(roots, vec!["e2e/tests"]); let entry = &direct.policy.as_ref().unwrap()["filtered"][0]; - assert_eq!( - (entry["project"].as_str(), entry["detail"].as_str()), - (Some("x/tests"), Some("tests/ (built-in default)")) - ); + assert_eq!((entry["project"].as_str(), entry["detail"].as_str()), (Some("x/tests"), Some("tests/ (built-in default)"))); // Disk patches the same root the same way. let disk = run_disk_in(&server, &repo, "e2e/tests", false); assert_eq!(disk.envelope["status"], "success", "{}", disk.stderr); assert_eq!(memory.projects[0].redirect, disk.envelope["redirect"]); let memory_changed = engine_changed(&memory); - assert_eq!( - memory_changed, - disk.changed, - "{}", - describe(&memory_changed) - ); + assert_eq!(memory_changed, disk.changed, "{}", describe(&memory_changed)); } diff --git a/crates/socket-patch-cli/tests/hosted_memory_rollout.rs b/crates/socket-patch-cli/tests/hosted_memory_rollout.rs index 3c104abf4..ccaf92cc4 100644 --- a/crates/socket-patch-cli/tests/hosted_memory_rollout.rs +++ b/crates/socket-patch-cli/tests/hosted_memory_rollout.rs @@ -237,9 +237,7 @@ async fn memory_selected( files: &BTreeMap>, mut o: HostedScanOptions, ) -> HostedScanOutput { - use socket_patch_cli::hosted_memory::{ - select_paths, PolicyFileInput, SelectOptions, TreeEntryInput, - }; + use socket_patch_cli::hosted_memory::{select_paths, PolicyFileInput, SelectOptions, TreeEntryInput}; let entries: Vec = files .iter() .map(|(p, bytes)| TreeEntryInput { @@ -267,11 +265,7 @@ async fn memory_selected( ..SelectOptions::default() }, ); - assert!( - selection.policy_error.is_none(), - "{:?}", - selection.policy_error - ); + assert!(selection.policy_error.is_none(), "{:?}", selection.policy_error); let fetched: BTreeMap> = selection .fetch_text .iter() @@ -430,11 +424,7 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { b"version: 2\npatches:\n includePaths: [\"/apps/\"]\n minSeverity: high\n maxNewPatches: 2\n" .to_vec(), ); - lock( - &mut files, - "apps/one", - &["mem-a", "mem-b", "mem-c", "mem-d", "mem-e"], - ); + lock(&mut files, "apps/one", &["mem-a", "mem-b", "mem-c", "mem-d", "mem-e"]); lock(&mut files, "apps/two", &["mem-b", "mem-c", "mem-d"]); lock(&mut files, "legacy", &["mem-b", "mem-e"]); let dirs = ["apps/one", "apps/two", "legacy"]; @@ -445,16 +435,8 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { }; let expected: [Vec>; 3] = [ vec![vec!["mem-e", "mem-b"], vec!["mem-b"], vec![]], - vec![ - vec!["mem-e", "mem-b", "mem-c"], - vec!["mem-b", "mem-c"], - vec![], - ], - vec![ - vec!["mem-e", "mem-b", "mem-c"], - vec!["mem-b", "mem-c"], - vec![], - ], + vec![vec!["mem-e", "mem-b", "mem-c"], vec!["mem-b", "mem-c"], vec![]], + vec![vec!["mem-e", "mem-b", "mem-c"], vec!["mem-b", "mem-c"], vec![]], ]; let mut mem_files = files.clone(); @@ -467,10 +449,7 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { "run {}", run + 1 ); - assert_eq!( - mem.policy.as_ref().map(|p| p["source"].clone()), - Some(json!("file")) - ); + assert_eq!(mem.policy.as_ref().map(|p| p["source"].clone()), Some(json!("file"))); mem_files = apply(&mem_files, &mem); assert_eq!(&pins(&mem_files), want, "memory run {}", run + 1); @@ -490,14 +469,7 @@ async fn socket_yml_policy_and_cap_converge_on_disk_and_in_memory() { let (code, stdout, changed) = run_disk_args( &server, &disk_files, - &[ - "--no-socket-yml", - "--max-new-patches", - "1", - "apps/one", - "apps/two", - "legacy", - ], + &["--no-socket-yml", "--max-new-patches", "1", "apps/one", "apps/two", "legacy"], ); assert_eq!(code, 0, "{stdout}"); disk_files.extend(changed); diff --git a/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs b/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs index 1bad484a9..ff45dcd4b 100644 --- a/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs +++ b/crates/socket-patch-cli/tests/in_process_get_hosted_ecosystems.rs @@ -468,11 +468,7 @@ fn maven_hosted_get_state_attests_without_manifest( &[(purl, vlt_hosted_common::legacy_record_from_view(&view))], ); let out = run_vex(&binary(), project, &offline); - assert_eq!( - out.code, - Some(0), - "a pre-v5 ledger record serves offline: {out}" - ); + assert_eq!(out.code, Some(0), "a pre-v5 ledger record serves offline: {out}"); assert_attested(out.doc(), purl, uuid, Marker::Redirected, &vulns); quiet.assert_no_requests(); @@ -753,11 +749,7 @@ fn nuget_hosted_manifestless_vex(root: &Path, uuid: &str, purl: &str) { &[(purl, vlt_hosted_common::legacy_record_from_view(&view))], ); let out = run(VexRun::offline()); - assert_eq!( - out.code, - Some(0), - "a pre-v5 ledger record serves offline: {out}" - ); + assert_eq!(out.code, Some(0), "a pre-v5 ledger record serves offline: {out}"); assert_attested(out.doc(), purl, uuid, Marker::Redirected, vulns); std::fs::write( diff --git a/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs b/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs index 33127617c..f90893f39 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect/vlt.rs @@ -308,15 +308,11 @@ async fn scan_redirect_vlt_artifact_fetch_error() { let detail = warning_detail(&doc, UNVERIFIABLE); let redacted = url.replace(&format!("/{TOKEN}/"), "//"); assert!( - detail.starts_with(&format!( - "vlt would fail to verify {redacted}: fetch error " - )) && detail.ends_with(&format!("; nothing was written for {PURL}")), + detail.starts_with(&format!("vlt would fail to verify {redacted}: fetch error ")) + && detail.ends_with(&format!("; nothing was written for {PURL}")), "the fetch-error refusal quotes the redacted URL" ); - assert!( - !detail.contains(TOKEN), - "the grant token never reaches the warning" - ); + assert!(!detail.contains(TOKEN), "the grant token never reaches the warning"); } async fn redirect_chain(hops: usize) -> (Value, tempfile::TempDir) { diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs index 4319bd6d3..97f4a171d 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pdm.rs @@ -187,9 +187,7 @@ async fn mock_api(server: &MockServer) { .mount(server) .await; Mock::given(method("GET")) - .and(path_regex(format!( - "^/v0/orgs/{ORG}/patches/by-package/.+$" - ))) + .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "patches": [{ "uuid": UUID, "purl": RECORD_PURL, @@ -370,12 +368,9 @@ fn legacy_record(view: &serde_json::Value) -> serde_json::Value { .remove("publishedAt") .unwrap_or_else(|| serde_json::json!("2024-01-01T00:00:00Z")); obj.insert("exportedAt".to_string(), exported); - obj.entry("description") - .or_insert_with(|| serde_json::json!("x")); - obj.entry("license") - .or_insert_with(|| serde_json::json!("MIT")); - obj.entry("tier") - .or_insert_with(|| serde_json::json!("free")); + obj.entry("description").or_insert_with(|| serde_json::json!("x")); + obj.entry("license").or_insert_with(|| serde_json::json!("MIT")); + obj.entry("tier").or_insert_with(|| serde_json::json!("free")); record } @@ -446,11 +441,7 @@ async fn lock_only_pdm_project_redirects_attests_rescans_and_rolls_back() { // 2. Idempotent re-scan: no further edits, lock byte-identical. let code = run(hosted_args(tmp.path(), server.uri(), None)).await; assert_eq!(code, 0); - assert_eq!( - read(&lock_path), - redirected, - "re-scan must not touch the lock" - ); + assert_eq!(read(&lock_path), redirected, "re-scan must not touch the lock"); // 3. The committed state, manifest-less, attests (and only while wired). assert_manifestless_vex(tmp.path(), LOCK); @@ -503,19 +494,12 @@ async fn hatchling_build_backend_does_not_veto_the_pdm_lock_redirect() { .iter() .filter(|r| r.url.path().ends_with(&format!("/patches/view/{UUID}"))) .count(); - assert_eq!( - views, 1, - "the pdm redirect must be confirmed despite the hatch backend" - ); + assert_eq!(views, 1, "the pdm redirect must be confirmed despite the hatch backend"); assert_manifestless_vex(tmp.path(), LOCK); let code = rollback_hosted(tmp.path(), &server).await; assert_eq!(code, 0, "rollback must succeed"); - assert_eq!( - read(&lock_path), - LOCK, - "rollback must restore the pristine lock" - ); + assert_eq!(read(&lock_path), LOCK, "rollback must restore the pristine lock"); } /// The legacy `[metadata.files]` lock (lock_version 2) redirects the package diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs index 335cf6cb3..bf2e00fd3 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -55,8 +55,7 @@ const MAJOR_ENV: &str = socket_patch_core::utils::pipenv::MAJOR_OVERRIDE_ENV; const LOCK: &str = include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile.lock"); -const PIPFILE: &str = - include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile"); +const PIPFILE: &str = include_str!("../../socket-patch-core/tests/fixtures/pipenv/2026.8.0/Pipfile"); /// The upstream and patched bytes of the record's one file, so the venv /// tests can materialize a real `Ready` (upstream) install. @@ -121,9 +120,7 @@ async fn mock_api(server: &MockServer) { .mount(server) .await; Mock::given(method("GET")) - .and(path_regex(format!( - "^/v0/orgs/{ORG}/patches/by-package/.+$" - ))) + .and(path_regex(format!("^/v0/orgs/{ORG}/patches/by-package/.+$"))) .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ "patches": [{ "uuid": UUID, "purl": RECORD_PURL, @@ -372,15 +369,8 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { ); let before: serde_json::Value = serde_json::from_str(LOCK).unwrap(); let after: serde_json::Value = serde_json::from_str(&redirected).unwrap(); - assert_eq!( - after["_meta"], before["_meta"], - "the Pipfile content hash stays" - ); - assert_eq!( - read(&tmp.path().join("Pipfile")), - PIPFILE, - "Pipfile untouched" - ); + assert_eq!(after["_meta"], before["_meta"], "the Pipfile content hash stays"); + assert_eq!(read(&tmp.path().join("Pipfile")), PIPFILE, "Pipfile untouched"); assert_no_ledger(tmp.path()); // Attested from this run's fetched record (keyed by RECORD_PURL, assume // applied) although the base purl the run confirmed differs from the @@ -388,25 +378,13 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { let vex: serde_json::Value = serde_json::from_str(&read(&vex_path)).unwrap(); let statements = vex["statements"].as_array().expect("statements"); assert_eq!(statements.len(), 1, "{vex}"); - assert_eq!( - statements[0]["vulnerability"]["name"].as_str(), - Some(GHSA), - "{vex}" - ); - assert_eq!( - statements[0]["status"].as_str(), - Some("not_affected"), - "{vex}" - ); + assert_eq!(statements[0]["vulnerability"]["name"].as_str(), Some(GHSA), "{vex}"); + assert_eq!(statements[0]["status"].as_str(), Some("not_affected"), "{vex}"); // 2. Idempotent re-scan: no further edits, lock byte-identical. let code = run(hosted_args(tmp.path(), server.uri(), None)).await; assert_eq!(code, 0); - assert_eq!( - read(&lock_path), - redirected, - "re-scan must not touch the lock" - ); + assert_eq!(read(&lock_path), redirected, "re-scan must not touch the lock"); assert_no_ledger(tmp.path()); // Manifest-less VEX over the committed state (the depscan / CI shape). @@ -416,11 +394,7 @@ async fn lock_only_pipenv_project_redirects_attests_rescans_and_rolls_back() { // 3. rollback restores the upstream registry entry. roll_back(tmp.path(), &server).await; - assert_eq!( - read(&lock_path), - LOCK, - "rollback must restore the pristine lock byte for byte" - ); + assert_eq!(read(&lock_path), LOCK, "rollback must restore the pristine lock byte for byte"); } #[tokio::test] @@ -443,10 +417,7 @@ async fn legacy_installer_major_selects_path_references() { "Pipenv 7–11 install `path` references: {redirected}" ); assert!(entry.get("file").is_none(), "{entry}"); - assert_eq!( - entry["hashes"], - serde_json::json!([format!("sha256:{}", sha256())]) - ); + assert_eq!(entry["hashes"], serde_json::json!([format!("sha256:{}", sha256())])); // The legacy `path` reference is discovered just like `file`. manifestless_vex(tmp.path(), "pipenv legacy path", &|p: &Path| { @@ -467,9 +438,7 @@ async fn stale_pipfile_lock_does_not_veto_the_requirements_redirect() { write_project(tmp.path()); // The Pipfile.lock left behind pins a DIFFERENT package; the project // installs from requirements.txt. - let stale = LOCK - .replace("\"urllib3\"", "\"six\"") - .replace("==1.26.18", "==1.16.0"); + let stale = LOCK.replace("\"urllib3\"", "\"six\"").replace("==1.26.18", "==1.16.0"); std::fs::write(tmp.path().join("Pipfile.lock"), &stale).unwrap(); // An unpatched, unhashed sibling makes the file's hash mode derivable, // so rollback can restore the hosted line (a file whose every line is a @@ -497,7 +466,10 @@ async fn stale_pipfile_lock_does_not_veto_the_requirements_redirect() { }); roll_back(tmp.path(), &server).await; - assert_eq!(read(&tmp.path().join("requirements.txt")), REQS); + assert_eq!( + read(&tmp.path().join("requirements.txt")), + REQS + ); assert_eq!(read(&tmp.path().join("Pipfile.lock")), stale); } @@ -518,27 +490,16 @@ async fn warm_venv_with_the_upstream_release_is_not_attested() { // attested and the embedded-VEX contract fails the command. let code = run(hosted_args(tmp.path(), server.uri(), Some(&vex_path))).await; let redirected = read(&lock_path); - assert!( - redirected.contains(HOSTED_URL), - "the lock is still repointed: {redirected}" - ); + assert!(redirected.contains(HOSTED_URL), "the lock is still repointed: {redirected}"); let attested = vex_path .exists() .then(|| serde_json::from_str::(&read(&vex_path)).unwrap()) .and_then(|v| v["statements"].as_array().map(Vec::len)) .unwrap_or(0); - assert_eq!( - attested, 0, - "a stale install must not be attested from the fetched record" - ); + assert_eq!(attested, 0, "a stale install must not be attested from the fetched record"); assert_ne!(code, 0, "nothing to attest fails the embedded-VEX run"); assert_eq!( - std::fs::read( - site_packages(tmp.path()) - .join("urllib3") - .join("response.py") - ) - .unwrap(), + std::fs::read(site_packages(tmp.path()).join("urllib3").join("response.py")).unwrap(), UPSTREAM, "the probe is read-only" ); diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs index e52a6bca2..592d72c14 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs @@ -56,10 +56,7 @@ async fn rollback_hosted(cwd: &Path, server: &MockServer) -> i32 { }))) .mount(server) .await; - std::env::set_var( - "SOCKET_NPM_REGISTRY", - format!("{}/npm-registry", server.uri()), - ); + std::env::set_var("SOCKET_NPM_REGISTRY", format!("{}/npm-registry", server.uri())); let code = rollback::run(RollbackArgs { targets: Vec::new(), common: socket_patch_cli::args::GlobalArgs { @@ -767,11 +764,7 @@ async fn hosted_pnpm_manifestless_vex_from_lockfile_legacy_ledger_and_api() { ..VexRun::offline() }, ); - assert_eq!( - out.code, - Some(0), - "[{lock_name}] legacy ledger, offline: {out}" - ); + assert_eq!(out.code, Some(0), "[{lock_name}] legacy ledger, offline: {out}"); assert_attested(out.doc(), PURL, UUID, Marker::Redirected, vulns); assert_eq!(api.request_count(), seen); diff --git a/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs b/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs index 9c08880b2..8779d2e84 100644 --- a/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs +++ b/crates/socket-patch-cli/tests/repair_vendor_flavors_e2e/vlt.rs @@ -221,10 +221,7 @@ async fn vlt_repair_reports_a_missing_ledger() { lock_bytes, "{lock:?}" ); - assert!( - tmp.path().join(rel()).join("index.js").is_file(), - "{lock:?}" - ); + assert!(tmp.path().join(rel()).join("index.js").is_file(), "{lock:?}"); } } diff --git a/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs b/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs index 31f457502..d4830cbd9 100644 --- a/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs +++ b/crates/socket-patch-cli/tests/rollback/rollback_duality_invariants.rs @@ -533,7 +533,8 @@ fn bare_word_target_stays_identifier_error() { )], false, ); - let manifest_before = std::fs::read(socket.join("manifest.json")).expect("read manifest bytes"); + let manifest_before = + std::fs::read(socket.join("manifest.json")).expect("read manifest bytes"); let (code, stdout, stderr) = run(tmp.path(), &["--offline", "lodash"]); assert_eq!( diff --git a/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs b/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs index 87d4900a3..5fee90f88 100644 --- a/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs +++ b/crates/socket-patch-cli/tests/scan/covgap_ecosystem_dispatch.rs @@ -253,10 +253,7 @@ fn rollback_dispatch_branch_deno() { .unwrap_or_else(|e| panic!("rollback envelope must parse ({e}); stdout={stdout}")); let code = out.status.code().unwrap_or(-1); - assert_eq!( - code, 0, - "rollback --ecosystems=deno: expected exit 0; env={env}" - ); + assert_eq!(code, 0, "rollback --ecosystems=deno: expected exit 0; env={env}"); assert_eq!( env["status"], "success", "rollback --ecosystems=deno: expected success; env={env}" @@ -297,8 +294,7 @@ fn rollback_dispatch_branch_deno() { // The decisive check: the on-disk bytes are restored to ORIGINAL. let restored = std::fs::read(&verify_file).unwrap(); assert_eq!( - restored, - ORIGINAL, + restored, ORIGINAL, "rollback --ecosystems=deno: {} was not restored to its original bytes", verify_file.display() ); diff --git a/crates/socket-patch-cli/tests/scan/scan_invariants.rs b/crates/socket-patch-cli/tests/scan/scan_invariants.rs index f4bb749e1..c3316ee78 100644 --- a/crates/socket-patch-cli/tests/scan/scan_invariants.rs +++ b/crates/socket-patch-cli/tests/scan/scan_invariants.rs @@ -1787,11 +1787,7 @@ async fn report_only_scan_json_redirect_state_keys_on_lock_pins() { serde_json::json!([{ "purl": purl, "uuid": AGENT_WARN_UUID }]), "the lock pin is the record; envelope={v}" ); - assert_eq!( - state["wiringLive"], - serde_json::json!([purl]), - "envelope={v}" - ); + assert_eq!(state["wiringLive"], serde_json::json!([purl]), "envelope={v}"); // No pin, no ledger: the key must stay absent (additive contract). let clean = tempfile::tempdir().expect("tempdir"); @@ -1836,8 +1832,7 @@ async fn report_only_scan_json_ignores_a_stale_pre_v5_ledger_record() { integrity sha512-orig==\n", ) .unwrap(); - let ledger_before = - std::fs::read(tmp.path().join(".socket/vendor/redirect-state.json")).unwrap(); + let ledger_before = std::fs::read(tmp.path().join(".socket/vendor/redirect-state.json")).unwrap(); for extra in [&["--prune"][..], &["--mode", "agent", "--dry-run"][..]] { let (code, stdout, stderr) = run_scan(tmp.path(), &mock.uri(), extra); @@ -2058,7 +2053,10 @@ async fn scan_ignores_a_malformed_pre_v5_ledger() { "{extra:?}: a pre-v5 ledger is never read, so never reported: {stderr}" ); let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON"); - assert!(v.get("redirectState").is_none(), "{extra:?}: envelope={v}"); + assert!( + v.get("redirectState").is_none(), + "{extra:?}: envelope={v}" + ); assert_eq!( std::fs::read(vendor_dir.join("redirect-state.json")).unwrap(), b"{ torn ledger", @@ -2092,11 +2090,7 @@ async fn ecosystems_filter_keeps_records_but_not_wiring_live() { /*with_record=*/ true, ); - let (code, stdout, stderr) = run_scan( - tmp.path(), - &mock.uri(), - &["--mode", "agent", "--dry-run", "--ecosystems", "pypi"], - ); + let (code, stdout, stderr) = run_scan(tmp.path(), &mock.uri(), &["--mode", "agent", "--dry-run", "--ecosystems", "pypi"]); assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON"); let state = &v["redirectState"]; diff --git a/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs b/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs index 64ea1197c..8ad94e551 100644 --- a/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs +++ b/crates/socket-patch-cli/tests/scan/scan_paths_e2e.rs @@ -216,11 +216,7 @@ async fn paths_scope_narrows_the_query() { let tmp = tempfile::tempdir().unwrap(); write_two_subtree_project(tmp.path()); - let (code, stdout, stderr) = run_scan( - tmp.path(), - &server.uri(), - &["packages/app", "--mode", "agent", "--dry-run"], - ); + let (code, stdout, stderr) = run_scan(tmp.path(), &server.uri(), &["packages/app", "--mode", "agent", "--dry-run"]); assert_eq!( code, 0, "scoped scan must exit 0; stdout={stdout}; stderr={stderr}" @@ -481,11 +477,7 @@ async fn supplements_excluded_with_warning() { // purl reaches the API. let scoped_server = MockServer::start().await; mock_batch_empty(&scoped_server).await; - let (code, stdout, stderr) = run_scan( - tmp.path(), - &scoped_server.uri(), - &["packages/app", "--mode", "agent", "--dry-run"], - ); + let (code, stdout, stderr) = run_scan(tmp.path(), &scoped_server.uri(), &["packages/app", "--mode", "agent", "--dry-run"]); assert_eq!( code, 0, "scoped scan must exit 0; stdout={stdout}; stderr={stderr}" diff --git a/crates/socket-patch-cli/tests/update/covgap_commands_update.rs b/crates/socket-patch-cli/tests/update/covgap_commands_update.rs index 16836e614..b2d75aafc 100644 --- a/crates/socket-patch-cli/tests/update/covgap_commands_update.rs +++ b/crates/socket-patch-cli/tests/update/covgap_commands_update.rs @@ -268,8 +268,9 @@ mod pty { let mut child = pair.slave.spawn_command(cmd).expect("spawn in PTY"); drop(pair.slave); - let reader_handle = - crate::pty_io::PtyOutput::spawn(pair.master.try_clone_reader().expect("clone reader")); + let reader_handle = crate::pty_io::PtyOutput::spawn( + pair.master.try_clone_reader().expect("clone reader"), + ); let mut killer = child.clone_killer(); std::thread::spawn(move || { @@ -337,8 +338,7 @@ mod pty { "a declined update exits 1 (codebase convention); got: {output}" ); assert!( - !output.contains("Updated socket-patch") - && !output.contains("Reinstalled socket-patch"), + !output.contains("Updated socket-patch") && !output.contains("Reinstalled socket-patch"), "a declined update must not report a swap; got: {output}" ); diff --git a/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs b/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs index a012b53d7..04ce2881e 100644 --- a/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs +++ b/crates/socket-patch-cli/tests/yarn_berry_common/mod.rs @@ -660,9 +660,7 @@ pub fn run_manifestless_vex_matrix(flow: &BerryVexFlow<'_>) -> Vec { crate::vex_e2e_common::assert_no_hosted_ledger(&fresh, "manifest-deleted"); } else { assert!( - fresh - .join(socket_patch_core::vendor::VENDOR_STATE_REL) - .is_file(), + fresh.join(socket_patch_core::vendor::VENDOR_STATE_REL).is_file(), "manifest-deleted: the vendored flow must have left its .socket/vendor ledger" ); } diff --git a/crates/socket-patch-core/src/api/ranking.rs b/crates/socket-patch-core/src/api/ranking.rs index 58ca27078..949931782 100644 --- a/crates/socket-patch-core/src/api/ranking.rs +++ b/crates/socket-patch-core/src/api/ranking.rs @@ -164,14 +164,8 @@ pub fn batch_supersedes(candidate: &BatchPatchInfo, applied: &BatchPatchInfo) -> /// classify a recorded patch (ALREADY vs UPGRADE) and to report /// `updates[]`, on the same records that pick the patch, so selection, /// classification and reporting cannot disagree. -pub fn search_result_supersedes( - candidate: &PatchSearchResult, - recorded: &PatchSearchResult, -) -> bool { - key_supersedes( - &rank_search_result(candidate), - &rank_search_result(recorded), - ) +pub fn search_result_supersedes(candidate: &PatchSearchResult, recorded: &PatchSearchResult) -> bool { + key_supersedes(&rank_search_result(candidate), &rank_search_result(recorded)) } fn key_supersedes(c: &RankKey<'_>, a: &RankKey<'_>) -> bool { @@ -377,7 +371,12 @@ mod tests { "2020-01-01T00:00:00Z", &["critical", "high"] ), - search_multi("z_new_low", "free", "2026-08-01T00:00:00Z", &["low", "low"]), + search_multi( + "z_new_low", + "free", + "2026-08-01T00:00:00Z", + &["low", "low"] + ), ]), "a_old_critical" ); diff --git a/crates/socket-patch-core/src/crawlers/npm_crawler.rs b/crates/socket-patch-core/src/crawlers/npm_crawler.rs index 9f7be693a..922578491 100644 --- a/crates/socket-patch-core/src/crawlers/npm_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/npm_crawler.rs @@ -958,11 +958,7 @@ impl NpmCrawler { /// Inside a store entry (`store_entry`) a link is a dependency edge into /// a sibling entry, whose own visit records that copy, so only a real /// directory there matches. - fn visit_resolver_dir( - nm_path: PathBuf, - store_entry: bool, - pending: &[Target], - ) -> ResolverVisit { + fn visit_resolver_dir(nm_path: PathBuf, store_entry: bool, pending: &[Target]) -> ResolverVisit { let listing = list_dir_sync(&nm_path); let probe_filter = ProbeFilter::new(&listing); let matched = pending diff --git a/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs b/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs index 2d6e225c1..d71a02127 100644 --- a/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs +++ b/crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs @@ -9,9 +9,9 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use super::{ - build_npm_purl, is_legacy_pnpm_store_dir_name, is_safe_npm_component, parse_package_name, - read_package_json, NpmCrawler, StoreEntry, Target, NESTED_STORE_MAX_DEPTH, - NESTED_STORE_MAX_DIRS, SKIP_DIRS, VLT_STORE_NAME, + build_npm_purl, is_legacy_pnpm_store_dir_name, + is_safe_npm_component, parse_package_name, read_package_json, NpmCrawler, StoreEntry, + Target, NESTED_STORE_MAX_DEPTH, NESTED_STORE_MAX_DIRS, SKIP_DIRS, VLT_STORE_NAME, }; use crate::crawlers::types::{CrawledPackage, CrawlerOptions}; use crate::utils::fs::is_dir; diff --git a/crates/socket-patch-core/src/crawlers/python_crawler.rs b/crates/socket-patch-core/src/crawlers/python_crawler.rs index 8cb26a0d0..621e0ce50 100644 --- a/crates/socket-patch-core/src/crawlers/python_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/python_crawler.rs @@ -1087,8 +1087,10 @@ pub async fn get_global_python_site_packages() -> Vec { } // 1. Ask Python for site-packages (subprocesses: on the blocking pool) - let site_output = - run_blocking(|| SITE_QUERY_MEMO.get_or_run(site_query_key(), run_site_query)).await; + let site_output = run_blocking(|| { + SITE_QUERY_MEMO.get_or_run(site_query_key(), run_site_query) + }) + .await; if let Some(stdout) = site_output { for p in parse_python_site_packages_output(&stdout) { add_path(p, &mut seen, &mut results); diff --git a/crates/socket-patch-core/src/formats/cargo/mod.rs b/crates/socket-patch-core/src/formats/cargo/mod.rs index 3e9cb9c5b..58b4dc2bc 100644 --- a/crates/socket-patch-core/src/formats/cargo/mod.rs +++ b/crates/socket-patch-core/src/formats/cargo/mod.rs @@ -34,6 +34,7 @@ use crate::utils::purl::simple_purl; use crate::vendor::cargo_tag; use crate::vendor::lock_inventory::{LockIntegrity, LockfileEntry, SourceKind}; + // ── entry model ── /// The `[metadata]` key a v1 lock files `name`+`version`'s checksum under. @@ -331,6 +332,7 @@ pub(crate) fn parse_ref(spelled: &str) -> (&str, Option<&str>, Option<&str>) { (name, version, source) } + // ── the model ── /// One `Cargo.lock`, parsed once (see the module docs). @@ -498,13 +500,7 @@ impl CargoLock { uuid: &str, copy_tagged: bool, ) -> CopyClaim<'_> { - vendored_copy_claim( - &self.packages, - &self.unused, - name, - version, - uuid, - copy_tagged, - ) + vendored_copy_claim(&self.packages, &self.unused, name, version, uuid, copy_tagged) } } + diff --git a/crates/socket-patch-core/src/formats/composer/hosted.rs b/crates/socket-patch-core/src/formats/composer/hosted.rs index d760cbcd0..9564e65d2 100644 --- a/crates/socket-patch-core/src/formats/composer/hosted.rs +++ b/crates/socket-patch-core/src/formats/composer/hosted.rs @@ -10,11 +10,11 @@ use std::sync::LazyLock; use regex::Regex; +use crate::utils::composer_version::composer_versions_equivalent; use super::source as composer_source; use crate::patch::redirect::{ artifact_url_present, full_name, DepOverride, RewriteResult, RewriteWarning, }; -use crate::utils::composer_version::composer_versions_equivalent; /// Byte offset of the `}` closing the JSON object that CONTAINS `from`, which /// must be a position inside that object. Brace counting skips string literals, diff --git a/crates/socket-patch-core/src/formats/composer/mod.rs b/crates/socket-patch-core/src/formats/composer/mod.rs index d45156ceb..8efa3c178 100644 --- a/crates/socket-patch-core/src/formats/composer/mod.rs +++ b/crates/socket-patch-core/src/formats/composer/mod.rs @@ -22,6 +22,7 @@ use crate::utils::digest::sha1_hex; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry, SourceKind}; use crate::vendor::path::{parse_vendor_path, VendorPathParts}; + // ── entry model ── /// One entry of a parsed `composer.lock` (see [`composer_lock_packages`]). @@ -106,6 +107,7 @@ pub(crate) fn composer_lock_packages(doc: &Value) -> Vec out } + // ── the model ── /// One `composer.lock`, read once (see the module docs). @@ -175,3 +177,4 @@ impl<'a> ComposerLock<'a> { out } } + diff --git a/crates/socket-patch-core/src/formats/gem/hosted.rs b/crates/socket-patch-core/src/formats/gem/hosted.rs index 5dd4dc8b3..0416c3594 100644 --- a/crates/socket-patch-core/src/formats/gem/hosted.rs +++ b/crates/socket-patch-core/src/formats/gem/hosted.rs @@ -304,3 +304,4 @@ pub(crate) fn checksum_entry_span(lock: &str, name: &str, version: &str) -> Opti } None } + diff --git a/crates/socket-patch-core/src/formats/gem/mod.rs b/crates/socket-patch-core/src/formats/gem/mod.rs index 4719f804f..a8054fc12 100644 --- a/crates/socket-patch-core/src/formats/gem/mod.rs +++ b/crates/socket-patch-core/src/formats/gem/mod.rs @@ -26,6 +26,7 @@ use crate::utils::digest::sha256_hex; use crate::utils::purl::simple_purl; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry, SourceKind}; + /// The Bundler lockfiles, legacy spelling first: `Gemfile.lock` and /// `gems.locked` (what bundler writes instead when the manifest is /// `gems.rb`). @@ -202,6 +203,7 @@ impl<'t> GemfileLock<'t> { } } + /// Where a rubygems-compatible registry at `base` (no trailing `/`) serves /// `name`-`version`'s `.gem` — the inventory's resolved URL and ledger /// recovery's fetch URL. `None` for a non-http(s) base. diff --git a/crates/socket-patch-core/src/formats/mod.rs b/crates/socket-patch-core/src/formats/mod.rs index 31ed9059e..f3ea013a3 100644 --- a/crates/socket-patch-core/src/formats/mod.rs +++ b/crates/socket-patch-core/src/formats/mod.rs @@ -26,13 +26,13 @@ //! [`registry()`] is the one table of which project files carry a lock or //! its wiring, and in which roles. -pub(crate) mod bun; pub mod cargo; pub mod composer; pub mod gem; pub(crate) mod maven; pub(crate) mod nuget; pub mod pnpm; +pub(crate) mod bun; pub mod registry; pub mod yarn; @@ -81,11 +81,7 @@ mod architecture_tests { .filter(|l| !l.trim_start().starts_with("//")) .collect::>() .join("\n"); - let used: Vec<&str> = IMPURE - .iter() - .copied() - .filter(|n| code.contains(n)) - .collect(); + let used: Vec<&str> = IMPURE.iter().copied().filter(|n| code.contains(n)).collect(); assert!( used.is_empty(), "{}: a format model uses {used:?} — models are pure (module docs)", diff --git a/crates/socket-patch-core/src/formats/pnpm/mod.rs b/crates/socket-patch-core/src/formats/pnpm/mod.rs index 6a8ce98da..1d495d69a 100644 --- a/crates/socket-patch-core/src/formats/pnpm/mod.rs +++ b/crates/socket-patch-core/src/formats/pnpm/mod.rs @@ -38,6 +38,7 @@ use crate::utils::digest::is_sri_pin; use crate::vendor::lock_inventory::{http_url, LockIntegrity, LockfileEntry}; use crate::vendor::path::parse_vendor_path; + // ── entry model ── /// One `packages:` entry of a pnpm lock, read with the entry grammar @@ -281,10 +282,7 @@ fn lock_versions(text: &str) -> impl Iterator, u32)> + '_ { let value = rest.trim().trim_matches(|c| c == '\'' || c == '"'); let mut parts = value.split('.'); let major = parts.next().and_then(|m| m.parse::().ok()); - let minor = parts - .next() - .and_then(|m| m.parse::().ok()) - .unwrap_or(0); + let minor = parts.next().and_then(|m| m.parse::().ok()).unwrap_or(0); Some((major, minor)) }) } @@ -304,8 +302,7 @@ pub fn lock_version_major(text: &str) -> Option { /// rejects it): a `shrinkwrapVersion` lock (pnpm 1–2) or lockfileVersion /// 5.0–5.2 (pnpm 3–5). Later locks never get the `--store` note. pub fn may_need_store_flag(text: &str) -> bool { - text.lines() - .any(|line| line.starts_with("shrinkwrapVersion:")) + text.lines().any(|line| line.starts_with("shrinkwrapVersion:")) || lock_versions(text).any(|(major, minor)| major == Some(5) && minor <= 2) } @@ -493,9 +490,7 @@ pub(crate) fn vendored_npm_uuids(text: &str) -> HashSet { if !in_section { continue; } - if let Some(uuid) = - lines::parse_key_line(line, 2).and_then(|(key, _, _)| vendored_npm_uuid(key)) - { + if let Some(uuid) = lines::parse_key_line(line, 2).and_then(|(key, _, _)| vendored_npm_uuid(key)) { out.insert(uuid); } } @@ -512,52 +507,17 @@ mod tests { fn resolves_reads_every_key_generation_boundary_anchored() { let lock = |keys: &str| format!("lockfileVersion: '9.0'\n\npackages:\n\n{keys}"); let yes = [ - ( - " left-pad@1.3.0:\n resolution: {integrity: sha512-x}\n", - "left-pad", - "1.3.0", - ), - ( - " /left-pad@1.3.0:\n resolution: {}\n", - "left-pad", - "1.3.0", - ), - ( - " /left-pad/1.3.0:\n resolution: {}\n", - "left-pad", - "1.3.0", - ), - ( - " 'left-pad@1.3.0(react@18.0.0)':\n dev: false\n", - "left-pad", - "1.3.0", - ), - ( - " /left-pad/1.3.0_react@18.0.0:\n dev: false\n", - "left-pad", - "1.3.0", - ), - ( - " '@scope/name@1.0.0':\n dev: false\n", - "@scope/name", - "1.0.0", - ), - ( - " /@scope/name@1.0.0:\n dev: false\n", - "@scope/name", - "1.0.0", - ), - ( - " /@scope/name/1.0.0:\n dev: false\n", - "@scope/name", - "1.0.0", - ), + (" left-pad@1.3.0:\n resolution: {integrity: sha512-x}\n", "left-pad", "1.3.0"), + (" /left-pad@1.3.0:\n resolution: {}\n", "left-pad", "1.3.0"), + (" /left-pad/1.3.0:\n resolution: {}\n", "left-pad", "1.3.0"), + (" 'left-pad@1.3.0(react@18.0.0)':\n dev: false\n", "left-pad", "1.3.0"), + (" /left-pad/1.3.0_react@18.0.0:\n dev: false\n", "left-pad", "1.3.0"), + (" '@scope/name@1.0.0':\n dev: false\n", "@scope/name", "1.0.0"), + (" /@scope/name@1.0.0:\n dev: false\n", "@scope/name", "1.0.0"), + (" /@scope/name/1.0.0:\n dev: false\n", "@scope/name", "1.0.0"), ]; for (keys, name, version) in yes { - assert!( - PnpmLock::parse(&lock(keys)).resolves(name, version), - "{keys}" - ); + assert!(PnpmLock::parse(&lock(keys)).resolves(name, version), "{keys}"); } let no = [ (" left-pad@1.3.0-beta.1:\n dev: false\n", "left-pad", "1.3.0"), @@ -573,10 +533,7 @@ mod tests { ), ]; for (keys, name, version) in no { - assert!( - !PnpmLock::parse(&lock(keys)).resolves(name, version), - "{keys}" - ); + assert!(!PnpmLock::parse(&lock(keys)).resolves(name, version), "{keys}"); } // Keys outside `packages:` (importers, overrides) resolve nothing. let importers = "lockfileVersion: '9.0'\n\nimporters:\n\n left-pad@1.3.0:\n x: y\n"; @@ -599,10 +556,7 @@ mod tests { let other = "22222222-2222-4222-8222-222222222222"; assert!(!PnpmLock::parse(text).vendored_in_use(other)); let crlf = text.replace('\n', "\r\n"); - assert!( - PnpmLock::parse(&crlf).vendored_in_use(UUID), - "CRLF reads like LF" - ); + assert!(PnpmLock::parse(&crlf).vendored_in_use(UUID), "CRLF reads like LF"); } // An overrides declaration alone is not usage. let overrides = format!( diff --git a/crates/socket-patch-core/src/formats/registry.rs b/crates/socket-patch-core/src/formats/registry.rs index e828d3de8..a0c703ee1 100644 --- a/crates/socket-patch-core/src/formats/registry.rs +++ b/crates/socket-patch-core/src/formats/registry.rs @@ -67,11 +67,7 @@ const fn row(path: &'static str, ecosystem: &'static str, roles: u8) -> FormatFi const REGISTRY: &[FormatFile] = &[ // ── npm family ── row("package-lock.json", "npm", HOSTED | VENDORED | PROBE | ROOT), - row( - "npm-shrinkwrap.json", - "npm", - HOSTED | VENDORED | PROBE | ROOT, - ), + row("npm-shrinkwrap.json", "npm", HOSTED | VENDORED | PROBE | ROOT), row( "pnpm-lock.yaml", "npm", @@ -118,11 +114,7 @@ const REGISTRY: &[FormatFile] = &[ row(".cargo/config", "cargo", HOSTED | VENDORED | PROBE), // ── composer ── row("composer.json", "composer", VENDORED), - row( - "composer.lock", - "composer", - HOSTED | VENDORED | PROBE | ROOT, - ), + row("composer.lock", "composer", HOSTED | VENDORED | PROBE | ROOT), // ── nuget ── row("nuget.config", "nuget", HOSTED | PROBE), row("NuGet.config", "nuget", HOSTED | PROBE), @@ -209,11 +201,7 @@ mod tests { paths.dedup(); assert_eq!(before, paths.len(), "duplicate registry path"); for f in REGISTRY.iter().filter(|f| f.has(ROOT)) { - assert!( - !f.path.contains('/'), - "{}: a root marker is a basename", - f.path - ); + assert!(!f.path.contains('/'), "{}: a root marker is a basename", f.path); } } diff --git a/crates/socket-patch-core/src/formats/yarn/mod.rs b/crates/socket-patch-core/src/formats/yarn/mod.rs index 64dbd6d9a..c7f51d5b2 100644 --- a/crates/socket-patch-core/src/formats/yarn/mod.rs +++ b/crates/socket-patch-core/src/formats/yarn/mod.rs @@ -62,10 +62,7 @@ mod tests { #[test] fn sniff_prefers_berry_and_skips_a_bom() { - assert_eq!( - sniff_grammar("__metadata:\n version: 8\n"), - Some(YarnLockGrammar::Berry) - ); + assert_eq!(sniff_grammar("__metadata:\n version: 8\n"), Some(YarnLockGrammar::Berry)); assert_eq!( sniff_grammar("\u{feff}# yarn lockfile v1\r\n"), Some(YarnLockGrammar::Classic) diff --git a/crates/socket-patch-core/src/hosted/guidance.rs b/crates/socket-patch-core/src/hosted/guidance.rs index 3f9cf2355..8473ff64d 100644 --- a/crates/socket-patch-core/src/hosted/guidance.rs +++ b/crates/socket-patch-core/src/hosted/guidance.rs @@ -155,7 +155,9 @@ pub fn pnpm_lock_carries_hosted_redirect( pub fn npm_lock_url_needles(artifact_url: &str) -> Vec { let mut needles: Vec = crate::patch::redirect::artifact_url_spellings(artifact_url).into(); - needles.push(crate::utils::uri::encode_uri_component(artifact_url)); + needles.push(crate::utils::uri::encode_uri_component( + artifact_url, + )); needles } @@ -279,7 +281,11 @@ fn npm_allow_remote_preamble(hosts: &[&str]) -> String { /// The auto-config variant: `allow-remote=all` was (or, on `--dry-run`, /// would be) written to the project `.npmrc`, so installs need no flags. -pub fn npm_allow_remote_configured_detail(hosts: &[&str], created: bool, dry_run: bool) -> String { +pub fn npm_allow_remote_configured_detail( + hosts: &[&str], + created: bool, + dry_run: bool, +) -> String { let how = match (created, dry_run) { (true, false) => "`allow-remote=all` was written to a new", (false, false) => "`allow-remote=all` was appended to the existing", diff --git a/crates/socket-patch-core/src/hosted/memory/discover.rs b/crates/socket-patch-core/src/hosted/memory/discover.rs index 9fc5ebfd9..6e5b36e09 100644 --- a/crates/socket-patch-core/src/hosted/memory/discover.rs +++ b/crates/socket-patch-core/src/hosted/memory/discover.rs @@ -14,7 +14,9 @@ use std::time::Duration; use crate::api::client::{ApiError, ApiFuture, PatchApi}; use crate::api::ranking::cmp_search_results; -use crate::api::types::{BatchPackagePatches, PackageVendorResult, PatchResponse, SearchResponse}; +use crate::api::types::{ + BatchPackagePatches, PackageVendorResult, PatchResponse, SearchResponse, +}; use crate::utils::purl::{normalize_purl, strip_purl_qualifiers}; use super::types::MAX_REFERENCE_BATCH; diff --git a/crates/socket-patch-core/src/hosted/memory/limits.rs b/crates/socket-patch-core/src/hosted/memory/limits.rs index da4dd695d..9f895d6c9 100644 --- a/crates/socket-patch-core/src/hosted/memory/limits.rs +++ b/crates/socket-patch-core/src/hosted/memory/limits.rs @@ -42,7 +42,12 @@ impl ResolvedOptions { /// as `flag`), then the socket.yml `patches.maxNewPatches`, then /// unlimited; `maxNewPatchesCap` only tightens it. pub(crate) fn max_new(&self, file: Option) -> crate::rollout::MaxNew { - crate::rollout::resolve_max_new(self.max_new_patches, None, file, self.max_new_patches_cap) + crate::rollout::resolve_max_new( + self.max_new_patches, + None, + file, + self.max_new_patches_cap, + ) } } @@ -74,9 +79,8 @@ pub(crate) fn resolve_options(options: &HostedScanOptions) -> Result None, Some(value) => Some(( - crate::policy::parse_min_severity(value).map_err(|e| { - EngineError::invalid("invalid_min_severity", format!("minSeverity: {e}")) - })?, + crate::policy::parse_min_severity(value) + .map_err(|e| EngineError::invalid("invalid_min_severity", format!("minSeverity: {e}")))?, crate::policy::OverrideSource::Flag, )), }; diff --git a/crates/socket-patch-core/src/hosted/memory/mod.rs b/crates/socket-patch-core/src/hosted/memory/mod.rs index b649621c1..e11aa036d 100644 --- a/crates/socket-patch-core/src/hosted/memory/mod.rs +++ b/crates/socket-patch-core/src/hosted/memory/mod.rs @@ -58,17 +58,17 @@ pub use limits::SessionBuilder; pub use select::{candidate_files, safe_repo_path, select_paths}; pub use types::*; -use crate::policy::{ - canon, patch_severity_order, policy_block, FilterReason, FilteredEntry, MemoryPolicyFs, - PolicyError, PolicySource, Root, RootFile, SelectionPolicy, PATCHES_DISABLED, - POLICY_FILE_NAMES, -}; use crate::rollout::stage::{ classify, lookup_incomplete, mentioned_uuids, offers_from_results, Offers, RecordedIndex, Row, - Stage, ROLLOUT_DEFERRED, + Stage, + ROLLOUT_DEFERRED, }; use discover::Provider; use stages::{Planned, RewriteRefused, Rewritten, StageOptions}; +use crate::policy::{ + canon, patch_severity_order, policy_block, FilterReason, FilteredEntry, MemoryPolicyFs, PolicyError, + PolicySource, Root, RootFile, SelectionPolicy, PATCHES_DISABLED, POLICY_FILE_NAMES, +}; /// `"+"`; the sha comes from the /// `SOCKET_PATCH_GIT_SHA` build-time variable. @@ -419,8 +419,11 @@ fn memory_recorded( .map(|p| (purl.clone(), p.uuid.clone())) }) .collect(); - let merged = - crate::ledgers::merge_ledger_records_for_updates(manifest.as_ref(), vendor.as_ref(), &pins); + let merged = crate::ledgers::merge_ledger_records_for_updates( + manifest.as_ref(), + vendor.as_ref(), + &pins, + ); RecordedIndex::new(merged.as_deref(), &pins) } @@ -447,20 +450,13 @@ async fn engine( // The repo's socket.yml policy, before any root is processed: a file // that cannot be honored fails the whole session closed. - let (policy, policy_warnings) = match SelectionPolicy::load( - &memory_policy_fs(&files, &options.policy_paths), - &options.policy_overrides, - ) { - Ok(loaded) => loaded, - Err(error) => { - return Ok(policy_error_output( - &error, - warnings, - files_input, - bytes_input, - )); - } - }; + let (policy, policy_warnings) = + match SelectionPolicy::load(&memory_policy_fs(&files, &options.policy_paths), &options.policy_overrides) { + Ok(loaded) => loaded, + Err(error) => { + return Ok(policy_error_output(&error, warnings, files_input, bytes_input)); + } + }; // Path selection chose which files to send by the policy it read; a // different policy here would judge roots it never fetched. let read = match policy.source() { @@ -469,27 +465,16 @@ async fn engine( }; // Selection returns no digest when it bypassed the file, so a digest // with a bypassed session means the two sides disagree. - let expected = if options.policy_overrides.bypass { - None - } else { - read.map(|(_, sha)| sha) - }; + let expected = if options.policy_overrides.bypass { None } else { read.map(|(_, sha)| sha) }; if expected != options.policy_sha256.as_deref() { let error = PolicyError::Invalid { - file: read - .map_or(POLICY_FILE_NAMES[0], |(path, _)| path) - .to_string(), + file: read.map_or(POLICY_FILE_NAMES[0], |(path, _)| path).to_string(), key: String::new(), message: "the policy content differs from the one path selection read: pass \ selectHostedScanPaths' policySha256 and stream the same text" .to_string(), }; - return Ok(policy_error_output( - &error, - warnings, - files_input, - bytes_input, - )); + return Ok(policy_error_output(&error, warnings, files_input, bytes_input)); } for w in policy_warnings { warnings.push(EngineWarning::new(w.code, w.detail, None)); @@ -737,25 +722,23 @@ async fn engine( // the tree's manifest and vendor ledger, and the hosted pins its // lockfiles name. ALREADY rows carry the recorded uuid, so a re-scan // re-confirms a pin instead of swapping it. - let mut stage = Stage::new( - options.max_new(policy.max_new_patches()), - None, - std::path::Path::new(""), - ); + let mut stage = Stage::new(options.max_new(policy.max_new_patches()), None, std::path::Path::new("")); // A root whose every lookup failed hides packages that could have been // NEW: a capped run then admits none anywhere (§5.2). - stage.incomplete |= states.iter().any(|s| { - s.error - .as_ref() - .is_some_and(|e| e.code == "patch_lookup_failed") - }); + stage.incomplete |= states + .iter() + .any(|s| s.error.as_ref().is_some_and(|e| e.code == "patch_lookup_failed")); let roots_by_path: Vec = states.iter().map(|s| s.root.clone()).collect(); for state in states.iter_mut().filter(|s| s.error.is_none()) { let Some(project) = state.project.as_ref() else { continue; }; let recorded = memory_recorded(project, &state.root, &roots_by_path, &state.offers); - stage.incomplete |= lookup_incomplete(&recorded, &state.failed_details, batch_failed); + stage.incomplete |= lookup_incomplete( + &recorded, + &state.failed_details, + batch_failed, + ); let mut rows = classify(&state.offers, &recorded, &state.root); for row in &mut rows { row.candidate.in_flight = options.in_flight.contains(&row.candidate.base_purl); @@ -876,11 +859,8 @@ async fn engine( unknown_roots.contains(&row.candidate.project) || confirmed.contains(&(row.candidate.project.clone(), row.writer.uuid.clone())) }); - let deferred_rows: Vec<(crate::rollout::Candidate, u32)> = stage - .plan - .as_ref() - .map(|p| p.deferred.clone()) - .unwrap_or_default(); + let deferred_rows: Vec<(crate::rollout::Candidate, u32)> = + stage.plan.as_ref().map(|p| p.deferred.clone()).unwrap_or_default(); if !deferred_rows.is_empty() { let root_index: BTreeMap = states .iter() @@ -1132,10 +1112,7 @@ fn select_with_policy( let mut by_purl: BTreeMap> = BTreeMap::new(); for (patch, reason) in dropped { if !chosen.contains(patch.purl.as_str()) { - by_purl - .entry(patch.purl.clone()) - .or_default() - .push((patch, reason)); + by_purl.entry(patch.purl.clone()).or_default().push((patch, reason)); } } for (purl, mut group) in by_purl { diff --git a/crates/socket-patch-core/src/hosted/memory/roots.rs b/crates/socket-patch-core/src/hosted/memory/roots.rs index cc4ea8c41..84e0dd8d7 100644 --- a/crates/socket-patch-core/src/hosted/memory/roots.rs +++ b/crates/socket-patch-core/src/hosted/memory/roots.rs @@ -40,23 +40,17 @@ pub const UNSUPPORTED_MARKERS: [(&str, &[&str]); 2] = [ /// trees, VCS and tool state, and vendored dependencies. Structural, so no /// policy can negate them. (Test and fixture trees are the socket.yml /// policy's overridable built-in ignores.) -pub(crate) const EXCLUDED_ROOT_SEGMENTS: [&str; 5] = - ["node_modules", ".git", ".socket", ".yarn", "vendor"]; +pub(crate) const EXCLUDED_ROOT_SEGMENTS: [&str; 5] = ["node_modules", ".git", ".socket", ".yarn", "vendor"]; /// The marker basenames of `root` among `paths` (the files the policy's /// path filters test for that root). -pub(crate) fn root_markers<'a>( - root: &str, - paths: impl IntoIterator, -) -> Vec { +pub(crate) fn root_markers<'a>(root: &str, paths: impl IntoIterator) -> Vec { let mut out: Vec = paths .into_iter() .filter_map(|path| { let (dir, base) = split_path(path); let marker = marker_ecosystem(base).is_some() - || UNSUPPORTED_MARKERS - .iter() - .any(|(_, names)| names.contains(&base)); + || UNSUPPORTED_MARKERS.iter().any(|(_, names)| names.contains(&base)); (dir == root && marker).then(|| base.to_string()) }) .collect(); @@ -217,15 +211,7 @@ mod tests { #[test] fn root_markers_name_every_marker_of_the_root_only() { assert_eq!( - root_markers( - "a", - [ - "a/yarn.lock", - "a/package.json", - "a/b/yarn.lock", - "a/pom.xml" - ] - ), + root_markers("a", ["a/yarn.lock", "a/package.json", "a/b/yarn.lock", "a/pom.xml"]), vec!["pom.xml".to_string(), "yarn.lock".to_string()] ); } diff --git a/crates/socket-patch-core/src/hosted/memory/select.rs b/crates/socket-patch-core/src/hosted/memory/select.rs index 5d84c8c7c..fb4dfc832 100644 --- a/crates/socket-patch-core/src/hosted/memory/select.rs +++ b/crates/socket-patch-core/src/hosted/memory/select.rs @@ -15,8 +15,8 @@ use crate::patch::redirect::npmrc::NPMRC_REL; use crate::utils::python_lock::is_python_lock_name; use crate::policy::{ - MemoryPolicyFs, PolicyOverrides, PolicySource, Root, RootFile, SelectionPolicy, - POLICY_FILE_NAMES, SOCKET_YML_INVALID, + MemoryPolicyFs, PolicyOverrides, PolicySource, Root, RootFile, SelectionPolicy, POLICY_FILE_NAMES, + SOCKET_YML_INVALID, }; use super::roots::{ @@ -46,12 +46,7 @@ pub(crate) const VENDOR_STATE_REL: &str = ".socket/vendor/state.json"; pub(crate) const MANIFEST_REL: &str = ".socket/manifest.json"; /// Root-relative text files read beyond `REDIRECT_CANDIDATE_FILES`. -const EXTRA_TEXT_FILES: [&str; 4] = [ - PNPM_WORKSPACE_REL, - NPMRC_REL, - VENDOR_STATE_REL, - MANIFEST_REL, -]; +const EXTRA_TEXT_FILES: [&str; 4] = [PNPM_WORKSPACE_REL, NPMRC_REL, VENDOR_STATE_REL, MANIFEST_REL]; /// The one directory name the disk Cargo member walk never enters (it /// follows `members`, `exclude`, path dependencies and `[patch]` paths @@ -180,10 +175,7 @@ fn classify(rel: &str, root_files: &BTreeSet<&str>) -> Option { /// The listed root policy files with the text the caller fetched first. A /// listed file with no text (not passed, `missing`, or a symlink) is present /// without content, so loading it fails closed. -fn selection_policy_fs( - blobs: &BTreeMap, - supplied: &[PolicyFileInput], -) -> MemoryPolicyFs { +fn selection_policy_fs(blobs: &BTreeMap, supplied: &[PolicyFileInput]) -> MemoryPolicyFs { let mut fs = MemoryPolicyFs::default(); for name in POLICY_FILE_NAMES { let Some(&symlink) = blobs.get(name) else { @@ -209,10 +201,7 @@ fn selection_policy( options: &SelectOptions, ) -> Result { let supplied = options.policy_files.as_deref().unwrap_or_default(); - if let Some(bad) = supplied - .iter() - .find(|f| !POLICY_FILE_NAMES.contains(&f.path.as_str())) - { + if let Some(bad) = supplied.iter().find(|f| !POLICY_FILE_NAMES.contains(&f.path.as_str())) { return Err(PolicyErrorInfo { code: SOCKET_YML_INVALID.to_string(), detail: format!( diff --git a/crates/socket-patch-core/src/hosted/memory/types.rs b/crates/socket-patch-core/src/hosted/memory/types.rs index fa81876e8..2a11daed7 100644 --- a/crates/socket-patch-core/src/hosted/memory/types.rs +++ b/crates/socket-patch-core/src/hosted/memory/types.rs @@ -171,9 +171,7 @@ impl<'de> Deserialize<'de> for MaxNewPatchesOption { if v == "none" { Ok(MaxNewPatchesOption(None)) } else { - Err(E::custom(format!( - "maxNewPatches must be a number or \"none\", not `{v}`" - ))) + Err(E::custom(format!("maxNewPatches must be a number or \"none\", not `{v}`"))) } } } diff --git a/crates/socket-patch-core/src/ledgers.rs b/crates/socket-patch-core/src/ledgers.rs index 582ca464f..9bcf56623 100644 --- a/crates/socket-patch-core/src/ledgers.rs +++ b/crates/socket-patch-core/src/ledgers.rs @@ -371,6 +371,7 @@ pub fn uuid_only_record(uuid: &str) -> PatchRecord { } } + /// Fold the hosted pins and the vendor ledger's patch records into the /// manifest view update detection consults. Hosted mode records purl→uuid /// ONLY in the lockfiles (`hosted_pins`, uuid only; v5 keeps no hosted diff --git a/crates/socket-patch-core/src/lib.rs b/crates/socket-patch-core/src/lib.rs index ec2fb15ee..f257d0bef 100644 --- a/crates/socket-patch-core/src/lib.rs +++ b/crates/socket-patch-core/src/lib.rs @@ -15,6 +15,7 @@ pub mod utils; pub mod vendor; pub mod vex; + #[cfg(test)] mod golden; #[cfg(test)] diff --git a/crates/socket-patch-core/src/manifest/records.rs b/crates/socket-patch-core/src/manifest/records.rs index 7032d435c..453e0ab2f 100644 --- a/crates/socket-patch-core/src/manifest/records.rs +++ b/crates/socket-patch-core/src/manifest/records.rs @@ -32,10 +32,7 @@ pub fn vulnerabilities_for_manifest( /// `patch`. `files` is the (purl-keyed) before/after-hash map the /// caller built — semantics for what counts as a "patchable file" differ /// between the get and download flows, so the caller owns that decision. -pub fn build_patch_record( - patch: &PatchResponse, - files: HashMap, -) -> PatchRecord { +pub fn build_patch_record(patch: &PatchResponse, files: HashMap) -> PatchRecord { PatchRecord { uuid: patch.uuid.clone(), exported_at: patch.published_at.clone(), diff --git a/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs index 5863631df..082849761 100644 --- a/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/cargo_lock_equivalence_tests.rs @@ -97,6 +97,7 @@ fn synth_lock(rng: &mut Rng, blocks: usize, v1: bool) -> String { out } + const INDEX: &str = "sparse+https://socket.example/cargo/index/"; fn plan_new(lock: &str, name: &str, version: &str, cksum: &str) -> CargoLockPlan { @@ -181,8 +182,7 @@ fn span_splice_matches_golden_on_hand_written_locks() { "[root]\nname = \"app\"\nversion = \"0.1.0\"\ndependencies = [\n \"d 1.0.0 ({crates_io})\",\n]\n\n[[package]]\nname = \"d\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\"\n\n[[package]]\nname = \"u\"\nversion = \"2.0.0\"\nsource = \"{crates_io}\"\ndependencies = [\n \"d 1.0.0 ({crates_io})\",\n]\n\n[metadata]\n\"checksum d 1.0.0 ({crates_io})\" = \"cc\"\n\"checksum u 2.0.0 ({crates_io})\" = \"dd\"\n" ); let sourceless_v1 = "[[package]]\nname = \"s\"\nversion = \"1.0.0\"\n\n[metadata]\n\"checksum s 1.0.0 (registry+x)\" = \"ee\"\n".to_string(); - let source_at_eof = - format!("[[package]]\nname = \"e\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\""); + let source_at_eof = format!("[[package]]\nname = \"e\"\nversion = \"1.0.0\"\nsource = \"{crates_io}\""); let bare = "version = 3\n\n[[package]]\nname = \"b\"\nversion = \"1.0.0\"\n\n[[package]]\nname = \"c\"\nversion = \"1.0.0\"\n".to_string(); let mut g = Golden::new( "cargo_lock_hand_written", diff --git a/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs index 075f630b4..3a8ebf5c2 100644 --- a/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/golang_equivalence_tests.rs @@ -10,11 +10,7 @@ use super::*; use crate::golden::Golden; use crate::test_rng::Rng; -fn run( - g: &mut Golden, - files: &BTreeMap, - overrides: &[DepOverride], -) -> RewriteResult { +fn run(g: &mut Golden, files: &BTreeMap, overrides: &[DepOverride]) -> RewriteResult { let mut got = RewriteResult::default(); rewrite_golang(files, overrides, &mut got); g.next(&(files, overrides), &got); diff --git a/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs b/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs index 480e315e9..10fe9d510 100644 --- a/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs +++ b/crates/socket-patch-core/src/patch/redirect/group_equivalence_tests.rs @@ -131,12 +131,11 @@ fn assert_same_with_metadata( bun_lockb_present, python_metadata, ); - let merged = merge_group_outputs(&prefix, run_groups_concurrently(&prefix, &groups)).map( - |mut merged| { + let merged = merge_group_outputs(&prefix, run_groups_concurrently(&prefix, &groups)) + .map(|mut merged| { merged.vlt_drives = vlt::vlt_drives(files, bun_lockb_present); merged - }, - ); + }); assert_eq!( merged.as_ref(), Some(&want), diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 05c37e6e3..59d148cdf 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -45,17 +45,16 @@ mod pdm; mod pipenv; pub mod presence; // The pnpm hosted planner lives with the format's model. -use crate::formats::cargo::hosted::CargoLockPlan; -#[cfg(test)] -use crate::formats::cargo::hosted::CARGO_LOCK_REFERENCE_KIND; +use crate::formats::pnpm::plan_hosted; use crate::formats::cargo::CargoLock; use crate::formats::composer::hosted::rewrite_composer_lock; use crate::formats::gem::hosted::{checksum_entry_span, converge_gem_lock_source}; +pub(crate) use crate::formats::yarn::is_berry_lock; +use crate::formats::cargo::hosted::CargoLockPlan; +#[cfg(test)] +use crate::formats::cargo::hosted::CARGO_LOCK_REFERENCE_KIND; #[cfg(test)] use crate::formats::pnpm::hosted::pnpm_unrewritten_instances; -use crate::formats::pnpm::plan_hosted; -pub(crate) use crate::formats::yarn::is_berry_lock; -mod hosted_url; #[cfg(test)] mod pnpm_equivalence_tests; mod poetry; @@ -64,17 +63,18 @@ mod python_lock_equivalence_tests; mod requirements; mod staged; mod state; +mod hosted_url; pub mod upstream; pub mod vlt; pub mod vlt_heal; pub mod vlt_preflight; +pub use state::{ + load_redirect_state, save_redirect_state, + CorruptRedirectState, RedirectState, REDIRECT_STATE_REL, +}; /// Hosted-artifact leaf ownership rule, shared with `vex`'s bun lockfile /// discovery (which recovers a URL tuple's version from that leaf). pub(crate) use hosted_url::{hosted_url_names, hosted_url_version}; -pub use state::{ - load_redirect_state, save_redirect_state, CorruptRedirectState, RedirectState, - REDIRECT_STATE_REL, -}; /// One ecosystem's integrity hashes (mirrors the TS `PatchArtifactIntegrity`). #[derive(Debug, Clone, Default, Deserialize)] @@ -4056,6 +4056,7 @@ fn rewrite_uv_lock( } } + // ── composer.lock ──────────────────────────────────────────────────────────── /// Whether `text` points at `artifact_url` in any spelling a rewritten file may /// carry: the raw url every rewriter emits — composer.lock included, since @@ -10148,11 +10149,7 @@ mod tests { let out = r.files.get("Gemfile.lock").expect("lock rewritten"); let rows: Vec<&str> = out .lines() - .filter(|l| { - l.trim_start().starts_with("rails (7.0.0)") - && l.starts_with(" ") - && !l.starts_with(" ") - }) + .filter(|l| l.trim_start().starts_with("rails (7.0.0)") && l.starts_with(" ") && !l.starts_with(" ")) .collect(); assert_eq!( rows, @@ -10165,11 +10162,7 @@ mod tests { "{entry}: the entry keeps its line ending: {out:?}" ); let model = crate::formats::gem::GemfileLock::parse(out); - assert_eq!( - model.checksum("rails", "7.0.0"), - Some(patched.as_str()), - "{entry}" - ); + assert_eq!(model.checksum("rails", "7.0.0"), Some(patched.as_str()), "{entry}"); assert!(!out.contains("\r\r"), "line endings kept: {out:?}"); let edit = r .edits @@ -10184,10 +10177,7 @@ mod tests { files.insert("Gemfile.lock".to_string(), out.clone()); let again = rewrite_registry_redirect(&files, &[gem_override("rails", "7.0.0")]); assert!( - !again - .edits - .iter() - .any(|e| e.kind == "redirect_gemfile_lock_checksum"), + !again.edits.iter().any(|e| e.kind == "redirect_gemfile_lock_checksum"), "{entry}: rerun is a no-op: {:?}", again.edits ); @@ -10555,19 +10545,11 @@ mod tests { let redacted = format!( "https://patch.socket.dev/patch/npm/left-pad/1.3.0//{uuid}/left-pad-1.3.0.tgz?x=1" ); - assert_eq!( - redact_grant_token(&url, &url, uuid), - redacted, - "the URL alone" - ); + assert_eq!(redact_grant_token(&url, &url, uuid), redacted, "the URL alone"); let text = format!("vlt would fail to verify {url}: fetch error GET {url}: reset"); - let want = - format!("vlt would fail to verify {redacted}: fetch error GET {redacted}: reset"); + let want = format!("vlt would fail to verify {redacted}: fetch error GET {redacted}: reset"); assert_eq!(redact_grant_token(&text, &url, uuid), want, "every quote"); - assert!( - !redact_grant_token(&text, &url, uuid).contains(token), - "no token left" - ); + assert!(!redact_grant_token(&text, &url, uuid).contains(token), "no token left"); let registry = format!("https://patch.socket.dev/patch-registry/npm/{token}/{uuid}"); assert_eq!( redact_grant_token(®istry, ®istry, uuid), diff --git a/crates/socket-patch-core/src/patch/redirect/npmrc.rs b/crates/socket-patch-core/src/patch/redirect/npmrc.rs index 6a9c4a17a..ac102ef78 100644 --- a/crates/socket-patch-core/src/patch/redirect/npmrc.rs +++ b/crates/socket-patch-core/src/patch/redirect/npmrc.rs @@ -33,6 +33,8 @@ //! and — when the project file is silent — the user / global / builtin //! config files ([`resolve_outer_allow_remote`]). + + /// Repo-relative path of the project `.npmrc` the auto-config edits. pub const NPMRC_REL: &str = ".npmrc"; @@ -1135,4 +1137,5 @@ mod tests { ); } } + } diff --git a/crates/socket-patch-core/src/patch/redirect/pdm.rs b/crates/socket-patch-core/src/patch/redirect/pdm.rs index 608dbfd74..95d910f23 100644 --- a/crates/socket-patch-core/src/patch/redirect/pdm.rs +++ b/crates/socket-patch-core/src/patch/redirect/pdm.rs @@ -235,18 +235,9 @@ mod tests { #[test] fn legacy_formats_warn_stale_install_risk_once() { for (fixture, warns) in [ - ( - include_str!("../../../tests/fixtures/pdm-native/0.12.3.lock"), - true, - ), - ( - include_str!("../../../tests/fixtures/pdm-native/2.8.2.lock"), - true, - ), - ( - include_str!("../../../tests/fixtures/pdm-native/2.29.2.lock"), - false, - ), + (include_str!("../../../tests/fixtures/pdm-native/0.12.3.lock"), true), + (include_str!("../../../tests/fixtures/pdm-native/2.8.2.lock"), true), + (include_str!("../../../tests/fixtures/pdm-native/2.29.2.lock"), false), ] { let mut result = RewriteResult::default(); rewrite( @@ -419,11 +410,7 @@ mod parse_reuse_equivalence_tests { let what = format!("{fixture} extra={extra} crlf={crlf}"); let mut got = RewriteResult::default(); rewrite(&files, &deps, &mut got); - g.case( - what.replace(' ', "/"), - &(&files, &deps), - &format!("{got:?}"), - ); + g.case(what.replace(' ', "/"), &(&files, &deps), &format!("{got:?}")); confirmed += got.confirmed_pdm_uuids.len(); let mut again = files.clone(); diff --git a/crates/socket-patch-core/src/patch/redirect/pipenv.rs b/crates/socket-patch-core/src/patch/redirect/pipenv.rs index 0dc2eb2bf..1eaf8cfda 100644 --- a/crates/socket-patch-core/src/patch/redirect/pipenv.rs +++ b/crates/socket-patch-core/src/patch/redirect/pipenv.rs @@ -482,10 +482,7 @@ mod tests { let original = serde_json::to_string(&value).unwrap(); // A live lock (Pipfile beside it): conflicts veto the siblings. let files = BTreeMap::from([ - ( - "Pipfile".to_string(), - "[packages]\nurllib3 = \"*\"\n".to_string(), - ), + ("Pipfile".to_string(), "[packages]\nurllib3 = \"*\"\n".to_string()), ("Pipfile.lock".to_string(), original), ]); let mut result = RewriteResult::default(); @@ -525,30 +522,20 @@ mod tests { for stale in &stale_locks { let files = BTreeMap::from([ ("Pipfile.lock".to_string(), stale.clone()), - ( - "requirements.txt".to_string(), - "urllib3==1.26.18\n".to_string(), - ), + ("requirements.txt".to_string(), "urllib3==1.26.18\n".to_string()), ]); - let result = - super::super::rewrite_registry_redirect(&files, std::slice::from_ref(&dep)); + let result = super::super::rewrite_registry_redirect(&files, std::slice::from_ref(&dep)); assert!( !result.refused_pipenv_uuids.contains("patch-one"), "a non-conflict must not veto: {stale}" ); assert!( - result - .warnings - .iter() - .any(|w| w.code == "redirect_pipenv_skipped"), + result.warnings.iter().any(|w| w.code == "redirect_pipenv_skipped"), "{:?}", result.warnings ); assert!( - result - .files - .get("requirements.txt") - .is_some_and(|t| t.contains("patch.socket.dev")), + result.files.get("requirements.txt").is_some_and(|t| t.contains("patch.socket.dev")), "requirements.txt must still be redirected past a stale Pipfile.lock: {result:?}" ); assert!(!result.files.contains_key("Pipfile.lock")); @@ -606,10 +593,7 @@ mod tests { let files = |text: &str| BTreeMap::from([("Pipfile.lock".to_string(), text.to_string())]); assert!(lock_targets(&files(&lock()), std::slice::from_ref(&dep))); assert!(!lock_targets(&files(&lock()), std::slice::from_ref(&other))); - assert!(!lock_targets( - &files("{ not json"), - std::slice::from_ref(&dep) - )); + assert!(!lock_targets(&files("{ not json"), std::slice::from_ref(&dep))); assert!(!lock_targets(&BTreeMap::new(), std::slice::from_ref(&dep))); let mut npm = dep.clone(); npm.ecosystem = "npm".into(); @@ -635,10 +619,7 @@ mod tests { let entry: Value = serde_json::from_str(&fixed).unwrap(); assert!(entry["default"]["urllib3"].get("version").is_none()); assert_eq!(entry["default"]["urllib3"]["index"], json!("pypi")); - assert!(entry["default"]["urllib3"]["file"] - .as_str() - .unwrap() - .contains("patch-one")); + assert!(entry["default"]["urllib3"]["file"].as_str().unwrap().contains("patch-one")); value["default"]["urllib3"]["version"] = json!("==2.0.0"); let conflicting = serde_json::to_string(&value).unwrap(); @@ -659,10 +640,7 @@ mod tests { assert!(owned_url(public, &dep)); assert!(!owned_url("https://example.org/patch/pypi/urllib3/1.26.18/tok/patch-one/urllib3-1.26.18-py3-none-any.whl", &dep)); dep.artifact_url = "https://patches.internal.example:8443/patch/pypi/urllib3/1.26.18/tok/patch-one/urllib3-1.26.18-py3-none-any.whl".into(); - assert!( - owned_url(&dep.artifact_url, &dep), - "the grant's own origin is ours" - ); + assert!(owned_url(&dep.artifact_url, &dep), "the grant's own origin is ours"); assert!(owned_url(public, &dep), "and so is the public service"); assert!(!owned_url("https://patches.internal.example:8443/patch/pypi/urllib3/1.26.19/tok/patch-one/urllib3-1.26.19-py3-none-any.whl", &dep), "another version is not"); // Rotation on the custom origin re-points the owned entry. @@ -672,6 +650,7 @@ mod tests { assert!(!rotation.is_empty()); assert!(second.contains("/rotated/") && !second.contains("/tok/")); } + } #[cfg(test)] @@ -722,9 +701,7 @@ mod compatibility_tests { assert!(!result.refused_pipenv_uuids.contains("patch-one")); assert!(result.files["requirements.txt"].contains("patch.socket.dev")); assert!(!result.files.contains_key("Pipfile.lock")); - assert!(result - .warnings - .iter() - .any(|w| w.code == "redirect_pipenv_refused" && w.detail.contains("no Pipfile"))); + assert!(result.warnings.iter().any(|w| w.code == "redirect_pipenv_refused" && w.detail.contains("no Pipfile"))); } + } diff --git a/crates/socket-patch-core/src/patch/redirect/poetry.rs b/crates/socket-patch-core/src/patch/redirect/poetry.rs index b84dde5fa..624b74c4a 100644 --- a/crates/socket-patch-core/src/patch/redirect/poetry.rs +++ b/crates/socket-patch-core/src/patch/redirect/poetry.rs @@ -92,9 +92,7 @@ pub(super) fn rewrite_poetry( } } Err(detail) => { - result - .refused_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), @@ -102,9 +100,7 @@ pub(super) fn rewrite_poetry( continue; } } - result - .confirmed_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); content = rewrite.text; if !stale_warned { if let Some(format) = @@ -140,18 +136,14 @@ pub(super) fn rewrite_poetry( } // Already redirected to this artifact (idempotent re-scan). Ok(Some(_)) => { - result - .confirmed_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); } Ok(None) => result.warnings.push(RewriteWarning { code: "redirect_poetry_entry_not_found".into(), detail: format!("no {path} entry for {}@{}", dep.name, dep.version), }), Err(detail) => { - result - .refused_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), @@ -276,11 +268,7 @@ mod equivalence_tests { let mut again = files.clone(); again.extend(got.files.clone()); let got = run(rewrite_poetry, &again, &deps); - g.case( - format!("{what}/re-run"), - &(&again, &deps), - &format!("{got:?}"), - ); + g.case(format!("{what}/re-run"), &(&again, &deps), &format!("{got:?}")); } } } diff --git a/crates/socket-patch-core/src/patch/redirect/requirements.rs b/crates/socket-patch-core/src/patch/redirect/requirements.rs index 91ebddd00..cdbd9eb6d 100644 --- a/crates/socket-patch-core/src/patch/redirect/requirements.rs +++ b/crates/socket-patch-core/src/patch/redirect/requirements.rs @@ -265,9 +265,7 @@ pub(super) fn rewrite( } } matched = true; - result - .confirmed_requirements_uuids - .insert(dep.patch_uuid.clone()); + result.confirmed_requirements_uuids.insert(dep.patch_uuid.clone()); let options = requirement_tokens(specifier) .into_iter() .skip_while(|token| !token.starts_with("--")) diff --git a/crates/socket-patch-core/src/patch/redirect/state.rs b/crates/socket-patch-core/src/patch/redirect/state.rs index 98d0b620e..6d1b2f5d0 100644 --- a/crates/socket-patch-core/src/patch/redirect/state.rs +++ b/crates/socket-patch-core/src/patch/redirect/state.rs @@ -56,6 +56,7 @@ impl RedirectState { records: BTreeMap::new(), } } + } impl Default for RedirectState { @@ -517,4 +518,5 @@ mod tests { "changed bytes still go through the (here refused) atomic write" ); } + } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs b/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs index 87c49a542..f51142f69 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/bun_lockb.rs @@ -332,10 +332,7 @@ mod tests { let package_start = u64::from_le_bytes(lock[110..118].try_into().unwrap()) as usize; // The root resolution's flag byte (its last). let flags_at = package_start + count * 16 + 63; - assert_eq!( - lock[flags_at], - crate::vendor::bun_lockb::NORMALIZED_FORMAT_1 - ); + assert_eq!(lock[flags_at], crate::vendor::bun_lockb::NORMALIZED_FORMAT_1); lock[flags_at] |= 0x40; BunLockb::parse(&lock).unwrap().validate_mutation().unwrap(); let (outcome, after) = run(&lock, &vendor_opts()).await; diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs index 70ca86a6d..c44d7919e 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs @@ -97,10 +97,7 @@ pub(crate) async fn restore( ) }); let cksums: BTreeMap> = - futures_util::future::join_all(lookups) - .await - .into_iter() - .collect(); + futures_util::future::join_all(lookups).await.into_iter().collect(); let mut changed = false; let mut restored: Vec<(&LockHit, String)> = Vec::new(); for hit in &hits { @@ -119,9 +116,7 @@ pub(crate) async fn restore( } // The entries' own source + checksum values, spliced at the parse's // spans (every hit is a distinct block: its source names its uuid). - let spans = model - .spans() - .expect("a lock parsed from text carries spans"); + let spans = model.spans().expect("a lock parsed from text carries spans"); let mut splices: Vec<(std::ops::Range, String)> = Vec::new(); for (hit, cksum) in &restored { let at = &spans.packages[hit.index]; @@ -138,10 +133,7 @@ pub(crate) async fn restore( } for (hit, cksum) in &restored { // Dependents' full-id references and the v1 `[metadata]` key. - lock = lock.replace( - &format!("({})", hit.source), - &format!("({CRATES_IO_SOURCE})"), - ); + lock = lock.replace(&format!("({})", hit.source), &format!("({CRATES_IO_SOURCE})")); let metadata_key = format!( "\"checksum {} {} ({CRATES_IO_SOURCE})\" = \"", hit.name, hit.version @@ -160,11 +152,7 @@ pub(crate) async fn restore( if changed { view.write( "Cargo.lock", - if crlf { - lock.replace('\n', "\r\n") - } else { - lock - }, + if crlf { lock.replace('\n', "\r\n") } else { lock }, ); } } @@ -329,10 +317,11 @@ fn remove_registry_block(config: &str, reg: &str) -> Option { end -= 1; } let fragment = format!("{}\n", lines[i..end].join("\n")); - let removed = remove_appended_cargo_block(&lf, &fragment).or_else(|| { - // The block ends the file with no final newline. - remove_appended_cargo_block(&lf, fragment.trim_end_matches('\n')) - })?; + let removed = remove_appended_cargo_block(&lf, &fragment) + .or_else(|| { + // The block ends the file with no final newline. + remove_appended_cargo_block(&lf, fragment.trim_end_matches('\n')) + })?; Some(if crlf { removed.replace('\n', "\r\n") } else { @@ -399,10 +388,7 @@ mod tests { #[test] fn table_form_line_is_dropped() { - assert_eq!( - unpin_line(&format!("registry = \"{REG}\""), REG), - Some(None) - ); + assert_eq!(unpin_line(&format!("registry = \"{REG}\""), REG), Some(None)); } #[test] @@ -411,10 +397,7 @@ mod tests { let hosted = format!( "{original}\n[registries.{REG}]\nindex = \"sparse+https://patch.socket.dev/x/index/\"\n" ); - assert_eq!( - remove_registry_block(&hosted, REG).as_deref(), - Some(original) - ); + assert_eq!(remove_registry_block(&hosted, REG).as_deref(), Some(original)); let created = format!("[registries.{REG}]\nindex = \"sparse+https://x/\"\n"); assert_eq!(remove_registry_block(&created, REG).as_deref(), Some("")); } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs b/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs index 49c9b7ee2..b804342c6 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/composer.rs @@ -27,11 +27,11 @@ use std::collections::BTreeMap; use serde_json::Value; -use super::{Ctx, FormatResult, HostedPin, View}; -use crate::crawlers::composer_crawler::normalize_version; use crate::formats::composer::hosted::{ find_composer_entry, json_object_end_from, json_string_field, ComposerEntry, }; +use super::{Ctx, FormatResult, HostedPin, View}; +use crate::crawlers::composer_crawler::normalize_version; const COMPOSER_LOCK: &str = "composer.lock"; const DIST_KEY: &str = "\"dist\": {"; diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs index af25a9b35..e42e183c8 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs @@ -56,11 +56,11 @@ use std::collections::{BTreeMap, BTreeSet}; use regex::Regex; use super::{Ctx, FormatResult, HostedPin, View}; +use crate::utils::line_endings::{to_lf, LineEndings}; +use crate::vendor::gem::{gem_declaration_any, quoted_literal}; use crate::formats::gem::{ bundler_manifest_for, parse_spec, same_remote, split_checksum_entry, BUNDLER_LOCKS, }; -use crate::utils::line_endings::{to_lf, LineEndings}; -use crate::vendor::gem::{gem_declaration_any, quoted_literal}; /// The default upstream `GEM` remote. const RUBYGEMS_REMOTE: &str = "https://rubygems.org/"; @@ -249,14 +249,17 @@ fn choose_upstream( /// The line after which a spec named `name-version` sorts into `sec` /// (bundler writes specs sorted by full name). fn insertion_point(sec: &GemSec, full_name: &str) -> Option { - let pred = sec.entries.iter().rfind(|e| { - let full = if e.version.is_empty() { - e.name.clone() - } else { - format!("{}-{}", e.name, e.version) - }; - full.as_str() < full_name - }); + let pred = sec + .entries + .iter() + .rfind(|e| { + let full = if e.version.is_empty() { + e.name.clone() + } else { + format!("{}-{}", e.name, e.version) + }; + full.as_str() < full_name + }); pred.map(|e| e.last).or(sec.specs_line) } diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs b/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs index cee422040..4ce16051f 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/golang.rs @@ -65,10 +65,7 @@ pub(crate) async fn restore( (uuid.clone(), ctx.client.go_sums(module, version).await) }); let sums: std::collections::BTreeMap> = - futures_util::future::join_all(lookups) - .await - .into_iter() - .collect(); + futures_util::future::join_all(lookups).await.into_iter().collect(); let mut go_mod_next = go_mod.clone(); let mut go_sum = view.read("go.sum").await.ok().flatten(); @@ -91,8 +88,8 @@ pub(crate) async fn restore( } } if let Some(text) = go_sum.as_deref() { - let mut next = - remove_module_prefix_lines(text, socket_module).unwrap_or_else(|| text.to_string()); + let mut next = remove_module_prefix_lines(text, socket_module) + .unwrap_or_else(|| text.to_string()); let upstream = format!( "{module} {version} {}\n{module} {version}/go.mod {}\n", sums.zip_h1, sums.mod_h1 diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs index c3aa0733f..4e0eaf2ac 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs @@ -344,7 +344,9 @@ pub struct RestoreOutcome { impl RestoreOutcome { pub fn restored(&self) -> impl Iterator { - self.pins.iter().filter(|p| p.status == PinStatus::Restored) + self.pins + .iter() + .filter(|p| p.status == PinStatus::Restored) } pub fn refused(&self) -> impl Iterator { @@ -671,7 +673,9 @@ async fn restore_pass(view: &mut View<'_>, active: &[&HostedPin], ctx: &Ctx<'_>) Format::YarnLock => npm::restore_yarn_locks(view, &pins, &files, ctx).await, Format::PnpmLock => npm::restore_pnpm_locks(view, &pins, &files, ctx).await, Format::BunLock => npm::restore_bun_locks(view, &pins, &files, ctx).await, - Format::BunLockb if ctx.bun_lockb => bun_lockb::restore(view, &pins, &files, ctx).await, + Format::BunLockb if ctx.bun_lockb => { + bun_lockb::restore(view, &pins, &files, ctx).await + } Format::Cargo => cargo::restore(view, &pins, &files, ctx).await, Format::Golang => golang::restore(view, &pins, &files, ctx).await, Format::Gem => gem::restore(view, &pins, &files, ctx).await, diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs b/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs index 8530ddf48..ac105569f 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/pypi_locks.rs @@ -57,16 +57,7 @@ fn files_value(release: &[PypiFile], by_url: bool) -> Option { let key = if by_url { "url" } else { "file" }; let mut located: Vec<(&str, &PypiFile)> = release .iter() - .map(|f| { - ( - if by_url { - f.url.as_str() - } else { - f.filename.as_str() - }, - f, - ) - }) + .map(|f| (if by_url { f.url.as_str() } else { f.filename.as_str() }, f)) .collect(); // PDM orders each entry's files by the location it writes: a `static_urls` // lock by URL (so an sdist under `0c/…` precedes a wheel under `b0/…`), diff --git a/crates/socket-patch-core/src/patch/redirect/vlt.rs b/crates/socket-patch-core/src/patch/redirect/vlt.rs index c3561b44a..149868520 100644 --- a/crates/socket-patch-core/src/patch/redirect/vlt.rs +++ b/crates/socket-patch-core/src/patch/redirect/vlt.rs @@ -985,4 +985,5 @@ mod tests { ); assert_eq!(carried_pin_original(&relocked, &old), None); } + } diff --git a/crates/socket-patch-core/src/policy/mod.rs b/crates/socket-patch-core/src/policy/mod.rs index 605dfd9ce..15778f264 100644 --- a/crates/socket-patch-core/src/policy/mod.rs +++ b/crates/socket-patch-core/src/policy/mod.rs @@ -456,8 +456,7 @@ fn compile(file: &str, lists: &[(&'static str, &[String])]) -> Result &'static SelectionPolicy { - static DEFAULTS: std::sync::LazyLock = - std::sync::LazyLock::new(SelectionPolicy::unrestricted); + static DEFAULTS: std::sync::LazyLock = std::sync::LazyLock::new(SelectionPolicy::unrestricted); &DEFAULTS } @@ -806,9 +805,7 @@ fn ceiling_dirs() -> Vec { #[cfg(unix)] fn trusted_owner(meta: &std::fs::Metadata) -> bool { use std::os::unix::fs::MetadataExt; - let sudo_uid = std::env::var("SUDO_UID") - .ok() - .and_then(|v| v.trim().parse::().ok()); + let sudo_uid = std::env::var("SUDO_UID").ok().and_then(|v| v.trim().parse::().ok()); // SAFETY: geteuid has no preconditions and cannot fail. owner_trusted(meta.uid(), unsafe { libc::geteuid() }, sudo_uid) } diff --git a/crates/socket-patch-core/src/policy/report.rs b/crates/socket-patch-core/src/policy/report.rs index 0d095c7dc..ba8ff4221 100644 --- a/crates/socket-patch-core/src/policy/report.rs +++ b/crates/socket-patch-core/src/policy/report.rs @@ -48,9 +48,7 @@ pub fn policy_block( let (floor, floor_source) = policy.min_severity(); // Sorted: crawl order is filesystem order, and the two engines differ. let mut filtered: Vec<&FilteredEntry> = filtered.iter().collect(); - filtered.sort_by(|a, b| { - (&a.project, &a.purl, a.reason.code()).cmp(&(&b.project, &b.purl, b.reason.code())) - }); + filtered.sort_by(|a, b| (&a.project, &a.purl, a.reason.code()).cmp(&(&b.project, &b.purl, b.reason.code()))); let mut retained: Vec<&RetainedEntry> = retained.iter().collect(); retained.sort_by(|a, b| (&a.project, &a.purl).cmp(&(&b.project, &b.purl))); let filtered: Vec = filtered diff --git a/crates/socket-patch-core/src/policy/socket_yml.rs b/crates/socket-patch-core/src/policy/socket_yml.rs index 103fe20bd..30a99499c 100644 --- a/crates/socket-patch-core/src/policy/socket_yml.rs +++ b/crates/socket-patch-core/src/policy/socket_yml.rs @@ -486,11 +486,7 @@ pub(crate) fn package_spec_error(spec: &str) -> Option<&'static str> { if spec.is_empty() { return Some("package spec is empty"); } - if let Some(rest) = spec - .get(..4) - .filter(|p| p.eq_ignore_ascii_case("pkg:")) - .map(|_| &spec[4..]) - { + if let Some(rest) = spec.get(..4).filter(|p| p.eq_ignore_ascii_case("pkg:")).map(|_| &spec[4..]) { let valid = rest.split_once('/').is_some_and(|(ty, name)| { !ty.is_empty() && !name.trim_matches('/').is_empty() && !name.starts_with('@') }); @@ -789,9 +785,7 @@ pub(crate) fn parse_file( Some(Err((key, message))) => { warnings.push(PolicyWarning { code: super::SOCKET_YML_IGNORED_VALUE, - detail: super::strip_unsafe(&format!( - "{file}: {key} {message}; the key is ignored" - )), + detail: super::strip_unsafe(&format!("{file}: {key} {message}; the key is ignored")), }); Vec::new() } @@ -922,12 +916,8 @@ mod tests { // YAML beats everything; the case variant beats the version gate; // the version gate beats the keys. assert_eq!(err_key("patches: {minSeverty: x}\n").0, "version"); - assert!(err_key("Patches: {}\npatches: {minSeverty: x}\n") - .1 - .contains("misspelled")); - assert!(err_key("patches: {minSeverty: x\n") - .1 - .contains("invalid YAML")); + assert!(err_key("Patches: {}\npatches: {minSeverty: x}\n").1.contains("misspelled")); + assert!(err_key("patches: {minSeverty: x\n").1.contains("invalid YAML")); } #[test] @@ -996,9 +986,12 @@ mod tests { let (key, message) = err_key(text); assert_eq!(key, "", "{text:?}"); assert!( - ["invalid YAML", "top level must be a mapping",] - .iter() - .any(|m| message.contains(m)), + [ + "invalid YAML", + "top level must be a mapping", + ] + .iter() + .any(|m| message.contains(m)), "{text:?}: {message}" ); } diff --git a/crates/socket-patch-core/src/policy/tests.rs b/crates/socket-patch-core/src/policy/tests.rs index 2c18534f4..5e03be9d7 100644 --- a/crates/socket-patch-core/src/policy/tests.rs +++ b/crates/socket-patch-core/src/policy/tests.rs @@ -417,14 +417,8 @@ fn composer_package_filters_match_release_identity_and_preserve_branch_case() { Err(FilterReason::PackageIgnored { .. }) )); assert!(policy.admits_purl("pkg:composer/psr/log@3.0.3").is_ok()); - assert!(package_spec_matches( - "pkg:composer/PSR/Log@3.0.2.0", - "pkg:composer/psr/log@3.0.2" - )); - assert!(!package_spec_matches( - "pkg:composer/psr/log@dev-Feature", - "pkg:composer/psr/log@dev-feature" - )); + assert!(package_spec_matches("pkg:composer/PSR/Log@3.0.2.0", "pkg:composer/psr/log@3.0.2")); + assert!(!package_spec_matches("pkg:composer/psr/log@dev-Feature", "pkg:composer/psr/log@dev-feature")); } #[test] @@ -582,10 +576,7 @@ mod disk { assert!(owner_trusted(1000, 1000, None)); assert!(owner_trusted(0, 1000, None)); assert!(!owner_trusted(1001, 1000, None)); - assert!( - owner_trusted(1001, 1000, Some(1001)), - "sudo's invoking user" - ); + assert!(owner_trusted(1001, 1000, Some(1001)), "sudo's invoking user"); assert!(owner_trusted(1001, 0, None), "root trusts every owner"); } @@ -606,21 +597,13 @@ mod disk { fn this_repos_socket_yml_loads_and_excludes_its_fixtures() { let repo = Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."); let (policy, warnings) = - SelectionPolicy::load(&DiskPolicyFs::new(&repo), &PolicyOverrides::default()) - .expect("valid"); + SelectionPolicy::load(&DiskPolicyFs::new(&repo), &PolicyOverrides::default()).expect("valid"); assert!(warnings.is_empty(), "{warnings:?}"); assert!(matches!(policy.source(), PolicySource::File { path, .. } if path == "socket.yml")); let lock = strings(&["package-lock.json"]); let err = policy - .admits_root(&root( - "crates/socket-patch-core/tests/fixtures/redirect/npm", - &lock, - true, - )) + .admits_root(&root("crates/socket-patch-core/tests/fixtures/redirect/npm", &lock, true)) .unwrap_err(); - assert_eq!( - err.detail(), - "crates/socket-patch-core/tests/fixtures/** (projectIgnorePaths)" - ); + assert_eq!(err.detail(), "crates/socket-patch-core/tests/fixtures/** (projectIgnorePaths)"); assert!(policy.admits_root(&root("", &lock, true)).is_ok()); } diff --git a/crates/socket-patch-core/src/rollout/stage.rs b/crates/socket-patch-core/src/rollout/stage.rs index 7c24ebadf..9ebd7e7ac 100644 --- a/crates/socket-patch-core/src/rollout/stage.rs +++ b/crates/socket-patch-core/src/rollout/stage.rs @@ -394,11 +394,7 @@ impl Stage { "a patch lookup failed for a package that could get its first patch, so \ no new patches were added this run ({} deferred) and none can take the \ missing package's place; re-run once the API answers", - if deferred == 1 { - "1 package".to_string() - } else { - format!("{deferred} packages") - } + if deferred == 1 { "1 package".to_string() } else { format!("{deferred} packages") } ), )); } @@ -419,9 +415,7 @@ impl Stage { purl: c.purl.clone(), uuid: c.uuid.clone(), reason: ROLLOUT_DEFERRED.to_string(), - detail: Some(format!( - "rank {rank} in the rollout queue; a later scan adds it" - )), + detail: Some(format!("rank {rank} in the rollout queue; a later scan adds it")), }) .collect() } @@ -508,3 +502,4 @@ pub fn rollout_json(configured: &MaxNew, plan: Option<&RolloutPlan>) -> serde_js "deferred": deferred, }) } + diff --git a/crates/socket-patch-core/src/telemetry.rs b/crates/socket-patch-core/src/telemetry.rs index 5f0eccb8d..d49aaa5c6 100644 --- a/crates/socket-patch-core/src/telemetry.rs +++ b/crates/socket-patch-core/src/telemetry.rs @@ -4,7 +4,9 @@ use once_cell::sync::Lazy; use uuid::Uuid; use crate::constants::USER_AGENT; -use crate::utils::env_compat::{is_debug_enabled, is_offline_env, proxy_url_from_env}; +use crate::utils::env_compat::{ + is_debug_enabled, is_offline_env, proxy_url_from_env, +}; use crate::utils::fs::home_dir; use crate::vex::time::unix_to_ymdhms; diff --git a/crates/socket-patch-core/src/update/download.rs b/crates/socket-patch-core/src/update/download.rs index be1476b19..f176426ce 100644 --- a/crates/socket-patch-core/src/update/download.rs +++ b/crates/socket-patch-core/src/update/download.rs @@ -741,10 +741,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let missing = tmp.path().join("never-existed"); sweep_stale_stages(&missing); - assert!( - !missing.exists(), - "sweep must not create the destination dir" - ); + assert!(!missing.exists(), "sweep must not create the destination dir"); } /// A write failure AFTER a successful open (EFBIG here, standing in @@ -760,7 +757,8 @@ mod tests { #[test] fn stage_write_failure_cleans_up_stage_file() { const CHILD_ENV: &str = "SOCKET_PATCH_CORE_TEST_STAGE_FSIZE_CHILD"; - const TEST_NAME: &str = "update::download::tests::stage_write_failure_cleans_up_stage_file"; + const TEST_NAME: &str = + "update::download::tests::stage_write_failure_cleans_up_stage_file"; if std::env::var_os(CHILD_ENV).is_none() { let exe = std::env::current_exe().expect("test binary path must resolve"); let output = std::process::Command::new(exe) @@ -826,10 +824,7 @@ mod tests { matches!(err, UpdateError::SwapFailed(_)), "expected SwapFailed, got: {err}" ); - assert!( - err.to_string().contains("error writing staged binary"), - "{err}" - ); + assert!(err.to_string().contains("error writing staged binary"), "{err}"); let leftovers: Vec = std::fs::read_dir(tmp.path()) .unwrap() .map(|e| e.unwrap().file_name().to_string_lossy().into_owned()) diff --git a/crates/socket-patch-core/src/update/release.rs b/crates/socket-patch-core/src/update/release.rs index 7c3b04c29..5b2869012 100644 --- a/crates/socket-patch-core/src/update/release.rs +++ b/crates/socket-patch-core/src/update/release.rs @@ -751,11 +751,9 @@ mod tests { .mount(&server) .await; - let client = metadata_client( - &short_timeouts(), - follow_redirect_policy(&default_endpoints()), - ) - .unwrap(); + let client = + metadata_client(&short_timeouts(), follow_redirect_policy(&default_endpoints())) + .unwrap(); let err = client .get(format!("{}/start", server.uri())) .send() @@ -788,11 +786,9 @@ mod tests { .mount(&server) .await; - let client = metadata_client( - &short_timeouts(), - follow_redirect_policy(&default_endpoints()), - ) - .unwrap(); + let client = + metadata_client(&short_timeouts(), follow_redirect_policy(&default_endpoints())) + .unwrap(); let err = client .get(format!("{}/start", server.uri())) .send() @@ -868,10 +864,7 @@ mod tests { .unwrap_err(); assert!(matches!(err, UpdateError::CheckFailed(_)), "{err:?}"); let msg = err.to_string(); - assert!( - msg.contains("expected a redirect to the latest tag"), - "{msg}" - ); + assert!(msg.contains("expected a redirect to the latest tag"), "{msg}"); assert!(msg.contains("API fallback:"), "{msg}"); assert!(msg.contains("returned 500"), "{msg}"); } @@ -952,14 +945,8 @@ mod tests { #[test] fn url_host_keeps_explicit_ports() { - assert_eq!( - url_host("http://127.0.0.1:9/x").as_deref(), - Some("127.0.0.1:9") - ); - assert_eq!( - url_host("https://github.com/a").as_deref(), - Some("github.com") - ); + assert_eq!(url_host("http://127.0.0.1:9/x").as_deref(), Some("127.0.0.1:9")); + assert_eq!(url_host("https://github.com/a").as_deref(), Some("github.com")); assert_eq!(url_host("not a url"), None); } @@ -972,9 +959,7 @@ mod tests { // code stays `check_failed` (stable contract). let server = MockServer::start().await; Mock::given(method("GET")) - .and(path( - "/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS", - )) + .and(path("/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS")) .respond_with(ResponseTemplate::new(404)) .mount(&server) .await; @@ -1007,9 +992,7 @@ mod tests { // silently. let server = MockServer::start().await; Mock::given(method("GET")) - .and(path( - "/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS", - )) + .and(path("/SocketDev/socket-patch/releases/download/v1.2.3/SHA256SUMS")) .respond_with(ResponseTemplate::new(500)) .mount(&server) .await; diff --git a/crates/socket-patch-core/src/utils/group_commit.rs b/crates/socket-patch-core/src/utils/group_commit.rs index 973c02877..e8f2284fe 100644 --- a/crates/socket-patch-core/src/utils/group_commit.rs +++ b/crates/socket-patch-core/src/utils/group_commit.rs @@ -304,9 +304,9 @@ where // write the lock edits beside the pre-run ledger. Put the caller's value // back before the unwind continues — the same value a caught-and- // continued caller holds. - if let Err(panic) = - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| edit(Arc::make_mut(value)))) - { + if let Err(panic) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + edit(Arc::make_mut(value)) + })) { files.insert(key, captured(value)); drop(files); std::panic::resume_unwind(panic); @@ -1608,10 +1608,7 @@ mod tests { .unwrap(); remove_dir_after_commit(&dir).await; drop(dropped); - assert!( - dir.join("config.toml").exists(), - "an abandoned commit removes nothing" - ); + assert!(dir.join("config.toml").exists(), "an abandoned commit removes nothing"); let group = GroupCommit::begin(root); super::super::fs::remove_file(&dir.join("config.toml")) @@ -1620,10 +1617,7 @@ mod tests { remove_dir_after_commit(&dir).await; assert!(dir.join("config.toml").exists(), "captured, still on disk"); group.commit().await.unwrap(); - assert!( - !dir.exists(), - "the emptied directory is removed after the commit" - ); + assert!(!dir.exists(), "the emptied directory is removed after the commit"); std::fs::create_dir_all(&dir).unwrap(); std::fs::write(dir.join("config.toml"), b"[patch]\n").unwrap(); @@ -1635,10 +1629,7 @@ mod tests { remove_dir_after_commit(&dir).await; group.commit().await.unwrap(); assert!(!dir.join("config.toml").exists()); - assert!( - dir.join("credentials.toml").exists(), - "a non-empty directory is kept" - ); + assert!(dir.join("credentials.toml").exists(), "a non-empty directory is kept"); remove_dir_after_commit(&root.join("gone")).await; std::fs::remove_file(dir.join("credentials.toml")).unwrap(); diff --git a/crates/socket-patch-core/src/utils/hatch.rs b/crates/socket-patch-core/src/utils/hatch.rs index 756dacd1f..a08d79a97 100644 --- a/crates/socket-patch-core/src/utils/hatch.rs +++ b/crates/socket-patch-core/src/utils/hatch.rs @@ -265,7 +265,8 @@ fn rewrite_environments( .is_some_and(|kind| kind != "virtual") { return Err( - "Hatch sources, overrides and custom environments require agent mode".into(), + "Hatch sources, overrides and custom environments require agent mode" + .into(), ); } for key in ["dependencies", "extra-dependencies"] { diff --git a/crates/socket-patch-core/src/utils/line_endings.rs b/crates/socket-patch-core/src/utils/line_endings.rs index c6f257359..889f6ad32 100644 --- a/crates/socket-patch-core/src/utils/line_endings.rs +++ b/crates/socket-patch-core/src/utils/line_endings.rs @@ -119,4 +119,5 @@ mod tests { assert_eq!(majority_terminator("a\r\nb\n"), "\n", "a tie is LF"); assert_eq!(majority_terminator("{}"), "\n", "no break: LF, not os.EOL"); } + } diff --git a/crates/socket-patch-core/src/utils/mod.rs b/crates/socket-patch-core/src/utils/mod.rs index 1fa8d934b..ee47573e8 100644 --- a/crates/socket-patch-core/src/utils/mod.rs +++ b/crates/socket-patch-core/src/utils/mod.rs @@ -7,9 +7,9 @@ pub mod env_compat; pub mod failpoint; pub mod fs; pub mod group_commit; +pub mod notice; pub(crate) mod http; pub(crate) mod line_endings; -pub mod notice; pub mod pdm_lock; pub mod pipenv; pub mod poetry_lock; diff --git a/crates/socket-patch-core/src/utils/python_script.rs b/crates/socket-patch-core/src/utils/python_script.rs index 5eb997005..2ca51e107 100644 --- a/crates/socket-patch-core/src/utils/python_script.rs +++ b/crates/socket-patch-core/src/utils/python_script.rs @@ -627,12 +627,7 @@ mod rendering_tests { "{direct}" ); assert!(uv_line.ends_with('}'), "{direct}"); - assert!( - direct.starts_with( - "[project]\nname = \"p\"\ndependencies = [\"alpha==1.0.0\"]\n\n[tool]\n" - ), - "{direct}" - ); + assert!(direct.starts_with("[project]\nname = \"p\"\ndependencies = [\"alpha==1.0.0\"]\n\n[tool]\n"), "{direct}"); assert_settled(&direct); let transitive = rewrite_project_metadata( diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index 546250c0d..2dbe53d91 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -516,4 +516,5 @@ mod tests { ); } } + } diff --git a/crates/socket-patch-core/src/vendor/bun_lockb.rs b/crates/socket-patch-core/src/vendor/bun_lockb.rs index 21f5ca832..27751bcd0 100644 --- a/crates/socket-patch-core/src/vendor/bun_lockb.rs +++ b/crates/socket-patch-core/src/vendor/bun_lockb.rs @@ -1779,10 +1779,7 @@ mod tests { lock.set_package(package.id, &repin, &digest()).unwrap(); assert_eq!(lock.bytes().len(), first.len(), "{version}"); assert!( - !lock - .bytes() - .windows(token.len()) - .any(|w| w == token.as_bytes()), + !lock.bytes().windows(token.len()).any(|w| w == token.as_bytes()), "{version}: the superseded URL is gone" ); // A remote tarball keeps the registry record's inactive bytes; a @@ -1825,9 +1822,7 @@ mod tests { .set_package(1, ".socket/vendor/npm/x/minimist-1.2.2.tgz", &digest()) .unwrap(); let at = local.resolution_at(1); - assert!(local.data[at + 16..at + local.resolution_size] - .iter() - .all(|b| *b == 0)); + assert!(local.data[at + 16..at + local.resolution_size].iter().all(|b| *b == 0)); } #[test] diff --git a/crates/socket-patch-core/src/vendor/cargo_lock.rs b/crates/socket-patch-core/src/vendor/cargo_lock.rs index 73bdf8275..7e50bccaf 100644 --- a/crates/socket-patch-core/src/vendor/cargo_lock.rs +++ b/crates/socket-patch-core/src/vendor/cargo_lock.rs @@ -64,9 +64,11 @@ use std::sync::Arc; use toml_edit::{DocumentMut, Item, Table}; use super::cargo_tag; +use crate::formats::cargo::{ + locked_packages, metadata_checksum_key, parse_ref, LockedPackage, +}; use super::parse_memo::ParseMemo; use super::state::CargoLockOriginal; -use crate::formats::cargo::{locked_packages, metadata_checksum_key, parse_ref, LockedPackage}; use crate::utils::fs::{atomic_write_bytes_preserving_mode, read_regular_to_string}; /// Why a lock edit could not be performed. diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs b/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs index 09af505f5..4960652e4 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs @@ -594,15 +594,9 @@ async fn inventory_requirements_txt(view: &ProjectView<'_>) -> Option None, - CopyClaim::OtherTag(other) => Some(format!( - "{CARGO_LOCK} builds the copy tagged for patch {other} ({name} {})", - cargo_tag::tag_version(version, other) - )), - CopyClaim::UntaggedOverride => Some(format!( - "{CARGO_LOCK} builds an untagged {name} {version}, not the copy (which \ + let why = + match lock.vendored_in_use(name, version, &vref.uuid, copy_tagged) { + CopyClaim::Consumed => None, + CopyClaim::OtherTag(other) => Some(format!( + "{CARGO_LOCK} builds the copy tagged for patch {other} ({name} {})", + cargo_tag::tag_version(version, other) + )), + CopyClaim::UntaggedOverride => Some(format!( + "{CARGO_LOCK} builds an untagged {name} {version}, not the copy (which \ cargo would lock as {}): another [patch] or path dependency overrides it", - cargo_tag::tag_version(version, &vref.uuid) - )), - CopyClaim::NotConsumed => Some(format!( - "{CARGO_LOCK} does not build {name}@{version} from it (an unused patch, \ + cargo_tag::tag_version(version, &vref.uuid) + )), + CopyClaim::NotConsumed => Some(format!( + "{CARGO_LOCK} does not build {name}@{version} from it (an unused patch, \ or the lock resolves it from a registry)" - )), - }; + )), + }; if let Some(why) = why { out.diag( DIAG_REF_INVALID, diff --git a/crates/socket-patch-core/src/vex/discover/gem.rs b/crates/socket-patch-core/src/vex/discover/gem.rs index 3e0718864..77f7388a8 100644 --- a/crates/socket-patch-core/src/vex/discover/gem.rs +++ b/crates/socket-patch-core/src/vex/discover/gem.rs @@ -125,10 +125,10 @@ use super::{ names_vendor_dir, simple_purl, vendor_ref, vendored_leaf_purl, DiscoverCtx, Discovery, PatchedRef, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; +use crate::vendor::gem::{gem_declaration_any, quoted_literal}; use crate::formats::gem::{ bundler_manifest_for, same_remote, GemfileLock, Section, SpecLine, BUNDLER_LOCKS, }; -use crate::vendor::gem::{gem_declaration_any, quoted_literal}; pub(crate) async fn extract(ctx: &DiscoverCtx<'_>, out: &mut Discovery) { // Both locks, legacy spelling first (order only affects diagnostics). diff --git a/crates/socket-patch-core/src/vex/discover/maven.rs b/crates/socket-patch-core/src/vex/discover/maven.rs index fc9e1fdb0..734f3e61d 100644 --- a/crates/socket-patch-core/src/vex/discover/maven.rs +++ b/crates/socket-patch-core/src/vex/discover/maven.rs @@ -88,15 +88,15 @@ use super::{ Discovery, PatchedRef, WiringMode, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; -use crate::formats::maven::{ - is_maven_coordinate, is_maven_version_text, parse_pom, split_socket_version, Pom, PomDep, - PomRepo, -}; use crate::patch::redirect::{ local_repo_artifact_path, MVN_CHECKSUMS, MVN_CONFIG, TRUSTED_CHECKSUMS_ON, }; use crate::utils::digest::sha256_hex; use crate::vendor::lock_inventory::LockIntegrity; +use crate::formats::maven::{ + is_maven_coordinate, is_maven_version_text, parse_pom, split_socket_version, Pom, PomDep, + PomRepo, +}; use crate::vendor::maven_repo::{sha1_sidecar_matches, VENDOR_REPO_URL_PREFIX}; use crate::vendor::path::{sweep_vendor_dirs, VENDOR_DIR}; diff --git a/crates/socket-patch-core/src/vex/discover/nuget.rs b/crates/socket-patch-core/src/vex/discover/nuget.rs index 3f608233f..d7f3cd95d 100644 --- a/crates/socket-patch-core/src/vex/discover/nuget.rs +++ b/crates/socket-patch-core/src/vex/discover/nuget.rs @@ -73,8 +73,8 @@ use super::{ Discovery, PatchedRef, UnlockedPin, WiringMode, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, DIAG_REF_UNATTRIBUTABLE, }; -use crate::formats::nuget::{parse_config, NugetConfig}; use crate::vendor::lock_inventory::LockIntegrity; +use crate::formats::nuget::{parse_config, NugetConfig}; use crate::vendor::nuget_config::{same_file, CONFIG_NAMES}; use crate::vendor::nuget_feed::{is_plain_nuget_token, nuget_lock_entries, nupkg_leaf}; use crate::vendor::path::VENDOR_DIR; diff --git a/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs b/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs index de8dc2e67..1e0bc6149 100644 --- a/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs +++ b/crates/socket-patch-core/tests/covgap_api_blob_fetcher.rs @@ -569,8 +569,5 @@ async fn fetch_missing_blobs_mixed_outcomes_aggregate_and_format() { // End-to-end formatter exercise with a genuinely mixed result. let rendered = format_fetch_result(&result); assert!(rendered.contains("Downloaded 1 blob\n"), "{rendered}"); - assert!( - rendered.contains("Failed to download 2 blobs"), - "{rendered}" - ); + assert!(rendered.contains("Failed to download 2 blobs"), "{rendered}"); } diff --git a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs index fbbda6290..24a50d9b9 100644 --- a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs +++ b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs @@ -95,11 +95,7 @@ async fn get_vendor_paths_global_nonexistent_composer_home_falls_back() { fn write_composer_shim(dir: &Path, echo_path: &Path) { use std::os::unix::fs::PermissionsExt; let shim = dir.join("composer"); - std::fs::write( - &shim, - format!("#!/bin/sh\necho '{}'\n", echo_path.display()), - ) - .unwrap(); + std::fs::write(&shim, format!("#!/bin/sh\necho '{}'\n", echo_path.display())).unwrap(); std::fs::set_permissions(&shim, std::fs::Permissions::from_mode(0o755)).unwrap(); } diff --git a/crates/socket-patch-core/tests/hosted_inventory.rs b/crates/socket-patch-core/tests/hosted_inventory.rs index db1ecd6ae..1bb3772d2 100644 --- a/crates/socket-patch-core/tests/hosted_inventory.rs +++ b/crates/socket-patch-core/tests/hosted_inventory.rs @@ -51,15 +51,9 @@ async fn contradicted_hosted_lock_is_contested_not_absent() { assert!(!inv.is_empty(), "contested wiring is hosted state: {inv:?}"); let refusal = inv.contested_refusal().expect("a refusal"); assert!(refusal.contains("npm-shrinkwrap.json"), "{refusal}"); - assert!( - refusal.contains("git checkout -- npm-shrinkwrap.json"), - "{refusal}" - ); + assert!(refusal.contains("git checkout -- npm-shrinkwrap.json"), "{refusal}"); assert!(refusal.contains("patched_ref_unattributable"), "{refusal}"); - assert!( - !refusal.contains(GRANT), - "the grant token is not a patch: {refusal}" - ); + assert!(!refusal.contains(GRANT), "the grant token is not a patch: {refusal}"); } #[tokio::test] diff --git a/crates/socket-patch-core/tests/poetry_hosted.rs b/crates/socket-patch-core/tests/poetry_hosted.rs index 2d2e17d5d..bd3ca3c83 100644 --- a/crates/socket-patch-core/tests/poetry_hosted.rs +++ b/crates/socket-patch-core/tests/poetry_hosted.rs @@ -1,4 +1,6 @@ -use socket_patch_core::patch::redirect::{rewrite_registry_redirect, DepOverride, Integrity}; +use socket_patch_core::patch::redirect::{ + rewrite_registry_redirect, DepOverride, Integrity, +}; use socket_patch_core::utils::poetry_lock::rewrite_poetry_lock; use std::collections::BTreeMap; @@ -61,11 +63,7 @@ fn native_lock_generations_redirect_idempotently() { let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); assert_eq!( codes, - if pre_1_4 { - vec!["redirect_poetry_stale_install_risk"] - } else { - vec![] - }, + if pre_1_4 { vec!["redirect_poetry_stale_install_risk"] } else { vec![] }, "{version}: {:?}", result.warnings ); @@ -94,24 +92,12 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert!( - lock10.contains(&format!("url = \"{URL}#sha256={sha}&\"")), - "{lock10}" - ); + assert!(lock10.contains(&format!("url = \"{URL}#sha256={sha}&\"")), "{lock10}"); assert!(lock10.contains("reference = \"\""), "{lock10}"); - assert!( - lock10.contains(&format!( - "urllib3 = [{{ file = \"{WHEEL}\", hash = \"sha256:{sha}\" }}]" - )), - "{lock10}" - ); + assert!(lock10.contains(&format!("urllib3 = [{{ file = \"{WHEEL}\", hash = \"sha256:{sha}\" }}]")), "{lock10}"); // Poetry >= 1.2 consuming this 1.0 lock verifies the package `files` // entry, so it is written too (1.0 ignores the extra key). - assert_eq!( - lock10.matches(&format!("sha256:{sha}")).count(), - 2, - "{lock10}" - ); + assert_eq!(lock10.matches(&format!("sha256:{sha}")).count(), 2, "{lock10}"); let doc: toml_edit::DocumentMut = lock10.parse().unwrap(); assert!(doc["package"][0]["files"].is_array(), "{lock10}"); @@ -138,11 +124,7 @@ fn hosted_shapes_match_each_lock_generations_installer() { &BTreeMap::from([("poetry.lock".to_string(), lock10_populated)]), &[patch()], ); - assert!( - rerun.files.is_empty() && rerun.warnings.is_empty(), - "{:?}", - rerun.warnings - ); + assert!(rerun.files.is_empty() && rerun.warnings.is_empty(), "{:?}", rerun.warnings); let lock11 = rewrite_registry_redirect( &BTreeMap::from([("poetry.lock".to_string(), original("1.2.2"))]), @@ -150,15 +132,8 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert_eq!( - lock11.matches(&format!("sha256:{sha}")).count(), - 2, - "package files + metadata.files:\n{lock11}" - ); - assert!( - lock11.contains(&format!("url = \"{URL}\"")), - "no fragment on 1.1" - ); + assert_eq!(lock11.matches(&format!("sha256:{sha}")).count(), 2, "package files + metadata.files:\n{lock11}"); + assert!(lock11.contains(&format!("url = \"{URL}\"")), "no fragment on 1.1"); assert!(!lock11.contains("reference"), "{lock11}"); let doc: toml_edit::DocumentMut = lock11.parse().unwrap(); assert!(doc["package"][0]["files"].is_array()); @@ -170,19 +145,11 @@ fn hosted_shapes_match_each_lock_generations_installer() { ) .files["poetry.lock"] .clone(); - assert_eq!( - lock21.matches(&format!("sha256:{sha}")).count(), - 1, - "{lock21}" - ); + assert_eq!(lock21.matches(&format!("sha256:{sha}")).count(), 1, "{lock21}"); assert!(!lock21.contains("reference")); let pristine: toml_edit::DocumentMut = original("2.4.3").parse().unwrap(); let doc: toml_edit::DocumentMut = lock21.parse().unwrap(); - assert_eq!( - doc["metadata"].to_string(), - pristine["metadata"].to_string(), - "[metadata] untouched on 2.x" - ); + assert_eq!(doc["metadata"].to_string(), pristine["metadata"].to_string(), "[metadata] untouched on 2.x"); } #[test] @@ -281,21 +248,14 @@ fn absent_entries_warn_once_and_missing_sha256_is_gated_once_per_dep() { let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); assert_eq!( codes, - vec![ - "redirect_poetry_entry_not_found", - "redirect_poetry_entry_not_found" - ] + vec!["redirect_poetry_entry_not_found", "redirect_poetry_entry_not_found"] ); let mut missing_hash = patch(); missing_hash.integrity.sha256 = None; let result = rewrite_registry_redirect(&files, &[missing_hash]); assert!(result.files.is_empty()); let codes: Vec<&str> = result.warnings.iter().map(|w| w.code.as_str()).collect(); - assert_eq!( - codes, - vec!["redirect_poetry_missing_sha256"], - "gated once, not once per lock" - ); + assert_eq!(codes, vec!["redirect_poetry_missing_sha256"], "gated once, not once per lock"); } /// A future Poetry that bumps the lock minor (2.2) is rewritten like 2.1 in @@ -318,20 +278,11 @@ fn rotated_grant_token_supersedes_the_prior_hosted_url() { let first = rewrite_registry_redirect(&files, &[patch()]); let mut rotated = patch(); rotated.token = "00000000-0000-4000-8000-000000000000".into(); - rotated.artifact_url = URL.replace( - "7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e", - "00000000-0000-4000-8000-000000000000", - ); + rotated.artifact_url = URL.replace("7e52b8b6-53f2-4dc8-860a-1ae7ebd8be0e", "00000000-0000-4000-8000-000000000000"); let second = rewrite_registry_redirect(&first.files, &[rotated.clone()]); assert!(second.warnings.is_empty(), "{:?}", second.warnings); let lock = &second.files["poetry.lock"]; assert!(lock.contains(&rotated.artifact_url) && !lock.contains(URL)); assert_eq!(second.edits.len(), 1); - assert!(second.edits[0] - .original - .as_ref() - .unwrap() - .as_str() - .unwrap() - .contains(URL)); + assert!(second.edits[0].original.as_ref().unwrap().as_str().unwrap().contains(URL)); } diff --git a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs index abde352bb..33c34297c 100644 --- a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs +++ b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs @@ -18,7 +18,11 @@ use socket_patch_core::telemetry::{is_telemetry_disabled, sanitize_error_message /// Every environment variable that can independently disable telemetry. /// Scrubbing the full set is what makes the per-var causation asserts honest. -const DISABLE_VARS: &[&str] = &["SOCKET_TELEMETRY_DISABLED", "VITEST", "SOCKET_OFFLINE"]; +const DISABLE_VARS: &[&str] = &[ + "SOCKET_TELEMETRY_DISABLED", + "VITEST", + "SOCKET_OFFLINE", +]; /// Run `f` with all telemetry-disabling vars removed, restoring the prior /// values afterward even if `f` panics (so one failing assert can't poison diff --git a/crates/socket-patch-core/tests/upstream_restore_golden.rs b/crates/socket-patch-core/tests/upstream_restore_golden.rs index 9885db0a9..373e0bc04 100644 --- a/crates/socket-patch-core/tests/upstream_restore_golden.rs +++ b/crates/socket-patch-core/tests/upstream_restore_golden.rs @@ -13,12 +13,10 @@ use std::fs; use std::path::{Path, PathBuf}; use serial_test::serial; +use socket_patch_core::patch::redirect::{rewrite_registry_redirect_with_pipenv_version, DepOverride}; use socket_patch_core::patch::redirect::upstream::{ restore_upstream, HostedPin, PinStatus, RestoreOptions, }; -use socket_patch_core::patch::redirect::{ - rewrite_registry_redirect_with_pipenv_version, DepOverride, -}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -31,10 +29,7 @@ fn walk(dir: &Path) -> BTreeMap { if !dir.is_dir() { return out; } - for entry in walkdir::WalkDir::new(dir) - .into_iter() - .filter_map(Result::ok) - { + for entry in walkdir::WalkDir::new(dir).into_iter().filter_map(Result::ok) { if entry.file_type().is_file() { let rel = entry .path() @@ -50,27 +45,16 @@ fn walk(dir: &Path) -> BTreeMap { /// Tokens of `pattern` that `input` holds and `expected` does not: the /// upstream values the hosted rewrite replaced. -fn vanished( - input: &BTreeMap, - expected: &BTreeMap, - re: &str, -) -> Vec { +fn vanished(input: &BTreeMap, expected: &BTreeMap, re: &str) -> Vec { let re = regex::Regex::new(re).unwrap(); let all = |files: &BTreeMap| -> BTreeSet { files .values() - .flat_map(|t| { - re.captures_iter(t) - .map(|c| c[1].to_string()) - .collect::>() - }) + .flat_map(|t| re.captures_iter(t).map(|c| c[1].to_string()).collect::>()) .collect() }; let after = all(expected); - let mut out: Vec = all(input) - .into_iter() - .filter(|t| !after.contains(t)) - .collect(); + let mut out: Vec = all(input).into_iter().filter(|t| !after.contains(t)).collect(); out.sort(); out } @@ -96,9 +80,10 @@ fn load(flavor: &str) -> Vec { // `expected/` holds only the files the rewrite changed. let mut expected = input.clone(); expected.extend(walk(&dir.join("expected"))); - let overrides = - serde_json::from_str(&fs::read_to_string(dir.join("overrides.json")).unwrap()) - .unwrap(); + let overrides = serde_json::from_str( + &fs::read_to_string(dir.join("overrides.json")).unwrap(), + ) + .unwrap(); Case { dir, input, @@ -147,23 +132,10 @@ async fn run_case_with( (walk(tmp.path()), statuses) } -fn assert_round_trip( - case: &Case, - after: &BTreeMap, - statuses: &[(String, PinStatus)], -) { - assert!( - !statuses.is_empty(), - "{}: discovery found no hosted pin", - case.dir.display() - ); +fn assert_round_trip(case: &Case, after: &BTreeMap, statuses: &[(String, PinStatus)]) { + assert!(!statuses.is_empty(), "{}: discovery found no hosted pin", case.dir.display()); for (purl, status) in statuses { - assert_eq!( - *status, - PinStatus::Restored, - "{}: {purl}", - case.dir.display() - ); + assert_eq!(*status, PinStatus::Restored, "{}: {purl}", case.dir.display()); } for (rel, want) in &case.input { assert_eq!( @@ -173,15 +145,8 @@ fn assert_round_trip( case.dir.display() ); } - let extra: Vec<&String> = after - .keys() - .filter(|k| !case.input.contains_key(*k)) - .collect(); - assert!( - extra.is_empty(), - "{}: left behind {extra:?}", - case.dir.display() - ); + let extra: Vec<&String> = after.keys().filter(|k| !case.input.contains_key(*k)).collect(); + assert!(extra.is_empty(), "{}: left behind {extra:?}", case.dir.display()); } /// Sets env vars for the guard's lifetime (tests using it are `#[serial]`). @@ -242,9 +207,10 @@ async fn npm_mock(case: &Case) -> MockServer { } Mock::given(method("GET")) .and(path(format!("/{}/{version}", name.replace('/', "%2f")))) - .respond_with(ResponseTemplate::new(200).set_body_json( - serde_json::json!({ "name": name, "version": version, "dist": dist }), - )) + .respond_with( + ResponseTemplate::new(200) + .set_body_json(serde_json::json!({ "name": name, "version": version, "dist": dist })), + ) .mount(&server) .await; } @@ -450,12 +416,7 @@ fn assert_refused( } other => panic!("{}: expected a refusal, got {other:?}", case.dir.display()), } - assert_eq!( - after, - &case.expected, - "{}: a refused pin must change nothing", - case.dir.display() - ); + assert_eq!(after, &case.expected, "{}: a refused pin must change nothing", case.dir.display()); } fn offline() -> RestoreOptions { @@ -547,8 +508,7 @@ fn transitive_lock() -> String { #[serial] async fn gem_edge_shapes_round_trip() { let gemfile = "source \"https://rubygems.org\"\n\ngem \"puma\"\n\ngroup :test do\n gem \"rails\", \"7.0.0\", require: false\nend\n"; - let crlf_gemfile = - "source \"https://rubygems.org\"\r\n\r\ngem \"rails\", \"7.0.0\"\r\ngem \"puma\"\r\n"; + let crlf_gemfile = "source \"https://rubygems.org\"\r\n\r\ngem \"rails\", \"7.0.0\"\r\ngem \"puma\"\r\n"; let two_sources_gemfile = "source \"https://rubygems.org\"\n\ngem \"rails\", \"7.0.0\"\nsource \"https://gems.example.com\" do\n gem \"private-gem\"\nend\n"; let two_sources_lock = "GEM\n remote: https://gems.example.com/\n specs:\n private-gem (1.0.0)\n\nGEM\n remote: https://rubygems.org/\n specs:\n rails (7.0.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n private-gem!\n rails (= 7.0.0)\n\nCHECKSUMS\n private-gem (1.0.0) sha256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n rails (7.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222\n\nBUNDLED WITH\n 2.6.2\n"; // Provably transitive: the rewriter appended after a trailing blank @@ -576,18 +536,12 @@ async fn gem_edge_shapes_round_trip() { ), synthetic( "multiple-gem-sections", - &[ - ("Gemfile", two_sources_gemfile), - ("Gemfile.lock", two_sources_lock), - ], + &[("Gemfile", two_sources_gemfile), ("Gemfile.lock", two_sources_lock)], gem_override("rails", "7.0.0"), ), synthetic( "transitive-appended", - &[ - ("Gemfile", transitive_gemfile), - ("Gemfile.lock", &transitive), - ], + &[("Gemfile", transitive_gemfile), ("Gemfile.lock", &transitive)], gem_override("zeitwerk", "2.6.0"), ), ]; @@ -646,10 +600,7 @@ async fn gem_pre_checksums_states() { .replace(" rails (~> 7.0)\n", " rails (= 7.0.0)!\n"); mixed.expected.insert("Gemfile.lock".into(), converged); let (after, statuses) = run_case_with(&mixed, None, &offline()).await; - assert_eq!( - statuses, - vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)] - ); + assert_eq!(statuses, vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)]); assert_eq!(after["Gemfile.lock"], restored_lock); assert_eq!(after["Gemfile"], restored_gemfile); @@ -662,10 +613,7 @@ async fn gem_pre_checksums_states() { .replace(" rails (~> 7.0)\n", " rails (= 7.0.0)!\n"); mixed.expected.insert("Gemfile.lock".into(), merged); let (after, statuses) = run_case_with(&mixed, None, &offline()).await; - assert_eq!( - statuses, - vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)] - ); + assert_eq!(statuses, vec![("pkg:gem/rails@7.0.0".to_string(), PinStatus::Restored)]); assert_eq!(after["Gemfile.lock"], restored_lock); assert_eq!(after["Gemfile"], restored_gemfile); } @@ -684,10 +632,7 @@ async fn gem_transitive_without_proof_stays_declared() { ); let (after, statuses) = gem_run(&case).await; assert_eq!(statuses[0].1, PinStatus::Restored); - assert_eq!( - after["Gemfile"], - format!("{gemfile}gem \"zeitwerk\", \"2.6.0\"\n") - ); + assert_eq!(after["Gemfile"], format!("{gemfile}gem \"zeitwerk\", \"2.6.0\"\n")); assert_eq!( after["Gemfile.lock"], lock.replace(" puma\n", " puma\n zeitwerk (= 2.6.0)\n") @@ -716,19 +661,10 @@ async fn gem_refusals_leave_everything_hosted() { // The upstream section is another registry's. let mut foreign = case.clone_with("foreign-upstream"); foreign.edit_both("Gemfile.lock", |t| { - t.replace( - "remote: https://rubygems.org/", - "remote: https://gems.example.com/", - ) + t.replace("remote: https://rubygems.org/", "remote: https://gems.example.com/") }); let (after, statuses) = gem_run(&foreign).await; - assert_refused( - &foreign, - &after, - &statuses, - "Gemfile.lock", - "not rubygems.org", - ); + assert_refused(&foreign, &after, &statuses, "Gemfile.lock", "not rubygems.org"); // Two upstream sections, neither singled out. let mut ambiguous = case.clone_with("ambiguous-upstream"); ambiguous.edit_both("Gemfile.lock", |t| { @@ -737,38 +673,18 @@ async fn gem_refusals_leave_everything_hosted() { "GEM\n remote: https://gems.example.com/\n specs:\n other (1.0.0)\n\nPLATFORMS", ) }); - ambiguous.edit_both("Gemfile", |t| { - t.replace("source \"https://rubygems.org\"\n", "") - }); - ambiguous.edit_both("Gemfile.lock", |t| { - t.replace( - "remote: https://rubygems.org/", - "remote: https://mirror.example.com/", - ) - }); + ambiguous.edit_both("Gemfile", |t| t.replace("source \"https://rubygems.org\"\n", "")); + ambiguous.edit_both("Gemfile.lock", |t| t.replace("remote: https://rubygems.org/", "remote: https://mirror.example.com/")); let (after, statuses) = gem_run(&ambiguous).await; - assert_refused( - &ambiguous, - &after, - &statuses, - "Gemfile.lock", - "upstream GEM sections", - ); + assert_refused(&ambiguous, &after, &statuses, "Gemfile.lock", "upstream GEM sections"); // The Gemfile block was hand-edited. let mut edited = case.clone_with("edited-block"); edited.expected.insert( "Gemfile".into(), - edited.expected["Gemfile"] - .replace(" gem \"rails\", \"7.0.0\"", " gem \"rails\", \"~> 7.0\""), + edited.expected["Gemfile"].replace(" gem \"rails\", \"7.0.0\"", " gem \"rails\", \"~> 7.0\""), ); let (after, statuses) = gem_run(&edited).await; - assert_refused( - &edited, - &after, - &statuses, - "Gemfile.lock", - "shape other than the source block", - ); + assert_refused(&edited, &after, &statuses, "Gemfile.lock", "shape other than the source block"); } // ── composer ──────────────────────────────────────────────────────────────── @@ -937,11 +853,7 @@ async fn composer_edge_shapes_round_trip() { &[("composer.lock", &escaped)], composer_override("acme/tool", "dev-main"), ), - synthetic( - "crlf", - &[("composer.lock", &crlf)], - composer_override("psr/log", "1.1.4"), - ), + synthetic("crlf", &[("composer.lock", &crlf)], composer_override("psr/log", "1.1.4")), ]; for case in &cases { let (after, statuses) = composer_run(case, |_| {}).await; @@ -960,42 +872,20 @@ async fn composer_refusals_leave_everything_hosted() { // Packagist now serves another commit for the version. let (after, statuses) = composer_run(&case, |d| d["dist"]["reference"] = "feedface".into()).await; - assert_refused( - &case, - &after, - &statuses, - "composer.lock", - "packagist now serves", - ); + assert_refused(&case, &after, &statuses, "composer.lock", "packagist now serves"); // Packagist does not list the version. let (after, statuses) = composer_run(&case, |d| d["version"] = "0.0.1".into()).await; - assert_refused( - &case, - &after, - &statuses, - "composer.lock", - "does not list version 1.1.4", - ); + assert_refused(&case, &after, &statuses, "composer.lock", "does not list version 1.1.4"); // Offline. let (after, statuses) = run_case_with(&case, None, &offline()).await; assert_refused(&case, &after, &statuses, "composer.lock", "offline"); // Locked from another repository. let mut foreign = case.clone_with("foreign"); foreign.edit_both("composer.lock", |t| { - t.replacen( - "https://packagist.org/downloads/", - "https://repo.example.com/downloads/", - 1, - ) + t.replacen("https://packagist.org/downloads/", "https://repo.example.com/downloads/", 1) }); let (after, statuses) = composer_run(&foreign, |_| {}).await; - assert_refused( - &foreign, - &after, - &statuses, - "composer.lock", - "not packagist", - ); + assert_refused(&foreign, &after, &statuses, "composer.lock", "not packagist"); // No notification-url, and composer.json names custom repositories. let mut custom = case.clone_with("custom-repos"); custom.edit_both("composer.lock", |t| { @@ -1012,13 +902,7 @@ async fn composer_refusals_leave_everything_hosted() { ); } let (after, statuses) = composer_run(&custom, |_| {}).await; - assert_refused( - &custom, - &after, - &statuses, - "composer.lock", - "custom repositories", - ); + assert_refused(&custom, &after, &statuses, "composer.lock", "custom repositories"); } // ── PyPI ───────────────────────────────────────────────────────────────────── @@ -1056,11 +940,7 @@ fn urllib3_dep() -> DepOverride { /// PyPI's blake2b-bucketed file URLs, with real urllib3 1.26.18's buckets: the /// sdist sorts before the wheel by URL, the reverse of filename order. fn pypi_file_url(filename: &str) -> String { - let bucket = if filename.ends_with(".tar.gz") { - "0c/39" - } else { - "b0/53" - }; + let bucket = if filename.ends_with(".tar.gz") { "0c/39" } else { "b0/53" }; format!("https://files.pythonhosted.org/packages/{bucket}/{filename}") } @@ -1073,18 +953,8 @@ fn urllib3_release() -> Release<'static> { "urllib3", "1.26.18", vec![ - ( - URLLIB3_WHEEL, - URLLIB3_WHEEL_SHA, - 143835, - "2023-10-17T17:46:21.184066Z", - ), - ( - URLLIB3_SDIST, - URLLIB3_SDIST_SHA, - 305687, - "2023-10-17T17:46:24.000000Z", - ), + (URLLIB3_WHEEL, URLLIB3_WHEEL_SHA, 143835, "2023-10-17T17:46:21.184066Z"), + (URLLIB3_SDIST, URLLIB3_SDIST_SHA, 305687, "2023-10-17T17:46:24.000000Z"), ], ) } @@ -1119,10 +989,7 @@ async fn pypi_mock(releases: &[Release<'_>]) -> (MockServer, EnvGuard) { } fn tree(files: &[(&str, String)]) -> BTreeMap { - files - .iter() - .map(|(k, v)| (k.to_string(), v.clone())) - .collect() + files.iter().map(|(k, v)| (k.to_string(), v.clone())).collect() } /// `input` as the real hosted rewriter leaves it. @@ -1169,24 +1036,14 @@ async fn assert_pypi_round_trip( pipenv: Option, ) { let rewritten = hosted(input, deps, pipenv); - assert_ne!( - &rewritten, input, - "{label}: the hosted rewrite changed nothing" - ); + assert_ne!(&rewritten, input, "{label}: the hosted rewrite changed nothing"); let (after, statuses) = restore_tree(&rewritten, &RestoreOptions::default()).await; - assert!( - !statuses.is_empty(), - "{label}: discovery found no hosted pin" - ); + assert!(!statuses.is_empty(), "{label}: discovery found no hosted pin"); for (purl, status) in &statuses { assert_eq!(*status, PinStatus::Restored, "{label}: {purl}"); } for (rel, want) in input { - assert_eq!( - after.get(rel), - Some(want), - "{label}: {rel} did not round-trip" - ); + assert_eq!(after.get(rel), Some(want), "{label}: {rel} did not round-trip"); } let extra: Vec<&String> = after.keys().filter(|k| !input.contains_key(*k)).collect(); assert!(extra.is_empty(), "{label}: left behind {extra:?}"); @@ -1209,20 +1066,13 @@ async fn pypi_refusal( PinStatus::Restored => None, }) .collect(); - assert!( - !refusals.is_empty() && refusals.len() == statuses.len(), - "{statuses:?}" - ); + assert!(!refusals.is_empty() && refusals.len() == statuses.len(), "{statuses:?}"); (refusals.join("\n"), rewritten, after) } fn fixture(rel: &str) -> String { - fs::read_to_string( - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures") - .join(rel), - ) - .unwrap() + fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(rel)) + .unwrap() } #[tokio::test] @@ -1235,12 +1085,7 @@ async fn requirements_golden_restores_modulo_name_casing() { let (after, statuses) = run_case(&case).await; assert!(!statuses.is_empty()); for (purl, status) in &statuses { - assert_eq!( - *status, - PinStatus::Restored, - "{}: {purl}", - case.dir.display() - ); + assert_eq!(*status, PinStatus::Restored, "{}: {purl}", case.dir.display()); } assert_eq!( after["requirements.txt"].to_ascii_lowercase(), @@ -1260,12 +1105,7 @@ async fn uv_golden_without_a_registry_sibling_is_refused() { let (_server, _env) = pypi_mock(&[( "click", "8.1.7", - vec![( - "click-8.1.7-py3-none-any.whl", - URLLIB3_WHEEL_SHA, - 1, - "2023-08-17T17:29:10Z", - )], + vec![("click-8.1.7-py3-none-any.whl", URLLIB3_WHEEL_SHA, 1, "2023-08-17T17:29:10Z")], )]) .await; let mut ran = 0; @@ -1280,10 +1120,7 @@ async fn uv_golden_without_a_registry_sibling_is_refused() { case.dir.display() ); } - assert_eq!( - after, case.expected, - "a refused pin must leave the files untouched" - ); + assert_eq!(after, case.expected, "a refused pin must leave the files untouched"); ran += 1; } assert!(ran > 0); @@ -1390,14 +1227,9 @@ async fn pdm_static_urls_round_trip() { &format!("{{url = \"{}\"", pypi_file_url(URLLIB3_SDIST)), ); // PDM writes a static_urls entry's files in URL order (sdist first here). - let wheel_line = format!( - " {{url = \"{}\", hash = \"sha256:{URLLIB3_WHEEL_SHA}\"}},\n", - pypi_file_url(URLLIB3_WHEEL) - ); + let wheel_line = format!(" {{url = \"{}\", hash = \"sha256:{URLLIB3_WHEEL_SHA}\"}},\n", pypi_file_url(URLLIB3_WHEEL)); assert!(lock.contains(&wheel_line), "{lock}"); - let lock = - lock.replacen(&wheel_line, "", 1) - .replacen("\n]\n", &format!("\n{wheel_line}]\n"), 1); + let lock = lock.replacen(&wheel_line, "", 1).replacen("\n]\n", &format!("\n{wheel_line}]\n"), 1); assert!( lock.find(URLLIB3_SDIST).unwrap() < lock.find(URLLIB3_WHEEL).unwrap(), "{lock}" @@ -1411,17 +1243,12 @@ async fn pdm_static_urls_round_trip() { async fn pdm_narrowed_lock_with_platform_wheels_is_refused() { let wheel = "urllib3-1.26.18-cp311-cp311-manylinux_2_17_x86_64.whl"; let mut release = urllib3_release(); - release - .2 - .push((wheel, URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); + release.2.push((wheel, URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); let (_server, _env) = pypi_mock(&[release]).await; let input = tree(&[("pdm.lock", fixture("pdm-native/2.29.2.lock"))]); let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!( - why.contains("not derivable") && why.contains("cross_platform"), - "{why}" - ); + assert!(why.contains("not derivable") && why.contains("cross_platform"), "{why}"); assert!(why.contains("git checkout -- pdm.lock"), "{why}"); assert_eq!(after, rewritten); // A cross-platform lock records every file, whatever its tags. @@ -1481,9 +1308,7 @@ async fn pipfile_lock_fixture_and_every_category_round_trip() { assert_pypi_round_trip(&label, &input, &[urllib3_dep()], None).await; } // Pipenv 7.x–2017 writes `path` (and, before 2018, no `index`). - let old = text - .replace(",\n \"index\": \"pypi\"", "") - .replace("\"index\": \"pypi\",\n ", ""); + let old = text.replace(",\n \"index\": \"pypi\"", "").replace("\"index\": \"pypi\",\n ", ""); assert!(!old.contains("\"index\""), "{old}"); let input = tree(&[("Pipfile.lock", old), ("Pipfile", "[packages]\n".into())]); assert_pypi_round_trip("pipenv 2017", &input, &[urllib3_dep()], Some(11)).await; @@ -1517,9 +1342,7 @@ async fn pipfile_lock_real_pipenv_shapes_round_trip_their_index() { let pipfile = fixture(&format!("{dir}/Pipfile")); let pristine: serde_json::Value = serde_json::from_str(&lock).unwrap(); assert_eq!( - pristine["default"]["urllib3"] - .get("index") - .and_then(|v| v.as_str()), + pristine["default"]["urllib3"].get("index").and_then(|v| v.as_str()), index, "{dir}: fixture drifted from what Pipenv writes" ); @@ -1534,16 +1357,9 @@ async fn pipfile_lock_real_pipenv_shapes_round_trip_their_index() { let hosted_lock = hosted(&input, &[urllib3_dep()], major)["Pipfile.lock"].clone(); let entry: serde_json::Value = serde_json::from_str(&hosted_lock).unwrap(); let entry = &entry["default"]["urllib3"]; - assert!( - entry.get("file").is_some() && entry.get("version").is_none(), - "{label}: {entry}" - ); + assert!(entry.get("file").is_some() && entry.get("version").is_none(), "{label}: {entry}"); for key in ["index", "markers", "extras"] { - assert_eq!( - entry.get(key), - pristine["default"]["urllib3"].get(key), - "{label}: {key}" - ); + assert_eq!(entry.get(key), pristine["default"]["urllib3"].get(key), "{label}: {key}"); } assert_pypi_round_trip(&label, &input, &[urllib3_dep()], major).await; } @@ -1567,17 +1383,12 @@ async fn pipfile_lock_marker_excluded_relock_hybrid_restores_the_original() { ("Pipfile", fixture(&format!("{dir}/Pipfile"))), ]); let rewritten = hosted(&input, &[urllib3_dep()], Some(2026)); - let mut relocked: serde_json::Value = serde_json::from_str(&rewritten["Pipfile.lock"]).unwrap(); + let mut relocked: serde_json::Value = + serde_json::from_str(&rewritten["Pipfile.lock"]).unwrap(); let pristine: serde_json::Value = serde_json::from_str(&lock).unwrap(); let entry = relocked["default"]["urllib3"].as_object_mut().unwrap(); - assert!( - entry.contains_key("file") && !entry.contains_key("index"), - "{entry:?}" - ); - entry.insert( - "hashes".into(), - pristine["default"]["urllib3"]["hashes"].clone(), - ); + assert!(entry.contains_key("file") && !entry.contains_key("index"), "{entry:?}"); + entry.insert("hashes".into(), pristine["default"]["urllib3"]["hashes"].clone()); entry.insert("version".into(), serde_json::json!("==1.26.18")); relocked.sort_all_objects(); let hybrid = reindent4(&serde_json::to_string_pretty(&relocked).unwrap()) + "\n"; @@ -1588,10 +1399,7 @@ async fn pipfile_lock_marker_excluded_relock_hybrid_restores_the_original() { for (purl, status) in &statuses { assert_eq!(*status, PinStatus::Restored, "{purl}"); } - assert_eq!( - after["Pipfile.lock"], lock, - "the hybrid restores the pristine bytes" - ); + assert_eq!(after["Pipfile.lock"], lock, "the hybrid restores the pristine bytes"); } #[tokio::test] @@ -1609,10 +1417,7 @@ async fn pipfile_lock_refusals() { ..Default::default() }; let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &offline).await; - assert!( - why.contains("offline") && why.contains("git checkout -- Pipfile.lock"), - "{why}" - ); + assert!(why.contains("offline") && why.contains("git checkout -- Pipfile.lock"), "{why}"); assert_eq!(after, rewritten); // A mirror as the only source. let mirror = lock.replace("https://pypi.org/simple", "https://mirror.example/simple"); @@ -1665,10 +1470,7 @@ async fn requirements_hash_mode_ambiguity_is_refused() { let (_server, _env) = pypi_mock(&[urllib3_release()]).await; let input = tree(&[("requirements.txt", "urllib3==1.26.18\n".into())]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!( - why.contains("hash-checking mode") && why.contains("not derivable"), - "{why}" - ); + assert!(why.contains("hash-checking mode") && why.contains("not derivable"), "{why}"); let input = tree(&[( "requirements.txt", "idna==3.4 --hash=sha256:aaaa\nsix==1.16.0\nurllib3==1.26.18\n".into(), @@ -1686,10 +1488,7 @@ async fn requirements_hash_mode_ambiguity_is_refused() { offline: true, ..Default::default() }; - let input = tree(&[( - "requirements.txt", - "flask==2.0.1\nurllib3==1.26.18\n".into(), - )]); + let input = tree(&[("requirements.txt", "flask==2.0.1\nurllib3==1.26.18\n".into())]); let rewritten = hosted(&input, &[urllib3_dep()], None); let (after, statuses) = restore_tree(&rewritten, &offline).await; assert_eq!(statuses[0].1, PinStatus::Restored); @@ -1697,9 +1496,7 @@ async fn requirements_hash_mode_ambiguity_is_refused() { // …and is refused in hash mode. let input = tree(&[( "requirements.txt", - format!( - "idna==3.4 --hash=sha256:aaaa\nurllib3==1.26.18 --hash=sha256:{URLLIB3_WHEEL_SHA}\n" - ), + format!("idna==3.4 --hash=sha256:aaaa\nurllib3==1.26.18 --hash=sha256:{URLLIB3_WHEEL_SHA}\n"), )]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &offline).await; assert!(why.contains("offline"), "{why}"); @@ -1710,12 +1507,7 @@ async fn requirements_hash_mode_ambiguity_is_refused() { async fn a_refused_pin_leaves_the_other_pins_restored() { // PyPI knows urllib3 only: idna's hashes cannot be re-derived. let (_server, _env) = pypi_mock(&[urllib3_release()]).await; - let idna = pypi_dep( - "idna", - "3.4", - "idna-3.4-py3-none-any.whl", - "44444444-4444-4444-4444-444444444444", - ); + let idna = pypi_dep("idna", "3.4", "idna-3.4-py3-none-any.whl", "44444444-4444-4444-4444-444444444444"); let input = tree(&[( "requirements.txt", format!( @@ -1729,10 +1521,7 @@ async fn a_refused_pin_leaves_the_other_pins_restored() { assert!(matches!(status("pkg:pypi/idna@3.4"), PinStatus::Refused(why) if why.contains("404"))); let lines: Vec<&str> = after["requirements.txt"].lines().collect(); assert_eq!(lines[0], "six==1.16.0 --hash=sha256:aaaa"); - assert!( - lines[1].starts_with("idna @ https://patch.socket.dev/"), - "{lines:?}" - ); + assert!(lines[1].starts_with("idna @ https://patch.socket.dev/"), "{lines:?}"); assert_eq!(lines[2], input["requirements.txt"].lines().nth(2).unwrap()); } @@ -1811,13 +1600,7 @@ async fn uv_project_locks_round_trip() { ("uv.lock", lock.replace('\n', eol)), ("pyproject.toml", pyproject.replace('\n', eol)), ]); - assert_pypi_round_trip( - &format!("uv direct {eol:?}"), - &input, - &[urllib3_dep()], - None, - ) - .await; + assert_pypi_round_trip(&format!("uv direct {eol:?}"), &input, &[urllib3_dep()], None).await; } // A transitive dependency: the override the rewrite pins in the // pyproject and the lock's `[manifest]` both go again. @@ -1890,10 +1673,7 @@ async fn uv_refusals() { { let (_server, _env) = pypi_mock(&[urllib3_release()]).await; // No other entry shows how this uv joins specifier clauses. - let input = tree(&[ - ("uv.lock", direct.clone()), - ("pyproject.toml", pyproject.into()), - ]); + let input = tree(&[("uv.lock", direct.clone()), ("pyproject.toml", pyproject.into())]); let (why, rewritten, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; assert!(why.contains("multi-clause"), "{why}"); @@ -1931,12 +1711,7 @@ async fn uv_refusals() { } // A release with interpreter-specific wheels. let mut release = urllib3_release(); - release.2.push(( - "urllib3-1.26.18-cp311-cp311-win_amd64.whl", - URLLIB3_WHEEL_SHA, - 1, - "2023-10-17T17:46:21Z", - )); + release.2.push(("urllib3-1.26.18-cp311-cp311-win_amd64.whl", URLLIB3_WHEEL_SHA, 1, "2023-10-17T17:46:21Z")); let (_server, _env) = pypi_mock(&[release]).await; let input = tree(&[("uv.lock", direct)]); let (why, _, _) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; @@ -1990,10 +1765,7 @@ async fn vlt_goldens_round_trip() { // refused a package whose package-lock.json entry the rewrite still // pinned; with vlt-lock.json upstream that pin is not live wiring, so // discovery (rightly) reports no pin to restore there. - let not_invertible = [ - "sibling-package-lock-vlt-installed", - "sibling-refused-in-vlt", - ]; + let not_invertible = ["sibling-package-lock-vlt-installed", "sibling-refused-in-vlt"]; let mut ran = 0; for case in load("npm/vlt") { let name = case.dir.file_name().unwrap().to_string_lossy().into_owned(); @@ -2038,10 +1810,7 @@ async fn maven_config_merge_keeps_the_resolver_lines() { .find(|c| c.dir.ends_with("mvn-config-merge")) .unwrap(); let (after, statuses) = run_case(&case).await; - assert!( - matches!(statuses[..], [(_, PinStatus::Restored)]), - "{statuses:?}" - ); + assert!(matches!(statuses[..], [(_, PinStatus::Restored)]), "{statuses:?}"); for rel in ["pom.xml", ".mvn/checksums/checksums.sha256"] { assert_eq!(after.get(rel), case.input.get(rel), "{rel}"); } @@ -2062,9 +1831,7 @@ async fn nuget_mock(case: &Case) -> MockServer { let (id, version) = (id.to_lowercase(), entry["resolved"].as_str().unwrap()); let catalog = format!("{}/catalog0/data/{id}.{version}.json", server.uri()); Mock::given(method("GET")) - .and(path(format!( - "/v3/registration5-gz-semver2/{id}/{version}.json" - ))) + .and(path(format!("/v3/registration5-gz-semver2/{id}/{version}.json"))) .respond_with( ResponseTemplate::new(200) .set_body_json(serde_json::json!({ "catalogEntry": catalog })), @@ -2134,17 +1901,11 @@ async fn nuget_non_invertible_goldens_restore_or_refuse_as_documented() { let PinStatus::Refused(why) = status else { panic!("{name}: {status:?}"); }; - assert!( - why.contains("corp-feed") && why.contains("git checkout"), - "{why}" - ); + assert!(why.contains("corp-feed") && why.contains("git checkout"), "{why}"); assert_eq!(after, case.expected, "{name}: a refusal changes nothing"); } else { assert_eq!(*status, PinStatus::Restored, "{name}"); - assert_eq!( - after.get("packages.lock.json"), - case.input.get("packages.lock.json") - ); + assert_eq!(after.get("packages.lock.json"), case.input.get("packages.lock.json")); let config = &after["nuget.config"]; assert!(!config.contains("socket-patch") && !config.contains("packageSourceMapping")); } diff --git a/crates/socket-patch-core/tests/uv_hosted.rs b/crates/socket-patch-core/tests/uv_hosted.rs index 81696f5dc..9a2526cd7 100644 --- a/crates/socket-patch-core/tests/uv_hosted.rs +++ b/crates/socket-patch-core/tests/uv_hosted.rs @@ -1,6 +1,8 @@ use std::collections::BTreeMap; -use socket_patch_core::patch::redirect::{rewrite_registry_redirect, DepOverride, Integrity}; +use socket_patch_core::patch::redirect::{ + rewrite_registry_redirect, DepOverride, Integrity, +}; fn patch(name: &str) -> DepOverride { DepOverride { diff --git a/crates/socket-patch-node/src/lib.rs b/crates/socket-patch-node/src/lib.rs index 29fa8e6ae..d83403b84 100644 --- a/crates/socket-patch-node/src/lib.rs +++ b/crates/socket-patch-node/src/lib.rs @@ -15,11 +15,11 @@ use std::sync::Arc; use napi::bindgen_prelude::{Buffer, External, Function, JsObjectValue, Object, PromiseRaw}; use napi::{Env, Status}; use napi_derive::napi; -use socket_patch_core::api::client::PatchApi; use socket_patch_core::hosted::memory::{ - self as hosted_memory, EngineError, HostedScanOptions, HostedScanOutput, PresentKind, - SelectOptions, SessionBuilder, TreeEntryInput, + self as hosted_memory, EngineError, HostedScanOptions, HostedScanOutput, PresentKind, SelectOptions, + SessionBuilder, TreeEntryInput, }; +use socket_patch_core::api::client::PatchApi; use tokio_util::sync::CancellationToken; use provider::{JsPatchApi, ProviderRefs};