{"record":{"id":"cbcbe7857443aac1","repo":"netbirdio/netbird","slug":"no-keys-found-in-bundle","errorCode":null,"errorMessage":"no keys found in bundle","messagePattern":"no keys found in bundle","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/updater/reposign/key.go","lineNumber":94,"sourceCode":"\n// PublicKey wraps a public Key with its Metadata\ntype PublicKey struct {\n\tKey      ed25519.PublicKey\n\tMetadata KeyMetadata\n}\n\nfunc parsePublicKeyBundle(bundle []byte, typeTag string) ([]PublicKey, error) {\n\tvar keys []PublicKey\n\tfor len(bundle) > 0 {\n\t\tkeyInfo, rest, err := parsePublicKey(bundle, typeTag)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkeys = append(keys, keyInfo)\n\t\tbundle = rest\n\t}\n\tif len(keys) == 0 {\n\t\treturn nil, errors.New(\"no keys found in bundle\")\n\t}\n\treturn keys, nil\n}\n\nfunc parsePublicKey(data []byte, typeTag string) (PublicKey, []byte, error) {\n\tb, rest := pem.Decode(data)\n\tif b == nil {\n\t\treturn PublicKey{}, nil, errors.New(\"failed to decode PEM data\")\n\t}\n\tif b.Type != typeTag {\n\t\treturn PublicKey{}, nil, fmt.Errorf(\"PEM type is %q, want %q\", b.Type, typeTag)\n\t}\n\n\t// Unmarshal JSON-embedded format\n\tvar pub PublicKey\n\tif err := json.Unmarshal(b.Bytes, &pub); err != nil {\n\t\treturn PublicKey{}, nil, fmt.Errorf(\"failed to unmarshal public key: %w\", err)\n\t}","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/updater/reposign/key.go#L76-L112","documentation":"Returned by parsePublicKeyBundle (client/internal/updater/reposign/key.go:94) when the loop over the bundle bytes produced zero keys, i.e. the input bundle was empty from the start (a non-empty bundle would have parsed at least one key or failed inside parsePublicKey). It is the fail-closed response to an artifact public-key file that contains no PEM key records.","triggerScenarios":"An empty (zero-length) public key bundle passed to parsePublicKeyBundle, typically from an empty or truncated download of the artifact keys file; a caller passing a nil/empty data slice after upstream verification steps.","commonSituations":"Update metadata points at an empty keys file; partial download or filesystem full during cache write; CI tooling generating an empty bundle during signing pipeline misconfiguration.","solutions":["Re-download the artifact keys file and check it is non-empty before parsing.","Validate the file size / first bytes (PEM header) before handing it to the verifier.","If you produce the bundle: ensure the signing pipeline actually embedded at least one key."],"exampleFix":"// before\nkeys, err := reposign.ParsePublicKeyBundle(data) // data may be empty\n\n// after: guard emptiness early with context\nif len(data) == 0 {\n    return fmt.Errorf(\"artifact key bundle is empty (download truncated?)\")\n}\nkeys, err := reposign.ParsePublicKeyBundle(data)","handlingStrategy":"try-catch","validationCode":"// Cheap pre-check: a usable bundle is non-empty and starts with a PEM block.\nif len(bundle) == 0 {\n    return fmt.Errorf(\"empty artifact key bundle\")\n}\nif _, rest := pem.Decode(bundle); len(rest) == len(bundle) {\n    return fmt.Errorf(\"bundle is not PEM-encoded\")\n}","typeGuard":null,"tryCatchPattern":"keys, err := reposign.ParsePublicKeyBundle(bundle)\nif err != nil {\n    return fmt.Errorf(\"parse artifact key bundle (re-download it): %w\", err)\n}","preventionTips":["Check content length and PEM header before parsing downloaded key files.","Treat parse failures of security metadata as fatal for the update flow."],"tags":["go","netbird","security","parsing","update","keys"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}