From 73f9af5eeaacb19d6f32bf0f755a7d531e660859 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 00:28:38 +0000 Subject: [PATCH 1/5] Start fix for #732 Assisted-by: Claude Code:claude-opus-5-5 From 8c9081c6b0b361fb8098a351709995c2adb6b988 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 00:34:18 +0000 Subject: [PATCH 2/5] Re-apply recorded patches in human scan output After a reinstall put pristine bytes back, `scan --mode agent` or `scan --sync` without `--json` printed "[skip] ... (already recorded)" and exited 0 while leaving the package unpatched. Only the `--json` path re-applied (#454). The human path now hands recorded selections to the same download step, whose nested apply re-applies them; they are listed as `[re-apply]`. `--dry-run` and report-only scans still change nothing and say what a wet run would do. Fixes #732 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/discovery.rs | 62 ++- .../src/commands/scan/hosted.rs | 54 +- .../socket-patch-cli/src/commands/scan/mod.rs | 114 +++-- .../src/commands/scan/policy.rs | 68 ++- .../src/commands/scan/render.rs | 26 +- .../src/commands/scan/rollout.rs | 20 +- .../src/commands/scan/rollout_args.rs | 2 - .../socket-patch-cli/src/commands/vendor.rs | 5 +- .../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 | 172 ++++++- 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_redirect_yarn_berry_build.rs | 6 +- .../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/e2e_yarn4_pnpm_linker_build.rs | 16 +- .../tests/e2e_yarn4_workspaces_build.rs | 16 +- .../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.rs | 7 +- .../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/in_process_vendor.rs | 10 +- .../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/python_crawler.rs | 14 +- .../src/formats/cargo/mod.rs | 12 +- .../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 | 14 +- .../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 | 187 +++++-- .../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/state.rs | 2 - .../src/patch/redirect/upstream/bun_lockb.rs | 5 +- .../src/patch/redirect/upstream/cargo.rs | 39 +- .../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 +- crates/socket-patch-core/src/utils/process.rs | 15 +- .../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/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 +- crates/socket-patch-core/src/vendor/pypi.rs | 9 +- .../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 | 418 ++++++++++++---- crates/socket-patch-core/tests/uv_hosted.rs | 4 +- crates/socket-patch-node/src/lib.rs | 6 +- 130 files changed, 2421 insertions(+), 863 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/apply.rs b/crates/socket-patch-cli/src/commands/apply.rs index f5918a3dd..4e446491c 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 ea45e6915..34ae4b1a3 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; @@ -141,9 +141,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 @@ -153,10 +155,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(), @@ -164,7 +164,8 @@ pub(crate) fn hosted_state_from_pins( description: String::new(), license: String::new(), tier: String::new(), - }); + } + }); } state } @@ -191,4 +192,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 0d4be4bb2..143382b02 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 5f4191038..36d4b4760 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)) @@ -1170,7 +1171,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/discovery.rs b/crates/socket-patch-cli/src/commands/scan/discovery.rs index b83f63990..33031413c 100644 --- a/crates/socket-patch-cli/src/commands/scan/discovery.rs +++ b/crates/socket-patch-cli/src/commands/scan/discovery.rs @@ -168,29 +168,32 @@ pub(crate) async fn vendored_ledger_supplement( } // `(ledger key, base purl, entry)`; the artifact fallback has no // entries to probe, so it never reports unwired keys. - let candidates: Vec<(String, String, Option<&socket_patch_core::vendor::VendorEntry>)> = - match state { - Ok(state) => state - .entries - .iter() - .map(|(key, entry)| { - ( - key.clone(), - strip_purl_qualifiers(&entry.base_purl).to_string(), - Some(entry), - ) - }) - .collect(), - // Corrupt/unreadable ledger (a MISSING file is Ok(empty) above): - // recover the vendored set from the committed artifacts, or - // `scan --prune` (whose ledger exemption also degrades to empty) - // would delete still-vendored packages' manifest entries and blobs. - Err(_) => vendored_purls_from_artifacts(common) - .await - .into_iter() - .map(|base| (base.clone(), base, None)) - .collect(), - }; + let candidates: Vec<( + String, + String, + Option<&socket_patch_core::vendor::VendorEntry>, + )> = match state { + Ok(state) => state + .entries + .iter() + .map(|(key, entry)| { + ( + key.clone(), + strip_purl_qualifiers(&entry.base_purl).to_string(), + Some(entry), + ) + }) + .collect(), + // Corrupt/unreadable ledger (a MISSING file is Ok(empty) above): + // recover the vendored set from the committed artifacts, or + // `scan --prune` (whose ledger exemption also degrades to empty) + // would delete still-vendored packages' manifest entries and blobs. + Err(_) => vendored_purls_from_artifacts(common) + .await + .into_iter() + .map(|base| (base.clone(), base, None)) + .collect(), + }; // Composer by release identity: a ledger `@3.0.2.0` is the crawled // `@3.0.2`, not a second package to supplement. let key = |p: &str| composer_purl_identity(p).unwrap_or_else(|| normalize_purl(p).into_owned()); @@ -1038,7 +1041,9 @@ mod tests { ..GlobalArgs::default() }; let state = socket_patch_core::vendor::load_state(root).await; - vendored_ledger_supplement(&args, crawled, &state).await.packages + vendored_ledger_supplement(&args, crawled, &state) + .await + .packages } /// A ledger entry vendored as `@3.0.2.0` is the crawled composer @@ -1073,7 +1078,9 @@ mod tests { out.iter().map(|p| &p.purl).collect::>() ); - let out = vendored_ledger_supplement(&args, &[], &Ok(state)).await.packages; + let out = vendored_ledger_supplement(&args, &[], &Ok(state)) + .await + .packages; assert_eq!( out.iter().map(|p| p.purl.as_str()).collect::>(), vec!["pkg:composer/psr/log@3.0.2.0"] @@ -1176,7 +1183,10 @@ mod tests { let state = npm_ledger_with_lock(tmp.path(), lock.as_deref()).await; let out = vendored_ledger_supplement(&args, &[], &state).await; assert_eq!( - out.packages.iter().map(|p| p.purl.as_str()).collect::>(), + out.packages + .iter() + .map(|p| p.purl.as_str()) + .collect::>(), vec!["pkg:npm/left-pad@1.3.0"], "lock={lock:?}" ); diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index 97e6866ce..119b2dc7e 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -932,7 +932,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, @@ -944,6 +945,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. @@ -2304,13 +2306,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 @@ -4391,19 +4399,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 8ce80d9f1..f9bc96d25 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 @@ -318,11 +318,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. @@ -500,9 +496,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)); @@ -568,7 +565,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 { @@ -1317,7 +1318,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 @@ -1390,7 +1392,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}"); @@ -1491,7 +1496,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}"); @@ -1499,11 +1507,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 @@ -1704,8 +1709,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 { @@ -1860,13 +1868,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; } @@ -2222,7 +2229,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, @@ -2515,12 +2523,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. @@ -2730,8 +2733,11 @@ async fn run_scan( plan_kept_rows(&mut stage, rows, selected) }; - // Drop selections the manifest already records at the same uuid. - // Agent mode only: vendored mode never reads the manifest. + // Set aside selections the manifest already records at the same uuid. + // A wet agent run still hands them to the download step below, whose + // nested apply re-applies them after a reinstall (#454, #732); a + // preview only names them. Agent mode only: vendored mode never reads + // the manifest. let recorded = |p: &PatchSearchResult| { existing_manifest .as_ref() @@ -2743,17 +2749,18 @@ async fn run_scan( } else { selected.into_iter().partition(|p| recorded(p)) }; + let reapply = !report_only && !args.common.dry_run; if !silent { for p in &already_recorded { open_paragraph(&mut skip_paragraph); println!( "{}", - render::already_recorded_line(&normalize_purl(&p.purl), &p.uuid) + render::already_recorded_line(&normalize_purl(&p.purl), &p.uuid, reapply) ); } } - if selected.is_empty() { + if selected.is_empty() && !(reapply && !already_recorded.is_empty()) { if !silent { open_paragraph(&mut skip_paragraph); if !stage.deferred_keys().is_empty() { @@ -2764,6 +2771,8 @@ async fn run_scan( } } else if already_recorded.is_empty() { println!("No patches selected."); + } else if args.common.dry_run { + println!("{}", render::ALL_ALREADY_RECORDED_DRY_RUN); } else { println!("{}", render::ALL_ALREADY_RECORDED); } @@ -2773,7 +2782,7 @@ async fn run_scan( } // Display detailed summary of selected patches (skipped under --silent). - if !silent { + if !silent && !selected.is_empty() { if vendor { println!("\nPatches to vendor:\n"); } else { @@ -2927,9 +2936,16 @@ async fn run_scan( ) .await } else { - let (code, _) = - download_and_apply_patches_with(&selected, ¶ms, &download_run(&args, &api_client)) - .await; + // The recorded selections ride along: the fetch loop skips their + // record and the nested apply re-applies them (a no-op on disk + // when the installed copy is still patched). + let to_download: Vec<_> = selected.iter().chain(&already_recorded).cloned().collect(); + let (code, _) = download_and_apply_patches_with( + &to_download, + ¶ms, + &download_run(&args, &api_client), + ) + .await; code }; @@ -2982,14 +2998,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 2f881da3e..9212145e7 100644 --- a/crates/socket-patch-cli/src/commands/scan/render.rs +++ b/crates/socket-patch-cli/src/commands/scan/render.rs @@ -300,18 +300,25 @@ pub(super) fn not_installed_skip_line(purl: &str) -> String { ) } -/// `[skip]` line for a selection the manifest already records. -pub(super) fn already_recorded_line(purl: &str, uuid: &str) -> String { +/// Line for a selection the manifest already records: `[re-apply]` on a +/// wet agent run (the nested apply re-applies it), `[skip]` in a preview. +pub(super) fn already_recorded_line(purl: &str, uuid: &str, reapply: bool) -> String { + let tag = if reapply { "re-apply" } else { "skip" }; format!( - " [skip] {purl} (already recorded: {})", + " [{tag}] {purl} (already recorded: {})", super::super::get::short_uuid(uuid) ) } -/// Printed when every selection is already recorded (agent mode). +/// Printed when every selection is already recorded and nothing is applied +/// (report-only scan). pub(super) const ALL_ALREADY_RECORDED: &str = "All selected patches are already recorded in the manifest; run `socket-patch apply` to re-apply them."; +/// [`ALL_ALREADY_RECORDED`] for an agent-mode `--dry-run`. +pub(super) const ALL_ALREADY_RECORDED_DRY_RUN: &str = + "All selected patches are already recorded in the manifest; a run without --dry-run re-applies them."; + /// The terminal error when no package's patch details could be fetched. pub(super) fn fetch_details_failed(failed: &[(String, String)]) -> String { match failed { @@ -746,7 +753,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")); } @@ -760,9 +770,13 @@ mod tests { assert!(l.contains("`socket-patch scan --mode vendored`"), "{l}"); assert!(!l.contains("--vendor`"), "{l}"); assert_eq!( - already_recorded_line("pkg:npm/x@1", "884e9f6d-aaaa"), + already_recorded_line("pkg:npm/x@1", "884e9f6d-aaaa", false), " [skip] pkg:npm/x@1 (already recorded: 884e9f6d)" ); + assert_eq!( + already_recorded_line("pkg:npm/x@1", "884e9f6d-aaaa", true), + " [re-apply] pkg:npm/x@1 (already recorded: 884e9f6d)" + ); } #[test] 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/src/commands/vendor.rs b/crates/socket-patch-cli/src/commands/vendor.rs index 59be95b85..ff8c46a97 100644 --- a/crates/socket-patch-cli/src/commands/vendor.rs +++ b/crates/socket-patch-cli/src/commands/vendor.rs @@ -257,10 +257,7 @@ pub(crate) async fn dispatch_revert_one_opts( /// dependency graph? `None` = cannot determine — callers must keep the /// entry (fail-safe): ecosystems other than npm and cargo have no in-use /// probe yet, and a missing/unreadable lockfile proves nothing. -pub(crate) async fn dispatch_in_use_one( - entry: &VendorEntry, - project_root: &Path, -) -> Option { +pub(crate) async fn dispatch_in_use_one(entry: &VendorEntry, project_root: &Path) -> Option { match entry.ecosystem.as_str() { "npm" => vendor::npm_flavor::vendored_entry_in_use(entry, project_root).await, // Cargo probes the lock entry's shape: detached + `[patch]` pointing 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 54ddf7441..235030104 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!( @@ -2429,7 +2440,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..54e8750ab 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") ); } - } // --------------------------------------------------------------------------- @@ -2262,10 +2267,153 @@ fn scan_mode_conflict_error_is_capitalized_and_names_no_hidden_flag() { assert!(!stderr.contains("--redirect"), "{stderr:?}"); } -/// A selection the manifest already records at the same uuid is not -/// offered again (it would only be downloaded to be skipped). +/// Mount batch, by-package and view endpoints for two patched packages +/// (each `index.js` goes from `before\n` to `after\n`). +async fn mount_two_patch_api(mock: &MockServer, pkgs: &[(&str, &str)], before: &[u8]) { + let patches = |purl: &str, uuid: &str| { + serde_json::json!({ + "purl": purl, + "patches": [{ + "uuid": uuid, "purl": purl, "tier": "free", + "cveIds": [], "ghsaIds": [], "severity": "high", + "title": "covgap test patch" + }] + }) + }; + Mock::given(method("POST")) + .and(path(format!("/v0/orgs/{ORG_SLUG}/patches/batch"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "packages": pkgs.iter().map(|(p, u)| patches(p, u)).collect::>(), + "canAccessPaidPatches": false, + }))) + .mount(mock) + .await; + for (purl, uuid) in pkgs { + mount_by_package(mock, purl, uuid, serde_json::json!({})).await; + Mock::given(method("GET")) + .and(path(format!("/v0/orgs/{ORG_SLUG}/patches/view/{uuid}"))) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({ + "uuid": uuid, + "purl": purl, + "publishedAt": "2024-01-01T00:00:00Z", + "files": { + "package/index.js": { + "beforeHash": git_sha256(before), + "afterHash": git_sha256(b"after\n"), + "blobContent": "YWZ0ZXIK", + } + }, + "vulnerabilities": {}, + "description": "Covgap test patch", + "license": "MIT", + "tier": "free", + }))) + .mount(mock) + .await; + } +} + +/// #732: a human-output `scan --mode agent` / `scan --sync` re-applies a +/// patch the manifest already records once a reinstall has put the +/// pristine bytes back (the `--json` path has done so since #454). #[tokio::test] -async fn scan_human_does_not_offer_an_already_recorded_patch() { +async fn scan_human_reapplies_an_already_recorded_patch_after_reinstall() { + for flags in [&["--mode", "agent"][..], &["--sync"][..]] { + let mock = MockServer::start().await; + let purl = "pkg:npm/minimist@1.2.2"; + let before = b"before\n"; + mount_one_patch_api(&mock, purl, before).await; + + let tmp = tempfile::tempdir().unwrap(); + write_root_package_json(tmp.path()); + write_npm_package(tmp.path(), "minimist", "1.2.2", before); + let index = tmp.path().join("node_modules/minimist/index.js"); + + let (code, stdout, stderr) = run_scan_human(tmp.path(), &mock.uri(), flags); + assert_eq!(code, 0, "flags={flags:?}: stdout={stdout}; stderr={stderr}"); + assert_eq!( + std::fs::read(&index).unwrap(), + b"after\n", + "flags={flags:?}" + ); + + // `npm ci` / `rm -rf node_modules && npm install`. + std::fs::write(&index, before).unwrap(); + + let (code, stdout, stderr) = run_scan_human(tmp.path(), &mock.uri(), flags); + assert_eq!(code, 0, "flags={flags:?}: stdout={stdout}; stderr={stderr}"); + assert_eq!( + std::fs::read(&index).unwrap(), + b"after\n", + "flags={flags:?}: the recorded patch must be re-applied; stdout={stdout}; stderr={stderr}" + ); + assert!( + stdout.contains(&format!("[re-apply] {purl} (already recorded: 11111111)")), + "flags={flags:?}: {stdout:?}" + ); + assert!( + !stdout.contains("run `socket-patch apply` to re-apply them"), + "flags={flags:?}: {stdout:?}" + ); + let manifest = + std::fs::read_to_string(tmp.path().join(".socket/manifest.json")).expect("manifest"); + let v: serde_json::Value = serde_json::from_str(&manifest).unwrap(); + assert_eq!(v["patches"][purl]["uuid"], UUID, "flags={flags:?}: {v}"); + } +} + +/// #732: when a run also has a new patch, the recorded-but-reverted one +/// is re-applied alongside it rather than dropped from the selection. +#[tokio::test] +async fn scan_human_reapplies_recorded_patch_alongside_a_new_one() { + const UUID_B: &str = "22222222-2222-4222-8222-222222222222"; + let a = "pkg:npm/minimist@1.2.2"; + let b = "pkg:npm/left-pad@1.3.0"; + let before = b"before\n"; + + let tmp = tempfile::tempdir().unwrap(); + write_root_package_json(tmp.path()); + write_npm_package(tmp.path(), "minimist", "1.2.2", before); + write_npm_package(tmp.path(), "left-pad", "1.3.0", before); + let index_a = tmp.path().join("node_modules/minimist/index.js"); + let index_b = tmp.path().join("node_modules/left-pad/index.js"); + + // First run: only `a` has a patch. + let first = MockServer::start().await; + mount_two_patch_api(&first, &[(a, UUID)], before).await; + let (code, stdout, stderr) = run_scan_agent(tmp.path(), &first.uri(), &[]); + assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); + assert_eq!(std::fs::read(&index_a).unwrap(), b"after\n"); + assert_eq!(std::fs::read(&index_b).unwrap(), before); + + // Reinstall, then a patch for `b` is published. + std::fs::write(&index_a, before).unwrap(); + let second = MockServer::start().await; + mount_two_patch_api(&second, &[(a, UUID), (b, UUID_B)], before).await; + let (code, stdout, stderr) = run_scan_agent(tmp.path(), &second.uri(), &[]); + assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); + assert_eq!( + std::fs::read(&index_b).unwrap(), + b"after\n", + "new patch applied; stdout={stdout}; stderr={stderr}" + ); + assert_eq!( + std::fs::read(&index_a).unwrap(), + b"after\n", + "recorded patch re-applied; stdout={stdout}; stderr={stderr}" + ); + assert!(stdout.contains("Patches to apply:"), "{stdout:?}"); + assert!( + stdout.contains(&format!("[re-apply] {a} (already recorded: 11111111)")), + "{stdout:?}" + ); +} + +/// `--dry-run` stays a non-mutating preview: an already-recorded +/// selection is neither downloaded nor re-applied, and the message says a +/// wet run re-applies it. +#[tokio::test] +async fn scan_human_dry_run_previews_reapply_of_a_recorded_patch() { let mock = MockServer::start().await; let purl = "pkg:npm/minimist@1.2.2"; mount_batch_one(&mock, purl, UUID, "free", &[], false).await; @@ -2275,24 +2423,32 @@ async fn scan_human_does_not_offer_an_already_recorded_patch() { write_root_package_json(tmp.path()); write_npm_package(tmp.path(), "minimist", "1.2.2", b"x\n"); seed_manifest(tmp.path(), &[(purl, UUID)]); + let manifest_before = std::fs::read(tmp.path().join(".socket/manifest.json")).unwrap(); - let (code, stdout, stderr) = run_scan_agent(tmp.path(), &mock.uri(), &["--yes"]); + let (code, stdout, stderr) = run_scan_agent(tmp.path(), &mock.uri(), &["--dry-run"]); assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); assert!( stdout.contains(&format!("[skip] {purl} (already recorded: 11111111)")), "{stdout:?}" ); assert!( - stdout.contains("All selected patches are already recorded in the manifest"), + stdout.contains("a run without --dry-run re-applies them"), "{stdout:?}" ); assert!(!stdout.contains("Patches to apply:"), "{stdout:?}"); - assert!(!stderr.contains("Download and apply"), "{stderr:?}"); assert_eq!( view_gets(&recorded(&mock).await), 0, "nothing is downloaded" ); + assert_eq!( + std::fs::read(tmp.path().join("node_modules/minimist/index.js")).unwrap(), + b"x\n" + ); + assert_eq!( + std::fs::read(tmp.path().join(".socket/manifest.json")).unwrap(), + manifest_before + ); } /// The human table's PACKAGE column grows to fit the PURL (a fixed-width 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 1fed4afd2..3e388d0ec 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_redirect_yarn_berry_build.rs b/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs index e021d9015..5a412ffe0 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs @@ -574,9 +574,9 @@ async fn berry_hosted_project( let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"] - .as_object() - .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"].as_object().is_some_and(|r| r + .iter() + .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); 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/e2e_yarn4_pnpm_linker_build.rs b/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs index ce5d1144b..ddadbb45d 100644 --- a/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs +++ b/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs @@ -571,9 +571,9 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"] - .as_object() - .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"].as_object().is_some_and(|r| r + .iter() + .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); @@ -596,7 +596,10 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes serde_json::from_slice(&std::fs::read(proj.join("package.json")).unwrap()).unwrap(); let before: serde_json::Value = serde_json::from_slice(&pkg_before).unwrap(); after.as_object_mut().unwrap().shift_remove("resolutions"); - assert_eq!(after, before, "the hosted pin only adds `resolutions` to package.json"); + assert_eq!( + after, before, + "the hosted pin only adds `resolutions` to package.json" + ); } eprintln!("HOSTED REWIRE OK"); @@ -625,7 +628,10 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes eprintln!("FRESH INSTALL + YARN NODE RESOLUTION OK"); // MANIFEST-LESS VEX over the hosted wiring (see `yarn_berry_common`). - let registry_state = [("yarn.lock", registry_lock), ("package.json", pkg_before.clone())]; + let registry_state = [ + ("yarn.lock", registry_lock), + ("package.json", pkg_before.clone()), + ]; let yarn = |cwd: &Path, args: &[&str], env: &[(&str, &str)]| corepack(cwd, yarn_berry(), args, env); let api_url = server.uri(); diff --git a/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs b/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs index d2aec0078..2794a4781 100644 --- a/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs +++ b/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs @@ -566,9 +566,9 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"] - .as_object() - .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"].as_object().is_some_and(|r| r + .iter() + .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); @@ -596,7 +596,10 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { serde_json::from_slice(&std::fs::read(proj.join("package.json")).unwrap()).unwrap(); let before: serde_json::Value = serde_json::from_slice(&root_pkg_before).unwrap(); after.as_object_mut().unwrap().shift_remove("resolutions"); - assert_eq!(after, before, "the hosted pin only adds `resolutions` to the root package.json"); + assert_eq!( + after, before, + "the hosted pin only adds `resolutions` to the root package.json" + ); } assert_eq!( std::fs::read(proj.join("packages/app/package.json")).unwrap(), @@ -630,7 +633,10 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { eprintln!("FRESH INSTALL + MEMBER RESOLUTION OK"); // MANIFEST-LESS VEX over the hosted wiring (see `yarn_berry_common`). - let registry_state = [("yarn.lock", registry_lock), ("package.json", root_pkg_before.clone())]; + let registry_state = [ + ("yarn.lock", registry_lock), + ("package.json", root_pkg_before.clone()), + ]; let yarn = |cwd: &Path, args: &[&str], env: &[(&str, &str)]| corepack(cwd, yarn_berry(), args, env); let api_url = server.uri(); 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 db2aab79f..6ce32e653 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 @@ -519,7 +519,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(); @@ -800,7 +804,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.rs b/crates/socket-patch-cli/tests/in_process_redirect.rs index 7ae650809..21ff2fa08 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect.rs @@ -851,9 +851,10 @@ async fn scan_redirect_rewrites_crlf_and_bom_yarn_berry_locks_and_rollback_resto "{label}: rollback restores the pristine CRLF lock (upstream checksum \ re-derived from the registry tarball)" ); - let pkg: serde_json::Value = - serde_json::from_str(&std::fs::read_to_string(tmp.path().join("package.json")).unwrap()) - .unwrap(); + let pkg: serde_json::Value = serde_json::from_str( + &std::fs::read_to_string(tmp.path().join("package.json")).unwrap(), + ) + .unwrap(); assert!( pkg.get("resolutions").is_none(), "{label}: rollback drops the resolutions pin: {pkg}" 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 61ec4bd3f..a78d10282 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 c7adfe131..f74c5c90c 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 ea25f497e..b0ffa2d21 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -58,7 +58,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. @@ -123,7 +124,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, @@ -372,8 +375,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 @@ -381,13 +391,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). @@ -397,7 +419,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] @@ -420,7 +446,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| { @@ -441,7 +470,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 @@ -469,10 +500,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); } @@ -557,16 +585,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 3c7338d28..eb57fb3b4 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 { @@ -805,7 +808,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/in_process_vendor.rs b/crates/socket-patch-cli/tests/in_process_vendor.rs index 1b6b410fc..b9dd90d0b 100644 --- a/crates/socket-patch-cli/tests/in_process_vendor.rs +++ b/crates/socket-patch-cli/tests/in_process_vendor.rs @@ -1359,16 +1359,16 @@ async fn berry_crlf_takeovers_round_trip_both_directions() { // The vendored `resolutions` entry is gone and the hosted pin (#404 // option C) took its place, in the manifest's own layout: BOM + CRLF. let hosted_pkg = std::fs::read_to_string(root.join("package.json")).unwrap(); - assert!(hosted_pkg.starts_with('\u{feff}'), "BOM kept: {hosted_pkg:?}"); + assert!( + hosted_pkg.starts_with('\u{feff}'), + "BOM kept: {hosted_pkg:?}" + ); let pin_line = format!(" \"left-pad@npm:1.3.0\": \"{hosted_url}\"\r\n"); assert!( hosted_pkg.contains(&pin_line) && !hosted_pkg.contains(".socket/vendor/"), "the hosted pin replaced the vendored resolutions entry: {hosted_pkg:?}" ); - let unpinned = hosted_pkg.replace( - &format!(",\r\n \"resolutions\": {{\r\n{pin_line} }}"), - "", - ); + let unpinned = hosted_pkg.replace(&format!(",\r\n \"resolutions\": {{\r\n{pin_line} }}"), ""); assert_eq!(unpinned, pkg, "nothing else in package.json changed"); let hosted_lock = std::fs::read_to_string(root.join("yarn.lock")).unwrap(); assert!( 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 e8ff74216..dffab3915 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/python_crawler.rs b/crates/socket-patch-core/src/crawlers/python_crawler.rs index dfdee4f52..5c0b94c1a 100644 --- a/crates/socket-patch-core/src/crawlers/python_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/python_crawler.rs @@ -365,8 +365,8 @@ async fn find_local_venv_site_packages_with( // it once `poetry env use` recorded an env for the project (see // [`poetry_active_prefix`]). PDM likewise skips an activated venv under // `PDM_IGNORE_ACTIVE_VENV`. - let pdm_ignores_active = pdm_env_flag(var, "PDM_IGNORE_ACTIVE_VENV") - && pdm_drives_project(cwd).await; + let pdm_ignores_active = + pdm_env_flag(var, "PDM_IGNORE_ACTIVE_VENV") && pdm_drives_project(cwd).await; let active_prefix = match &poetry { Some(project) => poetry_active_prefix(project, var), None if pdm_ignores_active => None, @@ -540,9 +540,7 @@ async fn pdm_saved_interpreter(cwd: &Path) -> Option { let saved = match read_regular_to_string(&cwd.join(".pdm-python")).await { Ok(text) => text.trim().to_string(), Err(_) => { - let text = read_regular_to_string(&cwd.join(".pdm.toml")) - .await - .ok()?; + let text = read_regular_to_string(&cwd.join(".pdm.toml")).await.ok()?; let doc = text.parse::().ok()?; doc.get("python")?.get("path")?.as_str()?.trim().to_string() } @@ -2813,7 +2811,11 @@ mod tests { fake_venv(&tmp.path().join("uv-env"), "venv"); let uv_env = env_of(&[( "UV_PROJECT_ENVIRONMENT", - tmp.path().join("uv-env").join("venv").to_string_lossy().into_owned(), + tmp.path() + .join("uv-env") + .join("venv") + .to_string_lossy() + .into_owned(), )]); assert_eq!( find_local_venv_site_packages_with(&project, &uv_env).await, 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/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 3c345cda8..f0a16029f 100644 --- a/crates/socket-patch-core/src/formats/gem/mod.rs +++ b/crates/socket-patch-core/src/formats/gem/mod.rs @@ -27,7 +27,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`). @@ -208,7 +207,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 c7d47c1f2..a632c9c3a 100644 --- a/crates/socket-patch-core/src/formats/pnpm/mod.rs +++ b/crates/socket-patch-core/src/formats/pnpm/mod.rs @@ -39,7 +39,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 @@ -283,7 +282,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)) }) } @@ -303,7 +305,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) } @@ -491,7 +494,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); } } @@ -508,17 +513,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"), @@ -534,7 +574,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"; @@ -557,7 +600,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 04d5e6312..3091134d2 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", @@ -118,7 +122,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), @@ -213,7 +221,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 51ec85483..81bf03d76 100644 --- a/crates/socket-patch-core/src/hosted/guidance.rs +++ b/crates/socket-patch-core/src/hosted/guidance.rs @@ -173,9 +173,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 } @@ -310,11 +308,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 f18efa81e..04dcee403 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::{ @@ -185,7 +185,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 { @@ -211,7 +214,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 c5b565053..08e162374 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -47,17 +47,18 @@ 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::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}; use crate::formats::gem::lock_lists_direct_dependency; -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; +pub(crate) mod hosted_url; #[cfg(test)] mod pnpm_equivalence_tests; mod poetry; @@ -66,18 +67,17 @@ mod python_lock_equivalence_tests; mod requirements; mod staged; mod state; -pub(crate) 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)] @@ -3822,7 +3822,12 @@ fn rewrite_yarn_berry( result.edits.push(FileEdit { path: BERRY_MANIFEST.into(), kind: "redirect_yarn_berry_resolution".into(), - action: if original.is_some() { "rewritten" } else { "added" }.into(), + action: if original.is_some() { + "rewritten" + } else { + "added" + } + .into(), key: Some(selector), original: original.map(Value::String), new: Some(Value::String(dep.artifact_url.clone())), @@ -4007,7 +4012,10 @@ impl BerryResolutionsPin { } let mut changed = Vec::new(); for selector in &self.selectors { - let previous = table.get(selector).and_then(Value::as_str).map(str::to_string); + let previous = table + .get(selector) + .and_then(Value::as_str) + .map(str::to_string); if previous.as_deref() != Some(url) { table.insert(selector.clone(), Value::String(url.to_string())); changed.push((selector.clone(), previous)); @@ -4115,7 +4123,11 @@ fn berry_resolutions_pin( /// order before the edit (`was_sorted`, from [`berry_entries_sorted`]; a /// hand-edited lock) keeps the entry in place, so a pin and its rollback /// still round-trip byte-exactly. -pub(crate) fn berry_reposition_blocks(blocks: &mut Vec, moved: &[String], was_sorted: bool) { +pub(crate) fn berry_reposition_blocks( + blocks: &mut Vec, + moved: &[String], + was_sorted: bool, +) { if !was_sorted { return; } @@ -4140,7 +4152,6 @@ pub(crate) fn berry_reposition_blocks(blocks: &mut Vec, moved: &[String] } } - // ── bun.lock (text lockfile) ───────────────────────────────────────────────── // A registry 4-tuple `["name@version", "", {deps}, "sha512-…"]` is // rewritten to a URL 3-tuple `["name@", {deps verbatim}, @@ -4711,7 +4722,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 @@ -8141,7 +8151,11 @@ mod tests { #[test] fn yarn_berry_hosted_pin_routes_resolutions_to_a_tarball_entry() { let checksum = format!("10c0/{}", "7".repeat(128)); - let scoped_url = berry_hosted_url("@isaacs/string-locale-compare", "string-locale-compare", "1.1.0"); + let scoped_url = berry_hosted_url( + "@isaacs/string-locale-compare", + "string-locale-compare", + "1.1.0", + ); let plain_url = berry_hosted_url("left-pad", "left-pad", "1.3.0"); let scoped = DepOverride { namespace: Some("@isaacs".into()), @@ -8174,8 +8188,14 @@ mod tests { )), "unscoped entry re-keyed to its tarball: {out}" ); - assert!(!out.contains("__archiveUrl") && !out.contains("@npm:"), "{out}"); - assert!(out.ends_with("linkType: hard\n"), "trailing newline kept: {out:?}"); + assert!( + !out.contains("__archiveUrl") && !out.contains("@npm:"), + "{out}" + ); + assert!( + out.ends_with("linkType: hard\n"), + "trailing newline kept: {out:?}" + ); let manifest: Value = serde_json::from_str(&r.files["package.json"]).unwrap(); assert_eq!( manifest["resolutions"], @@ -8187,11 +8207,17 @@ mod tests { ); assert_eq!(manifest["name"], "app", "the rest of the manifest is kept"); assert_eq!( - r.edits.iter().filter(|e| e.kind == "redirect_yarn_berry_entry").count(), + r.edits + .iter() + .filter(|e| e.kind == "redirect_yarn_berry_entry") + .count(), 2 ); assert_eq!( - r.edits.iter().filter(|e| e.kind == "redirect_yarn_berry_resolution").count(), + r.edits + .iter() + .filter(|e| e.kind == "redirect_yarn_berry_resolution") + .count(), 2 ); } @@ -8225,10 +8251,7 @@ mod tests { rewrite_yarn_berry(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.warnings.is_empty(), "{:?}", r.warnings); let out = &r.files["yarn.lock"]; - let keys: Vec<&str> = out - .lines() - .filter(|l| l.starts_with('"')) - .collect(); + let keys: Vec<&str> = out.lines().filter(|l| l.starts_with('"')).collect(); assert_eq!( keys, vec![ @@ -8272,7 +8295,11 @@ mod tests { let mut again = RewriteResult::default(); rewrite_yarn_berry(&pinned, std::slice::from_ref(&ovr), &mut again); assert!(again.warnings.is_empty(), "{:?}", again.warnings); - assert!(again.files.is_empty(), "repeat run rewrites nothing: {:?}", again.files); + assert!( + again.files.is_empty(), + "repeat run rewrites nothing: {:?}", + again.files + ); // A pin already complete is confirmed without a write. assert!(again.confirmed_yarn_berry_uuids.contains(BERRY_UUID)); @@ -8285,7 +8312,10 @@ mod tests { assert!(out.contains(&format!("\"left-pad@{new_url}\":")), "{out}"); assert!(!out.contains(BERRY_UUID), "{out}"); let manifest: Value = serde_json::from_str(&repin.files["package.json"]).unwrap(); - assert_eq!(manifest["resolutions"], json!({"left-pad@npm:^1.3.0": new_url})); + assert_eq!( + manifest["resolutions"], + json!({"left-pad@npm:^1.3.0": new_url}) + ); } /// The URL-keyed lock entry alone is half a pin: with its manifest @@ -8532,7 +8562,9 @@ mod tests { assert!(r.warnings.is_empty(), "{:?}", r.warnings); let out = &r.files["yarn.lock"]; assert!( - out.contains(&format!("\"left-pad@{url}\":\n version: 1.3.0\n resolution: \"left-pad@{url}\"\n")), + out.contains(&format!( + "\"left-pad@{url}\":\n version: 1.3.0\n resolution: \"left-pad@{url}\"\n" + )), "{out}" ); assert!(!out.contains("__archiveUrl"), "{out}"); @@ -8558,10 +8590,17 @@ mod tests { "{{\n \"name\": \"app\",\n \"resolutions\": {{\n \"{selector}\": \"1.3.0\"\n }}\n}}\n" ); let mut r = RewriteResult::default(); - rewrite_yarn_berry(&berry_files(berry_lock("10c0"), manifest), std::slice::from_ref(&ovr), &mut r); + rewrite_yarn_berry( + &berry_files(berry_lock("10c0"), manifest), + std::slice::from_ref(&ovr), + &mut r, + ); assert!(r.files.is_empty(), "{label}: {:?}", r.files); assert_eq!( - r.warnings.iter().map(|w| w.code.as_str()).collect::>(), + r.warnings + .iter() + .map(|w| w.code.as_str()) + .collect::>(), vec!["redirect_yarn_berry_resolutions_conflict"], "{label}" ); @@ -8586,12 +8625,20 @@ mod tests { "mirror tarball" ); // An unrelated user entry is kept as-is next to ours. - let manifest = "{\n \"name\": \"app\",\n \"resolutions\": {\n \"other\": \"2.0.0\"\n }\n}\n"; + let manifest = + "{\n \"name\": \"app\",\n \"resolutions\": {\n \"other\": \"2.0.0\"\n }\n}\n"; let mut r = RewriteResult::default(); - rewrite_yarn_berry(&berry_files(berry_lock("10c0"), manifest.into()), std::slice::from_ref(&ovr), &mut r); + rewrite_yarn_berry( + &berry_files(berry_lock("10c0"), manifest.into()), + std::slice::from_ref(&ovr), + &mut r, + ); assert!(r.warnings.is_empty(), "{:?}", r.warnings); let m: Value = serde_json::from_str(&r.files["package.json"]).unwrap(); - assert_eq!(m["resolutions"], json!({"other": "2.0.0", "left-pad@npm:^1.3.0": url})); + assert_eq!( + m["resolutions"], + json!({"other": "2.0.0", "left-pad@npm:^1.3.0": url}) + ); let mut files = BTreeMap::new(); files.insert("yarn.lock".to_string(), berry_lock("10c0")); @@ -8599,7 +8646,10 @@ mod tests { rewrite_yarn_berry(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.files.is_empty(), "{:?}", r.files); assert_eq!( - r.warnings.iter().map(|w| w.code.as_str()).collect::>(), + r.warnings + .iter() + .map(|w| w.code.as_str()) + .collect::>(), vec!["redirect_yarn_berry_manifest_missing"] ); @@ -8610,10 +8660,17 @@ mod tests { berry_lock("10c0") ); let mut r = RewriteResult::default(); - rewrite_yarn_berry(&berry_files(with_patch, berry_manifest()), std::slice::from_ref(&ovr), &mut r); + rewrite_yarn_berry( + &berry_files(with_patch, berry_manifest()), + std::slice::from_ref(&ovr), + &mut r, + ); assert!(r.files.is_empty(), "{:?}", r.files); let codes: Vec<&str> = r.warnings.iter().map(|w| w.code.as_str()).collect(); - assert!(codes.contains(&"redirect_yarn_berry_shared_descriptor"), "{codes:?}"); + assert!( + codes.contains(&"redirect_yarn_berry_shared_descriptor"), + "{codes:?}" + ); } /// Yarn routes a URL locator to its tarball fetcher only when it is an @@ -8638,7 +8695,10 @@ mod tests { assert!(r.files.is_empty(), "{url}: nothing written"); assert!(r.edits.is_empty(), "{url}: {:?}", r.edits); assert_eq!( - r.warnings.iter().map(|w| w.code.as_str()).collect::>(), + r.warnings + .iter() + .map(|w| w.code.as_str()) + .collect::>(), vec!["redirect_yarn_berry_artifact_url_unsupported"], "{url}" ); @@ -11710,7 +11770,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, @@ -11723,7 +11787,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 @@ -11738,7 +11806,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 ); @@ -12106,11 +12177,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), @@ -13537,7 +13616,10 @@ mod tests { ("crlf", lf.replace('\n', "\r\n")), ("tabs", lf.replace(" ", "\t")), ("bom", format!("\u{feff}{lf}")), - ("bom+crlf+tabs", format!("\u{feff}{}", lf.replace(" ", "\t").replace('\n', "\r\n"))), + ( + "bom+crlf+tabs", + format!("\u{feff}{}", lf.replace(" ", "\t").replace('\n', "\r\n")), + ), ]; for (shape, pristine) in shapes { let mut files = BTreeMap::new(); @@ -13553,7 +13635,10 @@ mod tests { "http://patch.test/left-pad-1.3.0.tgz", ) .replace("sha512-UPSTREAM==", "sha512-PATCHED=="); - assert_eq!(out, &expected, "{shape}: only the rewired values may change"); + assert_eq!( + out, &expected, + "{shape}: only the rewired values may change" + ); } } @@ -15833,7 +15918,8 @@ packages: ); // One edit; its fragments are the on-disk bytes of the entry. - let lock_edits: Vec<&FileEdit> = r.edits.iter().filter(|e| e.path == "yarn.lock").collect(); + let lock_edits: Vec<&FileEdit> = + r.edits.iter().filter(|e| e.path == "yarn.lock").collect(); assert_eq!(lock_edits.len(), 1, "{label}"); let edit = lock_edits[0]; let (orig, new) = ( @@ -15843,15 +15929,8 @@ packages: assert_eq!( (orig, new), ( - respell( - lf_edit - .original - .as_ref() - .unwrap() - .as_str() - .unwrap() - ) - .trim_start_matches('\u{feff}'), + respell(lf_edit.original.as_ref().unwrap().as_str().unwrap()) + .trim_start_matches('\u{feff}'), respell(lf_edit.new.as_ref().unwrap().as_str().unwrap()) .trim_start_matches('\u{feff}'), ), 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 87bd8ad5c..c1376d4bb 100644 --- a/crates/socket-patch-core/src/patch/redirect/pipenv.rs +++ b/crates/socket-patch-core/src/patch/redirect/pipenv.rs @@ -459,7 +459,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(); @@ -499,20 +502,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")); @@ -570,7 +583,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(); @@ -596,7 +612,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(); @@ -617,7 +636,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. @@ -628,7 +650,6 @@ mod tests { assert!(second.contains("/rotated/") && !second.contains("/tok/")); } - /// Hosted Pipenv recognizes its own pins through the shared recognizer /// (#563): a path-prefixed `--patch-server-url` deployment rotates its /// grant instead of refusing its own previous reference, and a hosted @@ -702,7 +723,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/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/gem.rs b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs index a20a3cb3c..c47227d7e 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs @@ -57,11 +57,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/"; @@ -250,17 +250,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 ea0fe324f..f88aeb347 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs @@ -348,9 +348,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 { @@ -678,9 +676,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 4be79627a..569727aed 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 e3ade4d63..e792f2f96 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(crate) mod pep440; pub mod pipenv; diff --git a/crates/socket-patch-core/src/utils/process.rs b/crates/socket-patch-core/src/utils/process.rs index 13038c679..a8eb22cd7 100644 --- a/crates/socket-patch-core/src/utils/process.rs +++ b/crates/socket-patch-core/src/utils/process.rs @@ -89,9 +89,7 @@ pub(crate) fn resolve_app_alias_with( std::env::split_paths(&path) .filter(|dir| dir.is_absolute()) .map(|dir| dir.join(format!("{name}.exe"))) - .find(|candidate| { - std::fs::symlink_metadata(candidate).is_ok_and(|meta| !meta.is_dir()) - }) + .find(|candidate| std::fs::symlink_metadata(candidate).is_ok_and(|meta| !meta.is_dir())) } /// A plain file that cannot be executed (a stray `bun` data file on PATH) @@ -427,7 +425,11 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let safe = tmp.path().join("bin"); std::fs::create_dir_all(&safe).unwrap(); - let relative = [PathBuf::from("."), PathBuf::from(""), PathBuf::from("planted")]; + let relative = [ + PathBuf::from("."), + PathBuf::from(""), + PathBuf::from("planted"), + ]; let only_relative = std::env::join_paths(&relative).unwrap(); let var = |name: &str| (name == "PATH").then(|| only_relative.clone()); @@ -437,7 +439,10 @@ mod tests { let with_safe = std::env::join_paths(relative.iter().cloned().chain([safe.clone()])).unwrap(); let var = |name: &str| (name == "PATH").then(|| with_safe.clone()); - assert_eq!(resolve_app_alias_with("yarn", &var), Some(safe.join("yarn.exe"))); + assert_eq!( + resolve_app_alias_with("yarn", &var), + Some(safe.join("yarn.exe")) + ); } #[test] 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 5a21c5bb4..cbdcc8d93 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -557,5 +557,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 7716a3b32..80c5a9617 100644 --- a/crates/socket-patch-core/src/vendor/bun_lockb.rs +++ b/crates/socket-patch-core/src/vendor/bun_lockb.rs @@ -1867,7 +1867,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 @@ -1910,7 +1913,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/view.rs b/crates/socket-patch-core/src/vendor/lock_inventory/view.rs index acd48d721..29a446efc 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/view.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/view.rs @@ -15,10 +15,10 @@ use std::sync::Arc; use crate::constants::npm_family::{ BUN_LOCK, BUN_LOCKB, NPM_LOCKS, PNPM_LOCK, PNP_MARKERS, VLT_LOCK, }; -use crate::utils::fs::{read_regular_to_bytes, read_regular_to_string}; -use crate::vendor::npm_flavor::NpmLockFlavor; use crate::formats::pnpm::{sniff_lock_grammar, PnpmLockGrammar}; use crate::formats::yarn::{sniff_grammar, YarnLockGrammar, UNIDENTIFIED_DETAIL}; +use crate::utils::fs::{read_regular_to_bytes, read_regular_to_string}; +use crate::vendor::npm_flavor::NpmLockFlavor; use crate::vendor::VendorWarning; /// One in-memory file. diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs b/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs index f84eed675..ba1324448 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs @@ -6,8 +6,8 @@ use std::path::Path; use serde_json::{Map, Value}; -use crate::constants::npm_family::VLT_LOCK; use super::view::ProjectView; +use crate::constants::npm_family::VLT_LOCK; use crate::vendor::vlt_lock_text::{ is_default_registry, sniff_lock, split_dep_id, DepId, DepIdKind, LockSniff, }; diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs b/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs index 9ed45e49d..c3c21f8e9 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs @@ -7,12 +7,12 @@ use toml_edit::{DocumentMut, Item}; use crate::constants::npm_family::{BUN_LOCK, BUN_LOCKB, NPM_LOCKS, PNPM_LOCK}; use crate::formats::pnpm::PnpmLock; +use crate::formats::yarn::is_berry_lock; use crate::utils::digest::is_sri_pin; use crate::utils::fs::{read_regular_to_bytes, read_regular_to_string}; use crate::utils::python_lock::{ lock_artifact, lock_package_collection, package_artifacts, uv_source_location, }; -use crate::formats::yarn::is_berry_lock; use crate::vendor::bun_lock_text::{decode_json_string, split_name_spec}; use crate::vendor::bun_lockb::BunLockb; use crate::vendor::yarn_berry_lock::berry_field; diff --git a/crates/socket-patch-core/src/vendor/prestage.rs b/crates/socket-patch-core/src/vendor/prestage.rs index b3149ccaa..b4cf64fb6 100644 --- a/crates/socket-patch-core/src/vendor/prestage.rs +++ b/crates/socket-patch-core/src/vendor/prestage.rs @@ -464,7 +464,10 @@ mod sweep_tests { for dir in &kept { assert!(v.join(dir).exists(), "{dir} kept"); } - assert!(!v.join("gem").exists(), "the levels only the tree kept alive are pruned"); + assert!( + !v.join("gem").exists(), + "the levels only the tree kept alive are pruned" + ); assert!(!v.join(format!("composer/{u}/psr/log@3.0.2")).exists()); assert!(v.join("state.json").exists()); assert_eq!(sweep_stale(root).await, 0, "idempotent"); diff --git a/crates/socket-patch-core/src/vendor/pypi.rs b/crates/socket-patch-core/src/vendor/pypi.rs index 74883c23e..1f57f74e2 100644 --- a/crates/socket-patch-core/src/vendor/pypi.rs +++ b/crates/socket-patch-core/src/vendor/pypi.rs @@ -3492,8 +3492,13 @@ wheels = [ tokio::fs::remove_dir_all(&uuid_dir).await.unwrap(); let bytes = served_wheel(b"service wheel at another filename"); let server = wiremock::MockServer::start().await; - mount_pypi_granted(&server, "six-1.16.0-py3-none-any.whl", &sri_sha512(&bytes), &bytes) - .await; + mount_pypi_granted( + &server, + "six-1.16.0-py3-none-any.whl", + &sri_sha512(&bytes), + &bytes, + ) + .await; let cfg = pypi_service_cfg(&server.uri(), VendorSource::Service, false); let error = crate::vendor::test_support::expect_failure(vendor(Some(cfg)).await); assert!( diff --git a/crates/socket-patch-core/src/vendor/toml_surgery.rs b/crates/socket-patch-core/src/vendor/toml_surgery.rs index 0b1777008..4d151f613 100644 --- a/crates/socket-patch-core/src/vendor/toml_surgery.rs +++ b/crates/socket-patch-core/src/vendor/toml_surgery.rs @@ -519,7 +519,8 @@ mod tests { // CRLF, and a hand edit can leave a mixed-ending file, so the // removal helpers must never normalize: every byte outside the // removed segment survives verbatim. - let wired = "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\nfoo = { path = \"w.whl\" }\n"; + let wired = + "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\nfoo = { path = \"w.whl\" }\n"; let after = remove_exact_line(wired, "foo = { path = \"w.whl\" }").unwrap(); assert_eq!(after, "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\n"); assert_eq!( diff --git a/crates/socket-patch-core/src/vex/discover/cargo.rs b/crates/socket-patch-core/src/vex/discover/cargo.rs index 86aab22de..4056ea30b 100644 --- a/crates/socket-patch-core/src/vex/discover/cargo.rs +++ b/crates/socket-patch-core/src/vex/discover/cargo.rs @@ -738,23 +738,22 @@ async fn vendored_from_patches( } let copy_tagged = matches!(tag, CopyTag::Tagged(_) | CopyTag::Unreadable); if let Lock::Parsed(lock) = lock { - 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 \ + 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 3c4c872f5..997175812 100644 --- a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs +++ b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs @@ -99,7 +99,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/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 231d221ba..a8ed7092f 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; } @@ -449,7 +483,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 { @@ -541,7 +580,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 @@ -569,12 +609,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"), ), ]; @@ -633,7 +679,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); @@ -646,7 +695,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); } @@ -676,7 +728,10 @@ async fn gem_transitive_append_round_trips_unless_unprovable() { case.expected.insert("Gemfile".into(), legacy); 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") @@ -705,10 +760,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| { @@ -717,18 +781,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 ──────────────────────────────────────────────────────────────── @@ -897,7 +981,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; @@ -916,20 +1004,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| { @@ -946,7 +1056,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 ───────────────────────────────────────────────────────────────────── @@ -984,7 +1100,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}") } @@ -997,8 +1117,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", + ), ], ) } @@ -1033,7 +1163,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. @@ -1080,14 +1213,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:?}"); @@ -1110,13 +1253,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] @@ -1129,7 +1279,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(), @@ -1149,7 +1304,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; @@ -1164,7 +1324,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); @@ -1271,9 +1434,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}" @@ -1287,12 +1455,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. @@ -1352,7 +1525,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; @@ -1386,7 +1561,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" ); @@ -1401,9 +1578,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; } @@ -1427,12 +1611,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"; @@ -1443,7 +1632,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] @@ -1461,7 +1653,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"); @@ -1514,7 +1709,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(), @@ -1532,7 +1730,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); @@ -1540,7 +1741,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}"); @@ -1551,7 +1754,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!( @@ -1565,7 +1773,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()); } @@ -1644,7 +1855,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. @@ -1709,7 +1926,11 @@ wheels = [{{ url = \"{wheel_url}\", upload-time = 2023-10-17T17:46:21.184Z, size /// microseconds), with (`uv export`) or without (`uv pip compile`) an /// `index` on each registry package. fn uv_pylock(index: bool) -> String { - let index = if index { "index = \"https://pypi.org/simple\"\n" } else { "" }; + let index = if index { + "index = \"https://pypi.org/simple\"\n" + } else { + "" + }; format!( "# This file was autogenerated by uv via the following command:\n\ # uv pip compile --format pylock.toml req.in -o pylock.toml\n\ @@ -1737,8 +1958,13 @@ async fn pylock_without_index_round_trips() { assert!(!lock.contains("index")); for eol in ["\n", "\r\n"] { let input = tree(&[("pylock.toml", lock.replace('\n', eol))]); - assert_pypi_round_trip(&format!("pip compile {eol:?}"), &input, &[urllib3_dep()], None) - .await; + assert_pypi_round_trip( + &format!("pip compile {eol:?}"), + &input, + &[urllib3_dep()], + None, + ) + .await; } // A sibling whose files come from another host is not PyPI. let mirror = lock.replace( @@ -1746,9 +1972,11 @@ async fn pylock_without_index_round_trips() { "https://mirror.example.com/packages/21/ed/", ); let input = tree(&[("pylock.toml", mirror)]); - let (why, _, after) = - pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!(after["pylock.toml"].contains("patch.socket.dev"), "the pin stays wired"); + let (why, _, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; + assert!( + after["pylock.toml"].contains("patch.socket.dev"), + "the pin stays wired" + ); assert!(why.contains("not PyPI"), "{why}"); } @@ -1774,7 +2002,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}"); @@ -1812,7 +2043,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; @@ -1866,7 +2102,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(); @@ -1911,7 +2150,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}"); } @@ -1932,7 +2174,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 })), @@ -2002,11 +2246,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 77ce09e630c6d8d1dad827439aff7a82dd307e70 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 00:41:59 +0000 Subject: [PATCH 3/5] Simplify the all-recorded early-return check Clippy's nonminimal_bool lint rejected the negated condition. Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/src/commands/scan/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index f9bc96d25..1e4d05180 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -2760,7 +2760,7 @@ async fn run_scan( } } - if selected.is_empty() && !(reapply && !already_recorded.is_empty()) { + if selected.is_empty() && (!reapply || already_recorded.is_empty()) { if !silent { open_paragraph(&mut skip_paragraph); if !stage.deferred_keys().is_empty() { From 5f7c0d34d4dce33de976f7c6fabe87c9c035c41c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 01:10:24 +0000 Subject: [PATCH 4/5] Keep apply advice for report-only dry runs `scan --prune --dry-run` with every selection already recorded said a run without --dry-run would re-apply them, but a report-only scan never applies, so following that advice changed nothing. It now points at `socket-patch apply` again; only an agent-mode dry run promises the re-apply. Refs #732 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EPsXDSe1x9i96swon5nxdE --- .../socket-patch-cli/src/commands/scan/mod.rs | 2 +- .../src/commands/scan/render.rs | 4 ++- .../tests/covgap_commands_scan_mod.rs | 32 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index 1e4d05180..183a88e8f 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -2771,7 +2771,7 @@ async fn run_scan( } } else if already_recorded.is_empty() { println!("No patches selected."); - } else if args.common.dry_run { + } else if args.common.dry_run && !report_only { println!("{}", render::ALL_ALREADY_RECORDED_DRY_RUN); } else { println!("{}", render::ALL_ALREADY_RECORDED); diff --git a/crates/socket-patch-cli/src/commands/scan/render.rs b/crates/socket-patch-cli/src/commands/scan/render.rs index 9212145e7..c58732214 100644 --- a/crates/socket-patch-cli/src/commands/scan/render.rs +++ b/crates/socket-patch-cli/src/commands/scan/render.rs @@ -315,7 +315,9 @@ pub(super) fn already_recorded_line(purl: &str, uuid: &str, reapply: bool) -> St pub(super) const ALL_ALREADY_RECORDED: &str = "All selected patches are already recorded in the manifest; run `socket-patch apply` to re-apply them."; -/// [`ALL_ALREADY_RECORDED`] for an agent-mode `--dry-run`. +/// [`ALL_ALREADY_RECORDED`] for an agent-mode `--dry-run`. A report-only +/// dry run keeps [`ALL_ALREADY_RECORDED`]: dropping `--dry-run` there still +/// only reports. pub(super) const ALL_ALREADY_RECORDED_DRY_RUN: &str = "All selected patches are already recorded in the manifest; a run without --dry-run re-applies them."; 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 54e8750ab..7d940fe31 100644 --- a/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs +++ b/crates/socket-patch-cli/tests/covgap_commands_scan_mod.rs @@ -2451,6 +2451,38 @@ async fn scan_human_dry_run_previews_reapply_of_a_recorded_patch() { ); } +/// A report-only `scan --prune --dry-run` never applies, so it must not say that +/// dropping `--dry-run` re-applies a recorded patch; it points at +/// `socket-patch apply` instead. +#[tokio::test] +async fn scan_report_only_dry_run_points_recorded_patch_at_apply() { + let mock = MockServer::start().await; + let purl = "pkg:npm/minimist@1.2.2"; + mount_batch_one(&mock, purl, UUID, "free", &[], false).await; + mount_by_package(&mock, purl, UUID, serde_json::json!({})).await; + + let tmp = tempfile::tempdir().unwrap(); + write_root_package_json(tmp.path()); + write_npm_package(tmp.path(), "minimist", "1.2.2", b"x\n"); + seed_manifest(tmp.path(), &[(purl, UUID)]); + + let (code, stdout, stderr) = run_scan_human(tmp.path(), &mock.uri(), &["--prune", "--dry-run"]); + assert_eq!(code, 0, "stdout={stdout}; stderr={stderr}"); + assert!( + stdout.contains(&format!("[skip] {purl} (already recorded: 11111111)")), + "{stdout:?}" + ); + assert!( + !stdout.contains("a run without --dry-run re-applies them"), + "{stdout:?}" + ); + assert!(!stdout.contains("[re-apply]"), "{stdout:?}"); + assert_eq!( + std::fs::read(tmp.path().join("node_modules/minimist/index.js")).unwrap(), + b"x\n" + ); +} + /// The human table's PACKAGE column grows to fit the PURL (a fixed-width /// cut would drop the version), and the rule matches the table. #[tokio::test] From e0cc8ebc166446d33bc92bf0534defa8ae03d4f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 02:16:42 +0000 Subject: [PATCH 5/5] Drop workspace-wide rustfmt churn from the #732 fix The fix commit ran cargo fmt over the whole workspace, reformatting 127 files the change does not touch. That noise hides the real diff from reviewers and conflicts with every other open PR editing those files. Restore them to main; the fix itself (scan/mod.rs, render.rs and its tests) is unchanged. Co-Authored-By: Claude --- 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/discovery.rs | 62 +-- .../src/commands/scan/hosted.rs | 54 +- .../src/commands/scan/policy.rs | 68 +-- .../src/commands/scan/rollout.rs | 20 +- .../src/commands/scan/rollout_args.rs | 2 + .../socket-patch-cli/src/commands/vendor.rs | 5 +- .../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 +- 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_redirect_yarn_berry_build.rs | 6 +- .../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/e2e_yarn4_pnpm_linker_build.rs | 16 +- .../tests/e2e_yarn4_workspaces_build.rs | 16 +- .../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.rs | 7 +- .../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/in_process_vendor.rs | 10 +- .../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/python_crawler.rs | 14 +- .../src/formats/cargo/mod.rs | 12 +- .../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 | 14 +- .../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 | 187 ++----- .../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/state.rs | 2 + .../src/patch/redirect/upstream/bun_lockb.rs | 5 +- .../src/patch/redirect/upstream/cargo.rs | 39 +- .../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 +- crates/socket-patch-core/src/utils/process.rs | 15 +- .../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/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 +- crates/socket-patch-core/src/vendor/pypi.rs | 9 +- .../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 | 418 ++++------------ crates/socket-patch-core/tests/uv_hosted.rs | 4 +- crates/socket-patch-node/src/lib.rs | 6 +- 127 files changed, 803 insertions(+), 2169 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/apply.rs b/crates/socket-patch-cli/src/commands/apply.rs index 4e446491c..f5918a3dd 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 34ae4b1a3..ea45e6915 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; @@ -141,11 +141,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 @@ -155,8 +153,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(), @@ -164,8 +164,7 @@ pub(crate) fn hosted_state_from_pins( description: String::new(), license: String::new(), tier: String::new(), - } - }); + }); } state } @@ -192,3 +191,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 143382b02..0d4be4bb2 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 36d4b4760..5f4191038 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)) @@ -1171,11 +1170,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/discovery.rs b/crates/socket-patch-cli/src/commands/scan/discovery.rs index 33031413c..b83f63990 100644 --- a/crates/socket-patch-cli/src/commands/scan/discovery.rs +++ b/crates/socket-patch-cli/src/commands/scan/discovery.rs @@ -168,32 +168,29 @@ pub(crate) async fn vendored_ledger_supplement( } // `(ledger key, base purl, entry)`; the artifact fallback has no // entries to probe, so it never reports unwired keys. - let candidates: Vec<( - String, - String, - Option<&socket_patch_core::vendor::VendorEntry>, - )> = match state { - Ok(state) => state - .entries - .iter() - .map(|(key, entry)| { - ( - key.clone(), - strip_purl_qualifiers(&entry.base_purl).to_string(), - Some(entry), - ) - }) - .collect(), - // Corrupt/unreadable ledger (a MISSING file is Ok(empty) above): - // recover the vendored set from the committed artifacts, or - // `scan --prune` (whose ledger exemption also degrades to empty) - // would delete still-vendored packages' manifest entries and blobs. - Err(_) => vendored_purls_from_artifacts(common) - .await - .into_iter() - .map(|base| (base.clone(), base, None)) - .collect(), - }; + let candidates: Vec<(String, String, Option<&socket_patch_core::vendor::VendorEntry>)> = + match state { + Ok(state) => state + .entries + .iter() + .map(|(key, entry)| { + ( + key.clone(), + strip_purl_qualifiers(&entry.base_purl).to_string(), + Some(entry), + ) + }) + .collect(), + // Corrupt/unreadable ledger (a MISSING file is Ok(empty) above): + // recover the vendored set from the committed artifacts, or + // `scan --prune` (whose ledger exemption also degrades to empty) + // would delete still-vendored packages' manifest entries and blobs. + Err(_) => vendored_purls_from_artifacts(common) + .await + .into_iter() + .map(|base| (base.clone(), base, None)) + .collect(), + }; // Composer by release identity: a ledger `@3.0.2.0` is the crawled // `@3.0.2`, not a second package to supplement. let key = |p: &str| composer_purl_identity(p).unwrap_or_else(|| normalize_purl(p).into_owned()); @@ -1041,9 +1038,7 @@ mod tests { ..GlobalArgs::default() }; let state = socket_patch_core::vendor::load_state(root).await; - vendored_ledger_supplement(&args, crawled, &state) - .await - .packages + vendored_ledger_supplement(&args, crawled, &state).await.packages } /// A ledger entry vendored as `@3.0.2.0` is the crawled composer @@ -1078,9 +1073,7 @@ mod tests { out.iter().map(|p| &p.purl).collect::>() ); - let out = vendored_ledger_supplement(&args, &[], &Ok(state)) - .await - .packages; + let out = vendored_ledger_supplement(&args, &[], &Ok(state)).await.packages; assert_eq!( out.iter().map(|p| p.purl.as_str()).collect::>(), vec!["pkg:composer/psr/log@3.0.2.0"] @@ -1183,10 +1176,7 @@ mod tests { let state = npm_ledger_with_lock(tmp.path(), lock.as_deref()).await; let out = vendored_ledger_supplement(&args, &[], &state).await; assert_eq!( - out.packages - .iter() - .map(|p| p.purl.as_str()) - .collect::>(), + out.packages.iter().map(|p| p.purl.as_str()).collect::>(), vec!["pkg:npm/left-pad@1.3.0"], "lock={lock:?}" ); diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index 119b2dc7e..97e6866ce 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -932,8 +932,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, @@ -945,7 +944,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. @@ -2306,19 +2304,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 @@ -4399,43 +4391,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/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/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/src/commands/vendor.rs b/crates/socket-patch-cli/src/commands/vendor.rs index ff8c46a97..59be95b85 100644 --- a/crates/socket-patch-cli/src/commands/vendor.rs +++ b/crates/socket-patch-cli/src/commands/vendor.rs @@ -257,7 +257,10 @@ pub(crate) async fn dispatch_revert_one_opts( /// dependency graph? `None` = cannot determine — callers must keep the /// entry (fail-safe): ecosystems other than npm and cargo have no in-use /// probe yet, and a missing/unreadable lockfile proves nothing. -pub(crate) async fn dispatch_in_use_one(entry: &VendorEntry, project_root: &Path) -> Option { +pub(crate) async fn dispatch_in_use_one( + entry: &VendorEntry, + project_root: &Path, +) -> Option { match entry.ecosystem.as_str() { "npm" => vendor::npm_flavor::vendored_entry_in_use(entry, project_root).await, // Cargo probes the lock entry's shape: detached + `[patch]` pointing 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 235030104..54ddf7441 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!( @@ -2440,8 +2429,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/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 3e388d0ec..1fed4afd2 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_redirect_yarn_berry_build.rs b/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs index 5a412ffe0..e021d9015 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs @@ -574,9 +574,9 @@ async fn berry_hosted_project( let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"].as_object().is_some_and(|r| r - .iter() - .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"] + .as_object() + .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); 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/e2e_yarn4_pnpm_linker_build.rs b/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs index ddadbb45d..ce5d1144b 100644 --- a/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs +++ b/crates/socket-patch-cli/tests/e2e_yarn4_pnpm_linker_build.rs @@ -571,9 +571,9 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"].as_object().is_some_and(|r| r - .iter() - .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"] + .as_object() + .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); @@ -596,10 +596,7 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes serde_json::from_slice(&std::fs::read(proj.join("package.json")).unwrap()).unwrap(); let before: serde_json::Value = serde_json::from_slice(&pkg_before).unwrap(); after.as_object_mut().unwrap().shift_remove("resolutions"); - assert_eq!( - after, before, - "the hosted pin only adds `resolutions` to package.json" - ); + assert_eq!(after, before, "the hosted pin only adds `resolutions` to package.json"); } eprintln!("HOSTED REWIRE OK"); @@ -628,10 +625,7 @@ async fn yarn4_pnpm_linker_hosted_redirect_fresh_checkout_installs_patched_bytes eprintln!("FRESH INSTALL + YARN NODE RESOLUTION OK"); // MANIFEST-LESS VEX over the hosted wiring (see `yarn_berry_common`). - let registry_state = [ - ("yarn.lock", registry_lock), - ("package.json", pkg_before.clone()), - ]; + let registry_state = [("yarn.lock", registry_lock), ("package.json", pkg_before.clone())]; let yarn = |cwd: &Path, args: &[&str], env: &[(&str, &str)]| corepack(cwd, yarn_berry(), args, env); let api_url = server.uri(); diff --git a/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs b/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs index 2794a4781..d2aec0078 100644 --- a/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs +++ b/crates/socket-patch-cli/tests/e2e_yarn4_workspaces_build.rs @@ -566,9 +566,9 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { let root_pkg = std::fs::read_to_string(proj.join("package.json")).unwrap(); let root_pkg: serde_json::Value = serde_json::from_str(&root_pkg).unwrap(); assert!( - root_pkg["resolutions"].as_object().is_some_and(|r| r - .iter() - .any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) + root_pkg["resolutions"] + .as_object() + .is_some_and(|r| r.iter().any(|(sel, v)| sel.starts_with(&format!("{DEP}@npm:")) && v.as_str() == Some(hosted_url.as_str()))), "package.json must route {DEP} to the hosted tarball: {root_pkg}" ); @@ -596,10 +596,7 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { serde_json::from_slice(&std::fs::read(proj.join("package.json")).unwrap()).unwrap(); let before: serde_json::Value = serde_json::from_slice(&root_pkg_before).unwrap(); after.as_object_mut().unwrap().shift_remove("resolutions"); - assert_eq!( - after, before, - "the hosted pin only adds `resolutions` to the root package.json" - ); + assert_eq!(after, before, "the hosted pin only adds `resolutions` to the root package.json"); } assert_eq!( std::fs::read(proj.join("packages/app/package.json")).unwrap(), @@ -633,10 +630,7 @@ async fn yarn4_workspaces_hosted_redirect_rewires_member_dep_from_root_scan() { eprintln!("FRESH INSTALL + MEMBER RESOLUTION OK"); // MANIFEST-LESS VEX over the hosted wiring (see `yarn_berry_common`). - let registry_state = [ - ("yarn.lock", registry_lock), - ("package.json", root_pkg_before.clone()), - ]; + let registry_state = [("yarn.lock", registry_lock), ("package.json", root_pkg_before.clone())]; let yarn = |cwd: &Path, args: &[&str], env: &[(&str, &str)]| corepack(cwd, yarn_berry(), args, env); let api_url = server.uri(); 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 6ce32e653..db2aab79f 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 @@ -519,11 +519,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(); @@ -804,11 +800,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.rs b/crates/socket-patch-cli/tests/in_process_redirect.rs index 21ff2fa08..7ae650809 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect.rs @@ -851,10 +851,9 @@ async fn scan_redirect_rewrites_crlf_and_bom_yarn_berry_locks_and_rollback_resto "{label}: rollback restores the pristine CRLF lock (upstream checksum \ re-derived from the registry tarball)" ); - let pkg: serde_json::Value = serde_json::from_str( - &std::fs::read_to_string(tmp.path().join("package.json")).unwrap(), - ) - .unwrap(); + let pkg: serde_json::Value = + serde_json::from_str(&std::fs::read_to_string(tmp.path().join("package.json")).unwrap()) + .unwrap(); assert!( pkg.get("resolutions").is_none(), "{label}: rollback drops the resolutions pin: {pkg}" 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 a78d10282..61ec4bd3f 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 f74c5c90c..c7adfe131 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 b0ffa2d21..ea25f497e 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -58,8 +58,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. @@ -124,9 +123,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, @@ -375,15 +372,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 @@ -391,25 +381,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). @@ -419,11 +397,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] @@ -446,10 +420,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| { @@ -470,9 +441,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 @@ -500,7 +469,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); } @@ -585,27 +557,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 eb57fb3b4..3c7338d28 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 { @@ -808,11 +805,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/in_process_vendor.rs b/crates/socket-patch-cli/tests/in_process_vendor.rs index b9dd90d0b..1b6b410fc 100644 --- a/crates/socket-patch-cli/tests/in_process_vendor.rs +++ b/crates/socket-patch-cli/tests/in_process_vendor.rs @@ -1359,16 +1359,16 @@ async fn berry_crlf_takeovers_round_trip_both_directions() { // The vendored `resolutions` entry is gone and the hosted pin (#404 // option C) took its place, in the manifest's own layout: BOM + CRLF. let hosted_pkg = std::fs::read_to_string(root.join("package.json")).unwrap(); - assert!( - hosted_pkg.starts_with('\u{feff}'), - "BOM kept: {hosted_pkg:?}" - ); + assert!(hosted_pkg.starts_with('\u{feff}'), "BOM kept: {hosted_pkg:?}"); let pin_line = format!(" \"left-pad@npm:1.3.0\": \"{hosted_url}\"\r\n"); assert!( hosted_pkg.contains(&pin_line) && !hosted_pkg.contains(".socket/vendor/"), "the hosted pin replaced the vendored resolutions entry: {hosted_pkg:?}" ); - let unpinned = hosted_pkg.replace(&format!(",\r\n \"resolutions\": {{\r\n{pin_line} }}"), ""); + let unpinned = hosted_pkg.replace( + &format!(",\r\n \"resolutions\": {{\r\n{pin_line} }}"), + "", + ); assert_eq!(unpinned, pkg, "nothing else in package.json changed"); let hosted_lock = std::fs::read_to_string(root.join("yarn.lock")).unwrap(); assert!( 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 dffab3915..e8ff74216 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/python_crawler.rs b/crates/socket-patch-core/src/crawlers/python_crawler.rs index 5c0b94c1a..dfdee4f52 100644 --- a/crates/socket-patch-core/src/crawlers/python_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/python_crawler.rs @@ -365,8 +365,8 @@ async fn find_local_venv_site_packages_with( // it once `poetry env use` recorded an env for the project (see // [`poetry_active_prefix`]). PDM likewise skips an activated venv under // `PDM_IGNORE_ACTIVE_VENV`. - let pdm_ignores_active = - pdm_env_flag(var, "PDM_IGNORE_ACTIVE_VENV") && pdm_drives_project(cwd).await; + let pdm_ignores_active = pdm_env_flag(var, "PDM_IGNORE_ACTIVE_VENV") + && pdm_drives_project(cwd).await; let active_prefix = match &poetry { Some(project) => poetry_active_prefix(project, var), None if pdm_ignores_active => None, @@ -540,7 +540,9 @@ async fn pdm_saved_interpreter(cwd: &Path) -> Option { let saved = match read_regular_to_string(&cwd.join(".pdm-python")).await { Ok(text) => text.trim().to_string(), Err(_) => { - let text = read_regular_to_string(&cwd.join(".pdm.toml")).await.ok()?; + let text = read_regular_to_string(&cwd.join(".pdm.toml")) + .await + .ok()?; let doc = text.parse::().ok()?; doc.get("python")?.get("path")?.as_str()?.trim().to_string() } @@ -2811,11 +2813,7 @@ mod tests { fake_venv(&tmp.path().join("uv-env"), "venv"); let uv_env = env_of(&[( "UV_PROJECT_ENVIRONMENT", - tmp.path() - .join("uv-env") - .join("venv") - .to_string_lossy() - .into_owned(), + tmp.path().join("uv-env").join("venv").to_string_lossy().into_owned(), )]); assert_eq!( find_local_venv_site_packages_with(&project, &uv_env).await, 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/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 f0a16029f..3c345cda8 100644 --- a/crates/socket-patch-core/src/formats/gem/mod.rs +++ b/crates/socket-patch-core/src/formats/gem/mod.rs @@ -27,6 +27,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`). @@ -207,6 +208,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 a632c9c3a..c7d47c1f2 100644 --- a/crates/socket-patch-core/src/formats/pnpm/mod.rs +++ b/crates/socket-patch-core/src/formats/pnpm/mod.rs @@ -39,6 +39,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 @@ -282,10 +283,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)) }) } @@ -305,8 +303,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) } @@ -494,9 +491,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); } } @@ -513,52 +508,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"), @@ -574,10 +534,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"; @@ -600,10 +557,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 3091134d2..04d5e6312 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", @@ -122,11 +118,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), @@ -221,11 +213,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 81bf03d76..51ec85483 100644 --- a/crates/socket-patch-core/src/hosted/guidance.rs +++ b/crates/socket-patch-core/src/hosted/guidance.rs @@ -173,7 +173,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 } @@ -308,7 +310,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 04dcee403..f18efa81e 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::{ @@ -185,10 +185,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 { @@ -214,10 +211,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 08e162374..c5b565053 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -47,18 +47,17 @@ 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}; use crate::formats::gem::lock_lists_direct_dependency; +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; -pub(crate) mod hosted_url; #[cfg(test)] mod pnpm_equivalence_tests; mod poetry; @@ -67,17 +66,18 @@ mod python_lock_equivalence_tests; mod requirements; mod staged; mod state; +pub(crate) 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)] @@ -3822,12 +3822,7 @@ fn rewrite_yarn_berry( result.edits.push(FileEdit { path: BERRY_MANIFEST.into(), kind: "redirect_yarn_berry_resolution".into(), - action: if original.is_some() { - "rewritten" - } else { - "added" - } - .into(), + action: if original.is_some() { "rewritten" } else { "added" }.into(), key: Some(selector), original: original.map(Value::String), new: Some(Value::String(dep.artifact_url.clone())), @@ -4012,10 +4007,7 @@ impl BerryResolutionsPin { } let mut changed = Vec::new(); for selector in &self.selectors { - let previous = table - .get(selector) - .and_then(Value::as_str) - .map(str::to_string); + let previous = table.get(selector).and_then(Value::as_str).map(str::to_string); if previous.as_deref() != Some(url) { table.insert(selector.clone(), Value::String(url.to_string())); changed.push((selector.clone(), previous)); @@ -4123,11 +4115,7 @@ fn berry_resolutions_pin( /// order before the edit (`was_sorted`, from [`berry_entries_sorted`]; a /// hand-edited lock) keeps the entry in place, so a pin and its rollback /// still round-trip byte-exactly. -pub(crate) fn berry_reposition_blocks( - blocks: &mut Vec, - moved: &[String], - was_sorted: bool, -) { +pub(crate) fn berry_reposition_blocks(blocks: &mut Vec, moved: &[String], was_sorted: bool) { if !was_sorted { return; } @@ -4152,6 +4140,7 @@ pub(crate) fn berry_reposition_blocks( } } + // ── bun.lock (text lockfile) ───────────────────────────────────────────────── // A registry 4-tuple `["name@version", "", {deps}, "sha512-…"]` is // rewritten to a URL 3-tuple `["name@", {deps verbatim}, @@ -4722,6 +4711,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 @@ -8151,11 +8141,7 @@ mod tests { #[test] fn yarn_berry_hosted_pin_routes_resolutions_to_a_tarball_entry() { let checksum = format!("10c0/{}", "7".repeat(128)); - let scoped_url = berry_hosted_url( - "@isaacs/string-locale-compare", - "string-locale-compare", - "1.1.0", - ); + let scoped_url = berry_hosted_url("@isaacs/string-locale-compare", "string-locale-compare", "1.1.0"); let plain_url = berry_hosted_url("left-pad", "left-pad", "1.3.0"); let scoped = DepOverride { namespace: Some("@isaacs".into()), @@ -8188,14 +8174,8 @@ mod tests { )), "unscoped entry re-keyed to its tarball: {out}" ); - assert!( - !out.contains("__archiveUrl") && !out.contains("@npm:"), - "{out}" - ); - assert!( - out.ends_with("linkType: hard\n"), - "trailing newline kept: {out:?}" - ); + assert!(!out.contains("__archiveUrl") && !out.contains("@npm:"), "{out}"); + assert!(out.ends_with("linkType: hard\n"), "trailing newline kept: {out:?}"); let manifest: Value = serde_json::from_str(&r.files["package.json"]).unwrap(); assert_eq!( manifest["resolutions"], @@ -8207,17 +8187,11 @@ mod tests { ); assert_eq!(manifest["name"], "app", "the rest of the manifest is kept"); assert_eq!( - r.edits - .iter() - .filter(|e| e.kind == "redirect_yarn_berry_entry") - .count(), + r.edits.iter().filter(|e| e.kind == "redirect_yarn_berry_entry").count(), 2 ); assert_eq!( - r.edits - .iter() - .filter(|e| e.kind == "redirect_yarn_berry_resolution") - .count(), + r.edits.iter().filter(|e| e.kind == "redirect_yarn_berry_resolution").count(), 2 ); } @@ -8251,7 +8225,10 @@ mod tests { rewrite_yarn_berry(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.warnings.is_empty(), "{:?}", r.warnings); let out = &r.files["yarn.lock"]; - let keys: Vec<&str> = out.lines().filter(|l| l.starts_with('"')).collect(); + let keys: Vec<&str> = out + .lines() + .filter(|l| l.starts_with('"')) + .collect(); assert_eq!( keys, vec![ @@ -8295,11 +8272,7 @@ mod tests { let mut again = RewriteResult::default(); rewrite_yarn_berry(&pinned, std::slice::from_ref(&ovr), &mut again); assert!(again.warnings.is_empty(), "{:?}", again.warnings); - assert!( - again.files.is_empty(), - "repeat run rewrites nothing: {:?}", - again.files - ); + assert!(again.files.is_empty(), "repeat run rewrites nothing: {:?}", again.files); // A pin already complete is confirmed without a write. assert!(again.confirmed_yarn_berry_uuids.contains(BERRY_UUID)); @@ -8312,10 +8285,7 @@ mod tests { assert!(out.contains(&format!("\"left-pad@{new_url}\":")), "{out}"); assert!(!out.contains(BERRY_UUID), "{out}"); let manifest: Value = serde_json::from_str(&repin.files["package.json"]).unwrap(); - assert_eq!( - manifest["resolutions"], - json!({"left-pad@npm:^1.3.0": new_url}) - ); + assert_eq!(manifest["resolutions"], json!({"left-pad@npm:^1.3.0": new_url})); } /// The URL-keyed lock entry alone is half a pin: with its manifest @@ -8562,9 +8532,7 @@ mod tests { assert!(r.warnings.is_empty(), "{:?}", r.warnings); let out = &r.files["yarn.lock"]; assert!( - out.contains(&format!( - "\"left-pad@{url}\":\n version: 1.3.0\n resolution: \"left-pad@{url}\"\n" - )), + out.contains(&format!("\"left-pad@{url}\":\n version: 1.3.0\n resolution: \"left-pad@{url}\"\n")), "{out}" ); assert!(!out.contains("__archiveUrl"), "{out}"); @@ -8590,17 +8558,10 @@ mod tests { "{{\n \"name\": \"app\",\n \"resolutions\": {{\n \"{selector}\": \"1.3.0\"\n }}\n}}\n" ); let mut r = RewriteResult::default(); - rewrite_yarn_berry( - &berry_files(berry_lock("10c0"), manifest), - std::slice::from_ref(&ovr), - &mut r, - ); + rewrite_yarn_berry(&berry_files(berry_lock("10c0"), manifest), std::slice::from_ref(&ovr), &mut r); assert!(r.files.is_empty(), "{label}: {:?}", r.files); assert_eq!( - r.warnings - .iter() - .map(|w| w.code.as_str()) - .collect::>(), + r.warnings.iter().map(|w| w.code.as_str()).collect::>(), vec!["redirect_yarn_berry_resolutions_conflict"], "{label}" ); @@ -8625,20 +8586,12 @@ mod tests { "mirror tarball" ); // An unrelated user entry is kept as-is next to ours. - let manifest = - "{\n \"name\": \"app\",\n \"resolutions\": {\n \"other\": \"2.0.0\"\n }\n}\n"; + let manifest = "{\n \"name\": \"app\",\n \"resolutions\": {\n \"other\": \"2.0.0\"\n }\n}\n"; let mut r = RewriteResult::default(); - rewrite_yarn_berry( - &berry_files(berry_lock("10c0"), manifest.into()), - std::slice::from_ref(&ovr), - &mut r, - ); + rewrite_yarn_berry(&berry_files(berry_lock("10c0"), manifest.into()), std::slice::from_ref(&ovr), &mut r); assert!(r.warnings.is_empty(), "{:?}", r.warnings); let m: Value = serde_json::from_str(&r.files["package.json"]).unwrap(); - assert_eq!( - m["resolutions"], - json!({"other": "2.0.0", "left-pad@npm:^1.3.0": url}) - ); + assert_eq!(m["resolutions"], json!({"other": "2.0.0", "left-pad@npm:^1.3.0": url})); let mut files = BTreeMap::new(); files.insert("yarn.lock".to_string(), berry_lock("10c0")); @@ -8646,10 +8599,7 @@ mod tests { rewrite_yarn_berry(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.files.is_empty(), "{:?}", r.files); assert_eq!( - r.warnings - .iter() - .map(|w| w.code.as_str()) - .collect::>(), + r.warnings.iter().map(|w| w.code.as_str()).collect::>(), vec!["redirect_yarn_berry_manifest_missing"] ); @@ -8660,17 +8610,10 @@ mod tests { berry_lock("10c0") ); let mut r = RewriteResult::default(); - rewrite_yarn_berry( - &berry_files(with_patch, berry_manifest()), - std::slice::from_ref(&ovr), - &mut r, - ); + rewrite_yarn_berry(&berry_files(with_patch, berry_manifest()), std::slice::from_ref(&ovr), &mut r); assert!(r.files.is_empty(), "{:?}", r.files); let codes: Vec<&str> = r.warnings.iter().map(|w| w.code.as_str()).collect(); - assert!( - codes.contains(&"redirect_yarn_berry_shared_descriptor"), - "{codes:?}" - ); + assert!(codes.contains(&"redirect_yarn_berry_shared_descriptor"), "{codes:?}"); } /// Yarn routes a URL locator to its tarball fetcher only when it is an @@ -8695,10 +8638,7 @@ mod tests { assert!(r.files.is_empty(), "{url}: nothing written"); assert!(r.edits.is_empty(), "{url}: {:?}", r.edits); assert_eq!( - r.warnings - .iter() - .map(|w| w.code.as_str()) - .collect::>(), + r.warnings.iter().map(|w| w.code.as_str()).collect::>(), vec!["redirect_yarn_berry_artifact_url_unsupported"], "{url}" ); @@ -11770,11 +11710,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, @@ -11787,11 +11723,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 @@ -11806,10 +11738,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 ); @@ -12177,19 +12106,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), @@ -13616,10 +13537,7 @@ mod tests { ("crlf", lf.replace('\n', "\r\n")), ("tabs", lf.replace(" ", "\t")), ("bom", format!("\u{feff}{lf}")), - ( - "bom+crlf+tabs", - format!("\u{feff}{}", lf.replace(" ", "\t").replace('\n', "\r\n")), - ), + ("bom+crlf+tabs", format!("\u{feff}{}", lf.replace(" ", "\t").replace('\n', "\r\n"))), ]; for (shape, pristine) in shapes { let mut files = BTreeMap::new(); @@ -13635,10 +13553,7 @@ mod tests { "http://patch.test/left-pad-1.3.0.tgz", ) .replace("sha512-UPSTREAM==", "sha512-PATCHED=="); - assert_eq!( - out, &expected, - "{shape}: only the rewired values may change" - ); + assert_eq!(out, &expected, "{shape}: only the rewired values may change"); } } @@ -15918,8 +15833,7 @@ packages: ); // One edit; its fragments are the on-disk bytes of the entry. - let lock_edits: Vec<&FileEdit> = - r.edits.iter().filter(|e| e.path == "yarn.lock").collect(); + let lock_edits: Vec<&FileEdit> = r.edits.iter().filter(|e| e.path == "yarn.lock").collect(); assert_eq!(lock_edits.len(), 1, "{label}"); let edit = lock_edits[0]; let (orig, new) = ( @@ -15929,8 +15843,15 @@ packages: assert_eq!( (orig, new), ( - respell(lf_edit.original.as_ref().unwrap().as_str().unwrap()) - .trim_start_matches('\u{feff}'), + respell( + lf_edit + .original + .as_ref() + .unwrap() + .as_str() + .unwrap() + ) + .trim_start_matches('\u{feff}'), respell(lf_edit.new.as_ref().unwrap().as_str().unwrap()) .trim_start_matches('\u{feff}'), ), 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 c1376d4bb..87bd8ad5c 100644 --- a/crates/socket-patch-core/src/patch/redirect/pipenv.rs +++ b/crates/socket-patch-core/src/patch/redirect/pipenv.rs @@ -459,10 +459,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(); @@ -502,30 +499,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")); @@ -583,10 +570,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(); @@ -612,10 +596,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(); @@ -636,10 +617,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. @@ -650,6 +628,7 @@ mod tests { assert!(second.contains("/rotated/") && !second.contains("/tok/")); } + /// Hosted Pipenv recognizes its own pins through the shared recognizer /// (#563): a path-prefixed `--patch-server-url` deployment rotates its /// grant instead of refusing its own previous reference, and a hosted @@ -723,9 +702,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/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/gem.rs b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs index c47227d7e..a20a3cb3c 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/gem.rs @@ -57,11 +57,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/"; @@ -250,14 +250,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 f88aeb347..ea0fe324f 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs @@ -348,7 +348,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 { @@ -676,7 +678,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 569727aed..4be79627a 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 e792f2f96..e3ade4d63 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(crate) mod pep440; pub mod pipenv; diff --git a/crates/socket-patch-core/src/utils/process.rs b/crates/socket-patch-core/src/utils/process.rs index a8eb22cd7..13038c679 100644 --- a/crates/socket-patch-core/src/utils/process.rs +++ b/crates/socket-patch-core/src/utils/process.rs @@ -89,7 +89,9 @@ pub(crate) fn resolve_app_alias_with( std::env::split_paths(&path) .filter(|dir| dir.is_absolute()) .map(|dir| dir.join(format!("{name}.exe"))) - .find(|candidate| std::fs::symlink_metadata(candidate).is_ok_and(|meta| !meta.is_dir())) + .find(|candidate| { + std::fs::symlink_metadata(candidate).is_ok_and(|meta| !meta.is_dir()) + }) } /// A plain file that cannot be executed (a stray `bun` data file on PATH) @@ -425,11 +427,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let safe = tmp.path().join("bin"); std::fs::create_dir_all(&safe).unwrap(); - let relative = [ - PathBuf::from("."), - PathBuf::from(""), - PathBuf::from("planted"), - ]; + let relative = [PathBuf::from("."), PathBuf::from(""), PathBuf::from("planted")]; let only_relative = std::env::join_paths(&relative).unwrap(); let var = |name: &str| (name == "PATH").then(|| only_relative.clone()); @@ -439,10 +437,7 @@ mod tests { let with_safe = std::env::join_paths(relative.iter().cloned().chain([safe.clone()])).unwrap(); let var = |name: &str| (name == "PATH").then(|| with_safe.clone()); - assert_eq!( - resolve_app_alias_with("yarn", &var), - Some(safe.join("yarn.exe")) - ); + assert_eq!(resolve_app_alias_with("yarn", &var), Some(safe.join("yarn.exe"))); } #[test] 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 cbdcc8d93..5a21c5bb4 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -557,4 +557,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 80c5a9617..7716a3b32 100644 --- a/crates/socket-patch-core/src/vendor/bun_lockb.rs +++ b/crates/socket-patch-core/src/vendor/bun_lockb.rs @@ -1867,10 +1867,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 @@ -1913,9 +1910,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/view.rs b/crates/socket-patch-core/src/vendor/lock_inventory/view.rs index 29a446efc..acd48d721 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/view.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/view.rs @@ -15,10 +15,10 @@ use std::sync::Arc; use crate::constants::npm_family::{ BUN_LOCK, BUN_LOCKB, NPM_LOCKS, PNPM_LOCK, PNP_MARKERS, VLT_LOCK, }; -use crate::formats::pnpm::{sniff_lock_grammar, PnpmLockGrammar}; -use crate::formats::yarn::{sniff_grammar, YarnLockGrammar, UNIDENTIFIED_DETAIL}; use crate::utils::fs::{read_regular_to_bytes, read_regular_to_string}; use crate::vendor::npm_flavor::NpmLockFlavor; +use crate::formats::pnpm::{sniff_lock_grammar, PnpmLockGrammar}; +use crate::formats::yarn::{sniff_grammar, YarnLockGrammar, UNIDENTIFIED_DETAIL}; use crate::vendor::VendorWarning; /// One in-memory file. diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs b/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs index ba1324448..f84eed675 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/vlt.rs @@ -6,8 +6,8 @@ use std::path::Path; use serde_json::{Map, Value}; -use super::view::ProjectView; use crate::constants::npm_family::VLT_LOCK; +use super::view::ProjectView; use crate::vendor::vlt_lock_text::{ is_default_registry, sniff_lock, split_dep_id, DepId, DepIdKind, LockSniff, }; diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs b/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs index c3c21f8e9..9ed45e49d 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/wired.rs @@ -7,12 +7,12 @@ use toml_edit::{DocumentMut, Item}; use crate::constants::npm_family::{BUN_LOCK, BUN_LOCKB, NPM_LOCKS, PNPM_LOCK}; use crate::formats::pnpm::PnpmLock; -use crate::formats::yarn::is_berry_lock; use crate::utils::digest::is_sri_pin; use crate::utils::fs::{read_regular_to_bytes, read_regular_to_string}; use crate::utils::python_lock::{ lock_artifact, lock_package_collection, package_artifacts, uv_source_location, }; +use crate::formats::yarn::is_berry_lock; use crate::vendor::bun_lock_text::{decode_json_string, split_name_spec}; use crate::vendor::bun_lockb::BunLockb; use crate::vendor::yarn_berry_lock::berry_field; diff --git a/crates/socket-patch-core/src/vendor/prestage.rs b/crates/socket-patch-core/src/vendor/prestage.rs index b4cf64fb6..b3149ccaa 100644 --- a/crates/socket-patch-core/src/vendor/prestage.rs +++ b/crates/socket-patch-core/src/vendor/prestage.rs @@ -464,10 +464,7 @@ mod sweep_tests { for dir in &kept { assert!(v.join(dir).exists(), "{dir} kept"); } - assert!( - !v.join("gem").exists(), - "the levels only the tree kept alive are pruned" - ); + assert!(!v.join("gem").exists(), "the levels only the tree kept alive are pruned"); assert!(!v.join(format!("composer/{u}/psr/log@3.0.2")).exists()); assert!(v.join("state.json").exists()); assert_eq!(sweep_stale(root).await, 0, "idempotent"); diff --git a/crates/socket-patch-core/src/vendor/pypi.rs b/crates/socket-patch-core/src/vendor/pypi.rs index 1f57f74e2..74883c23e 100644 --- a/crates/socket-patch-core/src/vendor/pypi.rs +++ b/crates/socket-patch-core/src/vendor/pypi.rs @@ -3492,13 +3492,8 @@ wheels = [ tokio::fs::remove_dir_all(&uuid_dir).await.unwrap(); let bytes = served_wheel(b"service wheel at another filename"); let server = wiremock::MockServer::start().await; - mount_pypi_granted( - &server, - "six-1.16.0-py3-none-any.whl", - &sri_sha512(&bytes), - &bytes, - ) - .await; + mount_pypi_granted(&server, "six-1.16.0-py3-none-any.whl", &sri_sha512(&bytes), &bytes) + .await; let cfg = pypi_service_cfg(&server.uri(), VendorSource::Service, false); let error = crate::vendor::test_support::expect_failure(vendor(Some(cfg)).await); assert!( diff --git a/crates/socket-patch-core/src/vendor/toml_surgery.rs b/crates/socket-patch-core/src/vendor/toml_surgery.rs index 4d151f613..0b1777008 100644 --- a/crates/socket-patch-core/src/vendor/toml_surgery.rs +++ b/crates/socket-patch-core/src/vendor/toml_surgery.rs @@ -519,8 +519,7 @@ mod tests { // CRLF, and a hand edit can leave a mixed-ending file, so the // removal helpers must never normalize: every byte outside the // removed segment survives verbatim. - let wired = - "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\nfoo = { path = \"w.whl\" }\n"; + let wired = "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\nfoo = { path = \"w.whl\" }\n"; let after = remove_exact_line(wired, "foo = { path = \"w.whl\" }").unwrap(); assert_eq!(after, "[project]\r\nname = \"x\"\r\n\n[tool.uv.sources]\n"); assert_eq!( diff --git a/crates/socket-patch-core/src/vex/discover/cargo.rs b/crates/socket-patch-core/src/vex/discover/cargo.rs index 4056ea30b..86aab22de 100644 --- a/crates/socket-patch-core/src/vex/discover/cargo.rs +++ b/crates/socket-patch-core/src/vex/discover/cargo.rs @@ -738,22 +738,23 @@ async fn vendored_from_patches( } let copy_tagged = matches!(tag, CopyTag::Tagged(_) | CopyTag::Unreadable); if let Lock::Parsed(lock) = lock { - 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 \ + 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 997175812..3c4c872f5 100644 --- a/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs +++ b/crates/socket-patch-core/tests/covgap_crawlers_composer_crawler.rs @@ -99,11 +99,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 a8ed7092f..231d221ba 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; } @@ -483,12 +449,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 { @@ -580,8 +541,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 @@ -609,18 +569,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"), ), ]; @@ -679,10 +633,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); @@ -695,10 +646,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); } @@ -728,10 +676,7 @@ async fn gem_transitive_append_round_trips_unless_unprovable() { case.expected.insert("Gemfile".into(), legacy); 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") @@ -760,19 +705,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| { @@ -781,38 +717,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 ──────────────────────────────────────────────────────────────── @@ -981,11 +897,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; @@ -1004,42 +916,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| { @@ -1056,13 +946,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 ───────────────────────────────────────────────────────────────────── @@ -1100,11 +984,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}") } @@ -1117,18 +997,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"), ], ) } @@ -1163,10 +1033,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. @@ -1213,24 +1080,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:?}"); @@ -1253,20 +1110,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] @@ -1279,12 +1129,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(), @@ -1304,12 +1149,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; @@ -1324,10 +1164,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); @@ -1434,14 +1271,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}" @@ -1455,17 +1287,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. @@ -1525,9 +1352,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; @@ -1561,9 +1386,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" ); @@ -1578,16 +1401,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; } @@ -1611,17 +1427,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"; @@ -1632,10 +1443,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] @@ -1653,10 +1461,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"); @@ -1709,10 +1514,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(), @@ -1730,10 +1532,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); @@ -1741,9 +1540,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}"); @@ -1754,12 +1551,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!( @@ -1773,10 +1565,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()); } @@ -1855,13 +1644,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. @@ -1926,11 +1709,7 @@ wheels = [{{ url = \"{wheel_url}\", upload-time = 2023-10-17T17:46:21.184Z, size /// microseconds), with (`uv export`) or without (`uv pip compile`) an /// `index` on each registry package. fn uv_pylock(index: bool) -> String { - let index = if index { - "index = \"https://pypi.org/simple\"\n" - } else { - "" - }; + let index = if index { "index = \"https://pypi.org/simple\"\n" } else { "" }; format!( "# This file was autogenerated by uv via the following command:\n\ # uv pip compile --format pylock.toml req.in -o pylock.toml\n\ @@ -1958,13 +1737,8 @@ async fn pylock_without_index_round_trips() { assert!(!lock.contains("index")); for eol in ["\n", "\r\n"] { let input = tree(&[("pylock.toml", lock.replace('\n', eol))]); - assert_pypi_round_trip( - &format!("pip compile {eol:?}"), - &input, - &[urllib3_dep()], - None, - ) - .await; + assert_pypi_round_trip(&format!("pip compile {eol:?}"), &input, &[urllib3_dep()], None) + .await; } // A sibling whose files come from another host is not PyPI. let mirror = lock.replace( @@ -1972,11 +1746,9 @@ async fn pylock_without_index_round_trips() { "https://mirror.example.com/packages/21/ed/", ); let input = tree(&[("pylock.toml", mirror)]); - let (why, _, after) = pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; - assert!( - after["pylock.toml"].contains("patch.socket.dev"), - "the pin stays wired" - ); + let (why, _, after) = + pypi_refusal(&input, &[urllib3_dep()], &RestoreOptions::default()).await; + assert!(after["pylock.toml"].contains("patch.socket.dev"), "the pin stays wired"); assert!(why.contains("not PyPI"), "{why}"); } @@ -2002,10 +1774,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}"); @@ -2043,12 +1812,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; @@ -2102,10 +1866,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(); @@ -2150,10 +1911,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}"); } @@ -2174,9 +1932,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 })), @@ -2246,17 +2002,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};