Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions crates/socket-patch-cli/tests/e2e_redirect_rush_sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,56 @@ async fn rush_hosted_scan_then_simulated_pnpm_install_lands_patched_bytes() {
String::from_utf8_lossy(&installed[..installed.len().min(120)])
);
assert_rush_manifestless_vex(root, &server.uri(), &patched, rush_common_lock().as_bytes());
assert_rush_stale_store_not_attested(root, &server.uri(), &patched, orig);
}

/// REGRESSION (#518): the copy rush installs lives under
/// `common/temp/node_modules`, which the crawler used to prune (`temp`), so
/// an UNPATCHED installed copy read as "nothing installed" and the pinned
/// hosted lock attested it anyway. Revert the real pnpm-installed file to
/// the upstream bytes (a stale or tampered install): installed evidence
/// wins, and the patch is omitted with `hash_mismatch`.
fn assert_rush_stale_store_not_attested(
root: &Path,
patch_server: &str,
patched: &[u8],
upstream: &[u8],
) {
let installed = std::fs::canonicalize(
root.join("common/temp/node_modules")
.join(DEP)
.join("index.js"),
)
.unwrap();
// pnpm hardlinks from its store: replace the file rather than writing
// through the link.
std::fs::remove_file(&installed).unwrap();
std::fs::write(&installed, upstream).unwrap();
std::thread::scope(|s| {
s.spawn(|| {
let api = PatchApi::start(vec![(
UUID.to_string(),
patch_view(
UUID,
PURL,
&[("package/index.js", &git_sha256(patched))],
VULNS,
),
)]);
let out = run_vex(
&binary(),
root,
&VexRun {
patch_server_url: Some(patch_server.to_string()),
..VexRun::online(&api)
},
);
assert_eq!(out.code, Some(1), "rush, unpatched store copy: {out}");
assert_not_attested(&out.envelope, PURL, "hash_mismatch");
})
.join()
.unwrap_or_else(|p| std::panic::resume_unwind(p))
});
}

/// Tamper twin: the hosted route serves DIFFERENT bytes than the pinned
Expand Down
143 changes: 143 additions & 0 deletions crates/socket-patch-cli/tests/e2e_vex_redirect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2161,3 +2161,146 @@ fn vlt_redirect_ledger_is_judged_by_the_vlt_store_copy_while_the_lock_pins_it()
);
}
}

/// The patch view for `name@version` (the [`left_pad_view`] shape).
fn npm_view(name: &str, version: &str, after_hash: &str) -> Value {
let mut view = left_pad_view(after_hash);
view["purl"] = Value::String(format!("pkg:npm/{name}@{version}"));
view
}

/// REGRESSION (#493): yarn classic's `.yarnrc` `--modules-folder deps`
/// installs into `deps/`. The crawler never looked there, so the
/// unpatched installed copy read as "nothing installed" and the pinned
/// hosted lock attested `not_affected`. Installed evidence wins: the
/// copy is hash-checked and omitted. With nothing installed the lock
/// basis still attests.
#[test]
fn yarn_modules_folder_install_is_hash_verified_not_lockfile_attested() {
let tmp = tempfile::tempdir().unwrap();
let cwd = tmp.path();
let purl = "pkg:npm/left-pad@1.3.0";
std::fs::write(
cwd.join("package.json"),
r#"{ "name": "app", "version": "1.0.0", "dependencies": { "left-pad": "1.3.0" } }"#,
)
.unwrap();
std::fs::write(cwd.join(".yarnrc"), "--modules-folder deps\n").unwrap();
std::fs::write(
cwd.join("yarn.lock"),
format!(
"# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n\
# yarn lockfile v1\n\n\nleft-pad@1.3.0:\n version \"1.3.0\"\n \
resolved \"{}\"\n integrity {SRI}\n",
hosted_npm_url("left-pad", "1.3.0", UUID)
),
)
.unwrap();
let pkg = cwd.join("deps/left-pad");
std::fs::create_dir_all(&pkg).unwrap();
std::fs::write(
pkg.join("package.json"),
r#"{ "name": "left-pad", "version": "1.3.0" }"#,
)
.unwrap();
std::fs::write(pkg.join("index.js"), b"unpatched upstream bytes\n").unwrap();
let patched = b"hosted patched index\n";
let (_rt, server) = serve_patch_views(vec![(
UUID.to_string(),
left_pad_view(&compute_git_sha256_from_bytes(patched)),
)]);

let (code, env) = vex_json(cwd, &["--proxy-url", &server.uri()]);
assert_eq!(
code,
Some(1),
"the unpatched deps/ copy must not attest: {env}"
);
assert_eq!(skipped_reason(&env, purl), "hash_mismatch", "{env}");

std::fs::write(pkg.join("index.js"), patched).unwrap();
let (code, env) = vex_json(cwd, &["--proxy-url", &server.uri()]);
assert_eq!(code, Some(0), "a patched deps/ copy attests: {env}");

std::fs::remove_dir_all(cwd.join("deps")).unwrap();
let (code, env) = vex_json(cwd, &["--proxy-url", &server.uri()]);
assert_eq!(
code,
Some(0),
"nothing installed: the lock basis attests: {env}"
);
}

