Skip to content

Commit 2fa9784

Browse files
committed
Fix workspace Go vex discovery bugs
Bug 1: Zip pins attest unknown workspace selection - Track go.work.sum zip versions separately from selection proof - When a higher version exists in go.work.sum, don't attest the Zip pin - Emit the ref but set locked_integrity to None (integrity_required = true) Bug 2: One bad member drops all requires - Don't set mains to None when a single member is out-of-root or unreadable - Skip only the bad member and continue collecting valid members' requires
1 parent 2beda00 commit 2fa9784

1 file changed

Lines changed: 48 additions & 17 deletions

File tree

  • crates/socket-patch-core/src/vex/discover

‎crates/socket-patch-core/src/vex/discover/golang.rs‎

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,10 @@ async fn read_workspace(
249249
go_mod: Option<&str>,
250250
go_work: &str,
251251
) -> (Option<Vec<MainModule>>, Vec<String>) {
252-
let mut mains = Some(Vec::new());
252+
let mut mains = Vec::new();
253253
let mut files = vec![GO_WORK_SUM.to_string()];
254254
for dir in go_mod_edit::parse_use_dirs(go_work) {
255255
let Some(dir) = member_dir(&dir) else {
256-
mains = None;
257256
continue;
258257
};
259258
let text = if dir.is_empty() {
@@ -265,17 +264,15 @@ async fn read_workspace(
265264
None => None,
266265
}
267266
};
268-
match (text, mains.as_mut()) {
269-
(Some(text), Some(mains)) => mains.push(MainModule::new(&text)),
270-
(Some(_), None) => {}
271-
(None, _) => mains = None,
267+
if let Some(text) = text {
268+
mains.push(MainModule::new(&text));
272269
}
273270
let sum = member_file(&dir, GO_SUM);
274271
if !files.contains(&sum) {
275272
files.push(sum);
276273
}
277274
}
278-
(mains, files)
275+
(Some(mains), files)
279276
}
280277

281278
/// Normalize a go.mod-grammar file (BOM, simple string literals) and check
@@ -357,7 +354,7 @@ fn extract_file(
357354
SumPin::Zip(_) => graph.workspace,
358355
SumPin::Unpinned => false,
359356
};
360-
if check {
357+
let workspace_higher = if check {
361358
let req = required.get(&entry.module).map(String::as_str);
362359
// `replaced_purl` resolved one of the two.
363360
let version = entry.version.as_deref().or(req).unwrap_or_default();
@@ -375,9 +372,23 @@ fn extract_file(
375372
);
376373
continue;
377374
}
378-
}
375+
// In workspace mode, also check go.work.sum for higher versions.
376+
// Those lines outlive the requirement that raised the module, so
377+
// they don't prove selection: emit the ref but don't attest it.
378+
if graph.workspace {
379+
let workspace_zips = sums.workspace_zip_versions(&entry.module);
380+
workspace_zips
381+
.iter()
382+
.any(|v| go_semver_cmp(v, version) == Ordering::Greater)
383+
} else {
384+
false
385+
}
386+
} else {
387+
false
388+
};
379389
let (integrity, integrity_required) = match pin {
380-
SumPin::Zip(h1) => (Some(LockIntegrity::GoH1(h1)), true),
390+
SumPin::Zip(h1) if !workspace_higher => (Some(LockIntegrity::GoH1(h1)), true),
391+
SumPin::Zip(_) => (None, true),
381392
SumPin::GoModOnly => (None, graph.workspace),
382393
SumPin::Unpinned => (None, true),
383394
};
@@ -536,6 +547,9 @@ struct SumPins {
536547
unparseable: bool,
537548
/// Every module's zip-line versions (the build-list evidence).
538549
zip_versions: HashMap<String, Vec<String>>,
550+
/// go.work.sum zip-line versions: used to detect inert workspace
551+
/// replaces but not as selection proof.
552+
workspace_zip_versions: HashMap<String, Vec<String>>,
539553
}
540554

541555
/// What the sum files pin for one Socket `module@version`.
@@ -553,7 +567,7 @@ impl SumPins {
553567
let mut pins = SumPins::default();
554568
for file in files {
555569
if let Some(text) = ctx.read_text(file, out).await {
556-
pins.add(&text, file != GO_WORK_SUM);
570+
pins.add(&text, file != GO_WORK_SUM, file == GO_WORK_SUM);
557571
}
558572
}
559573
pins
@@ -562,7 +576,9 @@ impl SumPins {
562576
/// `selects`: whether the file's zip lines show what the build selects.
563577
/// go only ever appends to `go.work.sum`, so its lines outlive the
564578
/// requirement that raised a module.
565-
fn add(&mut self, text: &str, selects: bool) {
579+
/// `workspace_sum`: whether this is go.work.sum (used to detect inert
580+
/// workspace replaces but not as selection proof).
581+
fn add(&mut self, text: &str, selects: bool, workspace_sum: bool) {
566582
// go rejects a whole go.sum with any non-blank line of other than
567583
// three tokens (only `go mod tidy` skips it).
568584
if text
@@ -573,11 +589,19 @@ impl SumPins {
573589
return;
574590
}
575591
for line in go_sum_lines(text) {
576-
if !line.go_mod && selects {
577-
self.zip_versions
578-
.entry(line.module.to_string())
579-
.or_default()
580-
.push(line.version.to_string());
592+
if !line.go_mod {
593+
if selects {
594+
self.zip_versions
595+
.entry(line.module.to_string())
596+
.or_default()
597+
.push(line.version.to_string());
598+
}
599+
if workspace_sum {
600+
self.workspace_zip_versions
601+
.entry(line.module.to_string())
602+
.or_default()
603+
.push(line.version.to_string());
604+
}
581605
}
582606
// go ignores hashes of algorithms it does not know.
583607
if !line.module.starts_with(HOSTED_GO_MODULE_PREFIX) || !line.hash.starts_with("h1:") {
@@ -624,6 +648,13 @@ impl SumPins {
624648
.map(|v| v.iter().map(String::as_str).collect())
625649
.unwrap_or_default()
626650
}
651+
652+
fn workspace_zip_versions(&self, module: &str) -> Vec<&str> {
653+
self.workspace_zip_versions
654+
.get(module)
655+
.map(|v| v.iter().map(String::as_str).collect())
656+
.unwrap_or_default()
657+
}
627658
}
628659

629660
/// Root-relative directory of a `go.work` `use` member (`""` for the root

0 commit comments

Comments
 (0)