Skip to content

Commit 868fe6f

Browse files
committed
Match only file: links, keep scoped folder names
Code review follow-ups. The link identity fill-in now applies only to a link npm reached through a file: spec, so a workspace member that happens to share a patched package's name and version is no longer reported as an unpatched copy. A source folder with no name field keeps its @scope/ parent, matching npm, so scoped file: directories are caught too. The npm rewrite golden is re-blessed: only its output digests change, for the seeded link and non-registry cases that now record the patch for the in-run VEX. Refs #1481 Assisted-by: Claude Code:claude-opus-5-5
1 parent 713e93c commit 868fe6f

3 files changed

Lines changed: 117 additions & 50 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,8 @@ async fn scan_redirect_npm_file_copy_is_not_attested_in_run() {
16811681
rewritten.contains(HOSTED_URL),
16821682
"{shape}: the registry entry is redirected:\n{rewritten}"
16831683
);
1684+
// No document is written when no statement survives, so a missing
1685+
// file is the expected "attests nothing" outcome here.
16841686
let attested = std::fs::read_to_string(&out)
16851687
.ok()
16861688
.and_then(|text| serde_json::from_str::<serde_json::Value>(&text).ok())

‎crates/socket-patch-core/src/patch/redirect/mod.rs‎

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,17 +1384,25 @@ impl From<NpmLockEntry<'_>> for NpmLockTarget {
13841384

13851385
/// The `(name, version)` of the source directory an npm `packages` link
13861386
/// entry (at `pointer`) points at: the entry its `resolved` path names,
1387-
/// by that entry's `name` field, else its directory's basename (npm omits
1388-
/// `name` when they agree). `None` when the lock records no version.
1387+
/// by that entry's `name` field, else its folder name, which keeps an
1388+
/// `@scope/` parent as npm's `nameFromFolder` does (npm omits `name` when
1389+
/// they agree). `None` when the lock records no version.
13891390
fn npm_link_source_identity(lock: &Value, pointer: &str) -> Option<(String, String)> {
13901391
let resolved = lock.pointer(pointer)?.get("resolved")?.as_str()?;
13911392
let source = lock.get("packages")?.get(resolved)?;
13921393
let version = source.get("version")?.as_str()?;
13931394
let name = match source.get("name").and_then(Value::as_str) {
1394-
Some(name) => name,
1395-
None => resolved.rsplit('/').next().unwrap_or(resolved),
1395+
Some(name) => name.to_string(),
1396+
None => {
1397+
let mut segments = resolved.rsplit('/');
1398+
let base = segments.next().unwrap_or(resolved);
1399+
match segments.next() {
1400+
Some(scope) if scope.starts_with('@') => format!("{scope}/{base}"),
1401+
_ => base.to_string(),
1402+
}
1403+
}
13961404
};
1397-
Some((name.to_string(), version.to_string()))
1405+
Some((name, version.to_string()))
13981406
}
13991407

