From 03f92c06aa6cdbfeccd615ba66d048dd9ed6f637 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Sun, 7 Jun 2026 06:46:45 -0400 Subject: [PATCH 1/2] fix: normalize checksum asset paths --- cmd/wfctl/plugin_checksum.go | 4 ++++ cmd/wfctl/plugin_checksum_test.go | 23 +++++++++++++++++++++++ cmd/wfctl/update_test.go | 17 +++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/cmd/wfctl/plugin_checksum.go b/cmd/wfctl/plugin_checksum.go index f8b2bae28..fcadf6421 100644 --- a/cmd/wfctl/plugin_checksum.go +++ b/cmd/wfctl/plugin_checksum.go @@ -34,6 +34,10 @@ func parseChecksumsTxt(body string) (map[string]string, error) { if strings.HasPrefix(name, " ") { return nil, fmt.Errorf("malformed checksums.txt line: %q (expected exactly two spaces between hash and filename)", line) } + name = strings.TrimPrefix(name, "./") + if existing, ok := result[name]; ok && existing != hash { + return nil, fmt.Errorf("conflicting checksums.txt entries for %q", name) + } result[name] = hash } return result, nil diff --git a/cmd/wfctl/plugin_checksum_test.go b/cmd/wfctl/plugin_checksum_test.go index be8fcf796..ef29c7c7e 100644 --- a/cmd/wfctl/plugin_checksum_test.go +++ b/cmd/wfctl/plugin_checksum_test.go @@ -30,6 +30,29 @@ func TestParseChecksumsTxt_Valid(t *testing.T) { } } +func TestParseChecksumsTxt_NormalizesDotSlashPrefix(t *testing.T) { + body := "abc123def456 ./wfctl-darwin-arm64\n" + got, err := parseChecksumsTxt(body) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got["wfctl-darwin-arm64"] != "abc123def456" { + t.Fatalf("normalized checksum = %q, want abc123def456", got["wfctl-darwin-arm64"]) + } +} + +func TestParseChecksumsTxt_RejectsConflictingDotSlashAliases(t *testing.T) { + body := "abc123def456 wfctl-darwin-arm64\n" + + "789abcdef012 ./wfctl-darwin-arm64\n" + _, err := parseChecksumsTxt(body) + if err == nil { + t.Fatal("expected conflicting checksum aliases to fail") + } + if !strings.Contains(err.Error(), "conflicting") { + t.Fatalf("error = %v, want conflicting checksum error", err) + } +} + func TestParseChecksumsTxt_SkipsBlankLines(t *testing.T) { body := "\nabc123 plugin.tar.gz\n\n" got, err := parseChecksumsTxt(body) diff --git a/cmd/wfctl/update_test.go b/cmd/wfctl/update_test.go index 32b1f32f9..4d21f9307 100644 --- a/cmd/wfctl/update_test.go +++ b/cmd/wfctl/update_test.go @@ -378,6 +378,23 @@ func TestVerifyAssetChecksum_Valid(t *testing.T) { } } +func TestVerifyAssetChecksum_AcceptsDotSlashAssetPrefix(t *testing.T) { + data := []byte("fake binary content") + h := sha256.Sum256(data) + hash := hex.EncodeToString(h[:]) + checksumsContent := fmt.Sprintf("%s ./wfctl-darwin-arm64\n", hash) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(checksumsContent)) + })) + defer srv.Close() + + checksumAsset := &githubAsset{Name: "checksums.txt", BrowserDownloadURL: srv.URL} + if err := verifyAssetChecksum(checksumAsset, "wfctl-darwin-arm64", data); err != nil { + t.Fatalf("verifyAssetChecksum: %v", err) + } +} + func TestVerifyAssetChecksum_Mismatch(t *testing.T) { data := []byte("fake binary content") checksumsContent := "deadbeef wfctl-linux-amd64\n" From 1159cc9d87c74b33e70d88926862a34dbaa52fb9 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Sun, 7 Jun 2026 07:01:33 -0400 Subject: [PATCH 2/2] test: reject empty normalized checksum names --- cmd/wfctl/plugin_checksum.go | 3 +++ cmd/wfctl/plugin_checksum_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/cmd/wfctl/plugin_checksum.go b/cmd/wfctl/plugin_checksum.go index fcadf6421..360cb3fdd 100644 --- a/cmd/wfctl/plugin_checksum.go +++ b/cmd/wfctl/plugin_checksum.go @@ -35,6 +35,9 @@ func parseChecksumsTxt(body string) (map[string]string, error) { return nil, fmt.Errorf("malformed checksums.txt line: %q (expected exactly two spaces between hash and filename)", line) } name = strings.TrimPrefix(name, "./") + if name == "" { + return nil, fmt.Errorf("malformed checksums.txt line: %q (empty filename after normalization)", line) + } if existing, ok := result[name]; ok && existing != hash { return nil, fmt.Errorf("conflicting checksums.txt entries for %q", name) } diff --git a/cmd/wfctl/plugin_checksum_test.go b/cmd/wfctl/plugin_checksum_test.go index ef29c7c7e..62e27f69f 100644 --- a/cmd/wfctl/plugin_checksum_test.go +++ b/cmd/wfctl/plugin_checksum_test.go @@ -53,6 +53,16 @@ func TestParseChecksumsTxt_RejectsConflictingDotSlashAliases(t *testing.T) { } } +func TestParseChecksumsTxt_RejectsEmptyDotSlashName(t *testing.T) { + _, err := parseChecksumsTxt("abc123def456 ./\n") + if err == nil { + t.Fatal("expected ./ checksum entry to fail") + } + if !strings.Contains(err.Error(), "empty filename") { + t.Fatalf("error = %v, want empty filename error", err) + } +} + func TestParseChecksumsTxt_SkipsBlankLines(t *testing.T) { body := "\nabc123 plugin.tar.gz\n\n" got, err := parseChecksumsTxt(body)