From 6fcb1329cf256f8c9ab690881c637bd8028b9394 Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Wed, 22 Jul 2026 11:53:20 -0400 Subject: [PATCH] fix(test): don't follow symlinks when walking the pnpm store fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm >= 10.34 records every project the store has served as a symlink under /v10/projects/ -> ../../proj_x. The e2e_safety_pnpm walker followed those links back INTO the project trees, so find_store_file_with_content could return proj_a's own node_modules index.js — freshly patched — and misreport it as 'the store entry', tripping the store-must-stay-unpatched asserts. Whether it did depended on readdir order: deterministic-red on CI's ubuntu ext4 since the runner image picked up pnpm 10.34.5 (first failed on main at 70b41d6, a dep-bump-only commit), green on macOS/Windows. The real CAFS entry was verified untouched in every failing run (inode forensics: proj_b + store share the original inode, nlink 2, original bytes) — the CoW defense itself was never broken. Skipping symlinks is lossless: the store's real content (files/, index/) contains none. Verified in a Linux container with pnpm 10.34.5: red-first-try before the fix, 5/5 suite runs green after. Co-Authored-By: Claude Fable 5 --- .../socket-patch-cli/tests/e2e_safety_pnpm.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs index 71da9be5..f814d228 100644 --- a/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs +++ b/crates/socket-patch-cli/tests/e2e_safety_pnpm.rs @@ -133,6 +133,23 @@ where }; while let Some(Ok(entry)) = entries.next() { let p = entry.path(); + // NEVER follow symlinks out of the store. pnpm ≥ 10.34 records + // every project the store has served as a symlink under + // `/v10/projects/` → `../../../proj_a`. Following + // it walks back INTO the project tree, where this search finds + // proj_a's node_modules copy of index.js — freshly PATCHED — + // and misreports it as "the store entry", failing the + // store-must-stay-unpatched asserts even though the real CAFS + // entry is untouched (whether that happens depends on readdir + // order, so it was ubuntu-deterministic but flaky elsewhere). + // The store's real content (`files/`, `index/`) contains no + // symlinks, so skipping them is lossless. + let is_symlink = std::fs::symlink_metadata(&p) + .map(|m| m.file_type().is_symlink()) + .unwrap_or(true); + if is_symlink { + continue; + } if let Some(hit) = f(&p) { return Some(hit); }