From a26013462f5207e1738e13d74466640ce3ffb1c0 Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Sat, 25 Apr 2026 02:54:13 +0530 Subject: [PATCH 1/3] Validate function tag literals before execution Signed-off-by: Akash Kumar --- pkg/fn/runtime/tag_resolution.go | 17 +++++++++++++++++ pkg/fn/runtime/tag_resolution_test.go | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pkg/fn/runtime/tag_resolution.go b/pkg/fn/runtime/tag_resolution.go index 18ae91e7f5..f4036bae4f 100644 --- a/pkg/fn/runtime/tag_resolution.go +++ b/pkg/fn/runtime/tag_resolution.go @@ -17,6 +17,7 @@ package runtime import ( "context" "fmt" + "regexp" "slices" "strings" @@ -25,6 +26,10 @@ import ( "k8s.io/klog/v2" ) +const imageTagError = "start with an alphanumeric character or underscore, followed by at most 127 alphanumeric characters, underscores, periods, or dashes" + +var imageTagRegex = regexp.MustCompile(`^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$`) + // TagLister is an interface for listing tags for/from a function runtime/runner type TagLister interface { Name() string @@ -85,10 +90,22 @@ func (tr *TagResolver) ResolveFunctionImage(ctx context.Context, image, tag stri ref.Tag = allFilteredVersions[0].Original() return ref.CommonName(), nil } else { + resolvedImage, tagErr := imageWithLiteralTag(ref, tag) + if tagErr != nil { + return "", tagErr + } klog.Warningf("Tag %q could not be parsed as a semantic version (\"%s\") or constraint (\"%s\"), will use it literally", tag, versionErr, constraintErr) + return resolvedImage, nil } + return imageWithLiteralTag(ref, tag) +} + +func imageWithLiteralTag(ref regclientref.Ref, tag string) (string, error) { + if !imageTagRegex.MatchString(tag) { + return "", fmt.Errorf("`function.tag` %q must be a valid image tag: %s", tag, imageTagError) + } ref.Tag = tag return ref.CommonName(), nil } diff --git a/pkg/fn/runtime/tag_resolution_test.go b/pkg/fn/runtime/tag_resolution_test.go index 24b6099566..24cfe0dfa8 100644 --- a/pkg/fn/runtime/tag_resolution_test.go +++ b/pkg/fn/runtime/tag_resolution_test.go @@ -146,6 +146,12 @@ func TestResolveFunctionImage(t *testing.T) { repoTags: tagSet, expectedTag: "v0.3.1", }, + "exact semver tag with invalid image tag syntax": { + functionImage: image, + functionTag: "v0.3.1+build", + repoTags: tagSet, + expectedErr: "`function.tag` \"v0.3.1+build\" must be a valid image tag", + }, "no listing with exact semver tag": { functionImage: image, functionTag: "v0.3.1", @@ -164,6 +170,12 @@ func TestResolveFunctionImage(t *testing.T) { repoErr: "test", expectedTag: "master-git-38f885f", }, + "invalid non-semver tag": { + functionImage: image, + functionTag: ">>0.1", + repoTags: tagSet, + expectedErr: "`function.tag` \">>0.1\" must be a valid image tag", + }, "list failure": { functionImage: image, functionTag: "~0.1", From b47453b82f9e9ffef562dd2027f138212a1d89f6 Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Sat, 25 Apr 2026 03:00:32 +0530 Subject: [PATCH 2/3] Clarify image tag validation error Signed-off-by: Akash Kumar --- pkg/fn/runtime/tag_resolution.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/fn/runtime/tag_resolution.go b/pkg/fn/runtime/tag_resolution.go index f4036bae4f..9572dede50 100644 --- a/pkg/fn/runtime/tag_resolution.go +++ b/pkg/fn/runtime/tag_resolution.go @@ -26,7 +26,7 @@ import ( "k8s.io/klog/v2" ) -const imageTagError = "start with an alphanumeric character or underscore, followed by at most 127 alphanumeric characters, underscores, periods, or dashes" +const imageTagError = "must start with an alphanumeric character or underscore, followed by at most 127 alphanumeric characters, underscores, periods, or dashes" var imageTagRegex = regexp.MustCompile(`^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$`) From 9782c09b29b5b27c3c2d09cc509a7a553251130d Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Thu, 30 Apr 2026 03:19:16 +0530 Subject: [PATCH 3/3] Address review: use distribution/reference TagRegexp, simplify control flow mozesl-nokia in #4503 review: - Suggested importing the canonical tag regexp from github.com/distribution/reference instead of duplicating the pattern. Switched imageWithLiteralTag to use reference.TagRegexp; since it is unanchored, require a full-string match via FindString to keep the same semantics as the previous local regex. - Restored the pre-PR control flow in ResolveFunctionImage so the literal-tag warning stays in the else branch and the validation / reference build happens once at the end via imageWithLiteralTag. Signed-off-by: Akash Kumar --- go.mod | 1 + go.sum | 4 ++-- pkg/fn/runtime/tag_resolution.go | 13 ++++--------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 00a6ad4b64..4e3eb56b8d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/bytecodealliance/wasmtime-go v1.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.7 + github.com/distribution/reference v0.6.0 github.com/go-errors/errors v1.5.1 github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.20.6 diff --git a/go.sum b/go.sum index 41ff2f319e..91b16f5416 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -127,8 +129,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= -github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/pkg/fn/runtime/tag_resolution.go b/pkg/fn/runtime/tag_resolution.go index 9572dede50..cbc47dd0dc 100644 --- a/pkg/fn/runtime/tag_resolution.go +++ b/pkg/fn/runtime/tag_resolution.go @@ -17,19 +17,17 @@ package runtime import ( "context" "fmt" - "regexp" "slices" "strings" "github.com/Masterminds/semver/v3" + "github.com/distribution/reference" regclientref "github.com/regclient/regclient/types/ref" "k8s.io/klog/v2" ) const imageTagError = "must start with an alphanumeric character or underscore, followed by at most 127 alphanumeric characters, underscores, periods, or dashes" -var imageTagRegex = regexp.MustCompile(`^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$`) - // TagLister is an interface for listing tags for/from a function runtime/runner type TagLister interface { Name() string @@ -90,20 +88,17 @@ func (tr *TagResolver) ResolveFunctionImage(ctx context.Context, image, tag stri ref.Tag = allFilteredVersions[0].Original() return ref.CommonName(), nil } else { - resolvedImage, tagErr := imageWithLiteralTag(ref, tag) - if tagErr != nil { - return "", tagErr - } klog.Warningf("Tag %q could not be parsed as a semantic version (\"%s\") or constraint (\"%s\"), will use it literally", tag, versionErr, constraintErr) - return resolvedImage, nil } return imageWithLiteralTag(ref, tag) } func imageWithLiteralTag(ref regclientref.Ref, tag string) (string, error) { - if !imageTagRegex.MatchString(tag) { + // reference.TagRegexp is the canonical OCI/Docker tag pattern but is + // unanchored, so require a full-string match to reject embedded matches. + if match := reference.TagRegexp.FindString(tag); match != tag { return "", fmt.Errorf("`function.tag` %q must be a valid image tag: %s", tag, imageTagError) } ref.Tag = tag