vendor: remove dep and use vndr
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
16f44674a4
commit
148e72d81e
16131 changed files with 73815 additions and 4235138 deletions
108
vendor/github.com/containers/image/signature/docker_test.go
generated
vendored
108
vendor/github.com/containers/image/signature/docker_test.go
generated
vendored
|
@ -1,108 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSignDockerManifest(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
if err := mech.SupportsSigning(); err != nil {
|
||||
t.Skipf("Signing not supported: %v", err)
|
||||
}
|
||||
|
||||
manifest, err := ioutil.ReadFile("fixtures/image.manifest.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful signing
|
||||
signature, err := SignDockerManifest(manifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
verified, err := VerifyDockerManifestSignature(signature, manifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, verified.DockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, verified.DockerManifestDigest)
|
||||
|
||||
// Error computing Docker manifest
|
||||
invalidManifest, err := ioutil.ReadFile("fixtures/v2s1-invalid-signatures.manifest.json")
|
||||
require.NoError(t, err)
|
||||
_, err = SignDockerManifest(invalidManifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Error creating blob to sign
|
||||
_, err = SignDockerManifest(manifest, "", mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Error signing
|
||||
_, err = SignDockerManifest(manifest, TestImageSignatureReference, mech, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyDockerManifestSignature(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
manifest, err := ioutil.ReadFile("fixtures/image.manifest.json")
|
||||
require.NoError(t, err)
|
||||
signature, err := ioutil.ReadFile("fixtures/image.signature")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful verification
|
||||
sig, err := VerifyDockerManifestSignature(signature, manifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, sig.DockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, sig.DockerManifestDigest)
|
||||
|
||||
// Verification using a different canonicalization of TestImageSignatureReference
|
||||
sig, err = VerifyDockerManifestSignature(signature, manifest, "docker.io/"+TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, sig.DockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, sig.DockerManifestDigest)
|
||||
|
||||
// For extra paranoia, test that we return nil data on error.
|
||||
|
||||
// Invalid docker reference on input
|
||||
sig, err = VerifyDockerManifestSignature(signature, manifest, "UPPERCASEISINVALID", mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Error computing Docker manifest
|
||||
invalidManifest, err := ioutil.ReadFile("fixtures/v2s1-invalid-signatures.manifest.json")
|
||||
require.NoError(t, err)
|
||||
sig, err = VerifyDockerManifestSignature(signature, invalidManifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Error verifying signature
|
||||
corruptSignature, err := ioutil.ReadFile("fixtures/corrupt.signature")
|
||||
sig, err = VerifyDockerManifestSignature(corruptSignature, manifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Key fingerprint mismatch
|
||||
sig, err = VerifyDockerManifestSignature(signature, manifest, TestImageSignatureReference, mech, "unexpected fingerprint")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Invalid reference in the signature
|
||||
invalidReferenceSignature, err := ioutil.ReadFile("fixtures/invalid-reference.signature")
|
||||
sig, err = VerifyDockerManifestSignature(invalidReferenceSignature, manifest, TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Docker reference mismatch
|
||||
sig, err = VerifyDockerManifestSignature(signature, manifest, "example.com/doesnt/match", mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
|
||||
// Docker manifest digest mismatch
|
||||
sig, err = VerifyDockerManifestSignature(signature, []byte("unexpected manifest"), TestImageSignatureReference, mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
}
|
6
vendor/github.com/containers/image/signature/fixtures/.gitignore
generated
vendored
6
vendor/github.com/containers/image/signature/fixtures/.gitignore
generated
vendored
|
@ -1,6 +0,0 @@
|
|||
/*.gpg~
|
||||
/.gpg-v21-migrated
|
||||
/private-keys-v1.d
|
||||
/random_seed
|
||||
/gnupg_spawn_agent_sentinel.lock
|
||||
/.#*
|
BIN
vendor/github.com/containers/image/signature/fixtures/corrupt.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/corrupt.signature
generated
vendored
Binary file not shown.
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 7023,
|
||||
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 32654,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 16724,
|
||||
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 73109,
|
||||
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
||||
}
|
||||
],
|
||||
"extra": "this manifest has been modified"
|
||||
}
|
BIN
vendor/github.com/containers/image/signature/fixtures/dir-img-valid-2/signature-2
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/dir-img-valid-2/signature-2
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/dir-img-valid/signature-1
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/dir-img-valid/signature-1
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/double.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/double.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/expired.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/expired.signature
generated
vendored
Binary file not shown.
26
vendor/github.com/containers/image/signature/fixtures/image.manifest.json
generated
vendored
26
vendor/github.com/containers/image/signature/fixtures/image.manifest.json
generated
vendored
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 7023,
|
||||
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 32654,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 16724,
|
||||
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 73109,
|
||||
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
||||
}
|
||||
]
|
||||
}
|
BIN
vendor/github.com/containers/image/signature/fixtures/image.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/image.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/invalid-blob.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/invalid-blob.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/invalid-reference.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/invalid-reference.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/no-optional-fields.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/no-optional-fields.signature
generated
vendored
Binary file not shown.
96
vendor/github.com/containers/image/signature/fixtures/policy.json
generated
vendored
96
vendor/github.com/containers/image/signature/fixtures/policy.json
generated
vendored
|
@ -1,96 +0,0 @@
|
|||
{
|
||||
"default": [
|
||||
{
|
||||
"type": "reject"
|
||||
}
|
||||
],
|
||||
"transports": {
|
||||
"dir": {
|
||||
"": [
|
||||
{
|
||||
"type": "insecureAcceptAnything"
|
||||
}
|
||||
]
|
||||
},
|
||||
"docker": {
|
||||
"example.com/playground": [
|
||||
{
|
||||
"type": "insecureAcceptAnything"
|
||||
}
|
||||
],
|
||||
"example.com/production": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "/keys/employee-gpg-keyring"
|
||||
}
|
||||
],
|
||||
"example.com/hardened": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "/keys/employee-gpg-keyring",
|
||||
"signedIdentity": {
|
||||
"type": "matchRepository"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "signedByGPGKeys",
|
||||
"keyPath": "/keys/public-key-signing-gpg-keyring",
|
||||
"signedIdentity": {
|
||||
"type": "matchExact"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "signedBaseLayer",
|
||||
"baseLayerIdentity": {
|
||||
"type": "exactRepository",
|
||||
"dockerRepository": "registry.access.redhat.com/rhel7/rhel"
|
||||
}
|
||||
}
|
||||
],
|
||||
"example.com/hardened-x509": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "X509Certificates",
|
||||
"keyPath": "/keys/employee-cert-file",
|
||||
"signedIdentity": {
|
||||
"type": "matchRepository"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "signedByX509CAs",
|
||||
"keyPath": "/keys/public-key-signing-ca-file"
|
||||
}
|
||||
],
|
||||
"registry.access.redhat.com": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "signedByGPGKeys",
|
||||
"keyPath": "/keys/RH-key-signing-key-gpg-keyring",
|
||||
"signedIdentity": {
|
||||
"type": "matchRepoDigestOrExact"
|
||||
}
|
||||
}
|
||||
],
|
||||
"bogus/key-data-example": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "signedByGPGKeys",
|
||||
"keyData": "bm9uc2Vuc2U="
|
||||
}
|
||||
],
|
||||
"bogus/signed-identity-example": [
|
||||
{
|
||||
"type": "signedBaseLayer",
|
||||
"baseLayerIdentity": {
|
||||
"type": "exactReference",
|
||||
"dockerReference": "registry.access.redhat.com/rhel7/rhel:latest"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
19
vendor/github.com/containers/image/signature/fixtures/public-key.gpg
generated
vendored
19
vendor/github.com/containers/image/signature/fixtures/public-key.gpg
generated
vendored
|
@ -1,19 +0,0 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
mI0EVurzqQEEAL3qkFq4K2URtSWVDYnQUNA9HdM9sqS2eAWfqUFMrkD5f+oN+LBL
|
||||
tPyaE5GNLA0vXY7nHAM2TeM8ijZ/eMP17Raj64JL8GhCymL3wn2jNvb9XaF0R0s6
|
||||
H0IaRPPu45A3SnxLwm4Orc/9Z7/UxtYjKSg9xOaTiVPzJgaf5Vm4J4ApABEBAAG0
|
||||
EnNrb3BlbyB0ZXN0aW5nIGtleYi4BBMBAgAiBQJW6vOpAhsDBgsJCAcDAgYVCAIJ
|
||||
CgsEFgIDAQIeAQIXgAAKCRDbcvIYi7RsyBbOBACgJFiKDlQ1UyvsNmGqJ7D0OpbS
|
||||
1OppJlradKgZXyfahFswhFI+7ZREvELLHbinq3dBy5cLXRWzQKdJZNHknSN5Tjf2
|
||||
0ipVBQuqpcBo+dnKiG4zH6fhTri7yeTZksIDfsqlI6FXDOdKLUSnahagEBn4yU+x
|
||||
jHPvZk5SuuZv56A45biNBFbq86kBBADIC/9CsAlOmRALuYUmkhcqEjuFwn3wKz2d
|
||||
IBjzgvro7zcVNNCgxQfMEjcUsvEh5cx13G3QQHcwOKy3M6Bv6VMhfZjd+1P1el4P
|
||||
0fJS8GFmhWRBknMN8jFsgyohQeouQ798RFFv94KszfStNnr/ae8oao5URmoUXSCa
|
||||
/MdUxn0YKwARAQABiJ8EGAECAAkFAlbq86kCGwwACgkQ23LyGIu0bMjUywQAq0dn
|
||||
lUpDNSoLTcpNWuVvHQ7c/qmnE4TyiSLiRiAywdEWA6gMiyhUUucuGsEhMFP1WX1k
|
||||
UNwArZ6UG7BDOUsvngP7jKGNqyUOQrq1s/r8D+0MrJGOWErGLlfttO2WeoijECkI
|
||||
5qm8cXzAra3Xf/Z3VjxYTKSnNu37LtZkakdTdYE=
|
||||
=tJAt
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
BIN
vendor/github.com/containers/image/signature/fixtures/pubring.gpg
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/pubring.gpg
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/secring.gpg
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/secring.gpg
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/trustdb.gpg
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/trustdb.gpg
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/unknown-key.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/unknown-key.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/unsigned-encrypted.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/unsigned-encrypted.signature
generated
vendored
Binary file not shown.
BIN
vendor/github.com/containers/image/signature/fixtures/unsigned-literal.signature
generated
vendored
BIN
vendor/github.com/containers/image/signature/fixtures/unsigned-literal.signature
generated
vendored
Binary file not shown.
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "mitr/buxybox",
|
||||
"tag": "latest",
|
||||
"architecture": "amd64",
|
||||
"fsLayers": [
|
||||
],
|
||||
"history": [
|
||||
],
|
||||
"signatures": 1
|
||||
}
|
14
vendor/github.com/containers/image/signature/fixtures_info_test.go
generated
vendored
14
vendor/github.com/containers/image/signature/fixtures_info_test.go
generated
vendored
|
@ -1,14 +0,0 @@
|
|||
package signature
|
||||
|
||||
import "github.com/opencontainers/go-digest"
|
||||
|
||||
const (
|
||||
// TestImageManifestDigest is the Docker manifest digest of "image.manifest.json"
|
||||
TestImageManifestDigest = digest.Digest("sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55")
|
||||
// TestImageSignatureReference is the Docker image reference signed in "image.signature"
|
||||
TestImageSignatureReference = "testing/manifest"
|
||||
// TestKeyFingerprint is the fingerprint of the private key in this directory.
|
||||
TestKeyFingerprint = "1D8230F6CDB6A06716E414C1DB72F2188BB46CC8"
|
||||
// TestKeyShortID is the short ID of the private key in this directory.
|
||||
TestKeyShortID = "DB72F2188BB46CC8"
|
||||
)
|
137
vendor/github.com/containers/image/signature/json_test.go
generated
vendored
137
vendor/github.com/containers/image/signature/json_test.go
generated
vendored
|
@ -1,137 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type mSI map[string]interface{} // To minimize typing the long name
|
||||
|
||||
// A short-hand way to get a JSON object field value or panic. No error handling done, we know
|
||||
// what we are working with, a panic in a test is good enough, and fitting test cases on a single line
|
||||
// is a priority.
|
||||
func x(m mSI, fields ...string) mSI {
|
||||
for _, field := range fields {
|
||||
// Not .(mSI) because type assertion of an unnamed type to a named type always fails (the types
|
||||
// are not "identical"), but the assignment is fine because they are "assignable".
|
||||
m = m[field].(map[string]interface{})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// implementsUnmarshalJSON is a minimalistic type used to detect that
|
||||
// paranoidUnmarshalJSONObject uses the json.Unmarshaler interface of resolved
|
||||
// pointers.
|
||||
type implementsUnmarshalJSON bool
|
||||
|
||||
// Compile-time check that Policy implements json.Unmarshaler.
|
||||
var _ json.Unmarshaler = (*implementsUnmarshalJSON)(nil)
|
||||
|
||||
func (dest *implementsUnmarshalJSON) UnmarshalJSON(data []byte) error {
|
||||
_ = data // We don't care, not really.
|
||||
*dest = true // Mark handler as called
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestParanoidUnmarshalJSONObject(t *testing.T) {
|
||||
type testStruct struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
ts := testStruct{}
|
||||
var unmarshalJSONCalled implementsUnmarshalJSON
|
||||
tsResolver := func(key string) interface{} {
|
||||
switch key {
|
||||
case "a":
|
||||
return &ts.A
|
||||
case "b":
|
||||
return &ts.B
|
||||
case "implementsUnmarshalJSON":
|
||||
return &unmarshalJSONCalled
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Empty object
|
||||
ts = testStruct{}
|
||||
err := paranoidUnmarshalJSONObject([]byte(`{}`), tsResolver)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testStruct{}, ts)
|
||||
|
||||
// Success
|
||||
ts = testStruct{}
|
||||
err = paranoidUnmarshalJSONObject([]byte(`{"a":"x", "b":2}`), tsResolver)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testStruct{A: "x", B: 2}, ts)
|
||||
|
||||
// json.Unamarshaler is used for decoding values
|
||||
ts = testStruct{}
|
||||
unmarshalJSONCalled = implementsUnmarshalJSON(false)
|
||||
err = paranoidUnmarshalJSONObject([]byte(`{"implementsUnmarshalJSON":true}`), tsResolver)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, unmarshalJSONCalled, implementsUnmarshalJSON(true))
|
||||
|
||||
// Various kinds of invalid input
|
||||
for _, input := range []string{
|
||||
``, // Empty input
|
||||
`&`, // Entirely invalid JSON
|
||||
`1`, // Not an object
|
||||
`{&}`, // Invalid key JSON
|
||||
`{1:1}`, // Key not a string
|
||||
`{"b":1, "b":1}`, // Duplicate key
|
||||
`{"thisdoesnotexist":1}`, // Key rejected by resolver
|
||||
`{"a":&}`, // Invalid value JSON
|
||||
`{"a":1}`, // Type mismatch
|
||||
`{"a":"value"}{}`, // Extra data after object
|
||||
} {
|
||||
ts = testStruct{}
|
||||
err := paranoidUnmarshalJSONObject([]byte(input), tsResolver)
|
||||
assert.Error(t, err, input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParanoidUnmarshalJSONObjectExactFields(t *testing.T) {
|
||||
var stringValue string
|
||||
var float64Value float64
|
||||
var rawValue json.RawMessage
|
||||
var unmarshallCalled implementsUnmarshalJSON
|
||||
exactFields := map[string]interface{}{
|
||||
"string": &stringValue,
|
||||
"float64": &float64Value,
|
||||
"raw": &rawValue,
|
||||
"unmarshaller": &unmarshallCalled,
|
||||
}
|
||||
|
||||
// Empty object
|
||||
err := paranoidUnmarshalJSONObjectExactFields([]byte(`{}`), map[string]interface{}{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Success
|
||||
err = paranoidUnmarshalJSONObjectExactFields([]byte(`{"string": "a", "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true}`), exactFields)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "a", stringValue)
|
||||
assert.Equal(t, 3.5, float64Value)
|
||||
assert.Equal(t, json.RawMessage(`{"a":"b"}`), rawValue)
|
||||
assert.Equal(t, implementsUnmarshalJSON(true), unmarshallCalled)
|
||||
|
||||
// Various kinds of invalid input
|
||||
for _, input := range []string{
|
||||
``, // Empty input
|
||||
`&`, // Entirely invalid JSON
|
||||
`1`, // Not an object
|
||||
`{&}`, // Invalid key JSON
|
||||
`{1:1}`, // Key not a string
|
||||
`{"string": "a", "string": "a", "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true}`, // Duplicate key
|
||||
`{"string": "a", "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true, "thisisunknown", 1}`, // Unknown key
|
||||
`{"string": &, "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true}`, // Invalid value JSON
|
||||
`{"string": 1, "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true}`, // Type mismatch
|
||||
`{"string": "a", "float64": 3.5, "raw": {"a":"b"}, "unmarshaller": true}{}`, // Extra data after object
|
||||
} {
|
||||
err := paranoidUnmarshalJSONObjectExactFields([]byte(input), exactFields)
|
||||
assert.Error(t, err, input)
|
||||
}
|
||||
}
|
37
vendor/github.com/containers/image/signature/mechanism_gpgme_test.go
generated
vendored
37
vendor/github.com/containers/image/signature/mechanism_gpgme_test.go
generated
vendored
|
@ -1,37 +0,0 @@
|
|||
// +build !containers_image_openpgp
|
||||
|
||||
package signature
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGPGMESigningMechanismClose(t *testing.T) {
|
||||
// Closing an ephemeral mechanism removes the directory.
|
||||
// (The non-ephemeral case is tested in the common TestGPGSigningMechanismClose)
|
||||
mech, _, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
gpgMech, ok := mech.(*gpgmeSigningMechanism)
|
||||
require.True(t, ok)
|
||||
dir := gpgMech.ephemeralDir
|
||||
assert.NotEmpty(t, dir)
|
||||
_, err = os.Lstat(dir)
|
||||
require.NoError(t, err)
|
||||
err = mech.Close()
|
||||
assert.NoError(t, err)
|
||||
_, err = os.Lstat(dir)
|
||||
require.Error(t, err)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
func TestGPGMESigningMechanismSupportsSigning(t *testing.T) {
|
||||
mech, _, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
err = mech.SupportsSigning()
|
||||
assert.NoError(t, err)
|
||||
}
|
28
vendor/github.com/containers/image/signature/mechanism_openpgp_test.go
generated
vendored
28
vendor/github.com/containers/image/signature/mechanism_openpgp_test.go
generated
vendored
|
@ -1,28 +0,0 @@
|
|||
// +build containers_image_openpgp
|
||||
|
||||
package signature
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOpenpgpSigningMechanismSupportsSigning(t *testing.T) {
|
||||
mech, _, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
err = mech.SupportsSigning()
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, SigningNotSupportedError(""), err)
|
||||
}
|
||||
|
||||
func TestOpenpgpSigningMechanismSign(t *testing.T) {
|
||||
mech, _, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
_, err = mech.Sign([]byte{}, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, SigningNotSupportedError(""), err)
|
||||
}
|
297
vendor/github.com/containers/image/signature/mechanism_test.go
generated
vendored
297
vendor/github.com/containers/image/signature/mechanism_test.go
generated
vendored
|
@ -1,297 +0,0 @@
|
|||
package signature
|
||||
|
||||
// These tests are expected to pass unmodified for _both_ mechanism_gpgme.go and mechanism_openpgp.go.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testGPGHomeDirectory = "./fixtures"
|
||||
)
|
||||
|
||||
func TestSigningNotSupportedError(t *testing.T) {
|
||||
// A stupid test just to keep code coverage
|
||||
s := "test"
|
||||
err := SigningNotSupportedError(s)
|
||||
assert.Equal(t, s, err.Error())
|
||||
}
|
||||
|
||||
func TestNewGPGSigningMechanism(t *testing.T) {
|
||||
// A dumb test just for code coverage. We test more with newGPGSigningMechanismInDirectory().
|
||||
mech, err := NewGPGSigningMechanism()
|
||||
assert.NoError(t, err)
|
||||
mech.Close()
|
||||
}
|
||||
|
||||
func TestNewGPGSigningMechanismInDirectory(t *testing.T) {
|
||||
// A dumb test just for code coverage.
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
assert.NoError(t, err)
|
||||
mech.Close()
|
||||
// The various GPG failure cases are not obviously easy to reach.
|
||||
|
||||
// Test that using the default directory (presumably in user’s home)
|
||||
// cannot use TestKeyFingerprint.
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
mech, err = newGPGSigningMechanismInDirectory("")
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
_, _, err = mech.Verify(signature)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Similarly, using a newly created empty directory makes TestKeyFingerprint
|
||||
// unavailable
|
||||
emptyDir, err := ioutil.TempDir("", "signing-empty-directory")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(emptyDir)
|
||||
mech, err = newGPGSigningMechanismInDirectory(emptyDir)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
_, _, err = mech.Verify(signature)
|
||||
assert.Error(t, err)
|
||||
|
||||
// If pubring.gpg is unreadable in the directory, either initializing
|
||||
// the mechanism fails (with openpgp), or it succeeds (sadly, gpgme) and
|
||||
// later verification fails.
|
||||
unreadableDir, err := ioutil.TempDir("", "signing-unreadable-directory")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(unreadableDir)
|
||||
f, err := os.OpenFile(filepath.Join(unreadableDir, "pubring.gpg"), os.O_RDONLY|os.O_CREATE, 0000)
|
||||
require.NoError(t, err)
|
||||
f.Close()
|
||||
mech, err = newGPGSigningMechanismInDirectory(unreadableDir)
|
||||
if err == nil {
|
||||
defer mech.Close()
|
||||
_, _, err = mech.Verify(signature)
|
||||
}
|
||||
assert.Error(t, err)
|
||||
|
||||
// Setting the directory parameter to testGPGHomeDirectory makes the key available.
|
||||
mech, err = newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
_, _, err = mech.Verify(signature)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// If we use the default directory mechanism, GNUPGHOME is respected.
|
||||
origGNUPGHOME := os.Getenv("GNUPGHOME")
|
||||
defer os.Setenv("GNUPGHOME", origGNUPGHOME)
|
||||
os.Setenv("GNUPGHOME", testGPGHomeDirectory)
|
||||
mech, err = newGPGSigningMechanismInDirectory("")
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
_, _, err = mech.Verify(signature)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestNewEphemeralGPGSigningMechanism(t *testing.T) {
|
||||
// Empty input: This is accepted anyway by GPG, just returns no keys.
|
||||
mech, keyIdentities, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
assert.Empty(t, keyIdentities)
|
||||
// Try validating a signature when the key is unknown.
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err := mech.Verify(signature)
|
||||
require.Error(t, err)
|
||||
|
||||
// Successful import
|
||||
keyBlob, err := ioutil.ReadFile("./fixtures/public-key.gpg")
|
||||
require.NoError(t, err)
|
||||
mech, keyIdentities, err = NewEphemeralGPGSigningMechanism(keyBlob)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
assert.Equal(t, []string{TestKeyFingerprint}, keyIdentities)
|
||||
// After import, the signature should validate.
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This is not JSON\n"), content)
|
||||
assert.Equal(t, TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// Two keys: Read the binary-format pubring.gpg, and concatenate it twice.
|
||||
// (Using two copies of public-key.gpg, in the ASCII-armored format, works with
|
||||
// gpgmeSigningMechanism but not openpgpSigningMechanism.)
|
||||
keyBlob, err = ioutil.ReadFile("./fixtures/pubring.gpg")
|
||||
require.NoError(t, err)
|
||||
mech, keyIdentities, err = NewEphemeralGPGSigningMechanism(bytes.Join([][]byte{keyBlob, keyBlob}, nil))
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
assert.Equal(t, []string{TestKeyFingerprint, TestKeyFingerprint}, keyIdentities)
|
||||
|
||||
// Invalid input: This is, sadly, accepted anyway by GPG, just returns no keys.
|
||||
// For openpgpSigningMechanism we can detect this and fail.
|
||||
mech, keyIdentities, err = NewEphemeralGPGSigningMechanism([]byte("This is invalid"))
|
||||
assert.True(t, err != nil || len(keyIdentities) == 0)
|
||||
if err == nil {
|
||||
mech.Close()
|
||||
}
|
||||
assert.Empty(t, keyIdentities)
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismClose(t *testing.T) {
|
||||
// Closing a non-ephemeral mechanism does not remove anything in the directory.
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
err = mech.Close()
|
||||
assert.NoError(t, err)
|
||||
_, err = os.Lstat(testGPGHomeDirectory)
|
||||
assert.NoError(t, err)
|
||||
_, err = os.Lstat(filepath.Join(testGPGHomeDirectory, "pubring.gpg"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismSign(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
if err := mech.SupportsSigning(); err != nil {
|
||||
t.Skipf("Signing not supported: %v", err)
|
||||
}
|
||||
|
||||
// Successful signing
|
||||
content := []byte("content")
|
||||
signature, err := mech.Sign(content, TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
signedContent, signingFingerprint, err := mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, content, signedContent)
|
||||
assert.Equal(t, TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// Error signing
|
||||
_, err = mech.Sign(content, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func assertSigningError(t *testing.T, content []byte, fingerprint string, err error) {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, content)
|
||||
assert.Empty(t, fingerprint)
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismVerify(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
// Successful verification
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err := mech.Verify(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This is not JSON\n"), content)
|
||||
assert.Equal(t, TestKeyFingerprint, signingFingerprint)
|
||||
|
||||
// For extra paranoia, test that we return nil data on error.
|
||||
|
||||
// Completely invalid signature.
|
||||
content, signingFingerprint, err = mech.Verify([]byte{})
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
content, signingFingerprint, err = mech.Verify([]byte("invalid signature"))
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Literal packet, not a signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-literal.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Encrypted data, not a signature.
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-encrypted.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// FIXME? Is there a way to create a multi-signature so that gpgme_op_verify returns multiple signatures?
|
||||
|
||||
// Expired signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/expired.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Corrupt signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/corrupt.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// Valid signature with an unknown key
|
||||
signature, err = ioutil.ReadFile("./fixtures/unknown-key.signature")
|
||||
require.NoError(t, err)
|
||||
content, signingFingerprint, err = mech.Verify(signature)
|
||||
assertSigningError(t, content, signingFingerprint, err)
|
||||
|
||||
// The various GPG/GPGME failures cases are not obviously easy to reach.
|
||||
}
|
||||
|
||||
func TestGPGSigningMechanismUntrustedSignatureContents(t *testing.T) {
|
||||
mech, _, err := NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
// A valid signature
|
||||
signature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err := mech.UntrustedSignatureContents(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This is not JSON\n"), content)
|
||||
assert.Equal(t, TestKeyShortID, shortKeyID)
|
||||
|
||||
// Completely invalid signature.
|
||||
_, _, err = mech.UntrustedSignatureContents([]byte{})
|
||||
assert.Error(t, err)
|
||||
|
||||
_, _, err = mech.UntrustedSignatureContents([]byte("invalid signature"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Literal packet, not a signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-literal.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err = mech.UntrustedSignatureContents(signature)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Encrypted data, not a signature.
|
||||
signature, err = ioutil.ReadFile("./fixtures/unsigned-encrypted.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err = mech.UntrustedSignatureContents(signature)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Expired signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/expired.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err = mech.UntrustedSignatureContents(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("This signature is expired.\n"), content)
|
||||
assert.Equal(t, TestKeyShortID, shortKeyID)
|
||||
|
||||
// Corrupt signature
|
||||
signature, err = ioutil.ReadFile("./fixtures/corrupt.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err = mech.UntrustedSignatureContents(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte(`{"critical":{"identity":{"docker-reference":"testing/manifest"},"image":{"docker-manifest-digest":"sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55"},"type":"atomic container signature"},"optional":{"creator":"atomic ","timestamp":1458239713}}`), content)
|
||||
assert.Equal(t, TestKeyShortID, shortKeyID)
|
||||
|
||||
// Valid signature with an unknown key
|
||||
signature, err = ioutil.ReadFile("./fixtures/unknown-key.signature")
|
||||
require.NoError(t, err)
|
||||
content, shortKeyID, err = mech.UntrustedSignatureContents(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte(`{"critical":{"identity":{"docker-reference":"testing/manifest"},"image":{"docker-manifest-digest":"sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55"},"type":"atomic container signature"},"optional":{"creator":"atomic 0.1.13-dev","timestamp":1464633474}}`), content)
|
||||
assert.Equal(t, "E5476D1110D07803", shortKeyID)
|
||||
}
|
1370
vendor/github.com/containers/image/signature/policy_config_test.go
generated
vendored
1370
vendor/github.com/containers/image/signature/policy_config_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
24
vendor/github.com/containers/image/signature/policy_eval_baselayer_test.go
generated
vendored
24
vendor/github.com/containers/image/signature/policy_eval_baselayer_test.go
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPRSignedBaseLayerIsSignatureAuthorAccepted(t *testing.T) {
|
||||
pr, err := NewPRSignedBaseLayer(NewPRMMatchRepository())
|
||||
require.NoError(t, err)
|
||||
// Pass nil pointers to, kind of, test that the return value does not depend on the parameters.
|
||||
sar, parsedSig, err := pr.isSignatureAuthorAccepted(nil, nil)
|
||||
assertSARUnknown(t, sar, parsedSig, err)
|
||||
}
|
||||
|
||||
func TestPRSignedBaseLayerIsRunningImageAllowed(t *testing.T) {
|
||||
// This will obviously need to change after signedBaseLayer is implemented.
|
||||
pr, err := NewPRSignedBaseLayer(NewPRMMatchRepository())
|
||||
require.NoError(t, err)
|
||||
// Pass a nil pointer to, kind of, test that the return value does not depend on the image.
|
||||
res, err := pr.isRunningImageAllowed(nil)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
}
|
264
vendor/github.com/containers/image/signature/policy_eval_signedby_test.go
generated
vendored
264
vendor/github.com/containers/image/signature/policy_eval_signedby_test.go
generated
vendored
|
@ -1,264 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/directory"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/image"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// 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.ParseNormalizedNamed(dockerReference)
|
||||
require.NoError(t, err)
|
||||
return dirImageMockWithRef(t, dir, refImageReferenceMock{ref})
|
||||
}
|
||||
|
||||
// dirImageMockWithRef returns a types.UnparsedImage for a directory, claiming a specified ref.
|
||||
// The caller must call .Close() on the returned UnparsedImage.
|
||||
func dirImageMockWithRef(t *testing.T, dir string, ref types.ImageReference) types.UnparsedImage {
|
||||
srcRef, err := directory.NewReference(dir)
|
||||
require.NoError(t, err)
|
||||
src, err := srcRef.NewImageSource(nil, nil)
|
||||
require.NoError(t, err)
|
||||
return image.UnparsedFromSource(&dirImageSourceMock{
|
||||
ImageSource: src,
|
||||
ref: ref,
|
||||
})
|
||||
}
|
||||
|
||||
// dirImageSourceMock inherits dirImageSource, but overrides its Reference method.
|
||||
type dirImageSourceMock struct {
|
||||
types.ImageSource
|
||||
ref types.ImageReference
|
||||
}
|
||||
|
||||
func (d *dirImageSourceMock) Reference() types.ImageReference {
|
||||
return d.ref
|
||||
}
|
||||
|
||||
func TestPRSignedByIsSignatureAuthorAccepted(t *testing.T) {
|
||||
ktGPG := SBKeyTypeGPGKeys
|
||||
prm := NewPRMMatchExact()
|
||||
testImage := dirImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer testImage.Close()
|
||||
testImageSig, err := ioutil.ReadFile("fixtures/dir-img-valid/signature-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Successful validation, with KeyData and KeyPath
|
||||
pr, err := NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err := pr.isSignatureAuthorAccepted(testImage, testImageSig)
|
||||
assertSARAccepted(t, sar, parsedSig, err, Signature{
|
||||
DockerManifestDigest: TestImageManifestDigest,
|
||||
DockerReference: "testing/manifest:latest",
|
||||
})
|
||||
|
||||
keyData, err := ioutil.ReadFile("fixtures/public-key.gpg")
|
||||
require.NoError(t, err)
|
||||
pr, err = NewPRSignedByKeyData(ktGPG, keyData, prm)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(testImage, testImageSig)
|
||||
assertSARAccepted(t, sar, parsedSig, err, Signature{
|
||||
DockerManifestDigest: TestImageManifestDigest,
|
||||
DockerReference: "testing/manifest:latest",
|
||||
})
|
||||
|
||||
// Unimplemented and invalid KeyType values
|
||||
for _, keyType := range []sbKeyType{SBKeyTypeSignedByGPGKeys,
|
||||
SBKeyTypeX509Certificates,
|
||||
SBKeyTypeSignedByX509CAs,
|
||||
sbKeyType("This is invalid"),
|
||||
} {
|
||||
// Do not use NewPRSignedByKeyData, because it would reject invalid values.
|
||||
pr := &prSignedBy{
|
||||
KeyType: keyType,
|
||||
KeyData: []byte("abc"),
|
||||
SignedIdentity: prm,
|
||||
}
|
||||
// Pass nil pointers to, kind of, test that the return value does not depend on the parameters.
|
||||
sar, parsedSig, err := pr.isSignatureAuthorAccepted(nil, nil)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
}
|
||||
|
||||
// Both KeyPath and KeyData set. Do not use NewPRSignedBy*, because it would reject this.
|
||||
prSB := &prSignedBy{
|
||||
KeyType: ktGPG,
|
||||
KeyPath: "/foo/bar",
|
||||
KeyData: []byte("abc"),
|
||||
SignedIdentity: prm,
|
||||
}
|
||||
// Pass nil pointers to, kind of, test that the return value does not depend on the parameters.
|
||||
sar, parsedSig, err = prSB.isSignatureAuthorAccepted(nil, nil)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// Invalid KeyPath
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "/this/does/not/exist", prm)
|
||||
require.NoError(t, err)
|
||||
// Pass nil pointers to, kind of, test that the return value does not depend on the parameters.
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(nil, nil)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// Errors initializing the temporary GPG directory and mechanism are not obviously easy to reach.
|
||||
|
||||
// KeyData has no public keys.
|
||||
pr, err = NewPRSignedByKeyData(ktGPG, []byte{}, prm)
|
||||
require.NoError(t, err)
|
||||
// Pass nil pointers to, kind of, test that the return value does not depend on the parameters.
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(nil, nil)
|
||||
assertSARRejectedPolicyRequirement(t, sar, parsedSig, err)
|
||||
|
||||
// A signature which does not GPG verify
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
// Pass a nil pointer to, kind of, test that the return value does not depend on the image parmater..
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(nil, []byte("invalid signature"))
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// A valid signature using an unknown key.
|
||||
// (This is (currently?) rejected through the "mech.Verify fails" path, not the "!identityFound" path,
|
||||
// because we use a temporary directory and only import the trusted keys.)
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sig, err := ioutil.ReadFile("fixtures/unknown-key.signature")
|
||||
require.NoError(t, err)
|
||||
// Pass a nil pointer to, kind of, test that the return value does not depend on the image parmater..
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(nil, sig)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// A valid signature of an invalid JSON.
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sig, err = ioutil.ReadFile("fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
// Pass a nil pointer to, kind of, test that the return value does not depend on the image parmater..
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(nil, sig)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
assert.IsType(t, InvalidSignatureError{}, err)
|
||||
|
||||
// A valid signature with a rejected identity.
|
||||
nonmatchingPRM, err := NewPRMExactReference("this/doesnt:match")
|
||||
require.NoError(t, err)
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", nonmatchingPRM)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(testImage, testImageSig)
|
||||
assertSARRejectedPolicyRequirement(t, sar, parsedSig, err)
|
||||
|
||||
// Error reading image manifest
|
||||
image := dirImageMock(t, "fixtures/dir-img-no-manifest", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
sig, err = ioutil.ReadFile("fixtures/dir-img-no-manifest/signature-1")
|
||||
require.NoError(t, err)
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(image, sig)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// Error computing manifest digest
|
||||
image = dirImageMock(t, "fixtures/dir-img-manifest-digest-error", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
sig, err = ioutil.ReadFile("fixtures/dir-img-manifest-digest-error/signature-1")
|
||||
require.NoError(t, err)
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(image, sig)
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
|
||||
// A valid signature with a non-matching manifest
|
||||
image = dirImageMock(t, "fixtures/dir-img-modified-manifest", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
sig, err = ioutil.ReadFile("fixtures/dir-img-modified-manifest/signature-1")
|
||||
require.NoError(t, err)
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
sar, parsedSig, err = pr.isSignatureAuthorAccepted(image, sig)
|
||||
assertSARRejectedPolicyRequirement(t, sar, parsedSig, err)
|
||||
}
|
||||
|
||||
// createInvalidSigDir creates a directory suitable for dirImageMock, in which image.Signatures()
|
||||
// fails.
|
||||
// The caller should eventually call os.RemoveAll on the returned path.
|
||||
func createInvalidSigDir(t *testing.T) string {
|
||||
dir, err := ioutil.TempDir("", "skopeo-test-unreadable-signature")
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(path.Join(dir, "manifest.json"), []byte("{}"), 0644)
|
||||
require.NoError(t, err)
|
||||
// Creating a 000-permissions file would work for unprivileged accounts, but root (in particular,
|
||||
// in the Docker container we use for testing) would still have access. So, create a symlink
|
||||
// pointing to itself, to cause an ELOOP. (Note that a symlink pointing to a nonexistent file would be treated
|
||||
// just like a nonexistent signature file, and not an error.)
|
||||
err = os.Symlink("signature-1", path.Join(dir, "signature-1"))
|
||||
require.NoError(t, err)
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestPRSignedByIsRunningImageAllowed(t *testing.T) {
|
||||
ktGPG := SBKeyTypeGPGKeys
|
||||
prm := NewPRMMatchExact()
|
||||
|
||||
// A simple success case: single valid signature.
|
||||
image := dirImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
pr, err := NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err := pr.isRunningImageAllowed(image)
|
||||
assertRunningAllowed(t, allowed, err)
|
||||
|
||||
// Error reading signatures
|
||||
invalidSigDir := createInvalidSigDir(t)
|
||||
defer os.RemoveAll(invalidSigDir)
|
||||
image = dirImageMock(t, invalidSigDir, "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningRejected(t, allowed, err)
|
||||
|
||||
// No signatures
|
||||
image = dirImageMock(t, "fixtures/dir-img-unsigned", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningRejectedPolicyRequirement(t, allowed, err)
|
||||
|
||||
// 1 invalid signature: use dir-img-valid, but a non-matching Docker reference
|
||||
image = dirImageMock(t, "fixtures/dir-img-valid", "testing/manifest:notlatest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningRejectedPolicyRequirement(t, allowed, err)
|
||||
|
||||
// 2 valid signatures
|
||||
image = dirImageMock(t, "fixtures/dir-img-valid-2", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningAllowed(t, allowed, err)
|
||||
|
||||
// One invalid, one valid signature (in this order)
|
||||
image = dirImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:latest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningAllowed(t, allowed, err)
|
||||
|
||||
// 2 invalid signatures: use dir-img-valid-2, but a non-matching Docker reference
|
||||
image = dirImageMock(t, "fixtures/dir-img-valid-2", "testing/manifest:notlatest")
|
||||
defer image.Close()
|
||||
pr, err = NewPRSignedByKeyPath(ktGPG, "fixtures/public-key.gpg", prm)
|
||||
require.NoError(t, err)
|
||||
allowed, err = pr.isRunningImageAllowed(image)
|
||||
assertRunningRejectedPolicyRequirement(t, allowed, err)
|
||||
}
|
74
vendor/github.com/containers/image/signature/policy_eval_simple_test.go
generated
vendored
74
vendor/github.com/containers/image/signature/policy_eval_simple_test.go
generated
vendored
|
@ -1,74 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
// nameOnlyImageMock is a mock of types.UnparsedImage which only allows transports.ImageName to work
|
||||
type nameOnlyImageMock struct {
|
||||
forbiddenImageMock
|
||||
}
|
||||
|
||||
func (nameOnlyImageMock) Reference() types.ImageReference {
|
||||
return nameOnlyImageReferenceMock("== StringWithinTransport mock")
|
||||
}
|
||||
|
||||
// nameOnlyImageReferenceMock is a mock of types.ImageReference which only allows transports.ImageName to work, returning self.
|
||||
type nameOnlyImageReferenceMock string
|
||||
|
||||
func (ref nameOnlyImageReferenceMock) Transport() types.ImageTransport {
|
||||
return nameImageTransportMock("== Transport mock")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) StringWithinTransport() string {
|
||||
return string(ref)
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) DockerReference() reference.Named {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) PolicyConfigurationIdentity() string {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) PolicyConfigurationNamespaces() []string {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref nameOnlyImageReferenceMock) DeleteImage(ctx *types.SystemContext) error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
func TestPRInsecureAcceptAnythingIsSignatureAuthorAccepted(t *testing.T) {
|
||||
pr := NewPRInsecureAcceptAnything()
|
||||
// Pass nil signature to, kind of, test that the return value does not depend on it.
|
||||
sar, parsedSig, err := pr.isSignatureAuthorAccepted(nameOnlyImageMock{}, nil)
|
||||
assertSARUnknown(t, sar, parsedSig, err)
|
||||
}
|
||||
|
||||
func TestPRInsecureAcceptAnythingIsRunningImageAllowed(t *testing.T) {
|
||||
pr := NewPRInsecureAcceptAnything()
|
||||
res, err := pr.isRunningImageAllowed(nameOnlyImageMock{})
|
||||
assertRunningAllowed(t, res, err)
|
||||
}
|
||||
|
||||
func TestPRRejectIsSignatureAuthorAccepted(t *testing.T) {
|
||||
pr := NewPRReject()
|
||||
// Pass nil signature to, kind of, test that the return value does not depend on it.
|
||||
sar, parsedSig, err := pr.isSignatureAuthorAccepted(nameOnlyImageMock{}, nil)
|
||||
assertSARRejectedPolicyRequirement(t, sar, parsedSig, err)
|
||||
}
|
||||
|
||||
func TestPRRejectIsRunningImageAllowed(t *testing.T) {
|
||||
pr := NewPRReject()
|
||||
res, err := pr.isRunningImageAllowed(nameOnlyImageMock{})
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
}
|
521
vendor/github.com/containers/image/signature/policy_eval_test.go
generated
vendored
521
vendor/github.com/containers/image/signature/policy_eval_test.go
generated
vendored
|
@ -1,521 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/docker"
|
||||
"github.com/containers/image/docker/policyconfiguration"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/transports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPolicyRequirementError(t *testing.T) {
|
||||
// A stupid test just to keep code coverage
|
||||
s := "test"
|
||||
err := PolicyRequirementError(s)
|
||||
assert.Equal(t, s, err.Error())
|
||||
}
|
||||
|
||||
func TestPolicyContextChangeState(t *testing.T) {
|
||||
pc, err := NewPolicyContext(&Policy{Default: PolicyRequirements{NewPRReject()}})
|
||||
require.NoError(t, err)
|
||||
defer pc.Destroy()
|
||||
|
||||
require.Equal(t, pcReady, pc.state)
|
||||
err = pc.changeState(pcReady, pcInUse)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = pc.changeState(pcReady, pcInUse)
|
||||
require.Error(t, err)
|
||||
|
||||
// Return state to pcReady to allow pc.Destroy to clean up.
|
||||
err = pc.changeState(pcInUse, pcReady)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPolicyContextNewDestroy(t *testing.T) {
|
||||
pc, err := NewPolicyContext(&Policy{Default: PolicyRequirements{NewPRReject()}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, pcReady, pc.state)
|
||||
|
||||
err = pc.Destroy()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, pcDestroyed, pc.state)
|
||||
|
||||
// Trying to destroy when not pcReady
|
||||
pc, err = NewPolicyContext(&Policy{Default: PolicyRequirements{NewPRReject()}})
|
||||
require.NoError(t, err)
|
||||
err = pc.changeState(pcReady, pcInUse)
|
||||
require.NoError(t, err)
|
||||
err = pc.Destroy()
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, pcInUse, pc.state) // The state, and hopefully nothing else, has changed.
|
||||
|
||||
err = pc.changeState(pcInUse, pcReady)
|
||||
require.NoError(t, err)
|
||||
err = pc.Destroy()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// pcImageReferenceMock is a mock of types.ImageReference which returns itself in DockerReference
|
||||
// and handles PolicyConfigurationIdentity and PolicyConfigurationReference consistently.
|
||||
type pcImageReferenceMock struct {
|
||||
transportName string
|
||||
ref reference.Named
|
||||
}
|
||||
|
||||
func (ref pcImageReferenceMock) Transport() types.ImageTransport {
|
||||
return nameImageTransportMock(ref.transportName)
|
||||
}
|
||||
func (ref pcImageReferenceMock) StringWithinTransport() string {
|
||||
// We use this in error messages, so sadly we must return something.
|
||||
return "== StringWithinTransport mock"
|
||||
}
|
||||
func (ref pcImageReferenceMock) DockerReference() reference.Named {
|
||||
return ref.ref
|
||||
}
|
||||
func (ref pcImageReferenceMock) PolicyConfigurationIdentity() string {
|
||||
res, err := policyconfiguration.DockerReferenceIdentity(ref.ref)
|
||||
if res == "" || err != nil {
|
||||
panic(fmt.Sprintf("Internal inconsistency: policyconfiguration.DockerReferenceIdentity returned %#v, %v", res, err))
|
||||
}
|
||||
return res
|
||||
}
|
||||
func (ref pcImageReferenceMock) PolicyConfigurationNamespaces() []string {
|
||||
if ref.ref == nil {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
return policyconfiguration.DockerReferenceNamespaces(ref.ref)
|
||||
}
|
||||
func (ref pcImageReferenceMock) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref pcImageReferenceMock) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref pcImageReferenceMock) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref pcImageReferenceMock) DeleteImage(ctx *types.SystemContext) error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
func TestPolicyContextRequirementsForImageRefNotRegisteredTransport(t *testing.T) {
|
||||
transports.Delete("docker")
|
||||
assert.Nil(t, transports.Get("docker"))
|
||||
|
||||
defer func() {
|
||||
assert.Nil(t, transports.Get("docker"))
|
||||
transports.Register(docker.Transport)
|
||||
assert.NotNil(t, transports.Get("docker"))
|
||||
}()
|
||||
|
||||
pr := []PolicyRequirement{
|
||||
xNewPRSignedByKeyData(SBKeyTypeSignedByGPGKeys, []byte("RH"), NewPRMMatchRepository()),
|
||||
}
|
||||
policy := &Policy{
|
||||
Default: PolicyRequirements{NewPRReject()},
|
||||
Transports: map[string]PolicyTransportScopes{
|
||||
"docker": {
|
||||
"registry.access.redhat.com": pr,
|
||||
},
|
||||
},
|
||||
}
|
||||
pc, err := NewPolicyContext(policy)
|
||||
require.NoError(t, err)
|
||||
ref, err := reference.ParseNormalizedNamed("registry.access.redhat.com/rhel7:latest")
|
||||
require.NoError(t, err)
|
||||
reqs := pc.requirementsForImageRef(pcImageReferenceMock{"docker", ref})
|
||||
assert.True(t, &(reqs[0]) == &(pr[0]))
|
||||
assert.True(t, len(reqs) == len(pr))
|
||||
|
||||
}
|
||||
|
||||
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.ParseNormalizedNamed(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)
|
||||
}
|
||||
}
|
||||
|
||||
// pcImageMock returns a types.UnparsedImage for a directory, claiming a specified dockerReference and implementing PolicyConfigurationIdentity/PolicyConfigurationNamespaces.
|
||||
// The caller must call .Close() on the returned Image.
|
||||
func pcImageMock(t *testing.T, dir, dockerReference string) types.UnparsedImage {
|
||||
ref, err := reference.ParseNormalizedNamed(dockerReference)
|
||||
require.NoError(t, err)
|
||||
return dirImageMockWithRef(t, dir, pcImageReferenceMock{"docker", ref})
|
||||
}
|
||||
|
||||
func TestPolicyContextGetSignaturesWithAcceptedAuthor(t *testing.T) {
|
||||
expectedSig := &Signature{
|
||||
DockerManifestDigest: TestImageManifestDigest,
|
||||
DockerReference: "testing/manifest:latest",
|
||||
}
|
||||
|
||||
pc, err := NewPolicyContext(&Policy{
|
||||
Default: PolicyRequirements{NewPRReject()},
|
||||
Transports: map[string]PolicyTransportScopes{
|
||||
"docker": {
|
||||
"docker.io/testing/manifest:latest": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchExact()),
|
||||
},
|
||||
"docker.io/testing/manifest:twoAccepts": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
},
|
||||
"docker.io/testing/manifest:acceptReject": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
NewPRReject(),
|
||||
},
|
||||
"docker.io/testing/manifest:acceptUnknown": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
xNewPRSignedBaseLayer(NewPRMMatchRepository()),
|
||||
},
|
||||
"docker.io/testing/manifest:rejectUnknown": {
|
||||
NewPRReject(),
|
||||
xNewPRSignedBaseLayer(NewPRMMatchRepository()),
|
||||
},
|
||||
"docker.io/testing/manifest:unknown": {
|
||||
xNewPRSignedBaseLayer(NewPRMMatchRepository()),
|
||||
},
|
||||
"docker.io/testing/manifest:unknown2": {
|
||||
NewPRInsecureAcceptAnything(),
|
||||
},
|
||||
"docker.io/testing/manifest:invalidEmptyRequirements": {},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer pc.Destroy()
|
||||
|
||||
// Success
|
||||
img := pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err := pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*Signature{expectedSig}, sigs)
|
||||
|
||||
// Two signatures
|
||||
// FIXME? Use really different signatures for this?
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid-2", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*Signature{expectedSig, expectedSig}, sigs)
|
||||
|
||||
// No signatures
|
||||
img = pcImageMock(t, "fixtures/dir-img-unsigned", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// Only invalid signatures
|
||||
img = pcImageMock(t, "fixtures/dir-img-modified-manifest", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// 1 invalid, 1 valid signature (in this order)
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*Signature{expectedSig}, sigs)
|
||||
|
||||
// Two sarAccepted results for one signature
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:twoAccepts")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*Signature{expectedSig}, sigs)
|
||||
|
||||
// sarAccepted+sarRejected for a signature
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:acceptReject")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// sarAccepted+sarUnknown for a signature
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:acceptUnknown")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*Signature{expectedSig}, sigs)
|
||||
|
||||
// sarRejected+sarUnknown for a signature
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:rejectUnknown")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// sarUnknown only
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:unknown")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:unknown2")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// Empty list of requirements (invalid)
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:invalidEmptyRequirements")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, sigs)
|
||||
|
||||
// Failures: Make sure we return nil sigs.
|
||||
|
||||
// Unexpected state (context already destroyed)
|
||||
destroyedPC, err := NewPolicyContext(pc.Policy)
|
||||
require.NoError(t, err)
|
||||
err = destroyedPC.Destroy()
|
||||
require.NoError(t, err)
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = destroyedPC.GetSignaturesWithAcceptedAuthor(img)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sigs)
|
||||
// Not testing the pcInUse->pcReady transition, that would require custom PolicyRequirement
|
||||
// implementations meddling with the state, or threads. This is for catching trivial programmer
|
||||
// mistakes only, anyway.
|
||||
|
||||
// Error reading signatures.
|
||||
invalidSigDir := createInvalidSigDir(t)
|
||||
defer os.RemoveAll(invalidSigDir)
|
||||
img = pcImageMock(t, invalidSigDir, "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
sigs, err = pc.GetSignaturesWithAcceptedAuthor(img)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sigs)
|
||||
}
|
||||
|
||||
func TestPolicyContextIsRunningImageAllowed(t *testing.T) {
|
||||
pc, err := NewPolicyContext(&Policy{
|
||||
Default: PolicyRequirements{NewPRReject()},
|
||||
Transports: map[string]PolicyTransportScopes{
|
||||
"docker": {
|
||||
"docker.io/testing/manifest:latest": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchExact()),
|
||||
},
|
||||
"docker.io/testing/manifest:twoAllows": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
},
|
||||
"docker.io/testing/manifest:allowDeny": {
|
||||
xNewPRSignedByKeyPath(SBKeyTypeGPGKeys, "fixtures/public-key.gpg", NewPRMMatchRepository()),
|
||||
NewPRReject(),
|
||||
},
|
||||
"docker.io/testing/manifest:reject": {
|
||||
NewPRReject(),
|
||||
},
|
||||
"docker.io/testing/manifest:acceptAnything": {
|
||||
NewPRInsecureAcceptAnything(),
|
||||
},
|
||||
"docker.io/testing/manifest:invalidEmptyRequirements": {},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer pc.Destroy()
|
||||
|
||||
// Success
|
||||
img := pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err := pc.IsRunningImageAllowed(img)
|
||||
assertRunningAllowed(t, res, err)
|
||||
|
||||
// Two signatures
|
||||
// FIXME? Use really different signatures for this?
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid-2", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningAllowed(t, res, err)
|
||||
|
||||
// No signatures
|
||||
img = pcImageMock(t, "fixtures/dir-img-unsigned", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
|
||||
// Only invalid signatures
|
||||
img = pcImageMock(t, "fixtures/dir-img-modified-manifest", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
|
||||
// 1 invalid, 1 valid signature (in this order)
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningAllowed(t, res, err)
|
||||
|
||||
// Two allowed results
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:twoAllows")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningAllowed(t, res, err)
|
||||
|
||||
// Allow + deny results
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:allowDeny")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
|
||||
// prReject works
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:reject")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
|
||||
// prInsecureAcceptAnything works
|
||||
img = pcImageMock(t, "fixtures/dir-img-mixed", "testing/manifest:acceptAnything")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningAllowed(t, res, err)
|
||||
|
||||
// Empty list of requirements (invalid)
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:invalidEmptyRequirements")
|
||||
defer img.Close()
|
||||
res, err = pc.IsRunningImageAllowed(img)
|
||||
assertRunningRejectedPolicyRequirement(t, res, err)
|
||||
|
||||
// Unexpected state (context already destroyed)
|
||||
destroyedPC, err := NewPolicyContext(pc.Policy)
|
||||
require.NoError(t, err)
|
||||
err = destroyedPC.Destroy()
|
||||
require.NoError(t, err)
|
||||
img = pcImageMock(t, "fixtures/dir-img-valid", "testing/manifest:latest")
|
||||
defer img.Close()
|
||||
res, err = destroyedPC.IsRunningImageAllowed(img)
|
||||
assertRunningRejected(t, res, err)
|
||||
// Not testing the pcInUse->pcReady transition, that would require custom PolicyRequirement
|
||||
// implementations meddling with the state, or threads. This is for catching trivial programmer
|
||||
// mistakes only, anyway.
|
||||
}
|
||||
|
||||
// Helpers for validating PolicyRequirement.isSignatureAuthorAccepted results:
|
||||
|
||||
// assertSARRejected verifies that isSignatureAuthorAccepted returns a consistent sarRejected result
|
||||
// with the expected signature.
|
||||
func assertSARAccepted(t *testing.T, sar signatureAcceptanceResult, parsedSig *Signature, err error, expectedSig Signature) {
|
||||
assert.Equal(t, sarAccepted, sar)
|
||||
assert.Equal(t, &expectedSig, parsedSig)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// assertSARRejected verifies that isSignatureAuthorAccepted returns a consistent sarRejected result.
|
||||
func assertSARRejected(t *testing.T, sar signatureAcceptanceResult, parsedSig *Signature, err error) {
|
||||
assert.Equal(t, sarRejected, sar)
|
||||
assert.Nil(t, parsedSig)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// assertSARRejectedPolicyRequiremnt verifies that isSignatureAuthorAccepted returns a consistent sarRejected resul,
|
||||
// and that the returned error is a PolicyRequirementError..
|
||||
func assertSARRejectedPolicyRequirement(t *testing.T, sar signatureAcceptanceResult, parsedSig *Signature, err error) {
|
||||
assertSARRejected(t, sar, parsedSig, err)
|
||||
assert.IsType(t, PolicyRequirementError(""), err)
|
||||
}
|
||||
|
||||
// assertSARRejected verifies that isSignatureAuthorAccepted returns a consistent sarUnknown result.
|
||||
func assertSARUnknown(t *testing.T, sar signatureAcceptanceResult, parsedSig *Signature, err error) {
|
||||
assert.Equal(t, sarUnknown, sar)
|
||||
assert.Nil(t, parsedSig)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Helpers for validating PolicyRequirement.isRunningImageAllowed results:
|
||||
|
||||
// assertRunningAllowed verifies that isRunningImageAllowed returns a consistent true result
|
||||
func assertRunningAllowed(t *testing.T, allowed bool, err error) {
|
||||
assert.Equal(t, true, allowed)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// assertRunningRejected verifies that isRunningImageAllowed returns a consistent false result
|
||||
func assertRunningRejected(t *testing.T, allowed bool, err error) {
|
||||
assert.Equal(t, false, allowed)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// assertRunningRejectedPolicyRequirement verifies that isRunningImageAllowed returns a consistent false result
|
||||
// and that the returned error is a PolicyRequirementError.
|
||||
func assertRunningRejectedPolicyRequirement(t *testing.T, allowed bool, err error) {
|
||||
assertRunningRejected(t, allowed, err)
|
||||
assert.IsType(t, PolicyRequirementError(""), err)
|
||||
}
|
365
vendor/github.com/containers/image/signature/policy_reference_match_test.go
generated
vendored
365
vendor/github.com/containers/image/signature/policy_reference_match_test.go
generated
vendored
|
@ -1,365 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
fullRHELRef = "registry.access.redhat.com/rhel7/rhel:7.2.3"
|
||||
untaggedRHELRef = "registry.access.redhat.com/rhel7/rhel"
|
||||
digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
digestSuffixOther = "@sha256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
)
|
||||
|
||||
func TestParseImageAndDockerReference(t *testing.T) {
|
||||
const (
|
||||
ok1 = "busybox"
|
||||
ok2 = fullRHELRef
|
||||
bad1 = "UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES"
|
||||
bad2 = ""
|
||||
)
|
||||
// Success
|
||||
ref, err := reference.ParseNormalizedNamed(ok1)
|
||||
require.NoError(t, err)
|
||||
r1, r2, err := parseImageAndDockerReference(refImageMock{ref}, ok2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ok1, reference.FamiliarString(r1))
|
||||
assert.Equal(t, ok2, reference.FamiliarString(r2))
|
||||
|
||||
// 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.ParseNormalizedNamed(refs[0])
|
||||
if err == nil {
|
||||
_, _, err := parseImageAndDockerReference(refImageMock{ref}, refs[1])
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refImageMock is a mock of types.UnparsedImage which returns itself in Reference().DockerReference.
|
||||
type refImageMock struct{ reference.Named }
|
||||
|
||||
func (ref refImageMock) Reference() types.ImageReference {
|
||||
return refImageReferenceMock{ref.Named}
|
||||
}
|
||||
func (ref refImageMock) Close() error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageMock) Manifest() ([]byte, string, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageMock) Signatures() ([][]byte, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
// refImageReferenceMock is a mock of types.ImageReference which returns itself in DockerReference.
|
||||
type refImageReferenceMock struct{ reference.Named }
|
||||
|
||||
func (ref refImageReferenceMock) Transport() types.ImageTransport {
|
||||
// We use this in error messages, so sady we must return something. But right now we do so only when DockerReference is nil, so restrict to that.
|
||||
if ref.Named == nil {
|
||||
return nameImageTransportMock("== Transport mock")
|
||||
}
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) StringWithinTransport() string {
|
||||
// We use this in error messages, so sadly we must return something. But right now we do so only when DockerReference is nil, so restrict to that.
|
||||
if ref.Named == nil {
|
||||
return "== StringWithinTransport for an image with no Docker support"
|
||||
}
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) DockerReference() reference.Named {
|
||||
return ref.Named
|
||||
}
|
||||
func (ref refImageReferenceMock) PolicyConfigurationIdentity() string {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) PolicyConfigurationNamespaces() []string {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) NewImage(ctx *types.SystemContext) (types.Image, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) NewImageSource(ctx *types.SystemContext, requestedManifestMIMETypes []string) (types.ImageSource, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref refImageReferenceMock) DeleteImage(ctx *types.SystemContext) error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
// nameImageTransportMock is a mock of types.ImageTransport which returns itself in Name.
|
||||
type nameImageTransportMock string
|
||||
|
||||
func (name nameImageTransportMock) Name() string {
|
||||
return string(name)
|
||||
}
|
||||
func (name nameImageTransportMock) ParseReference(reference string) (types.ImageReference, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (name nameImageTransportMock) ValidatePolicyConfigurationScope(scope string) error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
type prmSymmetricTableTest struct {
|
||||
refA, refB string
|
||||
result bool
|
||||
}
|
||||
|
||||
// Test cases for exact reference match. The behavior is supposed to be symmetric.
|
||||
var prmExactMatchTestTable = []prmSymmetricTableTest{
|
||||
// Success, simple matches
|
||||
{"busybox:latest", "busybox:latest", true},
|
||||
{fullRHELRef, fullRHELRef, true},
|
||||
{"busybox" + digestSuffix, "busybox" + digestSuffix, true}, // NOTE: This is not documented; signing digests is not recommended at this time.
|
||||
// Non-canonical reference format is canonicalized
|
||||
{"library/busybox:latest", "busybox:latest", true},
|
||||
{"docker.io/library/busybox:latest", "busybox:latest", true},
|
||||
{"library/busybox" + digestSuffix, "busybox" + digestSuffix, true},
|
||||
// Mismatch
|
||||
{"busybox:latest", "busybox:notlatest", false},
|
||||
{"busybox:latest", "notbusybox:latest", false},
|
||||
{"busybox:latest", "hostname/library/busybox:notlatest", false},
|
||||
{"hostname/library/busybox:latest", "busybox:notlatest", false},
|
||||
{"busybox:latest", fullRHELRef, false},
|
||||
{"busybox" + digestSuffix, "notbusybox" + digestSuffix, false},
|
||||
{"busybox:latest", "busybox" + digestSuffix, false},
|
||||
{"busybox" + digestSuffix, "busybox" + digestSuffixOther, false},
|
||||
// NameOnly references
|
||||
{"busybox", "busybox:latest", false},
|
||||
{"busybox", "busybox" + digestSuffix, false},
|
||||
{"busybox", "busybox", false},
|
||||
// References with both tags and digests: We match them exactly (requiring BOTH to match)
|
||||
// NOTE: Again, this is not documented behavior; the recommendation is to sign tags, not digests, and then tag-and-digest references won’t match the signed identity.
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest" + digestSuffix, true},
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest" + digestSuffixOther, false},
|
||||
{"busybox:latest" + digestSuffix, "busybox:notlatest" + digestSuffix, false},
|
||||
{"busybox:latest" + digestSuffix, "busybox" + digestSuffix, false},
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest", false},
|
||||
// Invalid format
|
||||
{"UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES", "busybox:latest", false},
|
||||
{"", "UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES", false},
|
||||
// Even if they are exactly equal, invalid values are rejected.
|
||||
{"INVALID", "INVALID", false},
|
||||
}
|
||||
|
||||
// Test cases for repository-only reference match. The behavior is supposed to be symmetric.
|
||||
var prmRepositoryMatchTestTable = []prmSymmetricTableTest{
|
||||
// Success, simple matches
|
||||
{"busybox:latest", "busybox:latest", true},
|
||||
{fullRHELRef, fullRHELRef, true},
|
||||
{"busybox" + digestSuffix, "busybox" + digestSuffix, true}, // NOTE: This is not documented; signing digests is not recommended at this time.
|
||||
// Non-canonical reference format is canonicalized
|
||||
{"library/busybox:latest", "busybox:latest", true},
|
||||
{"docker.io/library/busybox:latest", "busybox:latest", true},
|
||||
{"library/busybox" + digestSuffix, "busybox" + digestSuffix, true},
|
||||
// The same as above, but with mismatching tags
|
||||
{"busybox:latest", "busybox:notlatest", true},
|
||||
{fullRHELRef + "tagsuffix", fullRHELRef, true},
|
||||
{"library/busybox:latest", "busybox:notlatest", true},
|
||||
{"busybox:latest", "library/busybox:notlatest", true},
|
||||
{"docker.io/library/busybox:notlatest", "busybox:latest", true},
|
||||
{"busybox:notlatest", "docker.io/library/busybox:latest", true},
|
||||
{"busybox:latest", "busybox" + digestSuffix, true},
|
||||
{"busybox" + digestSuffix, "busybox" + digestSuffixOther, true}, // Even this is accepted here. (This could more reasonably happen with two different digest algorithms.)
|
||||
// The same as above, but with defaulted tags (should not actually happen)
|
||||
{"busybox", "busybox:notlatest", true},
|
||||
{fullRHELRef, untaggedRHELRef, true},
|
||||
{"busybox", "busybox" + digestSuffix, true},
|
||||
{"library/busybox", "busybox", true},
|
||||
{"docker.io/library/busybox", "busybox", true},
|
||||
// Mismatch
|
||||
{"busybox:latest", "notbusybox:latest", false},
|
||||
{"hostname/library/busybox:latest", "busybox:notlatest", false},
|
||||
{"busybox:latest", fullRHELRef, false},
|
||||
{"busybox" + digestSuffix, "notbusybox" + digestSuffix, false},
|
||||
// References with both tags and digests: We ignore both anyway.
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest" + digestSuffix, true},
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest" + digestSuffixOther, true},
|
||||
{"busybox:latest" + digestSuffix, "busybox:notlatest" + digestSuffix, true},
|
||||
{"busybox:latest" + digestSuffix, "busybox" + digestSuffix, true},
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest", true},
|
||||
// Invalid format
|
||||
{"UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES", "busybox:latest", false},
|
||||
{"", "UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES", false},
|
||||
// Even if they are exactly equal, invalid values are rejected.
|
||||
{"INVALID", "INVALID", false},
|
||||
}
|
||||
|
||||
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.ParseNormalizedNamed can not happen in practice.
|
||||
parsedImageRef, err := reference.ParseNormalizedNamed(imageRef)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res := prm.matchesDockerReference(refImageMock{parsedImageRef}, sigRef)
|
||||
assert.Equal(t, result, res, fmt.Sprintf("%s vs. %s", imageRef, sigRef))
|
||||
}
|
||||
|
||||
func TestPRMMatchExactMatchesDockerReference(t *testing.T) {
|
||||
prm := NewPRMMatchExact()
|
||||
for _, test := range prmExactMatchTestTable {
|
||||
testImageAndSig(t, prm, test.refA, test.refB, test.result)
|
||||
testImageAndSig(t, prm, test.refB, test.refA, test.result)
|
||||
}
|
||||
// Even if they are signed with an empty string as a reference, unidentified images are rejected.
|
||||
res := prm.matchesDockerReference(refImageMock{nil}, "")
|
||||
assert.False(t, res, `unidentified vs. ""`)
|
||||
}
|
||||
|
||||
func TestPMMMatchRepoDigestOrExactMatchesDockerReference(t *testing.T) {
|
||||
prm := NewPRMMatchRepoDigestOrExact()
|
||||
|
||||
// prmMatchRepoDigestOrExact is a middle ground between prmMatchExact and prmMatchRepository:
|
||||
// It accepts anything prmMatchExact accepts,…
|
||||
for _, test := range prmExactMatchTestTable {
|
||||
if test.result == true {
|
||||
testImageAndSig(t, prm, test.refA, test.refB, test.result)
|
||||
testImageAndSig(t, prm, test.refB, test.refA, test.result)
|
||||
}
|
||||
}
|
||||
// … and it rejects everything prmMatchRepository rejects.
|
||||
for _, test := range prmRepositoryMatchTestTable {
|
||||
if test.result == false {
|
||||
testImageAndSig(t, prm, test.refA, test.refB, test.result)
|
||||
testImageAndSig(t, prm, test.refB, test.refA, test.result)
|
||||
}
|
||||
}
|
||||
|
||||
// The other cases, possibly assymetrical:
|
||||
for _, test := range []struct {
|
||||
imageRef, sigRef string
|
||||
result bool
|
||||
}{
|
||||
// Tag mismatch
|
||||
{"busybox:latest", "busybox:notlatest", false},
|
||||
{fullRHELRef + "tagsuffix", fullRHELRef, false},
|
||||
{"library/busybox:latest", "busybox:notlatest", false},
|
||||
{"busybox:latest", "library/busybox:notlatest", false},
|
||||
{"docker.io/library/busybox:notlatest", "busybox:latest", false},
|
||||
{"busybox:notlatest", "docker.io/library/busybox:latest", false},
|
||||
// NameOnly references
|
||||
{"busybox", "busybox:latest", false},
|
||||
{"busybox:latest", "busybox", false},
|
||||
{"busybox", "busybox" + digestSuffix, false},
|
||||
{"busybox" + digestSuffix, "busybox", false},
|
||||
{fullRHELRef, untaggedRHELRef, false},
|
||||
{"busybox", "busybox", false},
|
||||
// Tag references only accept signatures with matching tags.
|
||||
{"busybox:latest", "busybox" + digestSuffix, false},
|
||||
// Digest references accept any signature with matching repository.
|
||||
{"busybox" + digestSuffix, "busybox:latest", true},
|
||||
{"busybox" + digestSuffix, "busybox" + digestSuffixOther, true}, // Even this is accepted here. (This could more reasonably happen with two different digest algorithms.)
|
||||
// References with both tags and digests: We match them exactly (requiring BOTH to match).
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest", false},
|
||||
{"busybox:latest" + digestSuffix, "busybox:notlatest", false},
|
||||
{"busybox:latest", "busybox:latest" + digestSuffix, false},
|
||||
{"busybox:latest" + digestSuffix, "busybox:latest" + digestSuffixOther, false},
|
||||
{"busybox:latest" + digestSuffix, "busybox:notlatest" + digestSuffixOther, false},
|
||||
} {
|
||||
testImageAndSig(t, prm, test.imageRef, test.sigRef, test.result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRMMatchRepositoryMatchesDockerReference(t *testing.T) {
|
||||
prm := NewPRMMatchRepository()
|
||||
for _, test := range prmRepositoryMatchTestTable {
|
||||
testImageAndSig(t, prm, test.refA, test.refB, test.result)
|
||||
testImageAndSig(t, prm, test.refB, test.refA, test.result)
|
||||
}
|
||||
// Even if they are signed with an empty string as a reference, unidentified images are rejected.
|
||||
res := prm.matchesDockerReference(refImageMock{nil}, "")
|
||||
assert.False(t, res, `unidentified vs. ""`)
|
||||
}
|
||||
|
||||
func TestParseDockerReferences(t *testing.T) {
|
||||
const (
|
||||
ok1 = "busybox"
|
||||
ok2 = fullRHELRef
|
||||
bad1 = "UPPERCASE_IS_INVALID_IN_DOCKER_REFERENCES"
|
||||
bad2 = ""
|
||||
)
|
||||
|
||||
// Success
|
||||
r1, r2, err := parseDockerReferences(ok1, ok2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ok1, reference.FamiliarString(r1))
|
||||
assert.Equal(t, ok2, reference.FamiliarString(r2))
|
||||
|
||||
// Failures
|
||||
for _, refs := range [][]string{
|
||||
{bad1, ok2},
|
||||
{ok1, bad2},
|
||||
{bad1, bad2},
|
||||
} {
|
||||
_, _, err := parseDockerReferences(refs[0], refs[1])
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// forbiddenImageMock is a mock of types.UnparsedImage which ensures Reference is not called
|
||||
type forbiddenImageMock struct{}
|
||||
|
||||
func (ref forbiddenImageMock) Reference() types.ImageReference {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref forbiddenImageMock) Close() error {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref forbiddenImageMock) Manifest() ([]byte, string, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
func (ref forbiddenImageMock) Signatures() ([][]byte, error) {
|
||||
panic("unexpected call to a mock function")
|
||||
}
|
||||
|
||||
func testExactPRMAndSig(t *testing.T, prmFactory func(string) PolicyReferenceMatch, imageRef, sigRef string, result bool) {
|
||||
prm := prmFactory(imageRef)
|
||||
res := prm.matchesDockerReference(forbiddenImageMock{}, sigRef)
|
||||
assert.Equal(t, result, res, fmt.Sprintf("%s vs. %s", imageRef, sigRef))
|
||||
}
|
||||
|
||||
func prmExactReferenceFactory(ref string) PolicyReferenceMatch {
|
||||
// Do not use NewPRMExactReference, we want to also test the case with an invalid DockerReference,
|
||||
// even though NewPRMExactReference should never let it happen.
|
||||
return &prmExactReference{DockerReference: ref}
|
||||
}
|
||||
|
||||
func TestPRMExactReferenceMatchesDockerReference(t *testing.T) {
|
||||
for _, test := range prmExactMatchTestTable {
|
||||
testExactPRMAndSig(t, prmExactReferenceFactory, test.refA, test.refB, test.result)
|
||||
testExactPRMAndSig(t, prmExactReferenceFactory, test.refB, test.refA, test.result)
|
||||
}
|
||||
}
|
||||
|
||||
func prmExactRepositoryFactory(ref string) PolicyReferenceMatch {
|
||||
// Do not use NewPRMExactRepository, we want to also test the case with an invalid DockerReference,
|
||||
// even though NewPRMExactRepository should never let it happen.
|
||||
return &prmExactRepository{DockerRepository: ref}
|
||||
}
|
||||
|
||||
func TestPRMExactRepositoryMatchesDockerReference(t *testing.T) {
|
||||
for _, test := range prmRepositoryMatchTestTable {
|
||||
testExactPRMAndSig(t, prmExactRepositoryFactory, test.refA, test.refB, test.result)
|
||||
testExactPRMAndSig(t, prmExactRepositoryFactory, test.refB, test.refA, test.result)
|
||||
}
|
||||
}
|
412
vendor/github.com/containers/image/signature/signature_test.go
generated
vendored
412
vendor/github.com/containers/image/signature/signature_test.go
generated
vendored
|
@ -1,412 +0,0 @@
|
|||
package signature
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/version"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
func TestInvalidSignatureError(t *testing.T) {
|
||||
// A stupid test just to keep code coverage
|
||||
s := "test"
|
||||
err := InvalidSignatureError{msg: s}
|
||||
assert.Equal(t, s, err.Error())
|
||||
}
|
||||
|
||||
func TestNewUntrustedSignature(t *testing.T) {
|
||||
timeBefore := time.Now()
|
||||
sig := newUntrustedSignature(TestImageManifestDigest, TestImageSignatureReference)
|
||||
assert.Equal(t, TestImageManifestDigest, sig.UntrustedDockerManifestDigest)
|
||||
assert.Equal(t, TestImageSignatureReference, sig.UntrustedDockerReference)
|
||||
require.NotNil(t, sig.UntrustedCreatorID)
|
||||
assert.Equal(t, "atomic "+version.Version, *sig.UntrustedCreatorID)
|
||||
require.NotNil(t, sig.UntrustedTimestamp)
|
||||
timeAfter := time.Now()
|
||||
assert.True(t, timeBefore.Unix() <= *sig.UntrustedTimestamp)
|
||||
assert.True(t, *sig.UntrustedTimestamp <= timeAfter.Unix())
|
||||
}
|
||||
|
||||
func TestMarshalJSON(t *testing.T) {
|
||||
// Empty string values
|
||||
s := newUntrustedSignature("", "_")
|
||||
_, err := s.MarshalJSON()
|
||||
assert.Error(t, err)
|
||||
s = newUntrustedSignature("_", "")
|
||||
_, err = s.MarshalJSON()
|
||||
assert.Error(t, err)
|
||||
|
||||
// Success
|
||||
// Use intermediate variables for these values so that we can take their addresses.
|
||||
creatorID := "CREATOR"
|
||||
timestamp := int64(1484683104)
|
||||
for _, c := range []struct {
|
||||
input untrustedSignature
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
untrustedSignature{
|
||||
UntrustedDockerManifestDigest: "digest!@#",
|
||||
UntrustedDockerReference: "reference#@!",
|
||||
UntrustedCreatorID: &creatorID,
|
||||
UntrustedTimestamp: ×tamp,
|
||||
},
|
||||
"{\"critical\":{\"identity\":{\"docker-reference\":\"reference#@!\"},\"image\":{\"docker-manifest-digest\":\"digest!@#\"},\"type\":\"atomic container signature\"},\"optional\":{\"creator\":\"CREATOR\",\"timestamp\":1484683104}}",
|
||||
},
|
||||
{
|
||||
untrustedSignature{
|
||||
UntrustedDockerManifestDigest: "digest!@#",
|
||||
UntrustedDockerReference: "reference#@!",
|
||||
},
|
||||
"{\"critical\":{\"identity\":{\"docker-reference\":\"reference#@!\"},\"image\":{\"docker-manifest-digest\":\"digest!@#\"},\"type\":\"atomic container signature\"},\"optional\":{}}",
|
||||
},
|
||||
} {
|
||||
marshaled, err := c.input.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte(c.expected), marshaled)
|
||||
|
||||
// Also call MarshalJSON through the JSON package.
|
||||
marshaled, err = json.Marshal(c.input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte(c.expected), marshaled)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the result of modifying validJSON with fn
|
||||
func modifiedUntrustedSignatureJSON(t *testing.T, validJSON []byte, modifyFn func(mSI)) []byte {
|
||||
var tmp mSI
|
||||
err := json.Unmarshal(validJSON, &tmp)
|
||||
require.NoError(t, err)
|
||||
|
||||
modifyFn(tmp)
|
||||
|
||||
modifiedJSON, err := json.Marshal(tmp)
|
||||
require.NoError(t, err)
|
||||
return modifiedJSON
|
||||
}
|
||||
|
||||
// Verify that input can be unmarshaled as an untrustedSignature, and that it passes JSON schema validation, and return the unmarshaled untrustedSignature.
|
||||
func succesfullyUnmarshalUntrustedSignature(t *testing.T, schemaLoader gojsonschema.JSONLoader, input []byte) untrustedSignature {
|
||||
inputString := string(input)
|
||||
|
||||
var s untrustedSignature
|
||||
err := json.Unmarshal(input, &s)
|
||||
require.NoError(t, err, inputString)
|
||||
|
||||
res, err := gojsonschema.Validate(schemaLoader, gojsonschema.NewStringLoader(inputString))
|
||||
assert.True(t, err == nil, inputString)
|
||||
assert.True(t, res.Valid(), inputString)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Verify that input can't be unmashaled as an untrusted signature, and that it fails JSON schema validation.
|
||||
func assertUnmarshalUntrustedSignatureFails(t *testing.T, schemaLoader gojsonschema.JSONLoader, input []byte) {
|
||||
inputString := string(input)
|
||||
|
||||
var s untrustedSignature
|
||||
err := json.Unmarshal(input, &s)
|
||||
assert.Error(t, err, inputString)
|
||||
|
||||
res, err := gojsonschema.Validate(schemaLoader, gojsonschema.NewStringLoader(inputString))
|
||||
assert.True(t, err != nil || !res.Valid(), inputString)
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
// NOTE: The schema at schemaPath is NOT authoritative; docs/atomic-signature.json and the code is, rather!
|
||||
// The schemaPath references are not testing that the code follows the behavior declared by the schema,
|
||||
// they are testing that the schema follows the behavior of the code!
|
||||
schemaPath, err := filepath.Abs("../docs/atomic-signature-embedded-json.json")
|
||||
require.NoError(t, err)
|
||||
schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath)
|
||||
|
||||
// Invalid input. Note that json.Unmarshal is guaranteed to validate input before calling our
|
||||
// UnmarshalJSON implementation; so test that first, then test our error handling for completeness.
|
||||
assertUnmarshalUntrustedSignatureFails(t, schemaLoader, []byte("&"))
|
||||
var s untrustedSignature
|
||||
err = s.UnmarshalJSON([]byte("&"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Not an object
|
||||
assertUnmarshalUntrustedSignatureFails(t, schemaLoader, []byte("1"))
|
||||
|
||||
// Start with a valid JSON.
|
||||
validSig := newUntrustedSignature("digest!@#", "reference#@!")
|
||||
validJSON, err := validSig.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Success
|
||||
s = succesfullyUnmarshalUntrustedSignature(t, schemaLoader, validJSON)
|
||||
assert.Equal(t, validSig, s)
|
||||
|
||||
// Various ways to corrupt the JSON
|
||||
breakFns := []func(mSI){
|
||||
// A top-level field is missing
|
||||
func(v mSI) { delete(v, "critical") },
|
||||
func(v mSI) { delete(v, "optional") },
|
||||
// Extra top-level sub-object
|
||||
func(v mSI) { v["unexpected"] = 1 },
|
||||
// "critical" not an object
|
||||
func(v mSI) { v["critical"] = 1 },
|
||||
// "optional" not an object
|
||||
func(v mSI) { v["optional"] = 1 },
|
||||
// A field of "critical" is missing
|
||||
func(v mSI) { delete(x(v, "critical"), "type") },
|
||||
func(v mSI) { delete(x(v, "critical"), "image") },
|
||||
func(v mSI) { delete(x(v, "critical"), "identity") },
|
||||
// Extra field of "critical"
|
||||
func(v mSI) { x(v, "critical")["unexpected"] = 1 },
|
||||
// Invalid "type"
|
||||
func(v mSI) { x(v, "critical")["type"] = 1 },
|
||||
func(v mSI) { x(v, "critical")["type"] = "unexpected" },
|
||||
// Invalid "image" object
|
||||
func(v mSI) { x(v, "critical")["image"] = 1 },
|
||||
func(v mSI) { delete(x(v, "critical", "image"), "docker-manifest-digest") },
|
||||
func(v mSI) { x(v, "critical", "image")["unexpected"] = 1 },
|
||||
// Invalid "docker-manifest-digest"
|
||||
func(v mSI) { x(v, "critical", "image")["docker-manifest-digest"] = 1 },
|
||||
// Invalid "identity" object
|
||||
func(v mSI) { x(v, "critical")["identity"] = 1 },
|
||||
func(v mSI) { delete(x(v, "critical", "identity"), "docker-reference") },
|
||||
func(v mSI) { x(v, "critical", "identity")["unexpected"] = 1 },
|
||||
// Invalid "docker-reference"
|
||||
func(v mSI) { x(v, "critical", "identity")["docker-reference"] = 1 },
|
||||
// Invalid "creator"
|
||||
func(v mSI) { x(v, "optional")["creator"] = 1 },
|
||||
// Invalid "timestamp"
|
||||
func(v mSI) { x(v, "optional")["timestamp"] = "unexpected" },
|
||||
func(v mSI) { x(v, "optional")["timestamp"] = 0.5 }, // Fractional input
|
||||
}
|
||||
for _, fn := range breakFns {
|
||||
testJSON := modifiedUntrustedSignatureJSON(t, validJSON, fn)
|
||||
assertUnmarshalUntrustedSignatureFails(t, schemaLoader, testJSON)
|
||||
}
|
||||
|
||||
// Modifications to unrecognized fields in "optional" are allowed and ignored
|
||||
allowedModificationFns := []func(mSI){
|
||||
// Add an optional field
|
||||
func(v mSI) { x(v, "optional")["unexpected"] = 1 },
|
||||
}
|
||||
for _, fn := range allowedModificationFns {
|
||||
testJSON := modifiedUntrustedSignatureJSON(t, validJSON, fn)
|
||||
s := succesfullyUnmarshalUntrustedSignature(t, schemaLoader, testJSON)
|
||||
assert.Equal(t, validSig, s)
|
||||
}
|
||||
|
||||
// Optional fields can be missing
|
||||
validSig = untrustedSignature{
|
||||
UntrustedDockerManifestDigest: "digest!@#",
|
||||
UntrustedDockerReference: "reference#@!",
|
||||
UntrustedCreatorID: nil,
|
||||
UntrustedTimestamp: nil,
|
||||
}
|
||||
validJSON, err = validSig.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
s = succesfullyUnmarshalUntrustedSignature(t, schemaLoader, validJSON)
|
||||
assert.Equal(t, validSig, s)
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
if err := mech.SupportsSigning(); err != nil {
|
||||
t.Skipf("Signing not supported: %v", err)
|
||||
}
|
||||
|
||||
sig := newUntrustedSignature("digest!@#", "reference#@!")
|
||||
|
||||
// Successful signing
|
||||
signature, err := sig.sign(mech, TestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
|
||||
verified, err := verifyAndExtractSignature(mech, signature, signatureAcceptanceRules{
|
||||
validateKeyIdentity: func(keyIdentity string) error {
|
||||
if keyIdentity != TestKeyFingerprint {
|
||||
return errors.Errorf("Unexpected keyIdentity")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerReference: func(signedDockerReference string) error {
|
||||
if signedDockerReference != sig.UntrustedDockerReference {
|
||||
return errors.Errorf("Unexpected signedDockerReference")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerManifestDigest: func(signedDockerManifestDigest digest.Digest) error {
|
||||
if signedDockerManifestDigest != sig.UntrustedDockerManifestDigest {
|
||||
return errors.Errorf("Unexpected signedDockerManifestDigest")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, sig.UntrustedDockerManifestDigest, verified.DockerManifestDigest)
|
||||
assert.Equal(t, sig.UntrustedDockerReference, verified.DockerReference)
|
||||
|
||||
// Error creating blob to sign
|
||||
_, err = untrustedSignature{}.sign(mech, TestKeyFingerprint)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Error signing
|
||||
_, err = sig.sign(mech, "this fingerprint doesn't exist")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyAndExtractSignature(t *testing.T) {
|
||||
mech, err := newGPGSigningMechanismInDirectory(testGPGHomeDirectory)
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
|
||||
type triple struct {
|
||||
keyIdentity string
|
||||
signedDockerReference string
|
||||
signedDockerManifestDigest digest.Digest
|
||||
}
|
||||
var wanted, recorded triple
|
||||
// recordingRules are a plausible signatureAcceptanceRules implementations, but equally
|
||||
// importantly record that we are passing the correct values to the rule callbacks.
|
||||
recordingRules := signatureAcceptanceRules{
|
||||
validateKeyIdentity: func(keyIdentity string) error {
|
||||
recorded.keyIdentity = keyIdentity
|
||||
if keyIdentity != wanted.keyIdentity {
|
||||
return errors.Errorf("keyIdentity mismatch")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerReference: func(signedDockerReference string) error {
|
||||
recorded.signedDockerReference = signedDockerReference
|
||||
if signedDockerReference != wanted.signedDockerReference {
|
||||
return errors.Errorf("signedDockerReference mismatch")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerManifestDigest: func(signedDockerManifestDigest digest.Digest) error {
|
||||
recorded.signedDockerManifestDigest = signedDockerManifestDigest
|
||||
if signedDockerManifestDigest != wanted.signedDockerManifestDigest {
|
||||
return errors.Errorf("signedDockerManifestDigest mismatch")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
signature, err := ioutil.ReadFile("./fixtures/image.signature")
|
||||
require.NoError(t, err)
|
||||
signatureData := triple{
|
||||
keyIdentity: TestKeyFingerprint,
|
||||
signedDockerReference: TestImageSignatureReference,
|
||||
signedDockerManifestDigest: TestImageManifestDigest,
|
||||
}
|
||||
|
||||
// Successful verification
|
||||
wanted = signatureData
|
||||
recorded = triple{}
|
||||
sig, err := verifyAndExtractSignature(mech, signature, recordingRules)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, sig.DockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, sig.DockerManifestDigest)
|
||||
assert.Equal(t, signatureData, recorded)
|
||||
|
||||
// For extra paranoia, test that we return a nil signature object on error.
|
||||
|
||||
// Completely invalid signature.
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, []byte{}, recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, triple{}, recorded)
|
||||
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, []byte("invalid signature"), recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, triple{}, recorded)
|
||||
|
||||
// Valid signature of non-JSON: asked for keyIdentity, only
|
||||
invalidBlobSignature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, invalidBlobSignature, recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, triple{keyIdentity: signatureData.keyIdentity}, recorded)
|
||||
|
||||
// Valid signature with a wrong key: asked for keyIdentity, only
|
||||
wanted = signatureData
|
||||
wanted.keyIdentity = "unexpected fingerprint"
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, signature, recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, triple{keyIdentity: signatureData.keyIdentity}, recorded)
|
||||
|
||||
// Valid signature with a wrong manifest digest: asked for keyIdentity and signedDockerManifestDigest
|
||||
wanted = signatureData
|
||||
wanted.signedDockerManifestDigest = "invalid digest"
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, signature, recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, triple{
|
||||
keyIdentity: signatureData.keyIdentity,
|
||||
signedDockerManifestDigest: signatureData.signedDockerManifestDigest,
|
||||
}, recorded)
|
||||
|
||||
// Valid signature with a wrong image reference
|
||||
wanted = signatureData
|
||||
wanted.signedDockerReference = "unexpected docker reference"
|
||||
recorded = triple{}
|
||||
sig, err = verifyAndExtractSignature(mech, signature, recordingRules)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, sig)
|
||||
assert.Equal(t, signatureData, recorded)
|
||||
}
|
||||
|
||||
func TestGetUntrustedSignatureInformationWithoutVerifying(t *testing.T) {
|
||||
signature, err := ioutil.ReadFile("./fixtures/image.signature")
|
||||
require.NoError(t, err)
|
||||
// Successful parsing, all optional fields present
|
||||
info, err := GetUntrustedSignatureInformationWithoutVerifying(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, info.UntrustedDockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, info.UntrustedDockerManifestDigest)
|
||||
assert.NotNil(t, info.UntrustedCreatorID)
|
||||
assert.Equal(t, "atomic ", *info.UntrustedCreatorID)
|
||||
assert.NotNil(t, info.UntrustedTimestamp)
|
||||
assert.Equal(t, time.Unix(1458239713, 0), *info.UntrustedTimestamp)
|
||||
assert.Equal(t, TestKeyShortID, info.UntrustedShortKeyIdentifier)
|
||||
// Successful parsing, no optional fields present
|
||||
signature, err = ioutil.ReadFile("./fixtures/no-optional-fields.signature")
|
||||
require.NoError(t, err)
|
||||
// Successful parsing
|
||||
info, err = GetUntrustedSignatureInformationWithoutVerifying(signature)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, TestImageSignatureReference, info.UntrustedDockerReference)
|
||||
assert.Equal(t, TestImageManifestDigest, info.UntrustedDockerManifestDigest)
|
||||
assert.Nil(t, info.UntrustedCreatorID)
|
||||
assert.Nil(t, info.UntrustedTimestamp)
|
||||
assert.Equal(t, TestKeyShortID, info.UntrustedShortKeyIdentifier)
|
||||
|
||||
// Completely invalid signature.
|
||||
_, err = GetUntrustedSignatureInformationWithoutVerifying([]byte{})
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = GetUntrustedSignatureInformationWithoutVerifying([]byte("invalid signature"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Valid signature of non-JSON
|
||||
invalidBlobSignature, err := ioutil.ReadFile("./fixtures/invalid-blob.signature")
|
||||
require.NoError(t, err)
|
||||
_, err = GetUntrustedSignatureInformationWithoutVerifying(invalidBlobSignature)
|
||||
assert.Error(t, err)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue