func TestParseImageAndDockerReference(t *testing.T) {
	const (
		ok1  = "busybox"
		ok2  = fullRHELRef
		bad1 = "UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES"
		bad2 = ""
	)
	// Success
	ref, err := reference.ParseNamed(ok1)
	require.NoError(t, err)
	r1, r2, err := parseImageAndDockerReference(refImageMock{ref}, ok2)
	require.NoError(t, err)
	assert.Equal(t, ok1, r1.String())
	assert.Equal(t, ok2, r2.String())

	// Unidentified images are rejected.
	_, _, err = parseImageAndDockerReference(refImageMock{nil}, ok2)
	require.Error(t, err)
	assert.IsType(t, PolicyRequirementError(""), err)

	// Failures
	for _, refs := range [][]string{
		{bad1, ok2},
		{ok1, bad2},
		{bad1, bad2},
	} {
		ref, err := reference.ParseNamed(refs[0])
		if err == nil {
			_, _, err := parseImageAndDockerReference(refImageMock{ref}, refs[1])
			assert.Error(t, err)
		}
	}
}
Example #2
0
// ParseStoreReference takes a name or an ID, tries to figure out which it is
// relative to the given store, and returns it in a reference object.
func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (*storageReference, error) {
	var name reference.Named
	var sum digest.Digest
	var err error
	if ref == "" {
		return nil, ErrInvalidReference
	}
	if ref[0] == '[' {
		// Ignore the store specifier.
		closeIndex := strings.IndexRune(ref, ']')
		if closeIndex < 1 {
			return nil, ErrInvalidReference
		}
		ref = ref[closeIndex+1:]
	}
	refInfo := strings.SplitN(ref, "@", 2)
	if len(refInfo) == 1 {
		// A name.
		name, err = reference.ParseNamed(refInfo[0])
		if err != nil {
			return nil, err
		}
	} else if len(refInfo) == 2 {
		// An ID, possibly preceded by a name.
		if refInfo[0] != "" {
			name, err = reference.ParseNamed(refInfo[0])
			if err != nil {
				return nil, err
			}
		}
		sum, err = digest.Parse("sha256:" + refInfo[1])
		if err != nil {
			return nil, err
		}
	} else { // Coverage: len(refInfo) is always 1 or 2
		// Anything else: store specified in a form we don't
		// recognize.
		return nil, ErrInvalidReference
	}
	storeSpec := "[" + store.GetGraphDriverName() + "@" + store.GetGraphRoot() + "]"
	id := ""
	if sum.Validate() == nil {
		id = sum.Hex()
	}
	refname := ""
	if name != nil {
		name = reference.WithDefaultTag(name)
		refname = verboseName(name)
	}
	if refname == "" {
		logrus.Debugf("parsed reference into %q", storeSpec+"@"+id)
	} else if id == "" {
		logrus.Debugf("parsed reference into %q", storeSpec+refname)
	} else {
		logrus.Debugf("parsed reference into %q", storeSpec+refname+"@"+id)
	}
	return newReference(storageTransport{store: store}, refname, id, name), nil
}
Example #3
0
func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
	// Check that there's a store location prefix.  Values we're passed are
	// expected to come from PolicyConfigurationIdentity or
	// PolicyConfigurationNamespaces, so if there's no store location,
	// something's wrong.
	if scope[0] != '[' {
		return ErrInvalidReference
	}
	// Parse the store location prefix.
	closeIndex := strings.IndexRune(scope, ']')
	if closeIndex < 1 {
		return ErrInvalidReference
	}
	storeSpec := scope[1:closeIndex]
	scope = scope[closeIndex+1:]
	storeInfo := strings.SplitN(storeSpec, "@", 2)
	if len(storeInfo) == 1 && storeInfo[0] != "" {
		// One component: the graph root.
		if !filepath.IsAbs(storeInfo[0]) {
			return ErrPathNotAbsolute
		}
	} else if len(storeInfo) == 2 && storeInfo[0] != "" && storeInfo[1] != "" {
		// Two components: the driver type and the graph root.
		if !filepath.IsAbs(storeInfo[1]) {
			return ErrPathNotAbsolute
		}
	} else {
		// Anything else: store specified in a form we don't
		// recognize.
		return ErrInvalidReference
	}
	// That might be all of it, and that's okay.
	if scope == "" {
		return nil
	}
	// But if there is anything left, it has to be a name, with or without
	// a tag, with or without an ID, since we don't return namespace values
	// that are just bare IDs.
	scopeInfo := strings.SplitN(scope, "@", 2)
	if len(scopeInfo) == 1 && scopeInfo[0] != "" {
		_, err := reference.ParseNamed(scopeInfo[0])
		if err != nil {
			return err
		}
	} else if len(scopeInfo) == 2 && scopeInfo[0] != "" && scopeInfo[1] != "" {
		_, err := reference.ParseNamed(scopeInfo[0])
		if err != nil {
			return err
		}
		_, err = ddigest.Parse("sha256:" + scopeInfo[1])
		if err != nil {
			return err
		}
	} else {
		return ErrInvalidReference
	}
	return nil
}
func TestNewReference(t *testing.T) {
	// An ID reference.
	id, err := digest.Parse(sha256digest)
	require.NoError(t, err)
	ref, err := NewReference(id, nil)
	require.NoError(t, err)
	daemonRef, ok := ref.(daemonReference)
	require.True(t, ok)
	assert.Equal(t, id, daemonRef.id)
	assert.Nil(t, daemonRef.ref)

	// Named references
	for _, c := range validNamedReferenceTestCases {
		parsed, err := reference.ParseNamed(c.input)
		require.NoError(t, err)
		ref, err := NewReference("", parsed)
		require.NoError(t, err, c.input)
		daemonRef, ok := ref.(daemonReference)
		require.True(t, ok, c.input)
		assert.Equal(t, "", daemonRef.id.String())
		require.NotNil(t, daemonRef.ref)
		assert.Equal(t, c.dockerRef, daemonRef.ref.String(), c.input)
	}

	// Both an ID and a named reference provided
	parsed, err := reference.ParseNamed("busybox:latest")
	require.NoError(t, err)
	_, err = NewReference(id, parsed)
	assert.Error(t, err)

	// A reference with neither a tag nor digest
	parsed, err = reference.ParseNamed("busybox")
	require.NoError(t, err)
	_, err = NewReference("", parsed)
	assert.Error(t, err)

	// A github.com/distribution/reference value can have a tag and a digest at the same time!
	parsed, err = reference.ParseNamed("busybox@" + sha256digest)
	require.NoError(t, err)
	refDigested, ok := parsed.(reference.Canonical)
	require.True(t, ok)
	tagDigestRef := refWithTagAndDigest{refDigested}
	_, err = NewReference("", tagDigestRef)
	assert.Error(t, err)
}
Example #5
0
// newPRMExactRepository is NewPRMExactRepository, except it resturns the private type.
func newPRMExactRepository(dockerRepository string) (*prmExactRepository, error) {
	if _, err := reference.ParseNamed(dockerRepository); err != nil {
		return nil, InvalidPolicyFormatError(fmt.Sprintf("Invalid format of dockerRepository %s: %s", dockerRepository, err.Error()))
	}
	return &prmExactRepository{
		prmCommon:        prmCommon{Type: prmTypeExactRepository},
		DockerRepository: dockerRepository,
	}, nil
}
func testImageAndSig(t *testing.T, prm PolicyReferenceMatch, imageRef, sigRef string, result bool) {
	// This assumes that all ways to obtain a reference.Named perform equivalent validation,
	// and therefore values refused by reference.ParseNamed can not happen in practice.
	parsedImageRef, err := reference.ParseNamed(imageRef)
	if err != nil {
		return
	}
	res := prm.matchesDockerReference(refImageMock{parsedImageRef}, sigRef)
	assert.Equal(t, result, res, fmt.Sprintf("%s vs. %s", imageRef, sigRef))
}
Example #7
0
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an Docker ImageReference.
func ParseReference(refString string) (types.ImageReference, error) {
	if !strings.HasPrefix(refString, "//") {
		return nil, errors.Errorf("docker: image reference %s does not start with //", refString)
	}
	ref, err := reference.ParseNamed(strings.TrimPrefix(refString, "//"))
	if err != nil {
		return nil, err
	}
	ref = reference.WithDefaultTag(ref)
	return NewReference(ref)
}
Example #8
0
// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OpenShift ImageReference.
func ParseReference(ref string) (types.ImageReference, error) {
	r, err := reference.ParseNamed(ref)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse image reference %q", ref)
	}
	tagged, ok := r.(reference.NamedTagged)
	if !ok {
		return nil, errors.Errorf("invalid image reference %s, %#v", ref, r)
	}
	return NewReference(tagged)
}
Example #9
0
// newPRMExactReference is NewPRMExactReference, except it resturns the private type.
func newPRMExactReference(dockerReference string) (*prmExactReference, error) {
	ref, err := reference.ParseNamed(dockerReference)
	if err != nil {
		return nil, InvalidPolicyFormatError(fmt.Sprintf("Invalid format of dockerReference %s: %s", dockerReference, err.Error()))
	}
	if reference.IsNameOnly(ref) {
		return nil, InvalidPolicyFormatError(fmt.Sprintf("dockerReference %s contains neither a tag nor digest", dockerReference))
	}
	return &prmExactReference{
		prmCommon:       prmCommon{Type: prmTypeExactReference},
		DockerReference: dockerReference,
	}, nil
}
Example #10
0
func newOCI1ImageSource(t *testing.T, dockerRef string) *oci1ImageSource {
	realConfigJSON, err := ioutil.ReadFile("fixtures/oci1-config.json")
	require.NoError(t, err)

	ref, err := reference.ParseNamed(dockerRef)
	require.NoError(t, err)

	return &oci1ImageSource{
		configBlobImageSource: configBlobImageSource{
			f: func(digest digest.Digest) (io.ReadCloser, int64, error) {
				return ioutil.NopCloser(bytes.NewReader(realConfigJSON)), int64(len(realConfigJSON)), nil
			},
		},
		ref: ref,
	}
}
func TestTransportParseStoreReference(t *testing.T) {
	for _, c := range []struct{ input, expectedRef, expectedID string }{
		{"", "", ""}, // Empty input
		// Handling of the store prefix
		// FIXME? Should we be silently discarding input like this?
		{"[unterminated", "", ""},                                    // Unterminated store specifier
		{"[garbage]busybox", "docker.io/library/busybox:latest", ""}, // Store specifier is overridden by the store we pass to ParseStoreReference

		{"UPPERCASEISINVALID", "", ""},                                                     // Invalid single-component name
		{"sha256:" + sha256digestHex, "docker.io/library/sha256:" + sha256digestHex, ""},   // Valid single-component name; the hex part is not an ID unless it has a "@" prefix
		{sha256digestHex, "", ""},                                                          // Invalid single-component ID; not an ID without a "@" prefix, so it's parsed as a name, but names aren't allowed to look like IDs
		{"@" + sha256digestHex, "", sha256digestHex},                                       // Valid single-component ID
		{"sha256:ab", "docker.io/library/sha256:ab", ""},                                   // Valid single-component name, explicit tag
		{"busybox", "docker.io/library/busybox:latest", ""},                                // Valid single-component name, implicit tag
		{"busybox:notlatest", "docker.io/library/busybox:notlatest", ""},                   // Valid single-component name, explicit tag
		{"docker.io/library/busybox:notlatest", "docker.io/library/busybox:notlatest", ""}, // Valid single-component name, everything explicit

		{"UPPERCASEISINVALID@" + sha256digestHex, "", ""},                                                                  // Invalid name in name@ID
		{"busybox@ab", "", ""},                                                                                             // Invalid ID in name@ID
		{"busybox@", "", ""},                                                                                               // Empty ID in name@ID
		{"busybox@sha256:" + sha256digestHex, "", ""},                                                                      // This (a digested docker/docker reference format) is also invalid, since it's an invalid ID in name@ID
		{"@" + sha256digestHex, "", sha256digestHex},                                                                       // Valid two-component name, with ID only
		{"busybox@" + sha256digestHex, "docker.io/library/busybox:latest", sha256digestHex},                                // Valid two-component name, implicit tag
		{"busybox:notlatest@" + sha256digestHex, "docker.io/library/busybox:notlatest", sha256digestHex},                   // Valid two-component name, explicit tag
		{"docker.io/library/busybox:notlatest@" + sha256digestHex, "docker.io/library/busybox:notlatest", sha256digestHex}, // Valid two-component name, everything explicit
	} {
		storageRef, err := Transport.ParseStoreReference(Transport.(*storageTransport).store, c.input)
		if c.expectedRef == "" && c.expectedID == "" {
			assert.Error(t, err, c.input)
		} else {
			require.NoError(t, err, c.input)
			assert.Equal(t, *(Transport.(*storageTransport)), storageRef.transport, c.input)
			assert.Equal(t, c.expectedRef, storageRef.reference, c.input)
			assert.Equal(t, c.expectedID, storageRef.id, c.input)
			if c.expectedRef == "" {
				assert.Nil(t, storageRef.name, c.input)
			} else {
				dockerRef, err := reference.ParseNamed(c.expectedRef)
				require.NoError(t, err)
				require.NotNil(t, storageRef.name, c.input)
				assert.Equal(t, dockerRef.String(), storageRef.name.String())
			}
		}
	}
}
// dirImageMock returns a types.UnparsedImage for a directory, claiming a specified dockerReference.
// The caller must call .Close() on the returned UnparsedImage.
func dirImageMock(t *testing.T, dir, dockerReference string) types.UnparsedImage {
	ref, err := reference.ParseNamed(dockerReference)
	require.NoError(t, err)
	return dirImageMockWithRef(t, dir, refImageReferenceMock{ref})
}
Example #13
0
func TestPolicyContextRequirementsForImageRef(t *testing.T) {
	ktGPG := SBKeyTypeGPGKeys
	prm := NewPRMMatchRepoDigestOrExact()

	policy := &Policy{
		Default:    PolicyRequirements{NewPRReject()},
		Transports: map[string]PolicyTransportScopes{},
	}
	// Just put _something_ into the PolicyTransportScopes map for the keys we care about, and make it pairwise
	// distinct so that we can compare the values and show them when debugging the tests.
	for _, t := range []struct{ transport, scope string }{
		{"docker", ""},
		{"docker", "unmatched"},
		{"docker", "deep.com"},
		{"docker", "deep.com/n1"},
		{"docker", "deep.com/n1/n2"},
		{"docker", "deep.com/n1/n2/n3"},
		{"docker", "deep.com/n1/n2/n3/repo"},
		{"docker", "deep.com/n1/n2/n3/repo:tag2"},
		{"atomic", "unmatched"},
	} {
		if _, ok := policy.Transports[t.transport]; !ok {
			policy.Transports[t.transport] = PolicyTransportScopes{}
		}
		policy.Transports[t.transport][t.scope] = PolicyRequirements{xNewPRSignedByKeyData(ktGPG, []byte(t.transport+t.scope), prm)}
	}

	pc, err := NewPolicyContext(policy)
	require.NoError(t, err)

	for _, c := range []struct{ inputTransport, input, matchedTransport, matched string }{
		// Full match
		{"docker", "deep.com/n1/n2/n3/repo:tag2", "docker", "deep.com/n1/n2/n3/repo:tag2"},
		// Namespace matches
		{"docker", "deep.com/n1/n2/n3/repo:nottag2", "docker", "deep.com/n1/n2/n3/repo"},
		{"docker", "deep.com/n1/n2/n3/notrepo:tag2", "docker", "deep.com/n1/n2/n3"},
		{"docker", "deep.com/n1/n2/notn3/repo:tag2", "docker", "deep.com/n1/n2"},
		{"docker", "deep.com/n1/notn2/n3/repo:tag2", "docker", "deep.com/n1"},
		// Host name match
		{"docker", "deep.com/notn1/n2/n3/repo:tag2", "docker", "deep.com"},
		// Default
		{"docker", "this.doesnt/match:anything", "docker", ""},
		// No match within a matched transport which doesn't have a "" scope
		{"atomic", "this.doesnt/match:anything", "", ""},
		// No configuration available for this transport at all
		{"dir", "what/ever", "", ""}, // "what/ever" is not a valid scope for the real "dir" transport, but we only need it to be a valid reference.Named.
	} {
		var expected PolicyRequirements
		if c.matchedTransport != "" {
			e, ok := policy.Transports[c.matchedTransport][c.matched]
			require.True(t, ok, fmt.Sprintf("case %s:%s: expected reqs not found", c.inputTransport, c.input))
			expected = e
		} else {
			expected = policy.Default
		}

		ref, err := reference.ParseNamed(c.input)
		require.NoError(t, err)
		reqs := pc.requirementsForImageRef(pcImageReferenceMock{c.inputTransport, ref})
		comment := fmt.Sprintf("case %s:%s: %#v", c.inputTransport, c.input, reqs[0])
		// Do not use assert.Equal, which would do a deep contents comparison; we want to compare
		// the pointers. Also, == does not work on slices; so test that the slices start at the
		// same element and have the same length.
		assert.True(t, &(reqs[0]) == &(expected[0]), comment)
		assert.True(t, len(reqs) == len(expected), comment)
	}
}