diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75e9587b..71c67399 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,23 @@ jobs: - name: Run clippy run: cargo clippy --workspace --all-features -- -D warnings + # Moved-module aliases (patch::vendor → vendor, patch::go_mod_edit → + # vendor::go_mod_edit, patch::go_redirect → patch::redirect::golang_local) + # exist only for external consumers of the published core crate. + # #[deprecated] on a `pub use` re-export emits no warnings + # (rust-lang/rust#30827), so the compiler cannot pressure internal code + # off the old paths — this grep is the guard instead. + - name: Reject internal uses of moved-module alias paths + run: | + if grep -rn --include='*.rs' \ + -e 'patch::vendor' -e 'patch::go_mod_edit' \ + -e 'patch::go_redirect' -e 'patch::bun_lock_text' \ + -e 'utils::telemetry' -e 'utils::cleanup_blobs' \ + -e 'utils::date' -e 'utils::fuzzy_match' \ + crates; then + echo '::error::use the canonical module paths (crate::vendor, patch::redirect::golang_local, crate::telemetry, manifest::cleanup_blobs, api::date, crawlers::fuzzy_match); the old-path aliases exist only for external consumers' + exit 1 + fi # Lint the out-of-workspace packaging artifacts for the ecosystems whose setup # / CLI-distribution we added: the RubyGems CLI launcher gem + the Bundler diff --git a/crates/socket-patch-cli/src/args.rs b/crates/socket-patch-cli/src/args.rs index 32a7b552..6485d585 100644 --- a/crates/socket-patch-cli/src/args.rs +++ b/crates/socket-patch-cli/src/args.rs @@ -20,7 +20,7 @@ use clap::Args; use socket_patch_core::api::client::ApiClientEnvOverrides; use socket_patch_core::constants::DEFAULT_PATCH_MANIFEST_PATH; use socket_patch_core::crawlers::Ecosystem; -use socket_patch_core::patch::vendor::VendorSource; +use socket_patch_core::vendor::VendorSource; /// clap value-parser for each `--ecosystems` / `SOCKET_ECOSYSTEMS` token. /// @@ -317,7 +317,7 @@ impl GlobalArgs { /// flags are off. /// /// `offline` matters most: the telemetry kill-switch -/// (`socket_patch_core::utils::telemetry::is_telemetry_disabled`) honors the +/// (`socket_patch_core::telemetry::is_telemetry_disabled`) honors the /// strict-airgap contract by reading `SOCKET_OFFLINE` from the env, so /// without this mirror a bare `--offline` flag (or a truthy spelling like /// `SOCKET_OFFLINE=yes` that core's `"1" | "true"` match doesn't recognize) @@ -503,7 +503,7 @@ mod tests { } /// `--offline` promises "never contact the network", but the telemetry - /// kill-switch (`socket_patch_core::utils::telemetry::is_telemetry_disabled`) + /// kill-switch (`socket_patch_core::telemetry::is_telemetry_disabled`) /// reads the `SOCKET_OFFLINE` env var directly — it never sees the parsed /// flag. `apply_env_toggles` must therefore mirror `--offline` into the /// env exactly like `--debug` / `--no-telemetry`, or an airgapped @@ -520,7 +520,7 @@ mod tests { apply_env_toggles(&args); assert_eq!(std::env::var("SOCKET_OFFLINE").as_deref(), Ok("1")); assert!( - socket_patch_core::utils::telemetry::is_telemetry_disabled(), + socket_patch_core::telemetry::is_telemetry_disabled(), "--offline must disable telemetry (strict airgap: never contact the network)", ); }); @@ -541,7 +541,7 @@ mod tests { assert!(cli.common.offline, "SOCKET_OFFLINE=yes parses as offline"); apply_env_toggles(&cli.common); assert!( - socket_patch_core::utils::telemetry::is_telemetry_disabled(), + socket_patch_core::telemetry::is_telemetry_disabled(), "SOCKET_OFFLINE=yes must disable telemetry like SOCKET_OFFLINE=1", ); }); @@ -1082,7 +1082,7 @@ mod tests { }; // Guard against a vacuous pass: the gate must start open. - assert!(!socket_patch_core::utils::telemetry::is_telemetry_disabled()); + assert!(!socket_patch_core::telemetry::is_telemetry_disabled()); let tmp = tempfile::tempdir().unwrap(); rt.block_on(crate::commands::list::run( @@ -1091,7 +1091,7 @@ mod tests { }, )); assert!( - socket_patch_core::utils::telemetry::is_telemetry_disabled(), + socket_patch_core::telemetry::is_telemetry_disabled(), "`list --offline --no-telemetry` must mirror the toggles into the \ env — its telemetry kill-switch reads only SOCKET_OFFLINE / \ SOCKET_TELEMETRY_DISABLED", @@ -1115,7 +1115,7 @@ mod tests { }, )); assert!( - socket_patch_core::utils::telemetry::is_telemetry_disabled(), + socket_patch_core::telemetry::is_telemetry_disabled(), "`setup --offline --no-telemetry` must mirror the toggles into the env", ); }); diff --git a/crates/socket-patch-cli/src/commands/apply.rs b/crates/socket-patch-cli/src/commands/apply.rs index 7fceb3ec..c3df30d9 100644 --- a/crates/socket-patch-cli/src/commands/apply.rs +++ b/crates/socket-patch-cli/src/commands/apply.rs @@ -8,12 +8,12 @@ use socket_patch_core::manifest::schema::{PatchFileInfo, PatchManifest, PatchRec use socket_patch_core::patch::apply::{ apply_package_patch, verify_file_patch, ApplyResult, MismatchPolicy, PatchSources, VerifyStatus, }; -use socket_patch_core::patch::go_redirect::{ +use socket_patch_core::patch::redirect::golang_local::{ apply_go_redirect, reconcile_go_redirects, verify_go_redirect_state, }; +use socket_patch_core::telemetry::{track_patch_applied, track_patch_apply_failed}; use socket_patch_core::utils::purl::parse_golang_purl; use socket_patch_core::utils::purl::{normalize_purl, strip_purl_qualifiers}; -use socket_patch_core::utils::telemetry::{track_patch_applied, track_patch_apply_failed}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -268,7 +268,7 @@ async fn try_local_go_apply( version, pkg_path, &common.cwd, - socket_patch_core::patch::go_mod_edit::GO_PATCHES_DIR, + socket_patch_core::vendor::go_mod_edit::GO_PATCHES_DIR, &patch.files, sources, Some(&patch.uuid), @@ -339,12 +339,12 @@ async fn run_check(args: &ApplyArgs, manifest_path: &Path) -> i32 { let mut checked: usize = 0; { - use socket_patch_core::patch::go_redirect::Drift as GoDrift; + use socket_patch_core::patch::redirect::golang_local::Drift as GoDrift; if go_in_local_scope(&args.common) { // Vendored modules are excluded: their replace directives point at // `.socket/vendor/golang/` (the verify engine skips Vendor-owned // entries) and their state is audited by `vendor`, not `--check`. - let vendored = socket_patch_core::patch::vendor::load_state(&args.common.cwd) + let vendored = socket_patch_core::vendor::load_state(&args.common.cwd) .await .map(|s| { s.entries @@ -1002,8 +1002,7 @@ async fn apply_patches_inner( // by ledger key, resolved base purl, or qualifier-stripped key so // release-variant manifest keys (pypi `?artifact_id=`…) hit too; // unreadable state degrades to "nothing vendored" (fail-open). - let vendored_purls = - socket_patch_core::patch::vendor::vendored_purl_keys(&args.common.cwd).await; + let vendored_purls = socket_patch_core::vendor::vendored_purl_keys(&args.common.cwd).await; let is_vendored = |p: &str| vendored_purls.contains(p) || vendored_purls.contains(strip_purl_qualifiers(p)); let (mut results, mut matched_manifest_purls, vendored_bases) = diff --git a/crates/socket-patch-cli/src/commands/fetch_stage.rs b/crates/socket-patch-cli/src/commands/fetch_stage.rs index b081b31c..cad91c43 100644 --- a/crates/socket-patch-cli/src/commands/fetch_stage.rs +++ b/crates/socket-patch-cli/src/commands/fetch_stage.rs @@ -353,11 +353,8 @@ pub(crate) async fn stage_vendor_sources_in_memory( // The committed vendor artifact IS the patched content: harvest its // afterHash blobs into memory so in-sync re-runs and fresh clones of // already-vendored projects stage with no network and no disk blobs. - mem = socket_patch_core::patch::vendor::harvest_artifact_blobs( - project_root, - &manifest.patches, - ) - .await; + mem = socket_patch_core::vendor::harvest_artifact_blobs(project_root, &manifest.patches) + .await; if !mem.is_empty() { to_fetch.retain(|(purl, _)| { manifest.patches.get(*purl).is_none_or(|record| { diff --git a/crates/socket-patch-cli/src/commands/get.rs b/crates/socket-patch-cli/src/commands/get.rs index 1f5fe18e..88f04173 100644 --- a/crates/socket-patch-cli/src/commands/get.rs +++ b/crates/socket-patch-cli/src/commands/get.rs @@ -7,15 +7,15 @@ use socket_patch_core::api::ranking::{cmp_search_results, severity_order}; use socket_patch_core::api::types::{ PatchResponse, PatchSearchResult, SearchResponse, VulnerabilityResponse, }; +use socket_patch_core::crawlers::fuzzy_match::fuzzy_match_packages; use socket_patch_core::crawlers::{CrawlerOptions, Ecosystem}; use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; use socket_patch_core::manifest::schema::{ PatchFileInfo, PatchManifest, PatchRecord, VulnerabilityInfo, }; use socket_patch_core::patch::apply::select_installed_variants; -use socket_patch_core::utils::fuzzy_match::fuzzy_match_packages; +use socket_patch_core::telemetry::{track_patch_fetch_failed, track_patch_fetched}; use socket_patch_core::utils::purl::{is_purl, normalize_purl, strip_purl_qualifiers}; -use socket_patch_core::utils::telemetry::{track_patch_fetch_failed, track_patch_fetched}; use std::collections::HashMap; use std::fmt; use std::path::{Path, PathBuf}; @@ -869,7 +869,7 @@ pub(crate) async fn download_patch_records( let (selected, narrow_warnings) = filter_to_installed_releases(selected, params, &api_client).await; - let vendor_state = socket_patch_core::patch::vendor::load_state(¶ms.cwd) + let vendor_state = socket_patch_core::vendor::load_state(¶ms.cwd) .await .unwrap_or_default(); @@ -882,11 +882,9 @@ pub(crate) async fn download_patch_records( for search_result in &selected { // Idempotency: a detached entry already at this uuid carries its // own record — no view fetch needed. - let existing = socket_patch_core::patch::vendor::lookup_entry( - &vendor_state.entries, - &search_result.purl, - ) - .filter(|e| e.detached && e.uuid == search_result.uuid); + let existing = + socket_patch_core::vendor::lookup_entry(&vendor_state.entries, &search_result.purl) + .filter(|e| e.detached && e.uuid == search_result.uuid); if let Some(record) = existing.and_then(|e| e.record.clone()) { if !params.json && !params.silent { eprintln!(" [skip] {} (already vendored)", search_result.purl); @@ -993,7 +991,7 @@ async fn warn_on_vendored_uuid_drift( downloaded_patches: &[serde_json::Value], warnings: &mut Vec, ) { - let Ok(vendor_state) = socket_patch_core::patch::vendor::load_state(cwd).await else { + let Ok(vendor_state) = socket_patch_core::vendor::load_state(cwd).await else { return; }; if vendor_state.entries.is_empty() { @@ -1006,7 +1004,7 @@ async fn warn_on_vendored_uuid_drift( if !matches!(rec["action"].as_str(), Some("added" | "updated")) { continue; } - let entry = socket_patch_core::patch::vendor::lookup_entry(&vendor_state.entries, purl); + let entry = socket_patch_core::vendor::lookup_entry(&vendor_state.entries, purl); if let Some(entry) = entry.filter(|e| e.uuid != uuid) { let w = format!( "{purl} is vendored at patch {} but the manifest now records {uuid}; \ diff --git a/crates/socket-patch-cli/src/commands/list.rs b/crates/socket-patch-cli/src/commands/list.rs index 8f3b07b3..1475d748 100644 --- a/crates/socket-patch-cli/src/commands/list.rs +++ b/crates/socket-patch-cli/src/commands/list.rs @@ -1,8 +1,8 @@ use clap::Args; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::PatchManifest; +use socket_patch_core::telemetry::track_patch_listed; use socket_patch_core::utils::socket_cli_config; -use socket_patch_core::utils::telemetry::track_patch_listed; use crate::args::{apply_env_toggles, GlobalArgs}; use crate::json_envelope::{ diff --git a/crates/socket-patch-cli/src/commands/remove.rs b/crates/socket-patch-cli/src/commands/remove.rs index 31f627ec..96cd7de8 100644 --- a/crates/socket-patch-cli/src/commands/remove.rs +++ b/crates/socket-patch-cli/src/commands/remove.rs @@ -1,11 +1,11 @@ use clap::Args; use socket_patch_core::api::client::get_api_client_with_overrides; +use socket_patch_core::manifest::cleanup_blobs::{cleanup_unused_blobs, format_cleanup_result}; use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; use socket_patch_core::manifest::schema::PatchManifest; -use socket_patch_core::patch::vendor::{load_state, save_state, VendorEntry, VendorState}; -use socket_patch_core::utils::cleanup_blobs::{cleanup_unused_blobs, format_cleanup_result}; +use socket_patch_core::telemetry::{track_patch_remove_failed, track_patch_removed}; use socket_patch_core::utils::purl::purl_matches_identifier; -use socket_patch_core::utils::telemetry::{track_patch_remove_failed, track_patch_removed}; +use socket_patch_core::vendor::{load_state, save_state, VendorEntry, VendorState}; use std::path::Path; use std::time::Duration; diff --git a/crates/socket-patch-cli/src/commands/repair.rs b/crates/socket-patch-cli/src/commands/repair.rs index c75d11e3..e5722178 100644 --- a/crates/socket-patch-cli/src/commands/repair.rs +++ b/crates/socket-patch-cli/src/commands/repair.rs @@ -4,12 +4,12 @@ use socket_patch_core::api::blob_fetcher::{ DownloadMode, }; use socket_patch_core::api::client::get_api_client_with_overrides; -use socket_patch_core::manifest::operations::read_manifest; -use socket_patch_core::patch::apply::PatchSources; -use socket_patch_core::utils::cleanup_blobs::{ +use socket_patch_core::manifest::cleanup_blobs::{ cleanup_unused_archives, cleanup_unused_blobs, format_cleanup_result, }; -use socket_patch_core::utils::telemetry::{track_patch_repair_failed, track_patch_repaired}; +use socket_patch_core::manifest::operations::read_manifest; +use socket_patch_core::patch::apply::PatchSources; +use socket_patch_core::telemetry::{track_patch_repair_failed, track_patch_repaired}; use std::path::Path; use std::time::Duration; @@ -83,7 +83,7 @@ pub async fn run(args: RepairArgs) -> i32 { let state_file = args .common .cwd - .join(socket_patch_core::patch::vendor::VENDOR_STATE_REL); + .join(socket_patch_core::vendor::VENDOR_STATE_REL); let has_vendor_traces = tokio::fs::metadata(&state_file).await.is_ok() || !crate::commands::repair_vendor::scan_vendor_references(&args.common.cwd) .await @@ -272,7 +272,7 @@ async fn repair_inner( // packages` — repair must not re-litter them (or fail trying). The // cleanup phase below still uses the FULL manifest, so it never sweeps // sources an in-place apply may need for rollback. - let vendor_state = socket_patch_core::patch::vendor::load_state(&args.common.cwd) + let vendor_state = socket_patch_core::vendor::load_state(&args.common.cwd) .await .unwrap_or_default(); // Lockfile vendor references count as vendored even before the ledger @@ -290,7 +290,7 @@ async fn repair_inner( .iter() .filter(|(purl, rec)| { !referenced_uuids.contains(&rec.uuid) - && socket_patch_core::patch::vendor::lookup_entry(&vendor_state.entries, purl) + && socket_patch_core::vendor::lookup_entry(&vendor_state.entries, purl) .is_none_or(|e| e.uuid != rec.uuid) }) .map(|(k, v)| (k.clone(), v.clone())) diff --git a/crates/socket-patch-cli/src/commands/repair_vendor.rs b/crates/socket-patch-cli/src/commands/repair_vendor.rs index 734057c4..c5843751 100644 --- a/crates/socket-patch-cli/src/commands/repair_vendor.rs +++ b/crates/socket-patch-cli/src/commands/repair_vendor.rs @@ -26,14 +26,14 @@ use socket_patch_core::api::client::get_api_client_with_overrides; use socket_patch_core::crawlers::CrawlerOptions; use socket_patch_core::manifest::schema::{PatchManifest, PatchRecord}; use socket_patch_core::patch::copy_tree::remove_tree; -use socket_patch_core::patch::vendor::state::VendorArtifact; -use socket_patch_core::patch::vendor::{ - self, check_vendored_artifact, file_sha256_hex, load_state, lock_inventory, parse_vendor_path, - registry_fetch, ArtifactHealth, VendorEntry, VendorOutcome, -}; use socket_patch_core::utils::purl::{ normalize_purl, percent_decode_purl_component, strip_purl_qualifiers, }; +use socket_patch_core::vendor::state::VendorArtifact; +use socket_patch_core::vendor::{ + self, check_vendored_artifact, file_sha256_hex, load_state, lock_inventory, parse_vendor_path, + registry_fetch, ArtifactHealth, VendorEntry, VendorOutcome, +}; use socket_patch_core::vex::time::now_rfc3339; use crate::args::GlobalArgs; diff --git a/crates/socket-patch-cli/src/commands/rollback.rs b/crates/socket-patch-cli/src/commands/rollback.rs index fe0d136c..b96deb36 100644 --- a/crates/socket-patch-cli/src/commands/rollback.rs +++ b/crates/socket-patch-cli/src/commands/rollback.rs @@ -8,8 +8,8 @@ use socket_patch_core::patch::apply::select_installed_variants; use socket_patch_core::patch::rollback::{ rollback_package_patch, RollbackResult, VerifyRollbackStatus, }; +use socket_patch_core::telemetry::{track_patch_rollback_failed, track_patch_rolled_back}; use socket_patch_core::utils::purl::strip_purl_qualifiers; -use socket_patch_core::utils::telemetry::{track_patch_rollback_failed, track_patch_rolled_back}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -97,8 +97,8 @@ async fn try_rollback_local_go( patch: &PatchRecord, common: &GlobalArgs, ) -> Option { - use socket_patch_core::patch::go_mod_edit::{ReplaceOwner, GO_PATCHES_DIR}; - use socket_patch_core::patch::go_redirect::remove_go_redirect; + use socket_patch_core::patch::redirect::golang_local::remove_go_redirect; + use socket_patch_core::vendor::go_mod_edit::{ReplaceOwner, GO_PATCHES_DIR}; if !is_local_go(purl, common) { return None; } @@ -511,8 +511,7 @@ async fn rollback_patches_inner( // `vendor --revert` undoes it wholesale. Matching mirrors apply's // ledger-key / base-purl / qualifier-stripped triple; unreadable state // degrades to "nothing vendored". - let vendored_keys = - socket_patch_core::patch::vendor::vendored_purl_keys(&args.common.cwd).await; + let vendored_keys = socket_patch_core::vendor::vendored_purl_keys(&args.common.cwd).await; let is_vendored = |p: &str| vendored_keys.contains(p) || vendored_keys.contains(strip_purl_qualifiers(p)); let (vendored_targets, patches_to_rollback): (Vec<_>, Vec<_>) = patches_to_rollback @@ -1246,7 +1245,7 @@ mod tests { /// kept using the patched copy. #[tokio::test] async fn try_rollback_local_go_drops_redirect_and_copy() { - use socket_patch_core::patch::go_mod_edit::{ + use socket_patch_core::vendor::go_mod_edit::{ ensure_replace_entry, read_replace_entries, GO_PATCHES_DIR, }; @@ -1331,7 +1330,7 @@ mod tests { /// that mutated nothing. #[tokio::test] async fn try_rollback_local_go_dry_run_reports_no_files_rolled_back() { - use socket_patch_core::patch::go_mod_edit::{ + use socket_patch_core::vendor::go_mod_edit::{ ensure_replace_entry, read_replace_entries, GO_PATCHES_DIR, }; @@ -1423,7 +1422,7 @@ mod tests { /// record deleted, i.e. an active patch nothing tracks. #[tokio::test] async fn rollback_drops_local_go_redirect_when_module_cache_has_no_copy() { - use socket_patch_core::patch::go_mod_edit::{ + use socket_patch_core::vendor::go_mod_edit::{ ensure_replace_entry, read_replace_entries, GO_PATCHES_DIR, }; @@ -1520,7 +1519,7 @@ mod tests { /// filter's back. #[tokio::test] async fn undiscovered_local_go_redirect_respects_ecosystem_filter() { - use socket_patch_core::patch::go_mod_edit::{ + use socket_patch_core::vendor::go_mod_edit::{ ensure_replace_entry, read_replace_entries, GO_PATCHES_DIR, }; diff --git a/crates/socket-patch-cli/src/commands/scan/discovery.rs b/crates/socket-patch-cli/src/commands/scan/discovery.rs index 666fec4e..a8404c61 100644 --- a/crates/socket-patch-cli/src/commands/scan/discovery.rs +++ b/crates/socket-patch-cli/src/commands/scan/discovery.rs @@ -39,7 +39,7 @@ pub(super) async fn lockfile_supplement( common: &GlobalArgs, crawled: &[socket_patch_core::crawlers::types::CrawledPackage], ) -> LockfileSupplement { - use socket_patch_core::patch::vendor::lock_inventory; + use socket_patch_core::vendor::lock_inventory; let mut out = LockfileSupplement::default(); if common.global || common.global_prefix.is_some() { @@ -99,7 +99,7 @@ pub(super) async fn vendored_ledger_supplement( if common.global || common.global_prefix.is_some() { return Vec::new(); } - let Ok(state) = socket_patch_core::patch::vendor::load_state(&common.cwd).await else { + let Ok(state) = socket_patch_core::vendor::load_state(&common.cwd).await else { return Vec::new(); }; let crawled_norm: HashSet = crawled diff --git a/crates/socket-patch-cli/src/commands/scan/gc.rs b/crates/socket-patch-cli/src/commands/scan/gc.rs index 77926ea4..094e2d6e 100644 --- a/crates/socket-patch-cli/src/commands/scan/gc.rs +++ b/crates/socket-patch-cli/src/commands/scan/gc.rs @@ -2,11 +2,11 @@ //! orphan blob/diff/package-archive sweeps, in both mutating (apply) and //! read-only (preview) forms. -use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; -use socket_patch_core::manifest::schema::PatchManifest; -use socket_patch_core::utils::cleanup_blobs::{ +use socket_patch_core::manifest::cleanup_blobs::{ cleanup_unused_archives, cleanup_unused_blobs, CleanupResult, }; +use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; +use socket_patch_core::manifest::schema::PatchManifest; use socket_patch_core::utils::purl::{normalize_purl, strip_purl_qualifiers}; use std::collections::HashSet; use std::path::Path; @@ -86,7 +86,7 @@ impl GcSummary { /// Compute GC actions without performing them. `dry_run = true` for the /// preview path; `dry_run = false` for the apply path. The cleanup helpers -/// from `socket_patch_core::utils::cleanup_blobs` natively support dry-run, +/// from `socket_patch_core::manifest::cleanup_blobs` natively support dry-run, /// so the same function works for both. async fn run_gc( manifest: &PatchManifest, diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index 7305f7e0..40da341e 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -14,8 +14,8 @@ use socket_patch_core::api::types::{BatchPackagePatches, PatchSearchResult}; use socket_patch_core::crawlers::{CrawlerOptions, Ecosystem}; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::PatchManifest; +use socket_patch_core::telemetry::{track_patch_scan_failed, track_patch_scanned}; use socket_patch_core::utils::purl::{normalize_purl, strip_purl_qualifiers}; -use socket_patch_core::utils::telemetry::{track_patch_scan_failed, track_patch_scanned}; use std::collections::HashSet; use std::io::IsTerminal; use std::path::Path; @@ -560,8 +560,7 @@ pub async fn run(mut args: ScanArgs) -> i32 { // exemption (a vendored package is consumed from the committed // artifact, so "absent from the crawl" is its normal state, not // grounds for pruning) and the vendored-skip in the apply path. - let vendored_purls = - socket_patch_core::patch::vendor::vendored_purl_keys(&args.common.cwd).await; + let vendored_purls = socket_patch_core::vendor::vendored_purl_keys(&args.common.cwd).await; // Filter by --ecosystems if provided let filtered_crawled: Vec<_> = if let Some(ref allowed) = args.common.ecosystems { @@ -1547,7 +1546,7 @@ pub async fn run(mut args: ScanArgs) -> i32 { if gc.pruned.len() == 1 { "y" } else { "ies" }, total, if total == 1 { "" } else { "s" }, - socket_patch_core::utils::cleanup_blobs::format_bytes(gc.total_bytes()), + socket_patch_core::manifest::cleanup_blobs::format_bytes(gc.total_bytes()), ); } if !args.common.silent { diff --git a/crates/socket-patch-cli/src/commands/scan/vendor_flow.rs b/crates/socket-patch-cli/src/commands/scan/vendor_flow.rs index 94b71b7b..75ae65bf 100644 --- a/crates/socket-patch-cli/src/commands/scan/vendor_flow.rs +++ b/crates/socket-patch-cli/src/commands/scan/vendor_flow.rs @@ -8,8 +8,8 @@ use socket_patch_core::api::types::{BatchPackagePatches, PatchSearchResult}; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::{PatchManifest, PatchRecord}; use socket_patch_core::patch::apply_lock; -use socket_patch_core::patch::vendor::{load_state, lookup_entry}; -use socket_patch_core::utils::telemetry::track_patch_vendor_failed; +use socket_patch_core::telemetry::track_patch_vendor_failed; +use socket_patch_core::vendor::{load_state, lookup_entry}; use std::collections::{HashMap, HashSet}; use std::path::Path; use std::time::Duration; diff --git a/crates/socket-patch-cli/src/commands/setup.rs b/crates/socket-patch-cli/src/commands/setup.rs index b16e920c..805bb8b9 100644 --- a/crates/socket-patch-cli/src/commands/setup.rs +++ b/crates/socket-patch-cli/src/commands/setup.rs @@ -19,7 +19,7 @@ use socket_patch_core::pth_hook::edit::{ add_hook_dependency, pyproject_contains_hook, remove_hook_dependency, ManifestKind, PthEditResult, PthStatus, }; -use socket_patch_core::utils::telemetry::track_patch_setup; +use socket_patch_core::telemetry::track_patch_setup; use socket_patch_core::vex::applied_patches_with_vendor; use std::io::{self, Write}; use std::path::{Path, PathBuf}; diff --git a/crates/socket-patch-cli/src/commands/vendor.rs b/crates/socket-patch-cli/src/commands/vendor.rs index 03310205..644ee129 100644 --- a/crates/socket-patch-cli/src/commands/vendor.rs +++ b/crates/socket-patch-cli/src/commands/vendor.rs @@ -23,13 +23,13 @@ use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; use socket_patch_core::manifest::schema::{PatchManifest, PatchRecord}; use socket_patch_core::patch::apply::{verify_file_patch, PatchSources}; use socket_patch_core::patch::copy_tree::remove_tree; -use socket_patch_core::patch::vendor::{ +use socket_patch_core::telemetry::{track_patch_vendor_failed, track_patch_vendored}; +use socket_patch_core::utils::purl::{normalize_purl, strip_purl_qualifiers}; +use socket_patch_core::vendor::{ self, ecosystem_dir_for_purl, load_state, lock_inventory, lookup_entry, registry_fetch, save_state, RevertOutcome, VendorEntry, VendorOutcome, VendorServiceConfig, VendorSource, VendorState, VendorWarning, }; -use socket_patch_core::utils::purl::{normalize_purl, strip_purl_qualifiers}; -use socket_patch_core::utils::telemetry::{track_patch_vendor_failed, track_patch_vendored}; use socket_patch_core::vex::time::now_rfc3339; use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -1400,7 +1400,7 @@ pub(crate) async fn run_vendor_gc( #[cfg(test)] mod dispatch_tests { use super::*; - use socket_patch_core::patch::vendor::VendorSource; + use socket_patch_core::vendor::VendorSource; /// Fail-closed `--vendor-source=service` must not refuse maven at the /// dispatch gate: the maven backend has a full service path (prebuilt @@ -1582,7 +1582,7 @@ mod variant_probe_tests { #[cfg(test)] mod gc_tests { use super::*; - use socket_patch_core::patch::vendor::state::VendorArtifact; + use socket_patch_core::vendor::state::VendorArtifact; use std::path::PathBuf; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; diff --git a/crates/socket-patch-cli/src/commands/vex.rs b/crates/socket-patch-cli/src/commands/vex.rs index ae5d8b5f..179fc4f3 100644 --- a/crates/socket-patch-cli/src/commands/vex.rs +++ b/crates/socket-patch-cli/src/commands/vex.rs @@ -20,7 +20,7 @@ use clap::Args; use socket_patch_core::crawlers::Ecosystem; use socket_patch_core::manifest::operations::read_manifest; use socket_patch_core::manifest::schema::PatchManifest; -use socket_patch_core::utils::telemetry::{track_vex_failed, track_vex_generated}; +use socket_patch_core::telemetry::{track_vex_failed, track_vex_generated}; use socket_patch_core::vex::{ build_document, detect_product, BuildOptions, Document, FailedPatch, VendorContext, VerifyOutcome, @@ -314,14 +314,14 @@ async fn generate_vex( // not whether this run hashed it. The committed ledger is as // trustworthy as the manifest beside it, and reading it hashes // nothing. An unreadable ledger degrades to "nothing vendored". - let entries = socket_patch_core::patch::vendor::load_state(&common.cwd) + let entries = socket_patch_core::vendor::load_state(&common.cwd) .await .map(|state| state.entries) .unwrap_or_default(); let vendored = manifest .patches .keys() - .filter(|purl| socket_patch_core::patch::vendor::lookup_entry(&entries, purl).is_some()) + .filter(|purl| socket_patch_core::vendor::lookup_entry(&entries, purl).is_some()) .cloned() .collect(); VerifyOutcome { @@ -537,7 +537,7 @@ pub(crate) async fn generate_vex_from_manifest_path( /// still fails closed per-entry downstream, and `load_vendor_context` /// already warns about the unreadable state. async fn augment_with_detached(common: &GlobalArgs, mut manifest: PatchManifest) -> PatchManifest { - if let Ok(state) = socket_patch_core::patch::vendor::load_state(&common.cwd).await { + if let Ok(state) = socket_patch_core::vendor::load_state(&common.cwd).await { for (key, entry) in state.entries { if !entry.detached { continue; @@ -633,7 +633,7 @@ pub(crate) async fn load_vendor_context( common: &GlobalArgs, manifest: &PatchManifest, ) -> Option { - let entries = match socket_patch_core::patch::vendor::load_state(&common.cwd).await { + let entries = match socket_patch_core::vendor::load_state(&common.cwd).await { Ok(state) => state.entries, Err(e) => { if !common.silent { @@ -665,13 +665,15 @@ pub(crate) async fn load_vendor_context( async fn synthesize_go_patches( common: &GlobalArgs, manifest: &PatchManifest, - entries: &HashMap, + entries: &HashMap, ) -> HashMap { - use socket_patch_core::patch::go_mod_edit::{ - read_replace_entries, ReplaceOwner, GO_PATCHES_DIR, + use socket_patch_core::patch::redirect::golang_local::{ + are_safe_redirect_coords, copy_dir_for, }; - use socket_patch_core::patch::go_redirect::{are_safe_redirect_coords, copy_dir_for}; use socket_patch_core::utils::purl::build_golang_purl; + use socket_patch_core::vendor::go_mod_edit::{ + read_replace_entries, ReplaceOwner, GO_PATCHES_DIR, + }; let mut go_patches = HashMap::new(); for entry in read_replace_entries(&common.cwd).await { @@ -687,7 +689,7 @@ async fn synthesize_go_patches( } // Explicit vendor entries take precedence over the synthesis // (vendor may have taken over an apply redirect). - if socket_patch_core::patch::vendor::lookup_entry(entries, &purl).is_some() { + if socket_patch_core::vendor::lookup_entry(entries, &purl).is_some() { continue; } // SECURITY: module/version come from a committed (tamper-able) @@ -813,7 +815,7 @@ mod tests { /// an out-of-tree path into the go-patches verification map. #[test] fn go_redirect_coord_guard_matches_core_rules() { - use socket_patch_core::patch::go_redirect::are_safe_redirect_coords; + use socket_patch_core::patch::redirect::golang_local::are_safe_redirect_coords; assert!(are_safe_redirect_coords("github.com/foo/bar", "v1.4.2")); assert!(are_safe_redirect_coords("gopkg.in/inf.v0", "v0.9.1")); diff --git a/crates/socket-patch-cli/tests/e2e_hosted_production.rs b/crates/socket-patch-cli/tests/e2e_hosted_production.rs index fb4c4216..1356509e 100644 --- a/crates/socket-patch-cli/tests/e2e_hosted_production.rs +++ b/crates/socket-patch-cli/tests/e2e_hosted_production.rs @@ -793,7 +793,7 @@ async fn canary_published_at_is_a_patch_date_not_a_package_date() { // PyPI stamps ISO-8601; the patch API stamps RFC 2822. They cannot be // compared as strings, so compare the calendar DATE via the same parser // the ranking uses. - use socket_patch_core::utils::date::parse_timestamp_secs; + use socket_patch_core::api::date::parse_timestamp_secs; let upload_days: std::collections::HashSet = uploads .iter() .filter_map(|u| parse_timestamp_secs(u)) @@ -803,7 +803,7 @@ async fn canary_published_at_is_a_patch_date_not_a_package_date() { let Some(secs) = parse_timestamp_secs(published) else { panic!( "production publishedAt {published:?} (patch {uuid}) does not parse — \ - utils::date must handle every format the API emits" + api::date must handle every format the API emits" ); }; assert!( diff --git a/crates/socket-patch-cli/tests/e2e_vex_vendor.rs b/crates/socket-patch-cli/tests/e2e_vex_vendor.rs index 20379496..fc130a15 100644 --- a/crates/socket-patch-cli/tests/e2e_vex_vendor.rs +++ b/crates/socket-patch-cli/tests/e2e_vex_vendor.rs @@ -30,7 +30,7 @@ use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes; use socket_patch_core::manifest::schema::{ PatchFileInfo, PatchManifest, PatchRecord, SetupConfig, VulnerabilityInfo, }; -use socket_patch_core::patch::vendor::state::{VendorArtifact, VendorEntry, VendorState}; +use socket_patch_core::vendor::state::{VendorArtifact, VendorEntry, VendorState}; /// Canonical-grammar patch UUID — the vendored-artifact verifier validates /// the uuid path level, so fixtures must use the real shape. @@ -486,13 +486,15 @@ fn golang_go_patches_redirect_attested_without_module_cache() { .unwrap(); tokio::runtime::Runtime::new() .unwrap() - .block_on(socket_patch_core::patch::go_mod_edit::ensure_replace_entry( - cwd, - module, - version, - socket_patch_core::patch::go_mod_edit::GO_PATCHES_DIR, - false, - )) + .block_on( + socket_patch_core::vendor::go_mod_edit::ensure_replace_entry( + cwd, + module, + version, + socket_patch_core::vendor::go_mod_edit::GO_PATCHES_DIR, + false, + ), + ) .expect("write go.mod replace"); // The patched copy dir the redirect points at. diff --git a/crates/socket-patch-cli/tests/in_process_vendor.rs b/crates/socket-patch-cli/tests/in_process_vendor.rs index ddac1877..2b263cce 100644 --- a/crates/socket-patch-cli/tests/in_process_vendor.rs +++ b/crates/socket-patch-cli/tests/in_process_vendor.rs @@ -1008,7 +1008,7 @@ async fn remove_detached_only_purl_reverts() { /// exact state `vendor` persists) so the test needs no full go vendor run. #[tokio::test] async fn vendored_golang_purl_skipped_by_apply() { - use socket_patch_core::patch::vendor::state::{VendorArtifact, VendorEntry, VendorState}; + use socket_patch_core::vendor::state::{VendorArtifact, VendorEntry, VendorState}; const MODULE: &str = "github.com/foo/bar"; const VERSION: &str = "v1.4.2"; @@ -1088,7 +1088,7 @@ async fn vendored_golang_purl_skipped_by_apply() { pipenv: None, }, ); - socket_patch_core::patch::vendor::save_state(root, &state) + socket_patch_core::vendor::save_state(root, &state) .await .expect("seed state.json"); diff --git a/crates/socket-patch-cli/tests/scan_vendor_e2e.rs b/crates/socket-patch-cli/tests/scan_vendor_e2e.rs index faa6236f..d10a5446 100644 --- a/crates/socket-patch-cli/tests/scan_vendor_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_vendor_e2e.rs @@ -664,7 +664,7 @@ async fn scan_vendor_flag_conflicts_are_clap_errors() { /// No invocation in this suite may emit telemetry. Telemetry resolves its /// endpoint from `SOCKET_API_URL` / `SOCKET_PROXY_URL` env ONLY (the -/// `--api-url` flag is invisible to it — utils::telemetry), so the +/// `--api-url` flag is invisible to it — telemetry), so the /// unhardened harness sent every successful run's `patch_vendored` event to /// the LIVE `/v0/orgs/test-org/telemetry` with the fake bearer token. Seed /// the child env with a reachable endpoint (worst case for the kill-switch) diff --git a/crates/socket-patch-cli/tests/setup_contract_gaps.rs b/crates/socket-patch-cli/tests/setup_contract_gaps.rs index 92a55cca..b2a87779 100644 --- a/crates/socket-patch-cli/tests/setup_contract_gaps.rs +++ b/crates/socket-patch-cli/tests/setup_contract_gaps.rs @@ -195,7 +195,7 @@ const VENDOR_UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; /// ledger entry binding the purl to it, and a manifest record whose /// afterHash is the hash of `patched`. fn setup_vendored_fixture(proj: &Path, home: &Path, installed: &[u8], vendored: &[u8]) { - use socket_patch_core::patch::vendor::state::{VendorArtifact, VendorEntry, VendorState}; + use socket_patch_core::vendor::state::{VendorArtifact, VendorEntry, VendorState}; write( &proj.join("package.json"), diff --git a/crates/socket-patch-core/src/utils/date.rs b/crates/socket-patch-core/src/api/date.rs similarity index 100% rename from crates/socket-patch-core/src/utils/date.rs rename to crates/socket-patch-core/src/api/date.rs diff --git a/crates/socket-patch-core/src/api/mod.rs b/crates/socket-patch-core/src/api/mod.rs index b27a33bc..ab918e7a 100644 --- a/crates/socket-patch-core/src/api/mod.rs +++ b/crates/socket-patch-core/src/api/mod.rs @@ -1,4 +1,5 @@ pub mod blob_fetcher; pub mod client; +pub mod date; pub mod ranking; pub mod types; diff --git a/crates/socket-patch-core/src/api/ranking.rs b/crates/socket-patch-core/src/api/ranking.rs index cba276d9..71ce8b55 100644 --- a/crates/socket-patch-core/src/api/ranking.rs +++ b/crates/socket-patch-core/src/api/ranking.rs @@ -45,8 +45,8 @@ use std::cmp::{Ordering, Reverse}; +use crate::api::date::parse_timestamp_secs; use crate::api::types::{BatchPatchInfo, PatchSearchResult}; -use crate::utils::date::parse_timestamp_secs; /// Severity ordering for sorting: **most severe = lowest number**. /// @@ -117,7 +117,7 @@ struct RankKey<'a> { /// package's release date. Unparseable or absent timestamps collapse /// to 0 and therefore sort last: the right treatment for a date we /// cannot trust, and the reason this is epoch seconds rather than the - /// raw string (see [`crate::utils::date`]). + /// raw string (see [`crate::api::date`]). patch_published: Reverse, /// `false` sorts first, so paid leads. A tiebreak only: it can never /// override severity or recency. diff --git a/crates/socket-patch-core/src/api/types.rs b/crates/socket-patch-core/src/api/types.rs index 6ea6cb5e..ceca4dd0 100644 --- a/crates/socket-patch-core/src/api/types.rs +++ b/crates/socket-patch-core/src/api/types.rs @@ -39,7 +39,7 @@ pub struct PatchResponse { /// `Fri, 27 Mar 2026 19:12:42 GMT` (verified across npm, PyPI, cargo /// and gem), while this repo's fixtures use RFC 3339. Never compare /// these as raw strings; route through - /// [`crate::utils::date::parse_timestamp_secs`], which handles both. + /// [`crate::api::date::parse_timestamp_secs`], which handles both. pub published_at: String, pub files: HashMap, pub vulnerabilities: HashMap, diff --git a/crates/socket-patch-core/src/crawlers/composer_crawler.rs b/crates/socket-patch-core/src/crawlers/composer_crawler.rs index 58808f8e..63904a9f 100644 --- a/crates/socket-patch-core/src/crawlers/composer_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/composer_crawler.rs @@ -270,7 +270,7 @@ async fn get_composer_home() -> Option { /// `dev-main`, `1.0.x-dev`) are returned untouched. /// /// Also used by the composer vendor backend -/// (`patch::vendor::composer_lock`) to match lock versions against PURL +/// (`vendor::composer_lock`) to match lock versions against PURL /// versions through the same normalization. pub(crate) fn normalize_version(version: &str) -> &str { let mut chars = version.chars(); diff --git a/crates/socket-patch-core/src/utils/fuzzy_match.rs b/crates/socket-patch-core/src/crawlers/fuzzy_match.rs similarity index 100% rename from crates/socket-patch-core/src/utils/fuzzy_match.rs rename to crates/socket-patch-core/src/crawlers/fuzzy_match.rs diff --git a/crates/socket-patch-core/src/crawlers/mod.rs b/crates/socket-patch-core/src/crawlers/mod.rs index 8cdebf90..f95b4b51 100644 --- a/crates/socket-patch-core/src/crawlers/mod.rs +++ b/crates/socket-patch-core/src/crawlers/mod.rs @@ -1,6 +1,7 @@ pub mod cargo_crawler; pub mod composer_crawler; pub mod deno_crawler; +pub mod fuzzy_match; pub mod go_crawler; pub mod maven_crawler; pub mod npm_crawler; diff --git a/crates/socket-patch-core/src/lib.rs b/crates/socket-patch-core/src/lib.rs index 9697e185..5358a20d 100644 --- a/crates/socket-patch-core/src/lib.rs +++ b/crates/socket-patch-core/src/lib.rs @@ -8,6 +8,8 @@ pub mod manifest; pub mod package_json; pub mod patch; pub mod pth_hook; +pub mod telemetry; pub mod update; pub mod utils; +pub mod vendor; pub mod vex; diff --git a/crates/socket-patch-core/src/utils/cleanup_blobs.rs b/crates/socket-patch-core/src/manifest/cleanup_blobs.rs similarity index 100% rename from crates/socket-patch-core/src/utils/cleanup_blobs.rs rename to crates/socket-patch-core/src/manifest/cleanup_blobs.rs diff --git a/crates/socket-patch-core/src/manifest/mod.rs b/crates/socket-patch-core/src/manifest/mod.rs index 93413870..06c3ccf4 100644 --- a/crates/socket-patch-core/src/manifest/mod.rs +++ b/crates/socket-patch-core/src/manifest/mod.rs @@ -1,2 +1,3 @@ +pub mod cleanup_blobs; pub mod operations; pub mod schema; diff --git a/crates/socket-patch-core/src/package_json/detect.rs b/crates/socket-patch-core/src/package_json/detect.rs index 0d4521c5..62e0b15d 100644 --- a/crates/socket-patch-core/src/package_json/detect.rs +++ b/crates/socket-patch-core/src/package_json/detect.rs @@ -1,4 +1,4 @@ -use crate::patch::vendor::common::{detect_indent, serialize_json}; +use crate::vendor::common::{detect_indent, serialize_json}; /// Package manager type for selecting the correct command prefix. #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/crates/socket-patch-core/src/patch/copy_tree.rs b/crates/socket-patch-core/src/patch/copy_tree.rs index 8e34933b..a53725e1 100644 --- a/crates/socket-patch-core/src/patch/copy_tree.rs +++ b/crates/socket-patch-core/src/patch/copy_tree.rs @@ -1,5 +1,5 @@ //! Shared tree-copy helpers used by the Go `replace`-redirect backend -//! ([`crate::patch::go_redirect`]) and the vendor backends. They materialise a +//! ([`crate::patch::redirect::golang_local`]) and the vendor backends. They materialise a //! project-local **patched copy** of a package by copying its pristine source //! out of a read-only registry/module cache into a writable dir under //! `.socket/`, then patching the copy in place. diff --git a/crates/socket-patch-core/src/patch/mod.rs b/crates/socket-patch-core/src/patch/mod.rs index 28d4d66c..e7a41373 100644 --- a/crates/socket-patch-core/src/patch/mod.rs +++ b/crates/socket-patch-core/src/patch/mod.rs @@ -1,17 +1,21 @@ pub mod apply; pub mod apply_lock; -pub(crate) mod bun_lock_text; // Ungated: the vendor backends (npm/pypi/gem are unconditional) stage their // patched copies with `fresh_copy`/`remove_tree`, not just the golang redirect. pub mod copy_tree; pub mod cow; pub mod diff; pub(crate) mod file_hash; -pub mod go_mod_edit; -pub mod go_redirect; pub mod package; pub(crate) mod path_safety; pub mod redirect; pub mod rollback; pub mod sidecars; -pub mod vendor; + +// Moved modules — these re-exports keep the old `patch::*` paths compiling +// for external consumers of the published crate. Internal code must import +// the new canonical paths (`crate::vendor::*`, `redirect::golang_local`); +// CI greps reject new uses of the old ones. Drop these aliases at 4.0. +pub use crate::vendor; +pub use crate::vendor::go_mod_edit; +pub use redirect::golang_local as go_redirect; diff --git a/crates/socket-patch-core/src/patch/go_redirect.rs b/crates/socket-patch-core/src/patch/redirect/golang_local.rs similarity index 99% rename from crates/socket-patch-core/src/patch/go_redirect.rs rename to crates/socket-patch-core/src/patch/redirect/golang_local.rs index 69a1c313..6642db70 100644 --- a/crates/socket-patch-core/src/patch/go_redirect.rs +++ b/crates/socket-patch-core/src/patch/redirect/golang_local.rs @@ -31,17 +31,17 @@ use crate::patch::apply::{ MismatchPolicy, PatchSources, }; use crate::patch::file_hash::compute_file_git_sha256; -use crate::patch::vendor::common::{ +use crate::utils::purl::{build_golang_purl, parse_golang_purl, strip_purl_qualifiers}; +use crate::vendor::common::{ already_patched_result, copy_matches_after_hashes, synthesized_result, }; -use crate::utils::purl::{build_golang_purl, parse_golang_purl, strip_purl_qualifiers}; -use super::copy_tree::{fresh_copy, remove_tree}; -use super::go_mod_edit::{ +use crate::patch::copy_tree::{fresh_copy, remove_tree}; +use crate::patch::path_safety; +use crate::vendor::go_mod_edit::{ self, read_replace_entries, read_required_versions, replace_target_path, ReplaceOwner, GO_PATCHES_DIR, }; -use super::path_safety; /// A discrepancy between the committed redirect artifacts and the manifest, /// reported by [`verify_go_redirect_state`]. diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index e01f4344..0ff40eb5 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -21,8 +21,9 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use crate::crawlers::python_crawler::canonicalize_pypi_name; -use crate::patch::vendor::yarn_berry_lock::yarnrc_compression_level; +use crate::vendor::yarn_berry_lock::yarnrc_compression_level; +pub mod golang_local; mod state; pub use state::{load_redirect_state, RedirectState, REDIRECT_STATE_REL}; @@ -1077,7 +1078,7 @@ fn rewrite_bun_lock( overrides: &[DepOverride], result: &mut RewriteResult, ) { - use crate::patch::bun_lock_text::{ + use crate::vendor::bun_lock_text::{ check_lock_version, decode_json_string, parse_packages_section, }; @@ -1652,7 +1653,7 @@ fn rewrite_nuget( /// options (`require: false`, `group: :test`, …) that must survive the move /// into the source block. Empty when the line carries none; bails to empty on /// an unparseable tail (unbalanced quote), matching the previous behavior. -/// Shared with the vendor backend's Gemfile rewrite (`patch::vendor::gem`), +/// Shared with the vendor backend's Gemfile rewrite (`vendor::gem`), /// which has the same drop-the-options failure mode. pub(crate) fn gem_line_trailing_options(tail: &str) -> String { let mut rest = tail.trim_start(); diff --git a/crates/socket-patch-core/src/pth_hook/detect.rs b/crates/socket-patch-core/src/pth_hook/detect.rs index 9e72b24d..eb3c9a26 100644 --- a/crates/socket-patch-core/src/pth_hook/detect.rs +++ b/crates/socket-patch-core/src/pth_hook/detect.rs @@ -2,6 +2,8 @@ use std::path::Path; +use crate::utils::toml_edit_ext::has_table; + /// The dependency `setup` adds (PEP 508 form, used for `requirements.txt` and /// PEP 621 `[project].dependencies`): the `socket-patch[hook]` extra, which /// pulls both the socket-patch CLI and the socket-patch-hook wheel (the `.pth` @@ -96,27 +98,6 @@ pub async fn detect_python_pm(cwd: &Path) -> PythonPackageManager { PythonPackageManager::Pip } -/// True if a `[prefix]` or `[prefix.*]` table header appears in the TOML text. -/// Also used by the pypi vendor flavor router (`patch::vendor::pypi`). -pub(crate) fn has_table(content: &str, prefix: &str) -> bool { - content.lines().any(|line| { - let l = line.trim(); - let Some(rest) = l.strip_prefix('[') else { - return false; - }; - // Tolerate array-of-tables (`[[..]]`) by dropping a second opening - // bracket, then take everything up to the closing `]` so a trailing - // inline comment (`[tool.uv] # note`) or interior padding - // (`[ tool.uv ]`) — both valid TOML — doesn't defeat the match. - let rest = rest.trim_start_matches('['); - let Some(end) = rest.find(']') else { - return false; - }; - let header = rest[..end].trim(); - header == prefix || header.starts_with(&format!("{prefix}.")) - }) -} - /// True if the given manifest text already declares the hook dependency, in any /// form. Space- and case-insensitive so `socket-patch [hook]` / `Socket-Patch` /// are recognised. diff --git a/crates/socket-patch-core/src/pth_hook/edit.rs b/crates/socket-patch-core/src/pth_hook/edit.rs index fc297d1e..1c8d6b92 100644 --- a/crates/socket-patch-core/src/pth_hook/edit.rs +++ b/crates/socket-patch-core/src/pth_hook/edit.rs @@ -17,8 +17,9 @@ use tokio::fs; use toml_edit::{Array, DocumentMut, InlineTable, Item, Table, Value}; use super::detect::{deps_contain_hook, HOOK_DEP}; -use crate::patch::vendor::common::detect_eol; use crate::utils::fs::atomic_write_bytes_preserving_mode; +use crate::utils::toml_edit_ext::ensure_table; +use crate::vendor::common::detect_eol; /// Which manifest format a path is. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -240,25 +241,6 @@ fn pyproject_remove(content: &str) -> Result, String> { Ok(if changed { Some(doc.to_string()) } else { None }) } -/// Ensure `parent[key]` is a table, creating it if absent. Errors if present -/// but a non-table. Also used by the vendor backends' TOML editing -/// (`patch::vendor::cargo_config`, `patch::vendor::pypi_uv`). -pub(crate) fn ensure_table<'a>( - parent: &'a mut Table, - key: &str, - implicit: bool, -) -> Result<&'a mut Table, String> { - if !parent.contains_key(key) { - let mut t = Table::new(); - t.set_implicit(implicit); - parent.insert(key, Item::Table(t)); - } - parent - .get_mut(key) - .and_then(Item::as_table_mut) - .ok_or_else(|| format!("`{key}` is not a table")) -} - fn pep621_add(doc: &mut DocumentMut) -> Result { let root = doc.as_table_mut(); let project = ensure_table(root, "project", false)?; diff --git a/crates/socket-patch-core/src/utils/telemetry.rs b/crates/socket-patch-core/src/telemetry.rs similarity index 100% rename from crates/socket-patch-core/src/utils/telemetry.rs rename to crates/socket-patch-core/src/telemetry.rs diff --git a/crates/socket-patch-core/src/utils/mod.rs b/crates/socket-patch-core/src/utils/mod.rs index fc52ea80..cb1f4639 100644 --- a/crates/socket-patch-core/src/utils/mod.rs +++ b/crates/socket-patch-core/src/utils/mod.rs @@ -1,12 +1,18 @@ -pub mod cleanup_blobs; -pub mod date; pub mod env_compat; pub mod fs; -pub mod fuzzy_match; pub(crate) mod http; pub mod process; pub mod purl; pub(crate) mod serde; pub mod socket_cli_config; -pub mod telemetry; +pub(crate) mod toml_edit_ext; pub mod uri; + +// Moved modules — these re-exports keep the old `utils::*` paths compiling +// for external consumers of the published crate. Internal code must import +// the new canonical paths; CI greps reject new uses of the old ones. Drop +// these aliases at 4.0. +pub use crate::api::date; +pub use crate::crawlers::fuzzy_match; +pub use crate::manifest::cleanup_blobs; +pub use crate::telemetry; diff --git a/crates/socket-patch-core/src/utils/toml_edit_ext.rs b/crates/socket-patch-core/src/utils/toml_edit_ext.rs new file mode 100644 index 00000000..dd05a900 --- /dev/null +++ b/crates/socket-patch-core/src/utils/toml_edit_ext.rs @@ -0,0 +1,44 @@ +//! Small structured-TOML helpers shared by every module that edits or sniffs +//! TOML (`pth_hook`, `vendor::cargo_config`, `vendor::pypi`, +//! `vendor::pypi_uv`). Extracted from `pth_hook` so the pypi setup backend no +//! longer owns the crate's generic TOML seam. + +use toml_edit::{Item, Table}; + +/// Ensure `parent[key]` is a table, creating it if absent. Errors if present +/// but a non-table. +pub(crate) fn ensure_table<'a>( + parent: &'a mut Table, + key: &str, + implicit: bool, +) -> Result<&'a mut Table, String> { + if !parent.contains_key(key) { + let mut t = Table::new(); + t.set_implicit(implicit); + parent.insert(key, Item::Table(t)); + } + parent + .get_mut(key) + .and_then(Item::as_table_mut) + .ok_or_else(|| format!("`{key}` is not a table")) +} + +/// True if a `[prefix]` or `[prefix.*]` table header appears in the TOML text. +pub(crate) fn has_table(content: &str, prefix: &str) -> bool { + content.lines().any(|line| { + let l = line.trim(); + let Some(rest) = l.strip_prefix('[') else { + return false; + }; + // Tolerate array-of-tables (`[[..]]`) by dropping a second opening + // bracket, then take everything up to the closing `]` so a trailing + // inline comment (`[tool.uv] # note`) or interior padding + // (`[ tool.uv ]`) — both valid TOML — doesn't defeat the match. + let rest = rest.trim_start_matches('['); + let Some(end) = rest.find(']') else { + return false; + }; + let header = rest[..end].trim(); + header == prefix || header.starts_with(&format!("{prefix}.")) + }) +} diff --git a/crates/socket-patch-core/src/patch/vendor/berry_zip.rs b/crates/socket-patch-core/src/vendor/berry_zip.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/berry_zip.rs rename to crates/socket-patch-core/src/vendor/berry_zip.rs diff --git a/crates/socket-patch-core/src/patch/vendor/bun_lock.rs b/crates/socket-patch-core/src/vendor/bun_lock.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/bun_lock.rs rename to crates/socket-patch-core/src/vendor/bun_lock.rs index f5b5ba19..7acb22ac 100644 --- a/crates/socket-patch-core/src/patch/vendor/bun_lock.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock.rs @@ -33,12 +33,12 @@ use serde_json::Value; use crate::manifest::schema::PatchRecord; use crate::patch::apply::PatchSources; -use crate::patch::bun_lock_text::{ +use crate::patch::copy_tree::remove_tree; +use crate::utils::fs::atomic_write_bytes; +use crate::vendor::bun_lock_text::{ check_lock_version, decode_json_string, packages_bounds, parse_entry_line, parse_packages_section, split_name_spec, BunEntry, }; -use crate::patch::copy_tree::remove_tree; -use crate::utils::fs::atomic_write_bytes; use super::common::{already_patched_result, refused}; use super::npm_common::{done_failure, guard_coordinates, guard_revert_uuid_dir, stage_patch_pack}; @@ -409,7 +409,7 @@ fn revert_one_record( // ───────────────────────── vendor-specific classification ───────────────── // The conservative line grammar (`BunEntry`, `parse_*`, `scan_*`, …) lives in -// `crate::patch::bun_lock_text`; this module keeps only the vendor tuple +// `crate::vendor::bun_lock_text`; this module keeps only the vendor tuple // classification that decides which parsed entries to rewrite. /// What a matching entry's tuple looks like. diff --git a/crates/socket-patch-core/src/patch/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs similarity index 100% rename from crates/socket-patch-core/src/patch/bun_lock_text.rs rename to crates/socket-patch-core/src/vendor/bun_lock_text.rs diff --git a/crates/socket-patch-core/src/patch/vendor/cargo.rs b/crates/socket-patch-core/src/vendor/cargo.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/cargo.rs rename to crates/socket-patch-core/src/vendor/cargo.rs index b0a2d487..86c630e5 100644 --- a/crates/socket-patch-core/src/patch/vendor/cargo.rs +++ b/crates/socket-patch-core/src/vendor/cargo.rs @@ -711,7 +711,7 @@ mod tests { use super::*; use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::{PatchFileInfo, VulnerabilityInfo}; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use std::collections::HashMap; use std::path::PathBuf; @@ -1585,7 +1585,7 @@ mod tests { // Both the service path AND the local-build fallback are exercised. use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::{VendorServiceConfig, VendorSource}; + use crate::vendor::{VendorServiceConfig, VendorSource}; fn sri_sha512(bytes: &[u8]) -> String { use base64::Engine as _; diff --git a/crates/socket-patch-core/src/patch/vendor/cargo_config.rs b/crates/socket-patch-core/src/vendor/cargo_config.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/cargo_config.rs rename to crates/socket-patch-core/src/vendor/cargo_config.rs index d7dcb56b..e5585b67 100644 --- a/crates/socket-patch-core/src/patch/vendor/cargo_config.rs +++ b/crates/socket-patch-core/src/vendor/cargo_config.rs @@ -30,8 +30,8 @@ use std::path::{Path, PathBuf}; use tokio::fs; use toml_edit::{DocumentMut, InlineTable, Item, Table, Value}; -use crate::pth_hook::edit::ensure_table; use crate::utils::fs::atomic_write_bytes_preserving_mode; +use crate::utils::toml_edit_ext::ensure_table; /// Project-relative root of the vendor backend's committed crate copies. An /// entry whose `path` is under this prefix is socket-owned. diff --git a/crates/socket-patch-core/src/patch/vendor/cargo_lock.rs b/crates/socket-patch-core/src/vendor/cargo_lock.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/cargo_lock.rs rename to crates/socket-patch-core/src/vendor/cargo_lock.rs diff --git a/crates/socket-patch-core/src/patch/vendor/common.rs b/crates/socket-patch-core/src/vendor/common.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/common.rs rename to crates/socket-patch-core/src/vendor/common.rs index 4838f474..0383643c 100644 --- a/crates/socket-patch-core/src/patch/vendor/common.rs +++ b/crates/socket-patch-core/src/vendor/common.rs @@ -1,4 +1,4 @@ -//! Leaf helpers shared by the vendor backends (and [`crate::patch::go_redirect`]). +//! Leaf helpers shared by the vendor backends (and [`crate::patch::redirect::golang_local`]). //! //! Each backend used to carry a private, byte-identical copy of these; they //! are hoisted here so the shapes stay in lockstep. diff --git a/crates/socket-patch-core/src/patch/vendor/composer_lock.rs b/crates/socket-patch-core/src/vendor/composer_lock.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/composer_lock.rs rename to crates/socket-patch-core/src/vendor/composer_lock.rs index bb5d7fd1..0f7b393f 100644 --- a/crates/socket-patch-core/src/patch/vendor/composer_lock.rs +++ b/crates/socket-patch-core/src/vendor/composer_lock.rs @@ -751,7 +751,7 @@ mod tests { use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; use crate::patch::apply::{ApplyResult, VerifyStatus}; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use std::collections::HashMap; use std::path::PathBuf; @@ -1372,7 +1372,7 @@ mod tests { // ─────────────── service-download path (Tier B: composer) ─────────────── use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::{VendorServiceConfig, VendorSource}; + use crate::vendor::{VendorServiceConfig, VendorSource}; fn sri_sha512(bytes: &[u8]) -> String { use base64::Engine as _; diff --git a/crates/socket-patch-core/src/patch/vendor/gem.rs b/crates/socket-patch-core/src/vendor/gem.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/gem.rs rename to crates/socket-patch-core/src/vendor/gem.rs index 43f6dfb2..3f3ee42b 100644 --- a/crates/socket-patch-core/src/patch/vendor/gem.rs +++ b/crates/socket-patch-core/src/vendor/gem.rs @@ -1563,7 +1563,7 @@ mod tests { use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; use crate::patch::apply::VerifyStatus; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use std::collections::HashMap; use std::path::PathBuf; @@ -2912,7 +2912,7 @@ mod tests { // and the local-build fallback are exercised. use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::VendorSource; + use crate::vendor::VendorSource; /// A valid path-source stub (no native extensions). const SERVICE_STUB: &[u8] = b"# -*- encoding: utf-8 -*-\n# stub: rack 3.2.6 ruby lib\n\nGem::Specification.new do |s|\n s.name = \"rack\".freeze\n s.version = \"3.2.6\".freeze\n s.require_paths = [\"lib\".freeze]\nend\n"; diff --git a/crates/socket-patch-core/src/patch/go_mod_edit.rs b/crates/socket-patch-core/src/vendor/go_mod_edit.rs similarity index 99% rename from crates/socket-patch-core/src/patch/go_mod_edit.rs rename to crates/socket-patch-core/src/vendor/go_mod_edit.rs index 181569de..4facc9a6 100644 --- a/crates/socket-patch-core/src/patch/go_mod_edit.rs +++ b/crates/socket-patch-core/src/vendor/go_mod_edit.rs @@ -28,7 +28,7 @@ //! patched bytes build cleanly under the default `-mod=readonly`. The directive //! is keyed by *module + version*: a stale pin (the graph resolved a different //! version) is silently ignored and the build links the UNPATCHED module — -//! hence the version cross-check in [`crate::patch::go_redirect`]. +//! hence the version cross-check in [`crate::patch::redirect::golang_local`]. use std::collections::HashMap; use std::path::{Path, PathBuf}; diff --git a/crates/socket-patch-core/src/patch/vendor/golang.rs b/crates/socket-patch-core/src/vendor/golang.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/golang.rs rename to crates/socket-patch-core/src/vendor/golang.rs index 547a9af0..bb090387 100644 --- a/crates/socket-patch-core/src/patch/vendor/golang.rs +++ b/crates/socket-patch-core/src/vendor/golang.rs @@ -1,7 +1,7 @@ //! The golang vendor backend: committable `replace`-directive vendoring. //! //! Wraps the project-local Go redirect engine -//! ([`crate::patch::go_redirect`]) with a vendor copy base: the patched module +//! ([`crate::patch::redirect::golang_local`]) with a vendor copy base: the patched module //! copy lands under `.socket/vendor/golang//@/` //! and the `go.mod` `replace` points at it ([`ReplaceOwner::Vendor`]). A //! directory `replace` target bypasses the module cache, sumdb, and `go.sum` @@ -21,13 +21,13 @@ use std::path::Path; use crate::manifest::schema::PatchRecord; use crate::patch::apply::{MismatchPolicy, PatchSources}; use crate::patch::copy_tree::remove_tree; -use crate::patch::go_mod_edit::{ - self, read_replace_entries, replace_target_path, ReplaceOwner, GO_PATCHES_DIR, -}; -use crate::patch::go_redirect::{ +use crate::patch::redirect::golang_local::{ apply_go_redirect, are_safe_redirect_coords, copy_dir_for, ensure_module_go_mod, }; use crate::utils::purl::{parse_golang_purl, strip_purl_qualifiers}; +use crate::vendor::go_mod_edit::{ + self, read_replace_entries, replace_target_path, ReplaceOwner, GO_PATCHES_DIR, +}; use super::common::{ already_patched_result, copy_matches_after_hashes, done, failed_result, refused, @@ -574,7 +574,7 @@ mod tests { use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::{PatchFileInfo, VulnerabilityInfo}; use crate::patch::apply::ApplyResult; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use std::collections::HashMap; use std::path::PathBuf; @@ -1157,7 +1157,7 @@ mod tests { // the `h1:` dirhash), extracts it into the copy dir, and wires the replace. use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::{VendorServiceConfig, VendorSource}; + use crate::vendor::{VendorServiceConfig, VendorSource}; fn sri_sha512(bytes: &[u8]) -> String { use base64::Engine as _; diff --git a/crates/socket-patch-core/src/patch/vendor/lock_inventory.rs b/crates/socket-patch-core/src/vendor/lock_inventory.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/lock_inventory.rs rename to crates/socket-patch-core/src/vendor/lock_inventory.rs index be498302..fa1584b8 100644 --- a/crates/socket-patch-core/src/patch/vendor/lock_inventory.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory.rs @@ -23,9 +23,9 @@ use std::path::Path; use serde_json::Value; use crate::crawlers::python_crawler::canonicalize_pypi_name; -use crate::patch::bun_lock_text; use crate::patch::path_safety; use crate::utils::purl::{percent_decode_purl_component, strip_purl_qualifiers}; +use crate::vendor::bun_lock_text; use super::npm_common::is_safe_npm_name; use super::npm_flavor::{detect_npm_lock_flavor, NpmLockFlavor}; diff --git a/crates/socket-patch-core/src/patch/vendor/maven_repo.rs b/crates/socket-patch-core/src/vendor/maven_repo.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/maven_repo.rs rename to crates/socket-patch-core/src/vendor/maven_repo.rs index 9c162997..f36c0603 100644 --- a/crates/socket-patch-core/src/patch/vendor/maven_repo.rs +++ b/crates/socket-patch-core/src/vendor/maven_repo.rs @@ -1187,7 +1187,7 @@ mod tests { use super::*; use crate::hash::git_sha256::compute_git_sha256_from_bytes; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const PURL: &str = "pkg:maven/org.apache.commons/commons-text@1.10.0"; diff --git a/crates/socket-patch-core/src/patch/vendor/mod.rs b/crates/socket-patch-core/src/vendor/mod.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/mod.rs rename to crates/socket-patch-core/src/vendor/mod.rs index d040c8bd..74b382b8 100644 --- a/crates/socket-patch-core/src/patch/vendor/mod.rs +++ b/crates/socket-patch-core/src/vendor/mod.rs @@ -40,19 +40,21 @@ //! this Socket-vendored, by which patch" recoverable from the lockfile //! string alone ([`path`]). //! -//! [`ReplaceOwner::Vendor`]: crate::patch::go_mod_edit::ReplaceOwner +//! [`ReplaceOwner::Vendor`]: crate::vendor::go_mod_edit::ReplaceOwner pub mod path; pub mod state; mod berry_zip; pub mod bun_lock; +pub(crate) mod bun_lock_text; pub mod cargo; pub mod cargo_config; pub(crate) mod cargo_lock; pub(crate) mod common; pub mod composer_lock; pub mod gem; +pub mod go_mod_edit; pub mod golang; pub mod lock_inventory; pub mod maven_repo; diff --git a/crates/socket-patch-core/src/patch/vendor/npm_common.rs b/crates/socket-patch-core/src/vendor/npm_common.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/npm_common.rs rename to crates/socket-patch-core/src/vendor/npm_common.rs diff --git a/crates/socket-patch-core/src/patch/vendor/npm_flavor.rs b/crates/socket-patch-core/src/vendor/npm_flavor.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/npm_flavor.rs rename to crates/socket-patch-core/src/vendor/npm_flavor.rs index 1867dddc..b813251e 100644 --- a/crates/socket-patch-core/src/patch/vendor/npm_flavor.rs +++ b/crates/socket-patch-core/src/vendor/npm_flavor.rs @@ -400,7 +400,7 @@ mod tests { use super::*; use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; use std::collections::HashMap; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; diff --git a/crates/socket-patch-core/src/patch/vendor/npm_lock.rs b/crates/socket-patch-core/src/vendor/npm_lock.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/npm_lock.rs rename to crates/socket-patch-core/src/vendor/npm_lock.rs index 243f685c..139e1e61 100644 --- a/crates/socket-patch-core/src/patch/vendor/npm_lock.rs +++ b/crates/socket-patch-core/src/vendor/npm_lock.rs @@ -1822,7 +1822,7 @@ mod tests { // patch.socket.dev two-step (package-reference POST + serve GET). use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::{VendorServiceConfig, VendorSource}; + use crate::vendor::{VendorServiceConfig, VendorSource}; const SERVE_PATH: &str = "/patch/npm/left-pad/1.3.0/grant-tok/uuid/left-pad-1.3.0.tgz"; diff --git a/crates/socket-patch-core/src/patch/vendor/npm_pack.rs b/crates/socket-patch-core/src/vendor/npm_pack.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/npm_pack.rs rename to crates/socket-patch-core/src/vendor/npm_pack.rs diff --git a/crates/socket-patch-core/src/patch/vendor/nuget_feed.rs b/crates/socket-patch-core/src/vendor/nuget_feed.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/nuget_feed.rs rename to crates/socket-patch-core/src/vendor/nuget_feed.rs index 58109415..83e87ef1 100644 --- a/crates/socket-patch-core/src/patch/vendor/nuget_feed.rs +++ b/crates/socket-patch-core/src/vendor/nuget_feed.rs @@ -1326,7 +1326,7 @@ mod tests { use super::*; use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use serde_json::json; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; diff --git a/crates/socket-patch-core/src/patch/vendor/path.rs b/crates/socket-patch-core/src/vendor/path.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/path.rs rename to crates/socket-patch-core/src/vendor/path.rs diff --git a/crates/socket-patch-core/src/patch/vendor/pnpm_lock.rs b/crates/socket-patch-core/src/vendor/pnpm_lock.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/pnpm_lock.rs rename to crates/socket-patch-core/src/vendor/pnpm_lock.rs diff --git a/crates/socket-patch-core/src/patch/vendor/pypi.rs b/crates/socket-patch-core/src/vendor/pypi.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi.rs rename to crates/socket-patch-core/src/vendor/pypi.rs index 3a075191..9c6576f1 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi.rs +++ b/crates/socket-patch-core/src/vendor/pypi.rs @@ -13,9 +13,9 @@ use sha2::{Digest as _, Sha256}; use crate::crawlers::python_crawler::canonicalize_pypi_name; use crate::manifest::schema::PatchRecord; use crate::patch::apply::{ApplyResult, PatchSources}; -use crate::pth_hook::detect::has_table; use crate::utils::fs::atomic_write_bytes; use crate::utils::purl::{parse_pypi_purl, strip_purl_qualifiers}; +use crate::utils::toml_edit_ext::has_table; use super::common::{already_patched_result, done, refused, service_offline_conflict}; use super::path::vendor_uuid_dir_rel; @@ -869,7 +869,7 @@ mod tests { use super::*; use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; - use crate::patch::vendor::state::VENDOR_MARKER_FILE; + use crate::vendor::state::VENDOR_MARKER_FILE; use std::collections::HashMap; use std::path::PathBuf; @@ -1615,7 +1615,7 @@ wheels = [ // local-build fallback are exercised. use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::{VendorServiceConfig, VendorSource}; + use crate::vendor::{VendorServiceConfig, VendorSource}; const WHEEL_NAME: &str = "six-1.16.0-py2.py3-none-any.whl"; diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_pdm.rs b/crates/socket-patch-core/src/vendor/pypi_pdm.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi_pdm.rs rename to crates/socket-patch-core/src/vendor/pypi_pdm.rs index d72e0ff0..f1543cf9 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi_pdm.rs +++ b/crates/socket-patch-core/src/vendor/pypi_pdm.rs @@ -481,7 +481,7 @@ fn rewrite_target_package_unit( #[cfg(test)] mod tests { use super::*; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const REL_WHEEL: &str = diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_pipenv.rs b/crates/socket-patch-core/src/vendor/pypi_pipenv.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi_pipenv.rs rename to crates/socket-patch-core/src/vendor/pypi_pipenv.rs index c957def3..10a7509f 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi_pipenv.rs +++ b/crates/socket-patch-core/src/vendor/pypi_pipenv.rs @@ -468,7 +468,7 @@ fn to_canonical_json(value: &Value) -> String { #[cfg(test)] mod tests { use super::*; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const REL_WHEEL: &str = diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_poetry.rs b/crates/socket-patch-core/src/vendor/pypi_poetry.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi_poetry.rs rename to crates/socket-patch-core/src/vendor/pypi_poetry.rs index 5fee31c1..2306793a 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi_poetry.rs +++ b/crates/socket-patch-core/src/vendor/pypi_poetry.rs @@ -399,7 +399,7 @@ fn rewrite_target_package_unit( #[cfg(test)] mod tests { use super::*; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const REL_WHEEL: &str = diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_requirements.rs b/crates/socket-patch-core/src/vendor/pypi_requirements.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi_requirements.rs rename to crates/socket-patch-core/src/vendor/pypi_requirements.rs index b06f898d..51aba8c0 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi_requirements.rs +++ b/crates/socket-patch-core/src/vendor/pypi_requirements.rs @@ -754,7 +754,7 @@ fn parse_requirement_line(text: &str) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const REL_WHEEL: &str = diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_uv.rs b/crates/socket-patch-core/src/vendor/pypi_uv.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/pypi_uv.rs rename to crates/socket-patch-core/src/vendor/pypi_uv.rs index cbcc04dc..93132918 100644 --- a/crates/socket-patch-core/src/patch/vendor/pypi_uv.rs +++ b/crates/socket-patch-core/src/vendor/pypi_uv.rs @@ -717,7 +717,7 @@ fn ensure_table<'a>( ) -> Result<&'a mut Table, (&'static str, String)> { let mut table: &mut Table = doc.as_table_mut(); for key in path { - table = crate::pth_hook::edit::ensure_table(table, key, true).map_err(|_| { + table = crate::utils::toml_edit_ext::ensure_table(table, key, true).map_err(|_| { ( "pypi_uv_lock_parse_failed", format!( @@ -1030,7 +1030,7 @@ fn add_manifest_override( #[cfg(test)] mod tests { use super::*; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; const UUID: &str = "9f6b2c4e-1d3a-4f6b-8c2d-7e5a9b1c3d5f"; const REL_WHEEL: &str = diff --git a/crates/socket-patch-core/src/patch/vendor/pypi_wheel.rs b/crates/socket-patch-core/src/vendor/pypi_wheel.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/pypi_wheel.rs rename to crates/socket-patch-core/src/vendor/pypi_wheel.rs diff --git a/crates/socket-patch-core/src/patch/vendor/registry_fetch.rs b/crates/socket-patch-core/src/vendor/registry_fetch.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/registry_fetch.rs rename to crates/socket-patch-core/src/vendor/registry_fetch.rs diff --git a/crates/socket-patch-core/src/patch/vendor/service_fetch.rs b/crates/socket-patch-core/src/vendor/service_fetch.rs similarity index 97% rename from crates/socket-patch-core/src/patch/vendor/service_fetch.rs rename to crates/socket-patch-core/src/vendor/service_fetch.rs index e4b536b3..e2e0bbd5 100644 --- a/crates/socket-patch-core/src/patch/vendor/service_fetch.rs +++ b/crates/socket-patch-core/src/vendor/service_fetch.rs @@ -9,10 +9,10 @@ //! extract it into the vendor directory) and the build-vs-service policy. use crate::api::client::{SecondaryArtifact, VendorServiceOutcome}; -use crate::patch::vendor::lock_inventory::LockIntegrity; -use crate::patch::vendor::registry_fetch::{artifact_matches_integrity, verify_go_h1}; -use crate::patch::vendor::VendorServiceConfig; -use crate::patch::vendor::{ +use crate::vendor::lock_inventory::LockIntegrity; +use crate::vendor::registry_fetch::{artifact_matches_integrity, verify_go_h1}; +use crate::vendor::VendorServiceConfig; +use crate::vendor::{ common::{refused, service_offline_conflict}, VendorOutcome, VendorWarning, }; @@ -251,8 +251,8 @@ pub(crate) async fn fetch_verified_secondary( mod tests { use super::*; use crate::api::client::{ApiClient, ApiClientOptions}; - use crate::patch::vendor::npm_pack::PackedTarball; - use crate::patch::vendor::VendorSource; + use crate::vendor::npm_pack::PackedTarball; + use crate::vendor::VendorSource; use serde_json::json; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; diff --git a/crates/socket-patch-core/src/patch/vendor/state.rs b/crates/socket-patch-core/src/vendor/state.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/state.rs rename to crates/socket-patch-core/src/vendor/state.rs index 2a7a26b9..2333079e 100644 --- a/crates/socket-patch-core/src/patch/vendor/state.rs +++ b/crates/socket-patch-core/src/vendor/state.rs @@ -205,8 +205,8 @@ pub struct VendorEntry { #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub took_over_go_patches: bool, /// Which wiring flavor was used, for the multi-flavor ecosystems — - /// npm: `package-lock` | `yarn-classic` | `pnpm` | `bun` (absent on - /// pre-flavor entries ⇒ `package-lock`); pypi: `uv` | `requirements` | + /// npm: `package-lock` | `yarn-classic` | `yarn-berry` | `pnpm` | `bun` + /// (absent on pre-flavor entries ⇒ `package-lock`); pypi: `uv` | `requirements` | /// `poetry` | `pdm` | `pipenv`. Reverts route on this and fail closed /// on flavors this build has no backend for. #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/crates/socket-patch-core/src/patch/vendor/toml_surgery.rs b/crates/socket-patch-core/src/vendor/toml_surgery.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/toml_surgery.rs rename to crates/socket-patch-core/src/vendor/toml_surgery.rs diff --git a/crates/socket-patch-core/src/patch/vendor/verify.rs b/crates/socket-patch-core/src/vendor/verify.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/verify.rs rename to crates/socket-patch-core/src/vendor/verify.rs index 0fb826ea..44bcda9b 100644 --- a/crates/socket-patch-core/src/patch/vendor/verify.rs +++ b/crates/socket-patch-core/src/vendor/verify.rs @@ -309,7 +309,7 @@ fn verify_member_map( mod tests { use super::*; use crate::manifest::schema::PatchFileInfo; - use crate::patch::vendor::state::VendorArtifact; + use crate::vendor::state::VendorArtifact; use flate2::write::GzEncoder; use std::io::Write; diff --git a/crates/socket-patch-core/src/patch/vendor/yarn_berry_lock.rs b/crates/socket-patch-core/src/vendor/yarn_berry_lock.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/yarn_berry_lock.rs rename to crates/socket-patch-core/src/vendor/yarn_berry_lock.rs diff --git a/crates/socket-patch-core/src/patch/vendor/yarn_classic_lock.rs b/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs similarity index 100% rename from crates/socket-patch-core/src/patch/vendor/yarn_classic_lock.rs rename to crates/socket-patch-core/src/vendor/yarn_classic_lock.rs diff --git a/crates/socket-patch-core/src/patch/vendor/yarn_layering_tests.rs b/crates/socket-patch-core/src/vendor/yarn_layering_tests.rs similarity index 99% rename from crates/socket-patch-core/src/patch/vendor/yarn_layering_tests.rs rename to crates/socket-patch-core/src/vendor/yarn_layering_tests.rs index 8f19a98a..fb31cfe1 100644 --- a/crates/socket-patch-core/src/patch/vendor/yarn_layering_tests.rs +++ b/crates/socket-patch-core/src/vendor/yarn_layering_tests.rs @@ -39,12 +39,12 @@ use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::{PatchFileInfo, PatchRecord}; use crate::patch::apply::PatchSources; use crate::patch::redirect::{rewrite_registry_redirect, DepOverride, Integrity}; -use crate::patch::vendor::lock_inventory::{inventory_npm_lock, LockIntegrity}; -use crate::patch::vendor::npm_flavor::NpmLockFlavor; -use crate::patch::vendor::yarn_berry_lock::{revert_yarn_berry, vendor_yarn_berry}; -use crate::patch::vendor::yarn_classic_lock::{revert_yarn_classic, vendor_yarn_classic}; -use crate::patch::vendor::{RevertOutcome, VendorEntry, VendorOutcome}; use crate::utils::uri::encode_uri_component; +use crate::vendor::lock_inventory::{inventory_npm_lock, LockIntegrity}; +use crate::vendor::npm_flavor::NpmLockFlavor; +use crate::vendor::yarn_berry_lock::{revert_yarn_berry, vendor_yarn_berry}; +use crate::vendor::yarn_classic_lock::{revert_yarn_classic, vendor_yarn_classic}; +use crate::vendor::{RevertOutcome, VendorEntry, VendorOutcome}; /// Canonical-grammar patch uuid (the vendor path layer validates the shape /// fail-closed, so fixtures must use the real grammar). diff --git a/crates/socket-patch-core/src/vex/time.rs b/crates/socket-patch-core/src/vex/time.rs index e3ffd281..89d57f34 100644 --- a/crates/socket-patch-core/src/vex/time.rs +++ b/crates/socket-patch-core/src/vex/time.rs @@ -31,7 +31,7 @@ fn format_unix_secs_rfc3339(secs: u64) -> String { /// Adapted to operate on a non-negative second count — socket-patch only /// ever stamps "now", so pre-1970 inputs are out of scope. /// -/// Also the date backbone of `utils::telemetry`'s millisecond-precision +/// Also the date backbone of `telemetry`'s millisecond-precision /// timestamps, so this is the single civil-date implementation in the crate. pub(crate) fn unix_to_ymdhms(secs: u64) -> (i32, u32, u32, u32, u32, u32) { let days = (secs / 86_400) as i64; diff --git a/crates/socket-patch-core/src/vex/verify.rs b/crates/socket-patch-core/src/vex/verify.rs index f4e23f0e..cf63f2e6 100644 --- a/crates/socket-patch-core/src/vex/verify.rs +++ b/crates/socket-patch-core/src/vex/verify.rs @@ -17,8 +17,8 @@ use std::path::{Path, PathBuf}; use crate::manifest::schema::{PatchManifest, PatchRecord}; use crate::patch::apply::{verify_file_patch, VerifyStatus}; -use crate::patch::vendor::state::{lookup_entry, VendorEntry}; -use crate::patch::vendor::verify::verify_vendored_patch_record; +use crate::vendor::state::{lookup_entry, VendorEntry}; +use crate::vendor::verify::verify_vendored_patch_record; /// One entry per manifest PURL that did NOT pass verification. The /// `reason` is a short snake_case tag the CLI can route on (matches @@ -899,7 +899,7 @@ mod tests { // ── Vendored-patch awareness (`applied_patches_with_vendor`) ── - use crate::patch::vendor::state::{VendorArtifact, VendorEntry}; + use crate::vendor::state::{VendorArtifact, VendorEntry}; /// Canonical-grammar patch UUID — `verify_vendored_patch_record` /// validates the uuid path level, so vendor fixtures must use a real diff --git a/crates/socket-patch-core/tests/fuzzy_match_e2e.rs b/crates/socket-patch-core/tests/fuzzy_match_e2e.rs index 5ddb1068..8e3e4151 100644 --- a/crates/socket-patch-core/tests/fuzzy_match_e2e.rs +++ b/crates/socket-patch-core/tests/fuzzy_match_e2e.rs @@ -1,4 +1,4 @@ -//! Integration coverage for `socket_patch_core::utils::fuzzy_match`. +//! Integration coverage for `socket_patch_core::crawlers::fuzzy_match`. //! //! `fuzzy_match_packages` powers `socket-patch get `'s //! "did you mean…" fallback when the caller's identifier doesn't @@ -7,8 +7,8 @@ use std::path::PathBuf; +use socket_patch_core::crawlers::fuzzy_match::fuzzy_match_packages; use socket_patch_core::crawlers::types::CrawledPackage; -use socket_patch_core::utils::fuzzy_match::fuzzy_match_packages; fn pkg(name: &str, version: &str, namespace: Option<&str>) -> CrawledPackage { let ns = namespace.map(str::to_string); diff --git a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs index 76aa379a..827e4673 100644 --- a/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs +++ b/crates/socket-patch-core/tests/telemetry_helpers_e2e.rs @@ -1,4 +1,4 @@ -//! Integration coverage for `utils::telemetry`'s pub helpers +//! Integration coverage for `telemetry`'s pub helpers //! (`is_telemetry_disabled`, `sanitize_error_message`). These are //! exposed for tests + future external callers; the apply/scan //! suites never invoke them directly, so the env-var-branch logic @@ -14,7 +14,7 @@ //! that no other ambient var was secretly carrying the assertion). use serial_test::serial; -use socket_patch_core::utils::telemetry::{is_telemetry_disabled, sanitize_error_message}; +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.