14001408
/// Rewrite a single npm lockfile (`package-lock.json` or `npm-shrinkwrap.json`)
@@ -1429,18 +1437,22 @@ fn rewrite_one_npm_lock(
14291437
.filter(NpmLockEntry::is_dependency)
14301438
.map(NpmLockTarget::from)
14311439
.collect();
1440+
// Entries npm installs from a git / url / `file:` spec: see
1441+
// `vendor::npm_origin` (#326).
1442+
let non_registry = npm_non_registry_entries(&lock, manifest_overrides);
14321443
// A `file:` directory dependency is a versionless link entry whose
14331444
// source directory's own entry carries the package (#1481): give the
1434-
// link that identity, so a copy of a patched release is seen.
1435-
for target in targets.iter_mut().filter(|t| t.link && t.version.is_none()) {
1445+
// link that identity, so a copy of a patched release is seen. A plain
1446+
// workspace-member link is first-party source, never a copy.
1447+
for target in targets
1448+
.iter_mut()
1449+
.filter(|t| t.link && t.version.is_none() && non_registry.contains_key(&t.key))
1450+
{
14361451
if let Some((name, version)) = npm_link_source_identity(&lock, &target.pointer) {
14371452
target.name = name;
14381453
target.version = Some(version);
14391454
}
14401455
}
1441-
// Entries npm installs from a git / url / `file:` spec: see
1442-
// `vendor::npm_origin` (#326).
1443-
let non_registry = npm_non_registry_entries(&lock, manifest_overrides);
14441456
// Entries npm 7–11 install from a dependency's own shrinkwrap (#753).
14451457
let shrinkwrapped = npm_shrinkwrapped_entries(&lock);
14461458
// The legacy mirror of an entry the `packages` scan skips (and warns
@@ -19412,6 +19424,59 @@ mod tests {
1941219424
}
1941319425
}
1941419426

19427+
/// #1481: a scoped `file:` directory whose source entry has no `name`
19428+
/// (npm omits it when the folder spells the name) is still matched,
19429+
/// while a workspace member that shares a patched package's identity
19430+
/// is first-party source, not a copy.
19431+
#[test]
19432+
fn issue_1481_npm_link_identity_scoped_folder_and_workspace_member() {
19433+
let ovr = npm_override(
19434+
"pkg",
19435+
"1.0.0",
19436+
"http://patch.test/pkg.tgz",
19437+
"sha512-PATCHED==",
19438+
);
19439+
let mut ovr_scoped = ovr.clone();
19440+
ovr_scoped.namespace = Some("@scope".into());
19441+
let scoped = json!({
19442+
"lockfileVersion": 3,
19443+
"packages": {
19444+
"": { "name": "app", "dependencies": { "@scope/pkg": "file:vendor/@scope/pkg" } },
19445+
"vendor/@scope/pkg": { "version": "1.0.0" },
19446+
"node_modules/@scope/pkg": { "resolved": "vendor/@scope/pkg", "link": true }
19447+
}
19448+
});
19449+
let mut files = BTreeMap::new();
19450+
files.insert("package-lock.json".to_string(), scoped.to_string());
19451+
let r = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr_scoped));
19452+
assert!(r.edits.is_empty(), "{:?}", r.edits);
19453+
assert_eq!(
19454+
warning_codes(&r),
19455+
vec!["redirect_npm_link_entry_skipped"],
19456+
"{:?}",
19457+
r.warnings
19458+
);
19459+
assert!(r.bundled_skipped_uuids.contains(&ovr_scoped.patch_uuid));
19460+
19461+
let workspace = json!({
19462+
"lockfileVersion": 3,
19463+
"packages": {
19464+
"": { "name": "app", "workspaces": ["packages/*"] },
19465+
"packages/pkg": { "version": "1.0.0" },
19466+
"node_modules/pkg": { "resolved": "packages/pkg", "link": true }
19467+
}
19468+
});
19469+
files.insert("package-lock.json".to_string(), workspace.to_string());
19470+
let r = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
19471+
assert_eq!(
19472+
warning_codes(&r),
19473+
vec!["redirect_npm_entry_not_found"],
19474+
"{:?}",
19475+
r.warnings
19476+
);
19477+
assert!(r.bundled_skipped_uuids.is_empty());
19478+
}
19479+
1941519480
/// #326, lockfileVersion 2: the legacy `dependencies` mirror of a
1941619481
/// non-registry `packages` entry is left alone too, even when it stores
1941719482
/// the plain version, while the registry copy's mirror is rewired.

