fix(registry): Updates registry to handle go 1.12.8 changes

Go 1.12.8 introduced some breaking fixes (see 3226f2d492)
for a CVE. This broke the way we were doing registry reference parsing.
This removes the call to the containerd libraries in favor of our own
parsing and adds additional unit tests

Signed-off-by: Taylor Thomas <taylor.thomas@microsoft.com>
pull/6228/head
Taylor Thomas 5 years ago
parent 130f7934ff
commit 16544c8190

@ -19,22 +19,25 @@ package registry // import "helm.sh/helm/internal/experimental/registry"
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/url"
"regexp" "regexp"
"strings" "strings"
"github.com/containerd/containerd/reference"
) )
var ( var (
validPortRegEx = regexp.MustCompile(`^([1-9]\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`) // adapted from https://stackoverflow.com/a/12968117 validPortRegEx = regexp.MustCompile(`^([1-9]\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`) // adapted from https://stackoverflow.com/a/12968117
errEmptyRepo = errors.New("parsed repo was empty") // TODO: Currently we don't support digests, so we are only splitting on the
errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number") // colon. However, when we add support for digests, we'll need to use the
// regexp anyway to split on both colons and @, so leaving it like this for
// now
referenceDelimiter = regexp.MustCompile(`[:]`)
errEmptyRepo = errors.New("parsed repo was empty")
errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number")
) )
type ( type (
// Reference defines the main components of a reference specification // Reference defines the main components of a reference specification
Reference struct { Reference struct {
*reference.Spec
Tag string Tag string
Repo string Repo string
} }
@ -42,22 +45,34 @@ type (
// ParseReference converts a string to a Reference // ParseReference converts a string to a Reference
func ParseReference(s string) (*Reference, error) { func ParseReference(s string) (*Reference, error) {
spec, err := reference.Parse(s) if s == "" {
if err != nil { return nil, errEmptyRepo
return nil, err }
// Split the components of the string on the colon or @, if it is more than 3,
// immediately return an error. Other validation will be performed later in
// the function
splitComponents := referenceDelimiter.Split(s, -1)
if len(splitComponents) > 3 {
return nil, errTooManyColons
} }
// convert to our custom type and make necessary mods var ref *Reference
ref := Reference{Spec: &spec} switch len(splitComponents) {
ref.setExtraFields() case 1:
ref = &Reference{Repo: splitComponents[0]}
case 2:
ref = &Reference{Repo: splitComponents[0], Tag: splitComponents[1]}
case 3:
ref = &Reference{Repo: strings.Join(splitComponents[:2], ":"), Tag: splitComponents[2]}
}
// ensure the reference is valid // ensure the reference is valid
err = ref.validate() err := ref.validate()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ref, nil return ref, nil
} }
// FullName the full name of a reference (repo:tag) // FullName the full name of a reference (repo:tag)
@ -68,38 +83,6 @@ func (ref *Reference) FullName() string {
return fmt.Sprintf("%s:%s", ref.Repo, ref.Tag) return fmt.Sprintf("%s:%s", ref.Repo, ref.Tag)
} }
// setExtraFields adds the Repo and Tag fields to a Reference
func (ref *Reference) setExtraFields() {
ref.Tag = ref.Object
ref.Repo = ref.Locator
ref.fixNoTag()
ref.fixNoRepo()
}
// fixNoTag is a fix for ref strings such as "mychart:1.0.0", which result in missing tag
func (ref *Reference) fixNoTag() {
if ref.Tag == "" {
parts := strings.Split(ref.Repo, ":")
numParts := len(parts)
if 0 < numParts {
lastIndex := numParts - 1
lastPart := parts[lastIndex]
if !strings.Contains(lastPart, "/") {
ref.Repo = strings.Join(parts[:lastIndex], ":")
ref.Tag = lastPart
}
}
}
}
// fixNoRepo is a fix for ref strings such as "mychart", which have the repo swapped with tag
func (ref *Reference) fixNoRepo() {
if ref.Repo == "" {
ref.Repo = ref.Tag
ref.Tag = ""
}
}
// validate makes sure the ref meets our criteria // validate makes sure the ref meets our criteria
func (ref *Reference) validate() error { func (ref *Reference) validate() error {
err := ref.validateRepo() err := ref.validateRepo()
@ -114,7 +97,10 @@ func (ref *Reference) validateRepo() error {
if ref.Repo == "" { if ref.Repo == "" {
return errEmptyRepo return errEmptyRepo
} }
return nil // Makes sure the repo results in a parsable URL (similar to what is done
// with containerd reference parsing)
_, err := url.Parse(ref.Repo)
return err
} }
// validateNumColon ensures the ref only contains a single colon character (:) // validateNumColon ensures the ref only contains a single colon character (:)

@ -52,7 +52,7 @@ func TestChartSave(t *testing.T) {
t.Error(err) t.Error(err)
} }
ref, err = registry.ParseReference("localhost:5000/test:0.1.0") ref, err = registry.ParseReference("localhost:5000/test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

Loading…
Cancel
Save