{"record":{"id":"8ddc37182bc6db4e","repo":"kubernetes/kops","slug":"signer-certificate-is-required","errorCode":null,"errorMessage":"signer certificate is required","messagePattern":"signer certificate is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/azure/attest.go","lineNumber":336,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"parsing attested document expiration: %w\", err)\n\t\t}\n\t\tif expiresOn.Before(createdOn) {\n\t\t\treturn nil, fmt.Errorf(\"attested document expiresOn %s is before createdOn %s\", data.TimeStamp.ExpiresOn, data.TimeStamp.CreatedOn)\n\t\t}\n\t\tif expiresOn.Before(now.Add(-attestedDocumentMaxClockSkew)) {\n\t\t\treturn nil, fmt.Errorf(\"attested document expired at %s\", data.TimeStamp.ExpiresOn)\n\t\t}\n\t\tklog.V(4).Infof(\"Attested document not expired (expiresOn=%s)\", expiresOn.Format(time.RFC3339))\n\t}\n\n\treturn &data, nil\n}\n\n// intermediateCertPoolWithCaches performs a cached lookup against the supplied positive and\n// negative TTL caches, invoking fetch on a miss. Tests inject their own stores and fetchers.\nfunc intermediateCertPoolWithCaches(signer *x509.Certificate, fetch func(*x509.Certificate) (*x509.CertPool, error), positive, negative expirationcache.Store) (*x509.CertPool, error) {\n\tif signer == nil {\n\t\treturn nil, fmt.Errorf(\"signer certificate is required\")\n\t}\n\n\tkeyStr := intermediateCacheKeyForSigner(signer)\n\n\t// Positive cache wins over negative: a successful later fetch overwrites any stale negative entry,\n\t// which expires on its own shorter TTL.\n\tif obj, ok, _ := positive.GetByKey(keyStr); ok {\n\t\tklog.V(4).Infof(\"Intermediate certificate cache hit (positive) for signer issuer %q\", signer.Issuer)\n\t\treturn obj.(*intermediateCertCacheEntry).pool, nil\n\t}\n\tif _, ok, _ := negative.GetByKey(keyStr); ok {\n\t\tklog.V(4).Infof(\"Intermediate certificate cache hit (negative) for signer issuer %q\", signer.Issuer)\n\t\treturn nil, fmt.Errorf(\"intermediate certificate fetch recently failed for signer issuer %q (cached)\", signer.Issuer)\n\t}\n\n\tklog.V(2).Infof(\"Intermediate certificate cache miss for signer issuer %q\", signer.Issuer)\n\tpool, fetchErr := fetch(signer)\n\tentry := &intermediateCertCacheEntry{key: keyStr, pool: pool}","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/azure/attest.go#L318-L354","documentation":"intermediateCertPoolWithCaches builds an x509.CertPool of intermediate certificates keyed by the signer certificate, using positive/negative TTL caches. The signer certificate is the lookup key, so a nil signer makes the operation impossible; the function rejects it up front with this error rather than panicking later on a nil dereference.","triggerScenarios":"Calling intermediateCertPoolWithCaches (via intermediateCertPoolForSigner) with signer == nil, typically when an upstream parsing step failed silently or a nil certificate was propagated into the cache lookup path.","commonSituations":"A caller that skipped error handling when extracting the signer certificate from a chain; tests accidentally passing nil; refactoring that removed an earlier nil check.","solutions":["Ensure the caller extracts and validates the signer certificate before calling intermediateCertPoolForSigner","Return and handle errors from certificate parsing steps so a nil certificate never reaches the cache lookup","Add an assertion or guard in the calling code path to fail fast with a clearer message"],"exampleFix":"// before\npool, err := intermediateCertPoolForSigner(signerFromChain) // signer may be nil\n// after\nif signerFromChain == nil {\n    return nil, fmt.Errorf(\"no signer certificate available to build intermediate pool\")\n}\npool, err := intermediateCertPoolForSigner(signerFromChain)","handlingStrategy":"validation","validationCode":"if signer == nil {\n    return nil, fmt.Errorf(\"signer certificate missing before intermediate pool lookup\")\n}\npool, err := intermediateCertPoolForSigner(signer)","typeGuard":"func hasSigner(c *x509.Certificate) bool { return c != nil && len(c.Raw) > 0 }","tryCatchPattern":"pool, err := intermediateCertPoolForSigner(signer)\nif err != nil {\n    if strings.Contains(err.Error(), \"signer certificate is required\") {\n        return nil, fmt.Errorf(\"programming error: nil signer passed to cache lookup: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Check errors from every certificate-parsing call before using the result","Never propagate a nil certificate as a cache key","Add unit assertions that chain extraction always yields a non-nil signer on success"],"tags":["azure","tls","certificates","nil-argument"],"backgroundTag":"missing-signer-certificate","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}