{"record":{"id":"ea518448435e8fd4","repo":"henrygd/beszel","slug":"invalid-release-digest-q","errorCode":null,"errorMessage":"invalid release digest %q","messagePattern":"invalid release digest %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ghupdate/checksum.go","lineNumber":16,"sourceCode":"package ghupdate\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc verifyAssetChecksum(path, digest string) error {\n\talgorithm, expectedHex, ok := strings.Cut(digest, \":\")\n\tif !ok || algorithm == \"\" || expectedHex == \"\" {\n\t\treturn fmt.Errorf(\"invalid release digest %q\", digest)\n\t}\n\tif !strings.EqualFold(algorithm, \"sha256\") {\n\t\treturn fmt.Errorf(\"unsupported release digest algorithm %q\", algorithm)\n\t}\n\n\texpected, err := hex.DecodeString(expectedHex)\n\tif err != nil || len(expected) != sha256.Size {\n\t\treturn fmt.Errorf(\"invalid SHA-256 release digest %q\", digest)\n\t}\n\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open release for checksum verification: %w\", err)\n\t}\n\tdefer file.Close()\n\n\thash := sha256.New()\n\tif _, err := io.Copy(hash, file); err != nil {","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/internal/ghupdate/checksum.go#L1-L34","documentation":"verifyAssetChecksum validates the digest string published on the GitHub release asset before hashing the downloaded file. The digest must be in the form \"<algorithm>:<hex-value>\" (e.g. \"sha256:abcd...\"); strings.Cut must split on \":\" and both halves must be non-empty. When the digest is missing the colon, empty, or only a bare hash without an algorithm prefix, the library rejects it outright rather than guessing.","triggerScenarios":"Calling ghupdate.Update (which calls update -> verifyAssetChecksum) when the release asset's Digest field from the GitHub/mirror API response is not in \"algorithm:hex\" form — empty string, plain hex hash with no \"sha256:\" prefix, trailing colon, or \"sha256:\" with no value.","commonSituations":"A custom release built and uploaded without GitHub's automatic checksum annotations; a proxy/mirror (e.g. gh.beszel.dev or a self-hosted mirror) that strips the digest field; running against a fork repo whose releases lack digests; an API mock or cached response that omitted the digest.","solutions":["Ensure releases are published through GitHub Releases so the API populates the asset digest field (GitHub generates sha256 digests automatically).","If using a mirror or proxy, make sure it forwards the release JSON unchanged, including the assets' digest fields.","Verify the digest string format is \"sha256:<64 hex chars>\" if constructing release metadata manually."],"exampleFix":"// before (manually crafted digest)\ndigest := \"3f2a...c9\"            // bare hex, no algorithm prefix\n// after\ndigest := \"sha256:3f2a...c9\"      // \"<algorithm>:<hex>\" form expected by verifyAssetChecksum","handlingStrategy":"validation","validationCode":"parts := strings.Split(asset.Digest, \":\")\nif len(parts) != 2 || parts[0] == \"\" || parts[1] == \"\" {\n    return errors.New(\"release asset digest is missing or malformed; cannot verify download\")\n}","typeGuard":null,"tryCatchPattern":"updated, err := ghupdate.Update(cfg)\nif err != nil && strings.Contains(err.Error(), \"invalid release digest\") {\n    log.Printf(\"release digest missing/malformed (%v); skipping integrity check — update aborted\", err)\n}","preventionTips":["Publish releases through GitHub Releases so digests are auto-generated.","Verify mirrors forward release JSON untouched.","Unit-test release metadata with a digest-format assertion before shipping."],"tags":["checksum","digest-format","self-update","github-release"],"backgroundTag":"invalid-checksum-digest-format","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}