Skip to content

Commit 4a797e2

Browse files
committed
Skip npm lock entries installed from git or URLs
npm installs a git, remote-tarball or file: dependency from the dependent's spec and ignores the lock entry's resolved. Hosted and vendored mode rewired those entries anyway, so the scan reported the package patched and vex attested it while npm ci installed the original bytes. Both rewriters now skip such entries with a loud stays-UNPATCHED warning (vendored refuses when no registry copy is left), and vex no longer attests a name@version while a non-registry copy of it is in the lock. Fixes #326 Assisted-by: Claude Code:claude-opus-5-5
1 parent 75ecb07 commit 4a797e2

65 files changed

Lines changed: 1501 additions & 245 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎crates/socket-patch-cli/src/commands/get.rs‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7272,8 +7272,11 @@ mod tests {
72727272
let installed = |name: &str, body: &[u8]| {
72737273
let dist = site.path().join(format!("{name}-1.0.0.dist-info"));
72747274
std::fs::create_dir_all(&dist).unwrap();
7275-
std::fs::write(dist.join("METADATA"), format!("Name: {name}\nVersion: 1.0.0\n"))
7276-
.unwrap();
7275+
std::fs::write(
7276+
dist.join("METADATA"),
7277+
format!("Name: {name}\nVersion: 1.0.0\n"),
7278+
)
7279+
.unwrap();
72777280
std::fs::write(site.path().join(format!("{name}.py")), body).unwrap();
72787281
compute_git_sha256_from_bytes(body)
72797282
};
@@ -7317,7 +7320,10 @@ mod tests {
73177320
mount(uuid("bs"), "beta_sdist.py".into(), "0".repeat(64), 0).await;
73187321
for n in ["gw", "gs"] {
73197322
Mock::given(method("GET"))
7320-
.and(wm_path(format!("/v0/orgs/test-org/patches/view/{}", uuid(n))))
7323+
.and(wm_path(format!(
7324+
"/v0/orgs/test-org/patches/view/{}",
7325+
uuid(n)
7326+
)))
73217327
.respond_with(ResponseTemplate::new(500))
73227328
.expect(0)
73237329
.mount(&server)

‎crates/socket-patch-cli/src/commands/scan/discovery.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2094,7 +2094,11 @@ mod tests {
20942094
"pkg:npm/lockonly@1.0.0",
20952095
std::path::PathBuf::from("/nonexistent"),
20962096
),
2097-
crawled_pkg("alpha", "pkg:npm/alpha@1.0.0", installed("alpha", "alpha.js")),
2097+
crawled_pkg(
2098+
"alpha",
2099+
"pkg:npm/alpha@1.0.0",
2100+
installed("alpha", "alpha.js"),
2101+
),
20982102
crawled_pkg(
20992103
"embedded",
21002104
"pkg:npm/embedded@1.0.0",

‎crates/socket-patch-cli/src/commands/setup.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,7 @@ async fn hooked_vlt_members(found: &PackageJsonFindResult) -> Vec<PathBuf> {
247247
}
248248
let mut hooked = Vec::new();
249249
for loc in found.files.iter().filter(|loc| !loc.is_root) {
250-
if let Ok(content) = socket_patch_core::utils::fs::read_regular_to_string(&loc.path).await
251-
{
250+
if let Ok(content) = socket_patch_core::utils::fs::read_regular_to_string(&loc.path).await {
252251
let status = is_setup_configured_str(&content);
253252
if status.postinstall_configured || status.dependencies_configured {
254253
hooked.push(loc.path.clone());

‎crates/socket-patch-cli/src/commands/update.rs‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ fn cancelled_message(current: &semver::Version, target: &semver::Version) -> &'s
159159

160160
/// The result line after a successful install, naming the same action as
161161
/// [`confirm_prompt`].
162-
fn installed_message(current: &semver::Version, target: &semver::Version, path: &std::path::Path) -> String {
162+
fn installed_message(
163+
current: &semver::Version,
164+
target: &semver::Version,
165+
path: &std::path::Path,
166+
) -> String {
163167
let path = path.display();
164168
if target < current {
165169
format!("Downgraded socket-patch {current} \u{2192} {target} ({path})")
@@ -500,9 +504,18 @@ mod tests {
500504

501505
#[test]
502506
fn cancel_and_result_lines_match_the_prompt() {
503-
assert_eq!(cancelled_message(&v("4.0.0"), &v("9.9.9")), "Update cancelled.");
504-
assert_eq!(cancelled_message(&v("4.0.0"), &v("3.0.0")), "Downgrade cancelled.");
505-
assert_eq!(cancelled_message(&v("4.0.0"), &v("4.0.0")), "Reinstall cancelled.");
507+
assert_eq!(
508+
cancelled_message(&v("4.0.0"), &v("9.9.9")),
509+
"Update cancelled."
510+
);
511+
assert_eq!(
512+
cancelled_message(&v("4.0.0"), &v("3.0.0")),
513+
"Downgrade cancelled."
514+
);
515+
assert_eq!(
516+
cancelled_message(&v("4.0.0"), &v("4.0.0")),
517+
"Reinstall cancelled."
518+
);
506519
let p = std::path::Path::new("/opt/sp/socket-patch");
507520
assert_eq!(
508521
installed_message(&v("4.0.0"), &v("9.9.9"), p),

‎crates/socket-patch-cli/src/commands/vendor.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3864,7 +3864,11 @@ mod plan_gate_tests {
38643864
.unwrap();
38653865
let packages = [
38663866
("pkg:composer/psr/cache@1.0.0", "psr/cache", UUID_A),
3867-
("pkg:composer/psr/http-message@1.1.0", "psr/http-message", UUID_B),
3867+
(
3868+
"pkg:composer/psr/http-message@1.1.0",
3869+
"psr/http-message",
3870+
UUID_B,
3871+
),
38683872
("pkg:composer/psr/log@3.0.2", "psr/log", UUID_C),
38693873
];
38703874
let mut all_packages: Vec<(String, StagedSource)> = Vec::new();

‎crates/socket-patch-cli/src/hosted_memory/redirect.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ use socket_patch_core::patch::redirect::npmrc::{
1919
NPMRC_REL,
2020
};
2121
use socket_patch_core::patch::redirect::{
22-
rewrite_registry_redirect_withholding_vlt, DepOverride, FileEdit, RewriteResult,
23-
RewriteWarning,
22+
rewrite_registry_redirect_withholding_vlt, DepOverride, FileEdit, RewriteResult, RewriteWarning,
2423
};
2524
use socket_patch_core::utils::purl::{purl_parts, strip_purl_qualifiers};
2625
use socket_patch_core::vendor::lock_inventory::{MemoryEntry, MemoryProject};

‎crates/socket-patch-cli/tests/apply_network.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,10 +1075,7 @@ async fn mismatch_blob_topup_probes_every_copy_of_a_duplicated_package() {
10751075
v["summary"]["applied"], 1,
10761076
"the drifted nested copy must be warn-overwritten.\nstdout={v:#}"
10771077
);
1078-
assert_eq!(
1079-
v["summary"]["failed"], 0,
1080-
"no copy may fail.\nstdout={v:#}"
1081-
);
1078+
assert_eq!(v["summary"]["failed"], 0, "no copy may fail.\nstdout={v:#}");
10821079

10831080
// The nested copy's blob was fetched on demand…
10841081
let requests = mock.received_requests().await.unwrap();

‎crates/socket-patch-cli/tests/cli_config_fallback.rs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ fn scan_cmd(project: &Path, data_dir: &Path) -> Command {
5959
let mut cmd = Command::new(BINARY);
6060
// Human mode: core's proxy advisory (the oracle below) is muted under
6161
// `--json`/`--silent`.
62-
cmd.args(["scan", "-e", "npm", "--cwd"])
63-
.arg(project);
62+
cmd.args(["scan", "-e", "npm", "--cwd"]).arg(project);
6463
for (key, _) in std::env::vars_os() {
6564
let name = key.to_string_lossy();
6665
if name.starts_with("SOCKET_") {
@@ -298,7 +297,9 @@ async fn corrupt_config_warns_and_keeps_json_stdout_clean() {
298297
json_cmd.arg("--json");
299298
let json_out = run(json_cmd);
300299
assert!(
301-
json_out.stderr.contains("could not parse socket-cli config"),
300+
json_out
301+
.stderr
302+
.contains("could not parse socket-cli config"),
302303
"the parse warning must reach stderr under --json too; got:\n{}",
303304
json_out.stderr
304305
);

‎crates/socket-patch-cli/tests/cli_parse_list.rs‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,10 @@ fn missing_manifest_with_corrupt_ledger_keeps_warning_in_error_envelope_via_bina
12021202
assert_eq!(v["error"]["code"], "manifest_not_found", "envelope={v}");
12031203
let warnings = v["warnings"].as_array().expect("warnings[] present");
12041204
assert_eq!(warnings.len(), 1, "envelope={v}");
1205-
assert_eq!(warnings[0]["code"], "redirect_ledger_corrupt", "envelope={v}");
1205+
assert_eq!(
1206+
warnings[0]["code"], "redirect_ledger_corrupt",
1207+
"envelope={v}"
1208+
);
12061209
assert!(
12071210
out.stderr.is_empty(),
12081211
"--json must keep stderr clean: {}",
@@ -1214,7 +1217,10 @@ fn missing_manifest_with_corrupt_ledger_keeps_warning_in_error_envelope_via_bina
12141217
let stderr = String::from_utf8_lossy(&out.stderr);
12151218
assert_eq!(out.status.code(), Some(1));
12161219
assert!(stderr.contains("Warning: "), "stderr={stderr}");
1217-
assert!(stderr.contains("Error: Manifest not found at "), "stderr={stderr}");
1220+
assert!(
1221+
stderr.contains("Error: Manifest not found at "),
1222+
"stderr={stderr}"
1223+
);
12181224
}
12191225

12201226
#[test]

‎crates/socket-patch-cli/tests/cli_parse_rollback.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,11 @@ fn bare_bool_does_not_consume_next_token() {
378378
/// relied on the rejection get a test-visible flip instead of a silent one.
379379
#[test]
380380
fn multiple_targets_parse_in_order() {
381-
let args = parse_rollback(&["pkg:npm/foo@1", "packages/api/**", "b0630680-4da6-45f9-bba8-b888e0ffd58c"]);
381+
let args = parse_rollback(&[
382+
"pkg:npm/foo@1",
383+
"packages/api/**",
384+
"b0630680-4da6-45f9-bba8-b888e0ffd58c",
385+
]);
382386
assert_eq!(
383387
args.targets,
384388
vec![

0 commit comments

Comments
 (0)