mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1197 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1197 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2020 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package validation
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/ed25519"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"encoding/pem"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						|
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						|
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/certificates"
 | 
						|
	capi "k8s.io/kubernetes/pkg/apis/certificates"
 | 
						|
	capiv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
 | 
						|
	"k8s.io/kubernetes/pkg/apis/core"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	validObjectMeta = metav1.ObjectMeta{Name: "testcsr"}
 | 
						|
	validSignerName = "example.com/valid-name"
 | 
						|
	validUsages     = []capi.KeyUsage{capi.UsageKeyEncipherment}
 | 
						|
)
 | 
						|
 | 
						|
func TestValidateCertificateSigningRequestCreate(t *testing.T) {
 | 
						|
	specPath := field.NewPath("spec")
 | 
						|
	// maxLengthSignerName is a signerName that is of maximum length, utilising
 | 
						|
	// the max length specifications defined in validation.go.
 | 
						|
	// It is of the form <fqdn(253)>/<resource-namespace(63)>.<resource-name(253)>
 | 
						|
	maxLengthFQDN := fmt.Sprintf("%s.%s.%s.%s", repeatString("a", 63), repeatString("a", 63), repeatString("a", 63), repeatString("a", 61))
 | 
						|
	maxLengthSignerName := fmt.Sprintf("%s/%s.%s", maxLengthFQDN, repeatString("a", 63), repeatString("a", 253))
 | 
						|
	tests := map[string]struct {
 | 
						|
		csr  capi.CertificateSigningRequest
 | 
						|
		gv   schema.GroupVersion
 | 
						|
		errs field.ErrorList
 | 
						|
	}{
 | 
						|
		"CSR with empty request data should fail": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					SignerName: validSignerName,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("request"), []byte(nil), "PEM block type must be CERTIFICATE REQUEST"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"CSR with invalid request data should fail": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					SignerName: validSignerName,
 | 
						|
					Request:    []byte("invalid data"),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("request"), []byte("invalid data"), "PEM block type must be CERTIFICATE REQUEST"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"CSR with no usages should fail": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					SignerName: validSignerName,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Required(specPath.Child("usages"), "usages must be provided"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"CSR with no signerName set should fail": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:  validUsages,
 | 
						|
					Request: newCSRPEM(t),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Required(specPath.Child("signerName"), "signerName must be provided"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName contains no '/'": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "an-invalid-signer-name",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "an-invalid-signer-name", "must be a fully qualified domain and path of the form 'example.com/signer-name'"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName contains two '/'": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "an-invalid-signer-name.com/something/else",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "an-invalid-signer-name.com/something/else", "must be a fully qualified domain and path of the form 'example.com/signer-name'"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName domain component is not fully qualified": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "example/some-signer-name",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "example", "should be a domain with at least two segments separated by dots"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName path component is empty": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "example.com/",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "", `validating label "": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName path component ends with a symbol": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "example.com/something-",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "something-", `validating label "something-": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName path component is a symbol": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "example.com/-",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), "-", `validating label "-": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName path component contains no '.' but is valid": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: "example.com/some-signer-name",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{},
 | 
						|
		},
 | 
						|
		"signerName with a total length greater than 571 characters should be rejected": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:  validUsages,
 | 
						|
					Request: newCSRPEM(t),
 | 
						|
					// this string is longer than the max signerName limit (635 chars)
 | 
						|
					SignerName: maxLengthSignerName + ".toolong",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.TooLong(specPath.Child("signerName"), maxLengthSignerName+".toolong", len(maxLengthSignerName)),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName with a fqdn greater than 253 characters should be rejected": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:  validUsages,
 | 
						|
					Request: newCSRPEM(t),
 | 
						|
					// this string is longer than the max signerName limit (635 chars)
 | 
						|
					SignerName: fmt.Sprintf("%s.extra/valid-path", maxLengthFQDN),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.TooLong(specPath.Child("signerName"), fmt.Sprintf("%s.extra", maxLengthFQDN), len(maxLengthFQDN)),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName can have a longer path if the domain component is less than the max length": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: fmt.Sprintf("abc.io/%s.%s", repeatString("a", 253), repeatString("a", 253)),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{},
 | 
						|
		},
 | 
						|
		"signerName with a domain label greater than 63 characters will fail": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: fmt.Sprintf("%s.example.io/valid-path", repeatString("a", 66)),
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Invalid(specPath.Child("signerName"), fmt.Sprintf("%s.example.io", repeatString("a", 66)), fmt.Sprintf(`validating label "%s": must be no more than 63 characters`, repeatString("a", 66))),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"signerName of max length in format <fully-qualified-domain-name>/<resource-namespace>.<resource-name> is valid": {
 | 
						|
			// ensure signerName is of the form domain.com/something and up to 571 characters.
 | 
						|
			// This length and format is specified to accommodate signerNames like:
 | 
						|
			// <fqdn>/<resource-namespace>.<resource-name>.
 | 
						|
			// The max length of a FQDN is 253 characters (DNS1123Subdomain max length)
 | 
						|
			// The max length of a namespace name is 63 characters (DNS1123Label max length)
 | 
						|
			// The max length of a resource name is 253 characters (DNS1123Subdomain max length)
 | 
						|
			// We then add an additional 2 characters to account for the one '.' and one '/'.
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     validUsages,
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: maxLengthSignerName,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{},
 | 
						|
		},
 | 
						|
		"missing usages": {
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     []capi.KeyUsage{},
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: validSignerName,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.Required(specPath.Child("usages"), "usages must be provided"),
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"unknown and duplicate usages - v1beta1": {
 | 
						|
			gv: schema.GroupVersion{Group: capi.SchemeGroupVersion.Group, Version: "v1beta1"},
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     []capi.KeyUsage{"unknown", "unknown"},
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: validSignerName,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{},
 | 
						|
		},
 | 
						|
		"unknown and duplicate usages - v1": {
 | 
						|
			gv: schema.GroupVersion{Group: capi.SchemeGroupVersion.Group, Version: "v1"},
 | 
						|
			csr: capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: capi.CertificateSigningRequestSpec{
 | 
						|
					Usages:     []capi.KeyUsage{"unknown", "unknown"},
 | 
						|
					Request:    newCSRPEM(t),
 | 
						|
					SignerName: validSignerName,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			errs: field.ErrorList{
 | 
						|
				field.NotSupported(specPath.Child("usages").Index(0), capi.KeyUsage("unknown"), allValidUsages.List()),
 | 
						|
				field.NotSupported(specPath.Child("usages").Index(1), capi.KeyUsage("unknown"), allValidUsages.List()),
 | 
						|
				field.Duplicate(specPath.Child("usages").Index(1), capi.KeyUsage("unknown")),
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for name, test := range tests {
 | 
						|
		t.Run(name, func(t *testing.T) {
 | 
						|
			el := ValidateCertificateSigningRequestCreate(&test.csr, test.gv)
 | 
						|
			if !reflect.DeepEqual(el, test.errs) {
 | 
						|
				t.Errorf("returned and expected errors did not match - expected\n%v\nbut got\n%v", test.errs.ToAggregate(), el.ToAggregate())
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func repeatString(s string, num int) string {
 | 
						|
	l := make([]string, num)
 | 
						|
	for i := 0; i < num; i++ {
 | 
						|
		l[i] = s
 | 
						|
	}
 | 
						|
	return strings.Join(l, "")
 | 
						|
}
 | 
						|
 | 
						|
func newCSRPEM(t *testing.T) []byte {
 | 
						|
	template := &x509.CertificateRequest{
 | 
						|
		Subject: pkix.Name{
 | 
						|
			Organization: []string{"testing-org"},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	_, key, err := ed25519.GenerateKey(rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	csrDER, err := x509.CreateCertificateRequest(rand.Reader, template, key)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	csrPemBlock := &pem.Block{
 | 
						|
		Type:  "CERTIFICATE REQUEST",
 | 
						|
		Bytes: csrDER,
 | 
						|
	}
 | 
						|
 | 
						|
	p := pem.EncodeToMemory(csrPemBlock)
 | 
						|
	if p == nil {
 | 
						|
		t.Fatal("invalid pem block")
 | 
						|
	}
 | 
						|
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func Test_getValidationOptions(t *testing.T) {
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		version schema.GroupVersion
 | 
						|
		newCSR  *certificates.CertificateSigningRequest
 | 
						|
		oldCSR  *certificates.CertificateSigningRequest
 | 
						|
		want    certificateValidationOptions
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:    "v1beta1 compatible create",
 | 
						|
			version: capiv1beta1.SchemeGroupVersion,
 | 
						|
			oldCSR:  nil,
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowResettingCertificate:    true,
 | 
						|
				allowBothApprovedAndDenied:   false,
 | 
						|
				allowLegacySignerName:        true,
 | 
						|
				allowDuplicateConditionTypes: true,
 | 
						|
				allowEmptyConditionType:      true,
 | 
						|
				allowArbitraryCertificate:    true,
 | 
						|
				allowUnknownUsages:           true,
 | 
						|
				allowDuplicateUsages:         true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 strict create",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR:  nil,
 | 
						|
			want:    certificateValidationOptions{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1beta1 compatible update",
 | 
						|
			version: capiv1beta1.SchemeGroupVersion,
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowResettingCertificate:    true,
 | 
						|
				allowBothApprovedAndDenied:   true, // existing object has both approved and denied
 | 
						|
				allowLegacySignerName:        true,
 | 
						|
				allowDuplicateConditionTypes: true,
 | 
						|
				allowEmptyConditionType:      true,
 | 
						|
				allowArbitraryCertificate:    true,
 | 
						|
				allowUnknownUsages:           true,
 | 
						|
				allowDuplicateUsages:         true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 strict update",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR:  &capi.CertificateSigningRequest{},
 | 
						|
			want:    certificateValidationOptions{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, approved+denied",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowBothApprovedAndDenied: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, legacy signerName",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR:  &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowLegacySignerName: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, duplicate condition types",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateApproved}},
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowDuplicateConditionTypes: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, empty condition types",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{}},
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowEmptyConditionType: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, no diff to certificate",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: validCertificate,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: validCertificate,
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowArbitraryCertificate: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, existing invalid certificate",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: []byte(`new - no PEM blocks`),
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: []byte(`old - no PEM blocks`),
 | 
						|
			}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowArbitraryCertificate: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, existing unknown usages",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR:  &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowUnknownUsages: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:    "v1 compatible update, existing duplicate usages",
 | 
						|
			version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
 | 
						|
			oldCSR:  &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
 | 
						|
			want: certificateValidationOptions{
 | 
						|
				allowDuplicateUsages: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			if got := getValidationOptions(tt.version, tt.newCSR, tt.oldCSR); !reflect.DeepEqual(got, tt.want) {
 | 
						|
				t.Errorf("got  %#v\nwant %#v", got, tt.want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
 | 
						|
	validUpdateMeta := validObjectMeta
 | 
						|
	validUpdateMeta.ResourceVersion = "1"
 | 
						|
 | 
						|
	validUpdateMetaWithFinalizers := validUpdateMeta
 | 
						|
	validUpdateMetaWithFinalizers.Finalizers = []string{"foo"}
 | 
						|
 | 
						|
	validSpec := capi.CertificateSigningRequestSpec{
 | 
						|
		Usages:     validUsages,
 | 
						|
		Request:    newCSRPEM(t),
 | 
						|
		SignerName: "example.com/something",
 | 
						|
	}
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		name        string
 | 
						|
		newCSR      *certificates.CertificateSigningRequest
 | 
						|
		oldCSR      *certificates.CertificateSigningRequest
 | 
						|
		versionErrs map[string][]string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:   "no-op",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "finalizer change with invalid status",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR:      &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "set certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: validCertificate,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.certificate: Forbidden: updates may not set certificate content`},
 | 
						|
				"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		for _, version := range []string{"v1", "v1beta1"} {
 | 
						|
			t.Run(tt.name+"_"+version, func(t *testing.T) {
 | 
						|
				gotErrs := sets.NewString()
 | 
						|
				for _, err := range ValidateCertificateSigningRequestUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: certificates.GroupName, Version: version}) {
 | 
						|
					gotErrs.Insert(err.Error())
 | 
						|
				}
 | 
						|
				wantErrs := sets.NewString(tt.versionErrs[version]...)
 | 
						|
				for _, missing := range wantErrs.Difference(gotErrs).List() {
 | 
						|
					t.Errorf("missing expected error: %s", missing)
 | 
						|
				}
 | 
						|
				for _, unexpected := range gotErrs.Difference(wantErrs).List() {
 | 
						|
					t.Errorf("unexpected error: %s", unexpected)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
 | 
						|
	validUpdateMeta := validObjectMeta
 | 
						|
	validUpdateMeta.ResourceVersion = "1"
 | 
						|
 | 
						|
	validUpdateMetaWithFinalizers := validUpdateMeta
 | 
						|
	validUpdateMetaWithFinalizers.Finalizers = []string{"foo"}
 | 
						|
 | 
						|
	validSpec := capi.CertificateSigningRequestSpec{
 | 
						|
		Usages:     validUsages,
 | 
						|
		Request:    newCSRPEM(t),
 | 
						|
		SignerName: "example.com/something",
 | 
						|
	}
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		name        string
 | 
						|
		newCSR      *certificates.CertificateSigningRequest
 | 
						|
		oldCSR      *certificates.CertificateSigningRequest
 | 
						|
		versionErrs map[string][]string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:   "no-op",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "finalizer change with invalid status",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "finalizer change with duplicate and unknown usages",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: capi.CertificateSigningRequestSpec{
 | 
						|
				Usages:     []capi.KeyUsage{"unknown", "unknown"},
 | 
						|
				Request:    newCSRPEM(t),
 | 
						|
				SignerName: validSignerName,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: capi.CertificateSigningRequestSpec{
 | 
						|
				Usages:     []capi.KeyUsage{"unknown", "unknown"},
 | 
						|
				Request:    newCSRPEM(t),
 | 
						|
				SignerName: validSignerName,
 | 
						|
			}},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR:      &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "set valid certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: validCertificate,
 | 
						|
			}},
 | 
						|
			oldCSR:      &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "set invalid certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: invalidCertificateNoPEM,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1": {`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "reset certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: invalidCertificateNonCertificatePEM,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: invalidCertificateNoPEM,
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1": {`status.certificate: Forbidden: updates may not modify existing certificate content`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		for _, version := range []string{"v1", "v1beta1"} {
 | 
						|
			t.Run(tt.name+"_"+version, func(t *testing.T) {
 | 
						|
				gotErrs := sets.NewString()
 | 
						|
				for _, err := range ValidateCertificateSigningRequestStatusUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: certificates.GroupName, Version: version}) {
 | 
						|
					gotErrs.Insert(err.Error())
 | 
						|
				}
 | 
						|
				wantErrs := sets.NewString(tt.versionErrs[version]...)
 | 
						|
				for _, missing := range wantErrs.Difference(gotErrs).List() {
 | 
						|
					t.Errorf("missing expected error: %s", missing)
 | 
						|
				}
 | 
						|
				for _, unexpected := range gotErrs.Difference(wantErrs).List() {
 | 
						|
					t.Errorf("unexpected error: %s", unexpected)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
 | 
						|
	validUpdateMeta := validObjectMeta
 | 
						|
	validUpdateMeta.ResourceVersion = "1"
 | 
						|
 | 
						|
	validUpdateMetaWithFinalizers := validUpdateMeta
 | 
						|
	validUpdateMetaWithFinalizers.Finalizers = []string{"foo"}
 | 
						|
 | 
						|
	validSpec := capi.CertificateSigningRequestSpec{
 | 
						|
		Usages:     validUsages,
 | 
						|
		Request:    newCSRPEM(t),
 | 
						|
		SignerName: "example.com/something",
 | 
						|
	}
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		name        string
 | 
						|
		newCSR      *certificates.CertificateSigningRequest
 | 
						|
		oldCSR      *certificates.CertificateSigningRequest
 | 
						|
		versionErrs map[string][]string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:   "no-op",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "finalizer change with invalid certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Approved condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Denied condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "add Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			oldCSR:      &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:   "remove Failed condition",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
			}},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
				"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "set certificate",
 | 
						|
			newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
 | 
						|
				Certificate: validCertificate,
 | 
						|
			}},
 | 
						|
			oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
 | 
						|
			versionErrs: map[string][]string{
 | 
						|
				"v1":      {`status.certificate: Forbidden: updates may not set certificate content`},
 | 
						|
				"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		for _, version := range []string{"v1", "v1beta1"} {
 | 
						|
			t.Run(tt.name+"_"+version, func(t *testing.T) {
 | 
						|
				gotErrs := sets.NewString()
 | 
						|
				for _, err := range ValidateCertificateSigningRequestApprovalUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: certificates.GroupName, Version: version}) {
 | 
						|
					gotErrs.Insert(err.Error())
 | 
						|
				}
 | 
						|
				wantErrs := sets.NewString(tt.versionErrs[version]...)
 | 
						|
				for _, missing := range wantErrs.Difference(gotErrs).List() {
 | 
						|
					t.Errorf("missing expected error: %s", missing)
 | 
						|
				}
 | 
						|
				for _, unexpected := range gotErrs.Difference(wantErrs).List() {
 | 
						|
					t.Errorf("unexpected error: %s", unexpected)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Test_validateCertificateSigningRequestOptions verifies validation options are effective in tolerating specific aspects of CSRs
 | 
						|
func Test_validateCertificateSigningRequestOptions(t *testing.T) {
 | 
						|
	validSpec := capi.CertificateSigningRequestSpec{
 | 
						|
		Usages:     validUsages,
 | 
						|
		Request:    newCSRPEM(t),
 | 
						|
		SignerName: "example.com/something",
 | 
						|
	}
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		// testcase name
 | 
						|
		name string
 | 
						|
 | 
						|
		// csr being validated
 | 
						|
		csr *certificates.CertificateSigningRequest
 | 
						|
 | 
						|
		// options that allow the csr to pass validation
 | 
						|
		lenientOpts certificateValidationOptions
 | 
						|
 | 
						|
		// expected errors when validating strictly
 | 
						|
		strictErrs []string
 | 
						|
	}{
 | 
						|
		// valid strict cases
 | 
						|
		{
 | 
						|
			name: "no status",
 | 
						|
			csr:  &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "approved condition",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "denied condition",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "failed condition",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "approved+issued",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: validCertificate,
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
 | 
						|
		// legacy signer
 | 
						|
		{
 | 
						|
			name: "legacy signer",
 | 
						|
			csr: &capi.CertificateSigningRequest{
 | 
						|
				ObjectMeta: validObjectMeta,
 | 
						|
				Spec: func() capi.CertificateSigningRequestSpec {
 | 
						|
					specCopy := validSpec
 | 
						|
					specCopy.SignerName = capi.LegacyUnknownSignerName
 | 
						|
					return specCopy
 | 
						|
				}(),
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowLegacySignerName: true},
 | 
						|
			strictErrs:  []string{`spec.signerName: Invalid value: "kubernetes.io/legacy-unknown": the legacy signerName is not allowed via this API version`},
 | 
						|
		},
 | 
						|
 | 
						|
		// invalid condition cases
 | 
						|
		{
 | 
						|
			name: "empty condition type",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowEmptyConditionType: true},
 | 
						|
			strictErrs:  []string{`status.conditions[0].type: Required value`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "approved and denied",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}, {Type: capi.CertificateDenied, Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowBothApprovedAndDenied: true},
 | 
						|
			strictErrs:  []string{`status.conditions[1].type: Invalid value: "Denied": Approved and Denied conditions are mutually exclusive`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "duplicate condition",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}, {Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowDuplicateConditionTypes: true},
 | 
						|
			strictErrs:  []string{`status.conditions[1].type: Duplicate value: "Approved"`},
 | 
						|
		},
 | 
						|
 | 
						|
		// invalid allowArbitraryCertificate cases
 | 
						|
		{
 | 
						|
			name: "status.certificate, no PEM",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificateNoPEM,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "status.certificate, non-CERTIFICATE PEM",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificateNonCertificatePEM,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": only CERTIFICATE PEM blocks are allowed, found "CERTIFICATE1"`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "status.certificate, PEM headers",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificatePEMHeaders,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": no PEM block headers are permitted`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "status.certificate, non-base64 PEM",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificateNonBase64PEM,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "status.certificate, empty PEM block",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificateEmptyPEM,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": found CERTIFICATE PEM block containing 0 certificates`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "status.certificate, non-ASN1 data",
 | 
						|
			csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
 | 
						|
				Status: capi.CertificateSigningRequestStatus{
 | 
						|
					Conditions:  []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
 | 
						|
					Certificate: invalidCertificateNonASN1Data,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
 | 
						|
			strictErrs:  []string{`status.certificate: Invalid value: "<certificate data>": asn1: structure error: sequence tag mismatch`},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			// make sure the lenient options validate with no errors
 | 
						|
			for _, err := range validateCertificateSigningRequest(tt.csr, tt.lenientOpts) {
 | 
						|
				t.Errorf("unexpected error with lenient options: %s", err.Error())
 | 
						|
			}
 | 
						|
 | 
						|
			// make sure the strict options produce the expected errors
 | 
						|
			gotErrs := sets.NewString()
 | 
						|
			for _, err := range validateCertificateSigningRequest(tt.csr, certificateValidationOptions{}) {
 | 
						|
				gotErrs.Insert(err.Error())
 | 
						|
			}
 | 
						|
			wantErrs := sets.NewString(tt.strictErrs...)
 | 
						|
			for _, missing := range wantErrs.Difference(gotErrs).List() {
 | 
						|
				t.Errorf("missing expected strict error: %s", missing)
 | 
						|
			}
 | 
						|
			for _, unexpected := range gotErrs.Difference(wantErrs).List() {
 | 
						|
				t.Errorf("unexpected strict error: %s", unexpected)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	validCertificate = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
 | 
						|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
 | 
						|
MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
 | 
						|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
 | 
						|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
 | 
						|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
 | 
						|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
 | 
						|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
 | 
						|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Intermediate non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
MIIBqDCCAU6gAwIBAgIUfqZtjoFgczZ+oQZbEC/BDSS2J6wwCgYIKoZIzj0EAwIw
 | 
						|
EjEQMA4GA1UEAxMHUm9vdC1DQTAgFw0xNjEwMTEwNTA2MDBaGA8yMTE2MDkxNzA1
 | 
						|
MDYwMFowGjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMFkwEwYHKoZIzj0CAQYI
 | 
						|
KoZIzj0DAQcDQgAEyWHEMMCctJg8Xa5YWLqaCPbk3MjB+uvXac42JM9pj4k9jedD
 | 
						|
kpUJRkWIPzgJI8Zk/3cSzluUTixP6JBSDKtwwaN4MHYwDgYDVR0PAQH/BAQDAgGm
 | 
						|
MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
 | 
						|
FF+p0JcY31pz+mjNZnjv0Gum92vZMB8GA1UdIwQYMBaAFB7P6+i4/pfNjqZgJv/b
 | 
						|
dgA7Fe4tMAoGCCqGSM49BAMCA0gAMEUCIQCTT1YWQZaAqfQ2oBxzOkJE2BqLFxhz
 | 
						|
3smQlrZ5gCHddwIgcvT7puhYOzAgcvMn9+SZ1JOyZ7edODjshCVCRnuHK2c=
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
 | 
						|
	invalidCertificateNoPEM = []byte(`no PEM content`)
 | 
						|
 | 
						|
	invalidCertificateNonCertificatePEM = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE1-----
 | 
						|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
 | 
						|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
 | 
						|
MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
 | 
						|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
 | 
						|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
 | 
						|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
 | 
						|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
 | 
						|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
 | 
						|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
 | 
						|
-----END CERTIFICATE1-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
 | 
						|
	invalidCertificatePEMHeaders = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
Some-Header: Some-Value
 | 
						|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
 | 
						|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
 | 
						|
MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
 | 
						|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
 | 
						|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
 | 
						|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
 | 
						|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
 | 
						|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
 | 
						|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
 | 
						|
	invalidCertificateNonBase64PEM = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
 | 
						|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
 | 
						|
MTYwOTE3MDUwNjAwWjAUMRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
 | 
						|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
 | 
						|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
 | 
						|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
 | 
						|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
 | 
						|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
 | 
						|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1d?????????
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
 | 
						|
	invalidCertificateEmptyPEM = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
 | 
						|
	// first character is invalid
 | 
						|
	invalidCertificateNonASN1Data = []byte(`
 | 
						|
Leading non-PEM content
 | 
						|
-----BEGIN CERTIFICATE-----
 | 
						|
MIIBqDCCAU2gAwIBAgIUfbqeieihh/oERbfvRm38XvS/xHAwCgYIKoZIzj0EAwIw
 | 
						|
GjEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlLUNBMCAXDTE2MTAxMTA1MDYwMFoYDzIx
 | 
						|
MTYwOTE3MDUwNjAwWjAUNRIwEAYDVQQDEwlNeSBDbGllbnQwWTATBgcqhkjOPQIB
 | 
						|
BggqhkjOPQMBBwNCAARv6N4R/sjMR65iMFGNLN1GC/vd7WhDW6J4X/iAjkRLLnNb
 | 
						|
KbRG/AtOUZ+7upJ3BWIRKYbOabbQGQe2BbKFiap4o3UwczAOBgNVHQ8BAf8EBAMC
 | 
						|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU
 | 
						|
K/pZOWpNcYai6eHFpmJEeFpeQlEwHwYDVR0jBBgwFoAUX6nQlxjfWnP6aM1meO/Q
 | 
						|
a6b3a9kwCgYIKoZIzj0EAwIDSQAwRgIhAIWTKw/sjJITqeuNzJDAKU4xo1zL+xJ5
 | 
						|
MnVCuBwfwDXCAiEAw/1TA+CjPq9JC5ek1ifR0FybTURjeQqYkKpve1dveps=
 | 
						|
-----END CERTIFICATE-----
 | 
						|
Trailing non-PEM content
 | 
						|
`)
 | 
						|
)
 |