‎crates/socket-patch-core/tests/equivalence/npm_lock_rewrite.golden‎

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
20-23 b8ae78f255f6a932 36c2dd9b52c4f509
99
24-27 71eae0c3c0184ecd 58d66e565aa4fd51
1010
28-31 01fe36b5faeb29ff 29fc8116aa94fb5c
11-
32-35 cb046b19a5fdfdd6 c5fc2510910daa71
11+
32-35 cb046b19a5fdfdd6 e84c747f1b94fe84
1212
36-39 efaf1af6b3d201e1 9ea5d8d9e0d0b7bc
13-
40-43 0b146bc1275f5605 6dc1a2f316fea439
13+
40-43 0b146bc1275f5605 7972e2cbc7afe37f
1414
44-47 6131178fcff5f168 fe7bcd7668916be4
1515
48-51 7f23e01097535c51 7f6fc1a7d4b046f9
1616
52-55 0d5e691dad879076 7604ade4a0a89219
@@ -21,24 +21,24 @@
2121
72-75 18775954a617d9ad eb8e0a1834f3c4c9
2222
76-79 cd2aac77dd5fd50c 6ad26aa874db0b3f
2323
80-83 6914eec135779154 103252c10e0e4796
24-
84-87 23d6dfca3dc60d0c 078eeb46fd92ff43
24+
84-87 23d6dfca3dc60d0c 4b5b319ea04c46e2
2525
88-91 174afe518c036993 58d76ad81a0736f1
2626
92-95 55c49569b1ff28ea 2c66448481270ec3
2727
96-99 e9b8cddfc47c2410 e45224d4571298a6
2828
100-103 21dbb73e40e5a210 af5e7560a9d0c0bd
2929
104-107 0c170349f3052ef9 27e2b78479f64dee
30-
108-111 b9c3e6c7cdfad95f f0148e213da61805
30+
108-111 b9c3e6c7cdfad95f 2f5f52b4659153cd
3131
112-115 b7f86471de74f76c 392efc30f7f532cb
3232
116-119 5b317616e7ec450d 892261da3954f566
33-
120-123 1af385991cee8693 985f1874988de52b
33+
120-123 1af385991cee8693 6e0e39495e753304
3434
124-127 45f394bfeb103203 1903864d3d585314
35-
128-131 32b0731afbcedaf7 80e4b2ec800f162a
35+
128-131 32b0731afbcedaf7 966fb68dbaddde60
3636
132-135 bf40da5a6099a8ff 244c2f2ce05cadd7
3737
136-139 98773f82425a6fd9 9011cd7a515c0826
3838
140-143 7635eee1f74b943c 9da248d486b3c382
3939
144-147 43ccf3156a238f1d 55da03ffc362d938
4040
148-151 bd11851bf97a2c30 c8f6b758d93c1d43
41-
152-155 219d9afb7f6a45b2 7f198d2d08e2d656
41+
152-155 219d9afb7f6a45b2 e45485c3ef6b9658
4242
156-159 7c1e1e997c16b02e ca68697c227ebea9
4343
160-163 143279d02db7a456 11e2adc2d8e75624
4444
164-167 d4adfb48c56be5b6 a3fa28797bc91236
@@ -47,7 +47,7 @@
4747
176-179 2567d140ce3ceda0 4a9307775128fdc4
4848
180-183 8c63f24d3ac472f6 cd0f2d2f7aa925a8
4949
184-187 768a53cbd61c832c d64742504fdaaba0
50-
188-191 1b79c77ae63569a4 fc9fd305e1ddc085
50+
188-191 1b79c77ae63569a4 df5fb16dbeee42de
5151
192-195 a652530b7d9610fc 7ef3c98c60d82ab5
5252
196-199 e81b9c1564f40cb9 1834aaaa0f9cf4e5
5353
200-203 ac3292d6054c9914 45c7b2ecb0f2264f
@@ -69,22 +69,22 @@
6969
264-267 f98b36dd3f4431f7 66fda399b9e04c42
7070
268-271 2242e200be734689 961c2cbf0c72226b
7171
272-275 9fbf78e7d41a7e58 543f347b65a23c70
72-
276-279 ea0a7560bb20466c 0c715c2207044d29
72+
276-279 ea0a7560bb20466c 344819c0395d7b4e
7373
280-283 3ab69851cae70bcd 8e480b61d81592af
7474
284-287 1fdb596cc87f031b 64578f1e84310366
75-
288-291 ed84081f4124d522 68797a18e99c2dbd
75+
288-291 ed84081f4124d522 f06108e2c24dc456
7676
292-295 18bc713c335e243c 9c8a170ff899c58b
77-
296-299 658ce14ea92fffd5 536e77ae46114631
77+
296-299 658ce14ea92fffd5 3e048476cb3be792
7878
300-303 9a8fde3916e27342 bab49d863a06a18e
7979
304-307 ac9ed568f15ba5d7 ee0cc2e069d5e0f9
80-
308-311 8ddc66826aca8737 28296ac8c39c5dcf
80+
308-311 8ddc66826aca8737 d01a1614d0247062
8181
312-315 b5092b2eb6d28e04 399dc984fbbaab55
8282
316-319 27d1154fe85c9091 281b9314ec579043
8383
320-323 8f7ded70633dd0a3 2ab2a92cb15c09cc
8484
324-327 8c22483048c74dca 2e1a0cad7e7f49b8
8585
328-331 3b6a2eae6af90be3 45de24588519ef65
8686
332-335 291ee053b410dc25 4821eb617b8d86ec
87-
336-339 c00ae81f0a2be968 9f069a599d03be8b
87+
336-339 c00ae81f0a2be968 89f6b7eda51fe3ea
8888
340-343 1b3d34745caf497c 562af394d0cb24db
8989
344-347 8e350ee856023a1f 7f3b5b09c26ad562
9090
348-351 7b0511648fa4447c e0f1d4a90bf10bbc
@@ -94,17 +94,17 @@
9494
364-367 f8819f4cbe245541 5b51feb62d76b033
9595
368-371 3d30266cadaa3712 44f173d7028a72e7
9696
372-375 a65f748f7e3f6933 e612c7529ecd9fc1
97-
376-379 ee1ad63fa3935a76 2d0b8d59a4cc6d7c
98-
380-383 0c76dfd9b908af15 c62f2970357958d4
97+
376-379 ee1ad63fa3935a76 b5959ec08901a56d
98+
380-383 0c76dfd9b908af15 20cb1e0cbd34cdd7
9999
384-387 7d113d1c4bcf7ede 694120e2b01d24db
100-
388-391 392bfdf6873fb038 f681d116f11dc011
100+
388-391 392bfdf6873fb038 a8ae717944f9aa4f
101101
392-395 8ab3f5ef6c2260dc fdc1bd49d88b0f00
102102
396-399 e70ee73cf9f93cdd 1fe4da6c539ccf86
103103
400-403 dda6b03292721d0b 9795245a300e80da
104104
404-407 cd2441a35b1c5740 6fd4d2a386eed373
105105
408-411 91ac4721e77b4500 0bc11d72a0297bb9
106-
412-415 28af4189511719f9 3d29075156f4c9fa
107-
416-419 d9e2eae424bebb3e 97225086b3cde613
106+
412-415 28af4189511719f9 ddb2206e43e55a79
107+
416-419 d9e2eae424bebb3e 6be312fb7042abe2
108108
420-423 0f86f1432c081b6c c8cd109152c9000d
109109
424-427 6533c923cb60e539 88649c02c40ace42
110110
428-431 80c11eb13664c5fb 3875f53d37e204d1
@@ -119,63 +119,63 @@
119119
464-467 1b0fa950663ac590 e1259bda18595a9a
120120
468-471 9b8b2340d8531f3e 5cc104c917c7cfa8
121121
472-475 736df961ac44dd1f 6e0b231581610bca
122-
476-479 71839c074d059821 e32e1ee9c4690986
122+
476-479 71839c074d059821 91203a0525ff5d5a
123123
480-483 2dfe4501253d999f 3f2903a8a1f3d955
124124
484-487 6d4cfdfeda102637 f243f89ebb53ccb8
125125
488-491 8e04bbe242721495 4827195052919674
126-
492-495 4b804da1bed766bc bdeb1a87ef9305c2
126+
492-495 4b804da1bed766bc 72ccae5686528cd4
127127
496-499 e4358ad81f66c66c 1bd273b112273309
128128
500-503 d497faab2e6374d1 25ff9866dabb19b5
129-
504-507 bd134b4a4fd9c1af ec0b3fba5c88c493
129+
504-507 bd134b4a4fd9c1af 1367ab6e829c8629
130130
508-511 3a1c53db7ba9c199 fa3b06c10bf6d6b0
131131
512-515 9ac76bdafc788702 1b12e2367ef70fd8
132132
516-519 cc5cb99242e72eea 2412c7294ae0e7fb
133-
520-523 df8749c4f1d4c88d 2151d47d975cb37f
134-
524-527 df1f4eb0a17e6840 a8c5a985520801ad
133+
520-523 df8749c4f1d4c88d 0d35d0cb8daa69c3
134+
524-527 df1f4eb0a17e6840 34b3e746a9db16b3
135135
528-531 5bd935a1966450a1 5405033fc13d5f62
136136
532-535 d3c766b67ad93619 0eb262d5010e1fca
137-
536-539 0abea7838ee8c651 dafd54e8801c3793
137+
536-539 0abea7838ee8c651 f1cd1f6775fbd303
138138
540-543 5a34e67ca32acd76 441ba16c8b63b7b3
139139
544-547 8139c395ec116abf 1e3325d4109dd7b7
140-
548-551 7937635862cd0cc7 cdda5e17b4f48a72
140+
548-551 7937635862cd0cc7 7d895801e7f94082
141141
552-555 f18bccaae83b8a26 d0df0c9009bc78a6
142-
556-559 0d7281f2dc6cab7c f88a0cefea8fd9d1
142+
556-559 0d7281f2dc6cab7c 75df07196c1d5198
143143
560-563 67b3e9f59200ece4 adb2ddac66bceede
144144
564-567 eb7c20329e96b501 a3ca918e8d58299e
145145
568-571 d293cc2b575a278d b4e3b6ca4d70ca48
146146
572-575 c17b85264698e947 edcae00b1fdd32ad
147-
576-579 86db374e9417a100 b2589b8c5862526d
147+
576-579 86db374e9417a100 b757fd3fd011934d
148148
580-583 ce0fc0bd2408d817 03db723dd12019c4
149149
584-587 436c95276aa8074e df1bbf8d7663a3c7
150150
588-591 c09216f32360676b 66b0ec275885c59c
151151
592-595 2c4e9ab4e7011bcd b690d76fc146a3cb
152-
596-599 cc8ac6e16d3c70a2 a769d6b0522f7d3f
153-
600-603 5e61ab66a7b47ef0 0a0039c9514fa44a
152+
596-599 cc8ac6e16d3c70a2 99de81bd6529e4e2
153+
600-603 5e61ab66a7b47ef0 3309f43222c21702
154154
604-607 890a631fda14796b bdab2de6ec82c8da
155155
608-611 36c6f3f1734989cb 8a6246b8c05f144f
156156
612-615 918d05dba93bf7af ae6b338ed65863e2
157-
616-619 cc1fc9fe234e6237 e04ec4070749160b
157+
616-619 cc1fc9fe234e6237 7bd261119fc4428f
158158
620-623 2b1c61c2f9ffecf7 8fbb465964e46b0e
159-
624-627 9e25c994d6e9f497 2402434dc456687e
159+
624-627 9e25c994d6e9f497 ff0f5b5dabbdf81d
160160
628-631 9569acd0022c72e9 99eeb42b41eac287
161161
632-635 02a74614410dc8de 82d095eaed3f21e1
162162
636-639 0064b192beb2f16d fc9da6ebac16e671
163163
640-643 b2176154f9609f29 3ff41983caa23faf
164164
644-647 f865a601402d568c 943e9e9921ecb728
165-
648-651 8ddac4722587be4f ace2b06a8b8c8c2f
165+
648-651 8ddac4722587be4f 719e1eacdf1a74b7
166166
652-655 647ba94842de4a99 8fea29aad4870b17
167167
656-659 22c5789b07c59689 0aaa723ae5b9df03
168-
660-663 f981a3e36eabb727 9f448a9a173571e3
168+
660-663 f981a3e36eabb727 84eb8d1c170cc1d6
169169
664-667 6378568f624c46d3 cb9bf66dc3cda08a
170170
668-671 53b118efd7818da9 4564ab4a7f0d559d
171171
672-675 a3219e99fc477e55 c6df6f0d87b9b9c3
172-
676-679 2dbb5f9d13c34873 7580908dfdcfea1d
172+
676-679 2dbb5f9d13c34873 899937bb2d2ad28d
173173
680-683 f59699ffa5de8930 804313d49536b1eb
174174
684-687 59cb168e9f9a08b3 398c4bfcc9646dad
175175
688-691 6dfa1486645043d6 d81171f958bed162
176176
692-695 bf91bdd85d72f097 a2515c0d5601d71b
177177
696-699 30cef91da832598a 806297d879f04028
178-
700-703 9914c1d4b503c2fa b2d53e04d21b7bc6
178+
700-703 9914c1d4b503c2fa 1bbcb841dde6a8eb
179179
704-707 db31d2e2f05ae765 1ab6149d78be4168
180180
708-711 73b257c77137ec8d f77474faf5d33ba7
181181
712-715 2723b37a94e6f61d cac8d4e20c22b5a2
@@ -189,14 +189,14 @@
189189
744-747 13ff170d3d8cf8cf 34c8f3d49bf9d824
190190
748-751 2b646f2ebcca65e2 e1e2ef0f8acc106b
191191
752-755 9171f34232dc9d5d 1f3743ef48f2b715
192-
756-759 6e922d3893444fd8 5e1314867d30fa3d
192+
756-759 6e922d3893444fd8 b2e4c5edb8d864ad
193193
760-763 d11f1fcb3df5ee76 33ce5e620fa20e08
194194
764-767 b8a0235197ffeb8f 48ae6872a82dae8e
195195
768-771 84d8643c18a5e371 df626fca46bed1f9
196-
772-775 dd10317147218f0a 224f4602080a058c
197-
776-779 19a4e66646f42e84 84ab921e068c65f9
196+
772-775 dd10317147218f0a 05814afde8bd10d4
197+
776-779 19a4e66646f42e84 4bc8a478cb0b4b8a
198198
780-783 0bb4a00c871b5a42 e910828f4f26627c
199-
784-787 c0bb93b2e4e6cf4e aebb40e04549459e
200-
788-791 850c2fdb73cd8f87 4bed71a422cb5f7a
199+
784-787 c0bb93b2e4e6cf4e fdb7209055765194
200+
788-791 850c2fdb73cd8f87 833881782a09b19a
201201
792-795 fc1f34260f230d80 02a005a2a4cc27c6
202202
796-799 89400e26345a7cc1 71dc369195531335

0 commit comments

Comments
 (0)