From 1598b5d904089c5ae1e9265fb80ca19f0ba8dd71 Mon Sep 17 00:00:00 2001 From: "devsy-app[bot]" <277138668+devsy-app[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 00:21:16 +0000 Subject: [PATCH 1/2] test(image): cover docker tag validation Add table-driven tests for IsValidDockerTag and ValidateTags in pkg/image. --- pkg/image/image_test.go | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/pkg/image/image_test.go b/pkg/image/image_test.go index f6b4bb708f..097ceaf14b 100644 --- a/pkg/image/image_test.go +++ b/pkg/image/image_test.go @@ -2,6 +2,7 @@ package image import ( "sort" + "strings" "testing" "github.com/google/go-containerregistry/pkg/name" @@ -62,3 +63,77 @@ func TestParseReference_DoesNotSpecialCaseDockerInternalHost(t *testing.T) { ) } } + +func TestIsValidDockerTag(t *testing.T) { + // Docker tag grammar: [\w][\w.-]{0,127} — a single word char then up to 127 + // word/dot/hyphen chars. pkg/image accepts an empty string as valid because the + // build caller (cmd/workspace/build.go) only invokes ValidateTags when the tag + // slice is non-empty, so an empty element means "no tag"; pin that here. + longTag := strings.Repeat("a", DockerTagMaxSize) + overlongTag := strings.Repeat("a", DockerTagMaxSize+1) + const latestTag = "latest" + + tests := []struct { + name string + tag string + want bool + }{ + {"empty accepted as no-tag sentinel", "", true}, + {"single char", "a", true}, + {latestTag, latestTag, true}, + {"semver", "v1.0.0", true}, + {"numeric", "3.18", true}, + {"dotted and hyphenated", "ubuntu-20.04", true}, + {"underscore", "a_b", true}, + {"at max size", longTag, true}, + {"leading dot", ".latest", false}, + {"leading hyphen", "-v1", false}, + {"slash", "foo/bar", false}, + {"colon", "v1:0", false}, + {"space", "tag with space", false}, + {"at sign", "tag@2", false}, + {"non-ascii", "α", false}, + {"over max size", overlongTag, false}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := IsValidDockerTag(tc.tag); got != tc.want { + t.Errorf("IsValidDockerTag(%q) = %v, want %v", tc.tag, got, tc.want) + } + }) + } +} + +func TestValidateTags(t *testing.T) { + t.Run("all valid", func(t *testing.T) { + if err := ValidateTags([]string{"latest", "v1.0.0", "3.18"}); err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + + t.Run("empty slice", func(t *testing.T) { + if err := ValidateTags(nil); err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + + t.Run("rejects invalid tag with offending value in message", func(t *testing.T) { + err := ValidateTags([]string{"latest", "foo/bar"}) + if err == nil { + t.Fatal("expected error for invalid tag, got nil") + } + if !strings.Contains(err.Error(), `"foo/bar"`) { + t.Errorf("expected error to quote the offending tag %q, got: %v", "foo/bar", err) + } + }) + + t.Run("reports first invalid tag", func(t *testing.T) { + err := ValidateTags([]string{".bad", "-also-bad"}) + if err == nil { + t.Fatal("expected error for invalid tag, got nil") + } + if !strings.Contains(err.Error(), `".bad"`) { + t.Errorf("expected first invalid tag in error, got: %v", err) + } + }) +} From 6fc7814078a1b999bcefa25bb1461af4a2b8e10c Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 10 Aug 2026 03:04:10 +0000 Subject: [PATCH 2/2] chore: update comments Signed-off-by: Samuel K --- pkg/image/image_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/image/image_test.go b/pkg/image/image_test.go index 097ceaf14b..804b3bec48 100644 --- a/pkg/image/image_test.go +++ b/pkg/image/image_test.go @@ -65,10 +65,6 @@ func TestParseReference_DoesNotSpecialCaseDockerInternalHost(t *testing.T) { } func TestIsValidDockerTag(t *testing.T) { - // Docker tag grammar: [\w][\w.-]{0,127} — a single word char then up to 127 - // word/dot/hyphen chars. pkg/image accepts an empty string as valid because the - // build caller (cmd/workspace/build.go) only invokes ValidateTags when the tag - // slice is non-empty, so an empty element means "no tag"; pin that here. longTag := strings.Repeat("a", DockerTagMaxSize) overlongTag := strings.Repeat("a", DockerTagMaxSize+1) const latestTag = "latest"