/// REGRESSION (#518): Rush installs every package into
/// `common/temp/node_modules/.pnpm` and the projects' `node_modules` only
/// link their DIRECT deps. The crawler pruned `temp`, so an unpatched
/// transitive dep read as "nothing installed" and the hosted pin in
/// `common/config/rush/pnpm-lock.yaml` attested `not_affected`.
#[cfg(unix)]
#[test]
fn rush_common_temp_install_is_hash_verified_not_lockfile_attested() {
let tmp = tempfile::tempdir().unwrap();
let cwd = tmp.path();
let purl = "pkg:npm/is-number@7.0.0";
std::fs::write(cwd.join("rush.json"), r#"{ "rushVersion": "5.180.0" }"#).unwrap();
let lock_dir = cwd.join("common/config/rush");
std::fs::create_dir_all(&lock_dir).unwrap();
std::fs::write(
lock_dir.join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '9.0'\n\nimporters:\n ../../apps/app:\n dependencies:\n \
to-regex-range:\n specifier: 5.0.1\n version: 5.0.1\n\npackages:\n \
is-number@7.0.0:\n resolution: {{integrity: {SRI}, tarball: {}}}\n \
to-regex-range@5.0.1:\n resolution: {{integrity: sha512-UPSTREAMupstream==}}\n\n\
snapshots:\n is-number@7.0.0: {{}}\n to-regex-range@5.0.1:\n dependencies:\n \
is-number: 7.0.0\n",
hosted_npm_url("is-number", "7.0.0", UUID)
),
)
.unwrap();
let store = cwd.join("common/temp/node_modules/.pnpm");
let installed = store.join("is-number@7.0.0/node_modules/is-number");
let direct = store.join("to-regex-range@5.0.1/node_modules/to-regex-range");
for (dir, name, version) in [
(&installed, "is-number", "7.0.0"),
(&direct, "to-regex-range", "5.0.1"),
] {
std::fs::create_dir_all(dir).unwrap();
std::fs::write(
dir.join("package.json"),
format!(r#"{{ "name": "{name}", "version": "{version}" }}"#),
)
.unwrap();
}
std::fs::write(installed.join("index.js"), b"unpatched upstream bytes\n").unwrap();
std::os::unix::fs::symlink(&installed, direct.parent().unwrap().join("is-number")).unwrap();
let app = cwd.join("apps/app");
std::fs::create_dir_all(app.join("node_modules")).unwrap();
std::fs::write(
app.join("package.json"),
r#"{ "name": "app", "version": "1.0.0", "dependencies": { "to-regex-range": "5.0.1" } }"#,
)
.unwrap();
std::os::unix::fs::symlink(&direct, app.join("node_modules/to-regex-range")).unwrap();
let patched = b"hosted patched index\n";
let (_rt, server) = serve_patch_views(vec![(
UUID.to_string(),
npm_view(
"is-number",
"7.0.0",
&compute_git_sha256_from_bytes(patched),
),
)]);

let (code, env) = vex_json(cwd, &["--proxy-url", &server.uri()]);
assert_eq!(
code,
Some(1),
"the unpatched store copy must not attest: {env}"
);
assert_eq!(skipped_reason(&env, purl), "hash_mismatch", "{env}");

std::fs::write(installed.join("index.js"), patched).unwrap();
let (code, env) = vex_json(cwd, &["--proxy-url", &server.uri()]);
assert_eq!(code, Some(0), "a patched store copy attests: {env}");
}
58 changes: 58 additions & 0 deletions crates/socket-patch-cli/tests/e2e_vex_vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2481,3 +2481,61 @@ fn vex_attests_a_vlt_vendored_dir_and_omits_a_tampered_one() {
"a tampered dir is never attested"
);
}

/// REGRESSION (#493): the vendored out-of-sync disclosure for a yarn
/// classic project installed with `.yarnrc` `--modules-folder deps`. The
/// crawler never looked in `deps/`, so the drifted live tree went
/// unreported (the attestation itself stands, from the committed
/// artifact, as in [`vendored_live_tree_out_of_sync_warns_but_attests`]).
#[test]
fn vendored_modules_folder_tree_out_of_sync_warns() {
let tmp = tempfile::tempdir().expect("create tempdir");
let cwd = tmp.path();
let purl = "pkg:npm/lodash@4.17.21";
let uuid = "0a0a0a0a-1111-4111-8111-0a0a0a0a0a0b";

let patched = b"patched npm bytes\n";
let after_hash = compute_git_sha256_from_bytes(patched);
let rel = format!(".socket/vendor/npm/{uuid}/lodash-4.17.21.tgz");
let sha256 = sha256_hex(&write_member_tgz(
&cwd.join(&rel),
"package/index.js",
patched,
));
let record = make_record(
uuid,
"package/index.js",
&after_hash,
"GHSA-sync-bbbb",
&["CVE-2026-11"],
);
let wiring = write_matrix_wiring(cwd, "npm", uuid, &rel);
let mut state = VendorState::new();
state.entries.insert(
purl.to_string(),
detached_matrix_entry("npm", purl, uuid, &rel, sha256, record, wiring),
);
std::fs::write(
cwd.join(".socket/vendor/state.json"),
serde_json::to_string_pretty(&state).expect("serialize vendor state"),
)
.expect("write vendor state.json");

std::fs::write(cwd.join(".yarnrc"), "--modules-folder deps\n").unwrap();
let installed = cwd.join("deps/lodash");
std::fs::create_dir_all(&installed).unwrap();
std::fs::write(
installed.join("package.json"),
r#"{"name":"lodash","version":"4.17.21"}"#,
)
.unwrap();
std::fs::write(installed.join("index.js"), b"original unpatched bytes\n").unwrap();

let (code, env) = vex_json(cwd, &[]);
assert_eq!(code, Some(0), "{env}");
let w = env["warnings"]
.as_array()
.and_then(|ws| ws.iter().find(|w| w["code"] == "vendored_tree_out_of_sync"))
.unwrap_or_else(|| panic!("expected a vendored_tree_out_of_sync warning: {env}"));
assert!(w["detail"].as_str().unwrap().contains(purl), "{w}");
}
Loading
Loading