Skip to content

Commit

Permalink
Allow uppercase characters in image reference hostname
Browse files Browse the repository at this point in the history
This PR makes restores the pre-Docker 1.10 behavior of allowing
uppercase characters in registry hostnames.

Note that this only applies to hostnames, not remote image names.
Previous versions also prohibited uppercase letters after the hostname,
but Docker 1.10 extended this to the hostname itself.

- Vendor updated docker/distribution.

- Add a check to "normalize" that rejects remote names with uppercase
  letters.

- Add test cases to TestTagValidPrefixedRepo and
  TestTagInvalidUnprefixedRepo

Fixes: moby#20056

Signed-off-by: Aaron Lehmann <[email protected]>
  • Loading branch information
aaronlehmann committed Feb 10, 2016
1 parent fa860c8 commit e2afab9
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 12 deletions.
2 changes: 1 addition & 1 deletion hack/vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ clone git github.com/boltdb/bolt v1.1.0
clone git github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7

# get graph and distribution packages
clone git github.com/docker/distribution ab9b433fcaf7c8319562a8b80f2720f5faca712f
clone git github.com/docker/distribution 77534e734063a203981df7024fe8ca9228b86930
clone git github.com/vbatts/tar-split v0.9.11

# get desired notary commit, might also need to be updated in Dockerfile
Expand Down
4 changes: 2 additions & 2 deletions integration-cli/docker_cli_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (s *DockerSuite) TestTagUnprefixedRepoByID(c *check.C) {

// ensure we don't allow the use of invalid repository names; these tag operations should fail
func (s *DockerSuite) TestTagInvalidUnprefixedRepo(c *check.C) {
invalidRepos := []string{"fo$z$", "Foo@3cc", "Foo$3", "Foo*3", "Fo^3", "Foo!3", "F)xcz(", "fo%asd"}
invalidRepos := []string{"fo$z$", "Foo@3cc", "Foo$3", "Foo*3", "Fo^3", "Foo!3", "F)xcz(", "fo%asd", "FOO/bar"}

for _, repo := range invalidRepos {
out, _, err := dockerCmdWithError("tag", "busybox", repo)
Expand Down Expand Up @@ -61,7 +61,7 @@ func (s *DockerSuite) TestTagValidPrefixedRepo(c *check.C) {
}
}

validRepos := []string{"fooo/bar", "fooaa/test", "foooo:t"}
validRepos := []string{"fooo/bar", "fooaa/test", "foooo:t", "HOSTNAME.DOMAIN.COM:443/foo/bar"}

for _, repo := range validRepos {
_, _, err := dockerCmdWithError("tag", "busybox:latest", repo)
Expand Down
17 changes: 12 additions & 5 deletions reference/reference.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reference

import (
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -72,7 +73,10 @@ func ParseNamed(s string) (Named, error) {
// WithName returns a named object representing the given string. If the input
// is invalid ErrReferenceInvalidFormat will be returned.
func WithName(name string) (Named, error) {
name = normalize(name)
name, err := normalize(name)
if err != nil {
return nil, err
}
if err := validateName(name); err != nil {
return nil, err
}
Expand Down Expand Up @@ -172,15 +176,18 @@ func splitHostname(name string) (hostname, remoteName string) {

// normalize returns a repository name in its normalized form, meaning it
// will not contain default hostname nor library/ prefix for official images.
func normalize(name string) string {
func normalize(name string) (string, error) {
host, remoteName := splitHostname(name)
if strings.ToLower(remoteName) != remoteName {
return "", errors.New("invalid reference format: repository name must be lowercase")
}
if host == DefaultHostname {
if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
return strings.TrimPrefix(remoteName, DefaultRepoPrefix)
return strings.TrimPrefix(remoteName, DefaultRepoPrefix), nil
}
return remoteName
return remoteName, nil
}
return name
return name, nil
}

func validateName(name string) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// reference := repository [ ":" tag ] [ "@" digest ]
// name := [hostname '/'] component ['/' component]*
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
// hostcomponent := /([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])/
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
// port-number := /[0-9]+/
// component := alpha-numeric [separator alpha-numeric]*
// alpha-numeric := /[a-z0-9]+/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var (
// hostnameComponentRegexp restricts the registry hostname component of a
// repository name to start with a component as defined by hostnameRegexp
// and followed by an optional port.
hostnameComponentRegexp = match(`(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])`)
hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)

// hostnameRegexp defines the structure of potential hostname components
// that may be part of image names. This is purposely a subset of what is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,21 @@ func checkHTTPRedirect(req *http.Request, via []*http.Request) error {

if len(via) > 0 {
for headerName, headerVals := range via[0].Header {
if headerName == "Accept" || headerName == "Range" {
for _, val := range headerVals {
if headerName != "Accept" && headerName != "Range" {
continue
}
for _, val := range headerVals {
// Don't add to redirected request if redirected
// request already has a header with the same
// name and value.
hasValue := false
for _, existingVal := range req.Header[headerName] {
if existingVal == val {
hasValue = true
break
}
}
if !hasValue {
req.Header.Add(headerName, val)
}
}
Expand Down

0 comments on commit e2afab9

Please sign in to comment.