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 18ae91e7f5..cbc47dd0dc 100644 --- a/pkg/fn/runtime/tag_resolution.go +++ b/pkg/fn/runtime/tag_resolution.go @@ -21,10 +21,13 @@ import ( "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" + // TagLister is an interface for listing tags for/from a function runtime/runner type TagLister interface { Name() string @@ -89,6 +92,15 @@ func (tr *TagResolver) ResolveFunctionImage(ctx context.Context, image, tag stri tag, versionErr, constraintErr) } + return imageWithLiteralTag(ref, tag) +} + +func imageWithLiteralTag(ref regclientref.Ref, tag string) (string, error) { + // 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 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",